1 /*
2 |  Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users sourceforge net>
3 |  Part of the gtkpod project.
4 |
5 |  URL: http://www.gtkpod.org/
6 |  URL: http://gtkpod.sourceforge.net/
7 |
8 |  Much of the code in this file has originally been ported from the
9 |  perl script "mktunes.pl" (part of the gnupod-tools collection)
10 |  written by Adrian Ulrich <pab at blinkenlights.ch>:
11 |
12 |  gnupod-tools: http://www.blinkenlights.ch/cgi-bin/fm.pl?get=ipod
13 |
14 |  The code contained in this file is free software; you can redistribute
15 |  it and/or modify it under the terms of the GNU Lesser General Public
16 |  License as published by the Free Software Foundation; either version
17 |  2.1 of the License, or (at your option) any later version.
18 |
19 |  This file 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 GNU
22 |  Lesser General Public License for more details.
23 |
24 |  You should have received a copy of the GNU Lesser General Public
25 |  License along with this code; if not, write to the Free Software
26 |  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 |  USA
28 |
29 |  iTunes and iPod are trademarks of Apple
30 |
31 |  This product is not supported/written/published by Apple!
32 |
33 |  $Id$
34 */
35 
36 /* Some notes on how to use the functions in this file:
37 
38 
39    *** Reading the iTunesDB ***
40 
41    Itdb_iTunesDB *itunesdb_parse (gchar *path); /+ path to mountpoint +/
42    will read an Itdb_iTunesDB and pass the data over to your program.
43 
44    The information given in the "Play Counts" file is also read if
45    available and the playcounts, star rating and the time last played
46    is updated.
47 
48    Itdb_iTunesDB is a structure containing a GList for the tracks and a
49    GList for the playlists.
50 
51    For each track these fields are set as follows:
52 
53    "transferred" will be set to TRUE because all tracks read from a
54    Itdb_iTunesDB are obviously (or hopefully) already transferred to the
55    iPod.
56 
57    "recent_playcount" is for information only (it will allow to
58    generate playlists with tracks played since the last time) and will
59    not be stored to the iPod.
60 
61    The master playlist is guaranteed to be the first playlist, and
62    this must not be changed by your code.
63 
64 
65    *** Writing the Itdb_iTunesDB ***
66 
67    gboolean itunesdb_write (gchar *path, Itdb_iTunesDB *itb)
68    /+ @path to mountpoint, itb to @write +/
69    will write an updated version of the Itdb_iTunesDB.
70 
71    The "Play Counts" file is renamed to "Play Counts.bak" if it exists
72    to avoid reading it multiple times.
73 
74    Please note that non-transferred tracks are not automatically
75    transferred to the iPod. A function
76 
77    gboolean itunesdb_copy_track_to_ipod (gchar *path, Itdb_Track *track, gchar *pcfile)
78 
79    is provided to help you do that, however.
80 
81    The following functions most likely will be useful:
82 
83    Itdb_Track *itunesdb_new_track (void);
84    Use itunesdb_new_track() to get an "initialized" track structure
85    (the "unknowns" are initialized with reasonable values).
86 
87    gboolean itunesdb_cp (gchar *from_file, gchar *to_file);
88    void itunesdb_convert_filename_fs2ipod(gchar *ipod_file);
89    void itunesdb_convert_filename_ipod2fs(gchar *ipod_file);
90 
91    void itunesdb_rename_files (const gchar *dirname);
92 
93    (Renames/removes some files on the iPod (Playcounts, OTG
94    semaphore). Needs to be called if you write the Itdb_iTunesDB not
95    directly to the iPod but to some other location and then manually
96    copy the file from there to the iPod. That's much faster in the
97    case of using an iPod mounted in sync'ed mode.)
98 
99    Jorg Schuler, 29.12.2004 */
100 
101 
102 /* call itdb_parse () to read the Itdb_iTunesDB  */
103 /* call itdb_write () to write the Itdb_iTunesDB */
104 
105 
106 #ifdef HAVE_CONFIG_H
107 #  include <config.h>
108 #endif
109 
110 #include "db-artwork-parser.h"
111 #include "itdb_device.h"
112 #include "itdb_private.h"
113 #include "itdb_zlib.h"
114 #include "itdb_plist.h"
115 
116 #include <errno.h>
117 #include <fcntl.h>
118 #include <glib-object.h>
119 #include <glib/gi18n-lib.h>
120 #include <glib/gstdio.h>
121 #include <glib.h>
122 #include <stdio.h>
123 #include <string.h>
124 #include <sys/types.h>
125 #include <time.h>
126 #ifdef HAVE_UNISTD_H
127 #include <unistd.h>
128 #endif
129 
130 #define ITUNESDB_DEBUG 0
131 #define ITUNESDB_MHIT_DEBUG 0
132 
133 #define ITUNESDB_COPYBLK (1024*1024*4)      /* blocksize for cp () */
134 
135 
136 /* NOTE for developers:
137 
138    Sometimes new MHOD string fields are added by Apple in the
139    iTunesDB. In that case you need to modify the code in the following
140    places:
141 
142    itdb_itunesdb.c:
143    - enum MHOD_ID
144    - get_mhod(): inside the switch() statement. Currently no compiler
145      warning is given.
146    - get_mhod_string(): inside the switch() statement. A compiler warning
147      will help you find it.
148    - get_playlist(): inside the switch() statement. A compiler warning
149      will help you find it.
150    - get_mhit(): inside the switch() statement. A compiler warning
151      will help you find it.
152    - mk_mhod(): inside the switch() statement. A compiler warning
153      will help you find it.
154    - write_mhsd_tracks(): inside the for() loop.
155 
156    itdb_track.c:
157    - itdb_track_free()
158    - itdb_track_duplicate()
159 
160    itdb_playlists.c:
161    Analogous to itdb_track.c in case the string is part of the playlist
162    description.
163 */
164 
165 /* Note: some of the comments for the MHOD_IDs are copied verbatim
166  * from http://ipodlinux.org/ITunesDB */
167 enum MHOD_ID {
168   MHOD_ID_TITLE = 1,
169   MHOD_ID_PATH = 2,   /* file path on iPod (special format) */
170   MHOD_ID_ALBUM = 3,
171   MHOD_ID_ARTIST = 4,
172   MHOD_ID_GENRE = 5,
173   MHOD_ID_FILETYPE = 6,
174 /* MHOD_ID_EQSETTING = 7, */
175   MHOD_ID_COMMENT = 8,
176   /* Category - This is the category ("Technology", "Music", etc.) where
177      the podcast was located. Introduced in db version 0x0d. */
178   MHOD_ID_CATEGORY = 9,
179   MHOD_ID_COMPOSER = 12,
180   MHOD_ID_GROUPING = 13,
181   /* Description text (such as podcast show notes). Accessible by
182      wselecting the center button on the iPod, where this string is
183      displayed along with the song title, date, and
184      timestamp. Introduced in db version 0x0d. */
185   MHOD_ID_DESCRIPTION = 14,
186   /* Podcast Enclosure URL. Note: this is either a UTF-8 or ASCII
187      encoded string (NOT UTF-16). Also, there is no mhod::length value
188      for this type. Introduced in db version 0x0d.  */
189   MHOD_ID_PODCASTURL = 15,
190   /* Podcast RSS URL. Note: this is either a UTF-8 or ASCII encoded
191      string (NOT UTF-16). Also, there is no mhod::length value for this
192      type. Introduced in db version 0x0d. */
193   MHOD_ID_PODCASTRSS = 16,
194   /* Chapter data. This is a m4a-style entry that is used to display
195      subsongs within a mhit. Introduced in db version 0x0d. */
196   MHOD_ID_CHAPTERDATA = 17,
197   /* Subtitle (usually the same as Description). Introduced in db
198      version 0x0d. */
199   MHOD_ID_SUBTITLE = 18,
200   /* Show (for TV Shows only). Introduced in db version 0x0d? */
201   MHOD_ID_TVSHOW = 19,
202   /* Episode # (for TV Shows only). Introduced in db version 0x0d? */
203   MHOD_ID_TVEPISODE = 20,
204   /* TV Network (for TV Shows only). Introduced in db version 0x0d? */
205   MHOD_ID_TVNETWORK = 21,
206   /* Album Artist. Introduced in db version 0x13? */
207   MHOD_ID_ALBUMARTIST = 22,
208   /* Sort key for artist. */
209   MHOD_ID_SORT_ARTIST = 23,
210   /* Appears to be a list of keywords pertaining to a track. Introduced
211      in db version 0x13? */
212   MHOD_ID_KEYWORDS = 24,
213   /* more sort keys, taking precedence over the standard entries if
214      present */
215   MHOD_ID_SORT_TITLE = 27,
216   MHOD_ID_SORT_ALBUM = 28,
217   MHOD_ID_SORT_ALBUMARTIST = 29,
218   MHOD_ID_SORT_COMPOSER = 30,
219   MHOD_ID_SORT_TVSHOW = 31,
220   MHOD_ID_SPLPREF = 50,  /* settings for smart playlist */
221   MHOD_ID_SPLRULES = 51, /* rules for smart playlist     */
222   MHOD_ID_LIBPLAYLISTINDEX = 52,  /* Library Playlist Index */
223   MHOD_ID_LIBPLAYLISTJUMPTABLE=53,
224   MHOD_ID_PLAYLIST = 100,
225   MHOD_ID_ALBUM_ALBUM = 200, /* MHODs for the MHIAs */
226   MHOD_ID_ALBUM_ARTIST = 201,
227   MHOD_ID_ALBUM_SORT_ARTIST = 202,
228   MHOD_ID_ALBUM_ARTIST_MHII = 300,
229 };
230 
231 
232 /* Used as first iPod ID when renumbering (re-assigning) the iPod
233    (track) IDs before writing out the iTunesDB. */
234 static const gint FIRST_IPOD_ID=52;
235 
236 
237 enum MHOD52_SORTTYPE {
238     MHOD52_SORTTYPE_TITLE    = 0x03,
239     MHOD52_SORTTYPE_ALBUM    = 0x04,
240     MHOD52_SORTTYPE_ARTIST   = 0x05,
241     MHOD52_SORTTYPE_GENRE    = 0x07,
242     MHOD52_SORTTYPE_COMPOSER = 0x12/*,
243     MHOD52_SORTTYPE_TVSHOW   = 0x1d,
244     MHOD52_SORTTYPE_TVSEASON = 0x1e,
245     MHOD52_SORTTYPE_TVEPISODE= 0x1f*/
246 };
247 
248 struct mhod52track
249 {
250     gchar *album;
251     gchar *title;
252     gchar *artist;
253     gchar *genre;
254     gchar *composer;
255     gint track_nr;
256     gint cd_nr;
257     gint index;
258     gint numtracks;
259     gunichar2 letter_album;
260     gunichar2 letter_title;
261     gunichar2 letter_artist;
262     gunichar2 letter_genre;
263     gunichar2 letter_composer;
264 };
265 
266 struct mhod53_entry
267 {
268     gunichar2 letter;
269     gint32 start;
270     gint32 count;
271 };
272 
273 struct _MHODData
274 {
275     gboolean valid;
276     gint32 type;
277     union
278     {
279 	gint32 track_pos;
280 	gchar *string;
281 	Itdb_Chapterdata *chapterdata;
282 	Itdb_SPLPref *splpref;
283 	Itdb_SPLRules *splrules;
284 	GList *mhod52coltracks;
285     } data;
286     enum MHOD52_SORTTYPE mhod52sorttype;
287     GList *mhod53_list;
288 };
289 
290 typedef struct _MHODData MHODData;
291 
292 struct _PosEntry {
293     guint32 trackid;
294     gint32 track_pos;
295 };
296 
297 typedef struct _PosEntry PosEntry;
298 
299 /* Declarations */
300 static gboolean itdb_create_directories (Itdb_Device *device, GError **error);
301 
302 /* ID for error domain */
itdb_file_error_quark(void)303 GQuark itdb_file_error_quark (void)
304 {
305     static GQuark q = 0;
306     if (q == 0)
307 	q = g_quark_from_static_string ("itdb-file-error-quark");
308     return q;
309 }
310 
311 
312 static guint16 raw_get16lint (FContents *cts, glong seek);
313 static guint32 raw_get24lint (FContents *cts, glong seek);
314 static guint32 raw_get32lint (FContents *cts, glong seek);
315 static guint64 raw_get64lint (FContents *cts, glong seek);
316 static float raw_get32lfloat (FContents *cts, glong seek);
317 static guint16 raw_get16bint (FContents *cts, glong seek);
318 static guint32 raw_get24bint (FContents *cts, glong seek);
319 static guint32 raw_get32bint (FContents *cts, glong seek);
320 static guint64 raw_get64bint (FContents *cts, glong seek);
321 static float raw_get32bfloat (FContents *cts, glong seek);
322 
323 static gboolean is_shuffle_2g (Itdb_Device *device);
324 
325 static const ByteReader LITTLE_ENDIAN_READER =
326 {
327     raw_get16lint, raw_get24lint, raw_get32lint, raw_get64lint, raw_get32lfloat
328 };
329 static const ByteReader BIG_ENDIAN_READER =
330 {
331     raw_get16bint, raw_get24bint, raw_get32bint, raw_get64bint, raw_get32bfloat
332 };
333 
fcontents_set_reversed(FContents * cts,gboolean reversed)334 static void fcontents_set_reversed(FContents *cts, gboolean reversed)
335 {
336     cts->reversed = reversed;
337     if (!reversed)
338     {
339 	memcpy(&cts->le_reader, &LITTLE_ENDIAN_READER, sizeof (ByteReader));
340 	memcpy(&cts->be_reader, &BIG_ENDIAN_READER, sizeof (ByteReader));
341     }
342     else
343     {
344 	memcpy(&cts->le_reader, &BIG_ENDIAN_READER, sizeof (ByteReader));
345 	memcpy(&cts->be_reader, &LITTLE_ENDIAN_READER, sizeof (ByteReader));
346     }
347 }
348 
349 /* Read the contents of @filename and return a FContents
350    struct. Returns NULL in case of error and @error is set
351    accordingly */
fcontents_read(const gchar * fname,GError ** error)352 static FContents *fcontents_read (const gchar *fname, GError **error)
353 {
354     FContents *cts;
355 
356     g_return_val_if_fail (fname, NULL);
357 
358     cts = g_new0 (FContents, 1);
359     fcontents_set_reversed (cts, FALSE);
360 
361     if (g_file_get_contents (fname, &cts->contents, &cts->length, error))
362     {
363 	cts->filename = g_strdup (fname);
364     }
365     else
366     {
367 	g_free (cts);
368 	cts = NULL;
369     }
370     return cts;
371 }
372 
373 
374 /* Frees the memory taken by a FContents structure. NULL pointer will
375  * be ignored */
fcontents_free(FContents * cts)376 static void fcontents_free (FContents *cts)
377 {
378     if (cts)
379     {
380 	g_free (cts->filename);
381 	g_free (cts->contents);
382 	/* must not g_error_free (cts->error) because the error was
383 	   propagated -> might free the error twice */
384 	g_free (cts);
385     }
386 }
387 
itdb_fsync(void)388 static void itdb_fsync (void)
389 {
390 #ifndef WIN32
391     sync();
392 #endif
393 }
394 
395 /* There seems to be a problem with some distributions (kernel
396    versions or whatever -- even identical version numbers don't don't
397    show identical behaviour...): even though vfat is supposed to be
398    case insensitive, a difference is made between upper and lower case
399    under some special circumstances. As in "/iPod_Control/Music/F00"
400    and "/iPod_Control/Music/f00 "... If the former filename does not
401    exist, we try to find an existing case insensitive match for each
402    component of the filename.  If we can find such a match, we return
403    it.  Otherwise, we return NULL.*/
404 
405 /**
406  * itdb_resolve_path:
407  * @root: in local encoding
408  * @components: in utf8
409  *
410  * Resolve the path to a track on the iPod
411  *
412  * We start by assuming that the iPod mount point exists.  Then, for
413  * each component c of @track->ipod_path, we try to find an entry d in
414  * good_path that is case-insensitively equal to c.  If we find d, we
415  * append d to good_path and make the result the new good_path.
416  * Otherwise, we quit and return NULL.
417  *
418  * Returns: path to track on the iPod or NULL.
419  */
itdb_resolve_path(const gchar * root,const gchar * const * components)420 gchar * itdb_resolve_path (const gchar *root,
421 			   const gchar * const * components)
422 {
423   gchar *good_path;
424   guint32 i;
425 
426   if (!root) return NULL;
427   good_path = g_strdup (root);
428 
429   for(i = 0 ; components[i] ; i++) {
430     GDir *cur_dir;
431     gchar *component_as_filename;
432     gchar *test_path;
433     gchar *component_stdcase;
434     const gchar *dir_file=NULL;
435 
436     /* skip empty components */
437     if (strlen (components[i]) == 0) continue;
438     component_as_filename =
439       g_filename_from_utf8(components[i],-1,NULL,NULL,NULL);
440     test_path = g_build_filename(good_path,component_as_filename,NULL);
441     g_free(component_as_filename);
442     if(g_file_test(test_path,G_FILE_TEST_EXISTS)) {
443       /* This component does not require fixup */
444       g_free(good_path);
445       good_path = test_path;
446       continue;
447     }
448     g_free(test_path);
449     component_stdcase = g_utf8_casefold(components[i],-1);
450     /* Case insensitively compare the current component with each entry
451      * in the current directory. */
452 
453     cur_dir = g_dir_open(good_path,0,NULL);
454     if (cur_dir) while ((dir_file = g_dir_read_name(cur_dir)))
455     {
456 	gchar *file_utf8;
457 	gchar *file_stdcase;
458 	gboolean found;
459 	gchar *new_good_path;
460 
461 	file_utf8 = g_filename_to_utf8(dir_file,-1,NULL,NULL,NULL);
462 	if (file_utf8 == NULL) {
463 	    continue;
464 	}
465 	file_stdcase = g_utf8_casefold(file_utf8,-1);
466 	g_free(file_utf8);
467 	found = !g_utf8_collate(file_stdcase,component_stdcase);
468 	g_free(file_stdcase);
469 	if(!found)
470 	{
471 	    /* This is not the matching entry */
472 	    continue;
473 	}
474 
475 	new_good_path = g_build_filename(good_path,dir_file,NULL);
476 	g_free(good_path);
477 	good_path= new_good_path;
478 	/* This is the matching entry, so we can stop searching */
479 	break;
480     }
481 
482     if(!dir_file) {
483       /* We never found a matching entry */
484       g_free(good_path);
485       good_path = NULL;
486     }
487 
488     g_free(component_stdcase);
489     if (cur_dir) g_dir_close(cur_dir);
490     if(!good_path || !g_file_test(good_path,G_FILE_TEST_EXISTS))
491       break; /* We couldn't fix this component, so don't try later ones */
492   }
493 
494   if(good_path && g_file_test(good_path,G_FILE_TEST_EXISTS))
495     return good_path;
496 
497   g_free (good_path);
498   return NULL;
499 }
500 
501 
502 /* Check if the @seek with length @len is legal or out of
503  * range. Returns TRUE if legal and FALSE when it is out of range, in
504  * which case cts->error is set as well. */
check_seek(FContents * cts,glong seek,glong len)505 static gboolean check_seek (FContents *cts, glong seek, glong len)
506 {
507     g_return_val_if_fail (cts, FALSE);
508     g_return_val_if_fail (cts->contents, FALSE);
509 
510     if ((seek+len <= cts->length) && (seek >=0))
511     {
512 	return TRUE;
513     }
514     else
515     {
516 	g_return_val_if_fail (cts->filename, FALSE);
517 	g_set_error (&cts->error,
518 		     ITDB_FILE_ERROR,
519 		     ITDB_FILE_ERROR_SEEK,
520 		     _("Illegal seek to offset %ld (length %ld) in file '%s'."),
521 		     seek, len, cts->filename);
522 	return FALSE;
523     }
524 }
525 
526 
527 /* Copies @len bytes from position @seek in @cts->contents to
528    @data. Returns FALSE on error and sets cts->error accordingly. */
seek_get_n_bytes(FContents * cts,gchar * data,glong seek,glong len)529 static gboolean seek_get_n_bytes (FContents *cts, gchar *data,
530 			      glong seek, glong len)
531 {
532   if (check_seek (cts, seek, len))
533   {
534       memcpy (data, &cts->contents[seek], len);
535       return TRUE;
536   }
537   return FALSE;
538 }
539 
540 /* Compare @n bytes of @cts->contents starting at @seek and
541  * @data. Returns TRUE if equal, FALSE if not. Also returns FALSE on
542  * error, so you must check cts->error */
cmp_n_bytes_seek(FContents * cts,const gchar * data,glong seek,glong len)543 static gboolean cmp_n_bytes_seek (FContents *cts, const gchar *data,
544 				  glong seek, glong len)
545 {
546     if (check_seek (cts, seek, len))
547     {
548 	gint i;
549 	for (i=0; i<len; ++i)
550 	{
551 	    if (cts->contents[seek+i] != data[i]) return FALSE;
552 	}
553 	return TRUE;
554     }
555     return FALSE;
556 }
557 
558 
559 /* Compare 4 bytes of @header with 4 bytes at @seek taking into
560  * consideration the status of cts->reversed */
check_header_seek(FContents * cts,const gchar * data,glong seek)561 static gboolean check_header_seek (FContents *cts, const gchar *data,
562 				   glong seek)
563 {
564     gchar rdata[4];
565     gint i, offset, sign;
566 
567     g_return_val_if_fail (cts, FALSE);
568     g_return_val_if_fail (data, FALSE);
569     /* reverse data for compare if necessary */
570     if (cts->reversed)
571     {
572 	offset = 3;
573 	sign = -1;
574     }
575     else
576     {
577 	offset = 0;
578 	sign = 1;
579     }
580     for (i=0; i<4; ++i)
581     {
582 	    rdata[i] = data[offset + sign*i];
583     }
584 
585     return cmp_n_bytes_seek (cts, rdata, seek, 4);
586 }
587 
588 
589 /* ------------------------------------------------------------
590    Not Endian Dependent
591    ------------------------------------------------------------ */
592 
593 /* Returns the 1-byte number stored at position @seek. On error the
594  * GError in @cts is set. */
get8int(FContents * cts,glong seek)595 static inline guint8 get8int (FContents *cts, glong seek)
596 {
597     guint8 n=0;
598 
599     if (check_seek (cts, seek, 1))
600     {
601 	n = cts->contents[seek];
602     }
603     return n;
604 }
605 
606 
607 /* ------------------------------------------------------------
608    Little Endian
609    ------------------------------------------------------------ */
610 
611 /* Get the 2-byte-number stored at position "seek" in little endian
612    encoding. On error the GError in @cts is set. */
raw_get16lint(FContents * cts,glong seek)613 static guint16 raw_get16lint (FContents *cts, glong seek)
614 {
615     guint16 n=0;
616 
617     if (check_seek (cts, seek, 2))
618     {
619 	memcpy (&n, &cts->contents[seek], 2);
620 	n = GUINT16_FROM_LE (n);
621     }
622     return n;
623 }
624 
625 /* Get the 3-byte-number stored at position "seek" in little endian
626    encoding. On error the GError in @cts is set. */
raw_get24lint(FContents * cts,glong seek)627 static guint32 raw_get24lint (FContents *cts, glong seek)
628 {
629     guint32 n=0;
630 
631     if (check_seek (cts, seek, 3))
632     {
633 	n = ((guint32)get8int (cts, seek+0)) +
634 	    (((guint32)get8int (cts, seek+1)) >> 8) +
635 	    (((guint32)get8int (cts, seek+2)) >> 16);
636     }
637     return n;
638 }
639 
640 /* Get the 4-byte-number stored at position "seek" in little endian
641    encoding. On error the GError in @cts is set. */
raw_get32lint(FContents * cts,glong seek)642 static guint32 raw_get32lint (FContents *cts, glong seek)
643 {
644     guint32 n=0;
645 
646     if (check_seek (cts, seek, 4))
647     {
648 	memcpy (&n, &cts->contents[seek], 4);
649 	n = GUINT32_FROM_LE (n);
650     }
651     return n;
652 }
653 
654 /* Get 4 byte floating number */
raw_get32lfloat(FContents * cts,glong seek)655 static float raw_get32lfloat (FContents *cts, glong seek)
656 {
657     union
658     {
659 	guint32 i;
660 	float   f;
661     } flt;
662 
663     g_return_val_if_fail (sizeof (float) == 4, 0);
664 
665     flt.i = raw_get32lint (cts, seek);
666 
667     return flt.f;
668 }
669 
670 
671 /* Get the 8-byte-number stored at position "seek" in little endian
672    encoding. On error the GError in @cts is set. */
raw_get64lint(FContents * cts,glong seek)673 static guint64 raw_get64lint (FContents *cts, glong seek)
674 {
675     guint64 n=0;
676 
677     if (check_seek (cts, seek, 8))
678     {
679 	memcpy (&n, &cts->contents[seek], 8);
680 	n = GUINT64_FROM_LE (n);
681     }
682     return n;
683 }
684 
685 
686 /* ------------------------------------------------------------
687    Big Endian
688    ------------------------------------------------------------ */
689 
690 /* Get the 2-byte-number stored at position "seek" in little endian
691    encoding. On error the GError in @cts is set. */
raw_get16bint(FContents * cts,glong seek)692 static guint16 raw_get16bint (FContents *cts, glong seek)
693 {
694     guint16 n=0;
695 
696     if (check_seek (cts, seek, 2))
697     {
698 	memcpy (&n, &cts->contents[seek], 2);
699 	n = GUINT16_FROM_BE (n);
700     }
701     return n;
702 }
703 
704 /* Get the 3-byte-number stored at position "seek" in big endian
705    encoding. On error the GError in @cts is set. */
raw_get24bint(FContents * cts,glong seek)706 static guint32 raw_get24bint (FContents *cts, glong seek)
707 {
708     guint32 n=0;
709 
710     if (check_seek (cts, seek, 3))
711     {
712 	n = ((guint32)get8int (cts, seek+2)) +
713 	    (((guint32)get8int (cts, seek+1)) >> 8) +
714 	    (((guint32)get8int (cts, seek+0)) >> 16);
715     }
716     return n;
717 }
718 
719 /* Get the 4-byte-number stored at position "seek" in big endian
720    encoding. On error the GError in @cts is set. */
raw_get32bint(FContents * cts,glong seek)721 static guint32 raw_get32bint (FContents *cts, glong seek)
722 {
723     guint32 n=0;
724 
725     if (check_seek (cts, seek, 4))
726     {
727 	memcpy (&n, &cts->contents[seek], 4);
728 	n = GUINT32_FROM_BE (n);
729     }
730     return n;
731 }
732 
733 /* Get 4 byte floating number */
raw_get32bfloat(FContents * cts,glong seek)734 static float raw_get32bfloat (FContents *cts, glong seek)
735 {
736     union
737     {
738 	guint32 i;
739 	float   f;
740     } flt;
741 
742     g_return_val_if_fail (sizeof (float) == 4, 0);
743 
744     flt.i = raw_get32bint (cts, seek);
745 
746     return flt.f;
747 }
748 
749 /* Get the 8-byte-number stored at position "seek" in big endian
750    encoding. On error the GError in @cts is set. */
raw_get64bint(FContents * cts,glong seek)751 static guint64 raw_get64bint (FContents *cts, glong seek)
752 {
753     guint64 n=0;
754 
755     if (check_seek (cts, seek, 8))
756     {
757 	memcpy (&n, &cts->contents[seek], 8);
758 	n = GUINT64_FROM_BE (n);
759     }
760     return n;
761 }
762 
763 
764 /* ------------------------------------------------------------
765    Little Endian
766    ------------------------------------------------------------ */
767 
get16lint(FContents * cts,glong seek)768 static inline guint16 get16lint (FContents *cts, glong seek)
769 {
770     return cts->le_reader.get16int (cts, seek);
771 }
772 
get24lint(FContents * cts,glong seek)773 static inline guint32 get24lint (FContents *cts, glong seek)
774 {
775     return cts->le_reader.get24int (cts, seek);
776 }
777 #if 0
778 static inline guint32 get24bint (FContents *cts, glong seek)
779 {
780     return cts->be_reader.get24int (cts, seek);
781 }
782 #endif
get32lint(FContents * cts,glong seek)783 static inline guint32 get32lint (FContents *cts, glong seek)
784 {
785     return cts->le_reader.get32int (cts, seek);
786 }
787 
get32lfloat(FContents * cts,glong seek)788 static inline float get32lfloat (FContents *cts, glong seek)
789 {
790     return cts->le_reader.get32float (cts, seek);
791 }
792 
get64lint(FContents * cts,glong seek)793 static inline guint64 get64lint (FContents *cts, glong seek)
794 {
795     return cts->le_reader.get64int (cts, seek);
796 }
797 
798 
799 
800 /* ------------------------------------------------------------
801    Big Endian
802    ------------------------------------------------------------ */
803 
get16bint(FContents * cts,glong seek)804 static inline guint16 get16bint (FContents *cts, glong seek)
805 {
806     return cts->be_reader.get16int (cts, seek);
807 }
808 
get32bint(FContents * cts,glong seek)809 static inline guint32 get32bint (FContents *cts, glong seek)
810 {
811     return cts->be_reader.get32int (cts, seek);
812 }
813 
814 #if 0
815 static inline float get32bfloat (FContents *cts, glong seek)
816 {
817     return cts->be_reader.get32float (cts, seek);
818 }
819 #endif
820 
get64bint(FContents * cts,glong seek)821 static inline guint64 get64bint (FContents *cts, glong seek)
822 {
823     return cts->be_reader.get64int (cts, seek);
824 }
825 
826 /* Try to convert from UTF-16 to UTF-8 and handle partial characters
827  * at the end of the string */
utf16_to_utf8_with_partial(gunichar2 * entry_utf16)828 static char *utf16_to_utf8_with_partial (gunichar2 *entry_utf16)
829 {
830     GError *error = NULL;
831     char *entry_utf8;
832     glong items_read;
833 
834     entry_utf8 = g_utf16_to_utf8 (entry_utf16, -1, &items_read, NULL, &error);
835     if (entry_utf8 == NULL) {
836         if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT)) {
837             entry_utf8 = g_utf16_to_utf8 (entry_utf16, items_read, NULL, NULL, NULL);
838         }
839         g_error_free (error);
840     }
841 
842     return entry_utf8;
843 }
844 
845 /* Fix little endian UTF16 String to correct byteorder if necessary
846  * (all strings in the Itdb_iTunesDB are little endian except for the ones
847  * in smart playlists). */
fixup_little_utf16(gunichar2 * utf16_string)848 static gunichar2 *fixup_little_utf16 (gunichar2 *utf16_string)
849 {
850 #   if (G_BYTE_ORDER == G_BIG_ENDIAN)
851     gint32 i;
852     if (utf16_string)
853     {
854 	for(i=0; utf16_string[i]; i++)
855 	{
856 	    utf16_string[i] = GUINT16_SWAP_LE_BE (utf16_string[i]);
857 	}
858     }
859 #   endif
860     return utf16_string;
861 }
862 
863 /* Fix big endian UTF16 String to correct byteorder if necessary (only
864  * strings in smart playlists and chapter data are big endian) */
fixup_big_utf16(gunichar2 * utf16_string)865 static gunichar2 *fixup_big_utf16 (gunichar2 *utf16_string)
866 {
867 #   if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
868     gint32 i;
869     if (utf16_string)
870     {
871 	for(i=0; utf16_string[i]; i++)
872 	{
873 	    utf16_string[i] = GUINT16_SWAP_LE_BE (utf16_string[i]);
874 	}
875     }
876 #   endif
877     return utf16_string;
878 }
879 
880 
881 #define CHECK_ERROR(imp, val) if (cts->error) { g_propagate_error (&imp->error, cts->error); return (val); }
882 
883 
884 /* get next playcount, that is the first entry of GList
885  * playcounts. This entry is removed from the list. You must free the
886  * return value after use */
playcount_take_next(FImport * fimp)887 static struct playcount *playcount_take_next (FImport *fimp)
888 {
889     struct playcount *playcount;
890     g_return_val_if_fail (fimp, NULL);
891 
892     playcount = g_list_nth_data (fimp->playcounts, 0);
893 
894     if (playcount)
895 	fimp->playcounts = g_list_remove (fimp->playcounts, playcount);
896     return playcount;
897 }
898 
899 /* delete all entries of GList *playcounts */
playcounts_free(FImport * fimp)900 static void playcounts_free (FImport *fimp)
901 {
902     struct playcount *playcount;
903 
904     g_return_if_fail (fimp);
905 
906     while ((playcount=playcount_take_next (fimp))) g_free (playcount);
907 }
908 
909 
910 /* called by playcounts_init */
playcounts_read(FImport * fimp,FContents * cts)911 static gboolean playcounts_read (FImport *fimp, FContents *cts)
912 {
913     GList* playcounts = NULL;
914     guint32 header_length, entry_length, entry_num, i=0;
915 
916     g_return_val_if_fail (fimp, FALSE);
917     g_return_val_if_fail (cts, FALSE);
918 
919     if (!check_header_seek (cts, "mhdp", 0))
920     {
921 	if (cts->error)
922 	{
923 	    g_propagate_error (&fimp->error, cts->error);
924 	    return FALSE;
925 	}
926 	fcontents_set_reversed (cts, TRUE);
927 	if (!check_header_seek (cts, "mhdp", 0))
928 	{
929 	    if (cts->error)
930 	    {
931 		g_propagate_error (&fimp->error, cts->error);
932 		return FALSE;
933 	    }
934 	    else
935 	    {   /* set error */
936 		g_return_val_if_fail (cts->filename, FALSE);
937 		g_set_error (&fimp->error,
938 			     ITDB_FILE_ERROR,
939 			     ITDB_FILE_ERROR_CORRUPT,
940 			     _("Not a Play Counts file: '%s' (missing mhdp header)."),
941 			     cts->filename);
942 		return FALSE;
943 	    }
944 	}
945     }
946     header_length = get32lint (cts, 4);
947     CHECK_ERROR (fimp, FALSE);
948     /* all the headers I know are 0x60 long -- if this one is longer
949        we can simply ignore the additional information */
950     if (header_length < 0x60)
951     {
952 	g_set_error (&fimp->error,
953 		     ITDB_FILE_ERROR,
954 		     ITDB_FILE_ERROR_CORRUPT,
955 		     _("Play Counts file ('%s'): header length smaller than expected (%d<96)."),
956 		     cts->filename, header_length);
957 	return FALSE;
958     }
959     entry_length = get32lint (cts, 8);
960     CHECK_ERROR (fimp, FALSE);
961     /* all the entries I know are 0x0c (firmware 1.3) or 0x10
962      * (firmware 2.0), 0x14 (iTunesDB version 0x0d) or 0x1c (iTunesDB
963      * version 0x13) in length */
964     if (entry_length < 0x0c)
965     {
966 	g_set_error (&fimp->error,
967 		     ITDB_FILE_ERROR,
968 		     ITDB_FILE_ERROR_CORRUPT,
969 		     _("Play Counts file ('%s'): entry length smaller than expected (%d<12)."),
970 		     cts->filename, entry_length);
971 	return FALSE;
972     }
973     /* number of entries */
974     entry_num = get32lint (cts, 12);
975     CHECK_ERROR (fimp, FALSE);
976     for (i=0; i<entry_num; ++i)
977     {
978 	guint32 mac_time;
979 	struct playcount *playcount;
980 	glong seek = header_length + i*entry_length;
981 
982 	check_seek (cts, seek, entry_length);
983 	CHECK_ERROR (fimp, FALSE);
984 
985 	playcount = g_new0 (struct playcount, 1);
986 	playcounts = g_list_prepend (playcounts, playcount);
987 	playcount->playcount = get32lint (cts, seek);
988 	mac_time = get32lint (cts, seek+4);
989 	playcount->time_played = device_time_mac_to_time_t (fimp->itdb->device, mac_time);
990 	playcount->bookmark_time = get32lint (cts, seek+8);
991 
992 	/* rating only exists if the entry length is at least 0x10 */
993 	if (entry_length >= 0x10)
994 	{
995 	    playcount->rating = get32lint (cts, seek+12);
996 	}
997 	else
998 	{
999 	    playcount->rating = NO_PLAYCOUNT;
1000 	}
1001 	/* unk16 only exists if the entry length is at least 0x14 */
1002 	if (entry_length >= 0x14)
1003 	{
1004 	    playcount->pc_unk16 = get32lint (cts, seek+16);
1005 	}
1006 	/* skip_count and last_skipped only exists if the entry length
1007 	   is at least 0x1c */
1008 	if (entry_length >= 0x1c)
1009 	{
1010 	    playcount->skipcount = get32lint (cts, seek+20);
1011 	    mac_time = get32lint (cts, seek+24);
1012 	    playcount->last_skipped = device_time_mac_to_time_t (fimp->itdb->device,
1013 							       mac_time);
1014 
1015 	}
1016     }
1017     fimp->playcounts = g_list_reverse(playcounts);
1018     return TRUE;
1019 }
1020 
1021 
1022 /* called by playcounts_init */
itunesstats_read(FImport * fimp,FContents * cts)1023 static gboolean itunesstats_read (FImport *fimp, FContents *cts)
1024 {
1025     GList* playcounts = NULL;
1026     guint32 entry_num, entry_length, i=0;
1027     struct playcount *playcount;
1028     glong seek = 0;
1029 
1030     g_return_val_if_fail (fimp, FALSE);
1031     g_return_val_if_fail (cts, FALSE);
1032     g_return_val_if_fail (fimp->itdb, FALSE);
1033     g_return_val_if_fail (fimp->itdb->device, FALSE);
1034 
1035     if ( is_shuffle_2g (fimp->itdb->device))
1036     {
1037 	    /* Old iTunesStats format */
1038 
1039 	    /* number of entries */
1040 	    entry_num = get24lint (cts, seek);
1041 	    CHECK_ERROR (fimp, FALSE);
1042 	    seek = 6;
1043 	    for (i=0; i<entry_num; ++i)
1044 	    {
1045 		    entry_length = get24lint (cts, seek+0);
1046 		    CHECK_ERROR (fimp, FALSE);
1047 		    if (entry_length < 18)
1048 		    {
1049 			    g_set_error (&fimp->error,
1050 					    ITDB_FILE_ERROR,
1051 					    ITDB_FILE_ERROR_CORRUPT,
1052 					    _("iTunesStats file ('%s'): entry length smaller than expected (%d<18)."),
1053 					    cts->filename, entry_length);
1054 			    return FALSE;
1055 		    }
1056 		    playcount = g_new0 (struct playcount, 1);
1057 		    playcounts = g_list_prepend (playcounts, playcount);
1058 		    /* NOTE:
1059 		     *
1060 		     * The iPod (firmware 1.3, 2.0, ...?) doesn't seem to use the
1061 		     * timezone information correctly -- no matter what you set
1062 		     * iPod's timezone to, it will always record as if it were set
1063 		     * to UTC -- we need to subtract the difference between
1064 		     * current timezone and UTC to get a correct
1065 		     * display. -- this should be done by the application were
1066 		     * necessary */
1067 		    playcount->bookmark_time = get24lint (cts, seek+3);
1068 		    CHECK_ERROR (fimp, FALSE);
1069 		    playcount->st_unk06 = get24lint (cts, seek+6);
1070 		    CHECK_ERROR (fimp, FALSE);
1071 		    playcount->st_unk09 = get24lint (cts, seek+9);
1072 		    CHECK_ERROR (fimp, FALSE);
1073 		    playcount->playcount = get24lint (cts, seek+12);
1074 		    CHECK_ERROR (fimp, FALSE);
1075 		    playcount->skipped = get24lint (cts, seek+15);
1076 		    CHECK_ERROR (fimp, FALSE);
1077 
1078 		    playcount->rating = NO_PLAYCOUNT;
1079 
1080 		    seek += entry_length;
1081 	    }
1082     } else {
1083 	    /* New iTunesStats format */
1084 	    entry_num = get32lint (cts, seek); /* Number of entries */
1085 	    CHECK_ERROR (fimp, FALSE);
1086 
1087 	    seek = 8;
1088 	    for (i=0; i<entry_num; ++i)
1089 	    {
1090 		    entry_length = get32lint (cts, seek+0);
1091 		    CHECK_ERROR (fimp, FALSE);
1092 		    if (entry_length < 32)
1093 		    {
1094 			    g_set_error (&fimp->error,
1095 					    ITDB_FILE_ERROR,
1096 					    ITDB_FILE_ERROR_CORRUPT,
1097 					    _("iTunesStats file ('%s'): entry length smaller than expected (%d<32)."),
1098 					    cts->filename, entry_length);
1099 			    return FALSE;
1100 		    }
1101 		    playcount = g_new0 (struct playcount, 1);
1102 		    playcounts = g_list_prepend (playcounts, playcount);
1103 
1104 		    playcount->bookmark_time = get32lint (cts, seek+4);
1105 		    CHECK_ERROR (fimp, FALSE);
1106 		    playcount->playcount = get32lint (cts, seek+8);
1107 		    CHECK_ERROR (fimp, FALSE);
1108 		    playcount->time_played = get32lint (cts, seek+12);
1109 		    CHECK_ERROR (fimp, FALSE);
1110 		    playcount->skipped = get32lint (cts, seek+16);
1111 		    CHECK_ERROR (fimp, FALSE);
1112 		    playcount->last_skipped = get32lint (cts, seek+20);
1113 		    CHECK_ERROR (fimp, FALSE);
1114 		    /* 8 unknown bytes follow but it is unlikely they are
1115 		     * the same as the unknown fields in the old format.
1116 		     * I have only seen zeros in those spots so far so I'm
1117 		     * ignoring them for now. */
1118 	    }
1119     }
1120     fimp->playcounts = g_list_reverse(playcounts);
1121     return TRUE;
1122 }
1123 
playcounts_plist_get_gint64(GHashTable * track_dict,const char * key)1124 static gint64 playcounts_plist_get_gint64 (GHashTable *track_dict,
1125                                            const char *key)
1126 {
1127     GValue *value;
1128 
1129     value = g_hash_table_lookup (track_dict, key);
1130     if (value && G_VALUE_HOLDS (value, G_TYPE_INT64)) {
1131         return g_value_get_int64 (value);
1132     }
1133 
1134     return 0;
1135 }
1136 
1137 #ifndef HAVE_G_INT64_EQUAL
g_int64_equal(gconstpointer v1,gconstpointer v2)1138 static gboolean g_int64_equal (gconstpointer v1, gconstpointer v2)
1139 {
1140   return *((const gint64*) v1) == *((const gint64*) v2);
1141 }
1142 #endif
1143 
1144 #ifndef HAVE_G_INT64_HASH
g_int64_hash(gconstpointer v)1145 static guint g_int64_hash (gconstpointer v)
1146 {
1147   return (guint) *(const gint64*) v;
1148 }
1149 #endif
1150 
1151 /* called by playcounts_init */
playcounts_plist_read(FImport * fimp,GValue * plist_data)1152 static gboolean playcounts_plist_read (FImport *fimp, GValue *plist_data)
1153 {
1154     GHashTable *playcounts;
1155     struct playcount *playcount;
1156     GHashTable *pc_dict, *track_dict;
1157     GValue *to_parse;
1158     GArray *array;
1159     gint i;
1160     guint32 mac_time;
1161     guint64 *dbid;
1162 
1163     g_return_val_if_fail (G_VALUE_HOLDS (plist_data, G_TYPE_HASH_TABLE), FALSE);
1164     pc_dict = g_value_get_boxed (plist_data);
1165 
1166     to_parse = g_hash_table_lookup (pc_dict, "tracks");
1167     if (to_parse == NULL) {
1168         return FALSE;
1169     }
1170     if (!G_VALUE_HOLDS (to_parse, G_TYPE_ARRAY)) {
1171         return FALSE;
1172     }
1173 
1174     playcounts = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_free);
1175 
1176     array = (GArray*)g_value_get_boxed (to_parse);
1177     for (i = 0; i < array->len; i++) {
1178        if (!G_VALUE_HOLDS (g_array_index (array, GValue *, i), G_TYPE_HASH_TABLE)) {
1179           continue;
1180        }
1181 
1182        track_dict = g_value_get_boxed (g_array_index (array, GValue *, i));
1183        if (track_dict == NULL)
1184            continue;
1185 
1186        to_parse = g_hash_table_lookup (track_dict, "persistentID");
1187        if (!to_parse)
1188            continue;
1189 
1190        dbid = g_new0 (guint64, 1);
1191        if (!G_VALUE_HOLDS (to_parse, G_TYPE_INT64))
1192            continue;
1193        *dbid = g_value_get_int64 (to_parse);
1194        playcount = g_new0 (struct playcount, 1);
1195        g_hash_table_insert (playcounts, dbid, playcount);
1196 
1197        playcount->bookmark_time = playcounts_plist_get_gint64 (track_dict, "bookmarkTimeInMS");
1198        playcount->playcount = playcounts_plist_get_gint64 (track_dict, "playCount");
1199        mac_time = playcounts_plist_get_gint64 (track_dict, "playMacOSDate");
1200        playcount->time_played = device_time_mac_to_time_t (fimp->itdb->device, mac_time);
1201        playcount->skipcount = playcounts_plist_get_gint64 (track_dict, "skipCount");
1202        mac_time = playcounts_plist_get_gint64 (track_dict, "skipMacOSDate");
1203        playcount->last_skipped = device_time_mac_to_time_t (fimp->itdb->device, mac_time);
1204        playcount->rating = playcounts_plist_get_gint64 (track_dict, "userRating");
1205        if (!playcount->rating)
1206            playcount->rating = NO_PLAYCOUNT;
1207 
1208        to_parse = g_hash_table_lookup (track_dict, "playedState");
1209        if (to_parse && G_VALUE_HOLDS (to_parse, G_TYPE_BOOLEAN)) {
1210            ; /* What do we do with this? */
1211        }
1212     }
1213     fimp->pcounts2 = playcounts;
1214     return TRUE;
1215 }
1216 
1217 /* Delete the file containing the playcounts, once we saved the database,
1218  * the recent playcounts will have been added to the database, so this
1219  * file is no longer useful. */
playcounts_reset(const char * mountpoint)1220 static void playcounts_reset (const char *mountpoint)
1221 {
1222   const gchar *dirs[] = { "Play Counts", "iTunesStats",
1223                           "PlayCounts.plist", NULL };
1224   const gchar **it;
1225 
1226   for (it = dirs; *it != NULL; it++) {
1227       char *playcounts_path;
1228       const gchar *components[] = { *it, NULL };
1229       playcounts_path = itdb_resolve_path (mountpoint, components);
1230       if (playcounts_path != NULL) {
1231           g_unlink (playcounts_path);
1232           g_free (playcounts_path);
1233       }
1234   }
1235 }
1236 
1237 /* Read the Play Count file (formed by adding "Play Counts" to the
1238  * directory component of fimp->itdb->itdb_filename) and set up the
1239  * GList *playcounts. If no Play Count file is present, attempt to
1240  * read the iTunesStats file instead, which is used on the Shuffle for
1241  * the same purpose.
1242  *
1243  * Returns TRUE on success (also when no Play Count
1244  * file is found as this is not an error) and FALSE otherwise, in
1245  * which case fimp->error is set accordingly. */
playcounts_init(FImport * fimp)1246 static gboolean playcounts_init (FImport *fimp)
1247 {
1248   const gchar *plc[] = {"Play Counts", NULL};
1249   const gchar *ist[] = {"iTunesStats", NULL};
1250   const gchar *plcpl[] = {"PlayCounts.plist", NULL};
1251   gchar *plcname, *dirname, *istname, *plcplname;
1252   gboolean result=TRUE;
1253   struct stat filestat;
1254   FContents *cts;
1255   GValue *plist_data;
1256 
1257   g_return_val_if_fail (fimp, FALSE);
1258   g_return_val_if_fail (!fimp->error, FALSE);
1259   g_return_val_if_fail (!fimp->playcounts, FALSE);
1260   g_return_val_if_fail (fimp->itdb, FALSE);
1261   g_return_val_if_fail (fimp->itdb->filename, FALSE);
1262 
1263   dirname = g_path_get_dirname (fimp->itdb->filename);
1264 
1265   plcname = itdb_resolve_path (dirname, plc);
1266   istname = itdb_resolve_path (dirname, ist);
1267   plcplname = itdb_resolve_path (dirname, plcpl);
1268 
1269   g_free (dirname);
1270 
1271   /* skip if no playcounts file is present */
1272   if (plcname)
1273   {
1274       /* skip if playcounts file has zero-length (often happens after
1275        * dosfsck) */
1276       stat (plcname, &filestat);
1277       if (filestat.st_size >= 0x60)
1278       {
1279 	  cts = fcontents_read (plcname, &fimp->error);
1280 	  if (cts)
1281 	  {
1282 	      result = playcounts_read (fimp, cts);
1283 	      fcontents_free (cts);
1284 	  }
1285 	  else
1286 	  {
1287 	      result = FALSE;
1288 	  }
1289       }
1290   }
1291   else if (istname)
1292   {
1293       /* skip if iTunesStats file has zero-length (often happens after
1294        * dosfsck) */
1295       stat (istname, &filestat);
1296       if (filestat.st_size >= 0x06)
1297       {
1298 	  cts = fcontents_read (istname, &fimp->error);
1299 	  if (cts)
1300 	  {
1301 	      result = itunesstats_read (fimp, cts);
1302 	      fcontents_free (cts);
1303 	  }
1304 	  else
1305 	  {
1306 	      result = FALSE;
1307 	  }
1308       }
1309   }
1310   else if (plcplname)
1311   {
1312       /* skip if PlayCounts.plist file has zero-length */
1313       stat (plcplname, &filestat);
1314       if (filestat.st_size > 0)
1315       {
1316 	  plist_data = itdb_plist_parse_from_file (plcplname, &fimp->error);
1317 	  if (plist_data)
1318 	  {
1319 	      result = playcounts_plist_read (fimp, plist_data);
1320 	      g_value_unset (plist_data);
1321 	  }
1322 	  else
1323 	  {
1324 	      result = FALSE;
1325 	  }
1326       }
1327   }
1328 
1329   g_free (plcname);
1330   g_free (istname);
1331   g_free (plcplname);
1332 
1333   return result;
1334 }
1335 
1336 
1337 /* Free the memory taken by @fimp. fimp->itdb must be freed separately
1338  * before calling this function */
itdb_free_fimp(FImport * fimp)1339 static void itdb_free_fimp (FImport *fimp)
1340 {
1341     if (fimp)
1342     {
1343 	if (fimp->fcontents)  fcontents_free (fimp->fcontents);
1344 	g_list_free (fimp->pos_glist);
1345 	g_list_free (fimp->tracks);
1346 	playcounts_free (fimp);
1347 	if (fimp->pcounts2 != NULL) {
1348 	    g_hash_table_destroy (fimp->pcounts2);
1349 	}
1350 	g_free (fimp);
1351     }
1352 }
1353 
1354 /**
1355  * itdb_free:
1356  * @itdb: an #Itdb_iTunesDB
1357  *
1358  * Free the memory taken by @itdb.
1359  */
itdb_free(Itdb_iTunesDB * itdb)1360 void itdb_free (Itdb_iTunesDB *itdb)
1361 {
1362     if (itdb)
1363     {
1364 	g_list_foreach (itdb->playlists,
1365 			(GFunc)(itdb_playlist_free), NULL);
1366 
1367 	if (itdb->priv) {
1368 	    if (itdb->priv->mhsd5_playlists)
1369 	    {
1370 	    g_list_foreach (itdb->priv->mhsd5_playlists,
1371 	            (GFunc)(itdb_playlist_free), NULL);
1372 	    }
1373 
1374 	    g_free(itdb->priv->genius_cuid);
1375 	}
1376 
1377 	g_list_free (itdb->playlists);
1378 	g_list_foreach (itdb->tracks,
1379 			(GFunc)(itdb_track_free), NULL);
1380 	g_list_free (itdb->tracks);
1381 	g_free (itdb->filename);
1382 	itdb_device_free (itdb->device);
1383 	if (itdb->userdata && itdb->userdata_destroy)
1384 	    (*itdb->userdata_destroy) (itdb->userdata);
1385 	g_free (itdb->priv);
1386 	g_free (itdb);
1387     }
1388 }
1389 
1390 /**
1391  * itdb_duplicate:
1392  * @itdb: an #Itdb_iTunesDB
1393  *
1394  * Duplicate @itdb
1395  * FIXME: not implemented yet
1396  *
1397  * Returns: always return NULL since it's unimplemented
1398  */
itdb_duplicate(Itdb_iTunesDB * itdb)1399 Itdb_iTunesDB *itdb_duplicate (Itdb_iTunesDB *itdb)
1400 {
1401     g_return_val_if_fail (itdb, NULL);
1402     /* FIXME: not yet implemented */
1403     g_return_val_if_reached (NULL);
1404 }
1405 
1406 /**
1407  * itdb_playlists_number:
1408  * @itdb: an #Itdb_iTunesDB
1409  *
1410  * Counts the number of playlists stored in @itdb
1411  *
1412  * Returns: the number of playlists in @itdb (including the master
1413  * playlist)
1414  */
itdb_playlists_number(Itdb_iTunesDB * itdb)1415 guint32 itdb_playlists_number (Itdb_iTunesDB *itdb)
1416 {
1417     g_return_val_if_fail (itdb, 0);
1418 
1419     return g_list_length (itdb->playlists);
1420 }
1421 
1422 /**
1423  * itdb_tracks_number:
1424  * @itdb: an #Itdb_iTunesDB
1425  *
1426  * Counts the number of tracks stored in @itdb
1427  *
1428  * Returns: the number of tracks in @itdb
1429  */
itdb_tracks_number(Itdb_iTunesDB * itdb)1430 guint32 itdb_tracks_number (Itdb_iTunesDB *itdb)
1431 {
1432     g_return_val_if_fail (itdb, 0);
1433 
1434     return g_list_length (itdb->tracks);
1435 }
1436 
1437 /**
1438  * itdb_tracks_number_nontransferred:
1439  * @itdb: an #Itdb_iTunesDB
1440  *
1441  * Counts the number of non-transferred tracks in @itdb
1442  *
1443  * Returns: the number of tracks in @itdb that haven't been transferred
1444  * to the iPod yet (ie the number of #Itdb_Track in which the transferred field
1445  * is false)
1446  */
itdb_tracks_number_nontransferred(Itdb_iTunesDB * itdb)1447 guint32 itdb_tracks_number_nontransferred (Itdb_iTunesDB *itdb)
1448 {
1449     guint n = 0;
1450     GList *gl;
1451     g_return_val_if_fail (itdb, 0);
1452 
1453     for (gl=itdb->tracks; gl; gl=gl->next)
1454     {
1455 	Itdb_Track *track = gl->data;
1456 	g_return_val_if_fail (track, 0);
1457 	if (!track->transferred)   ++n;
1458     }
1459     return n;
1460 }
1461 
1462 /**
1463  * itdb_new:
1464  *
1465  * Creates a new Itdb_iTunesDB with the unknowns filled in to reasonable
1466  * values.
1467  *
1468  * Returns: a newly created Itdb_iTunesDB to be freed with itdb_free()
1469  * when it's no longer needed
1470  */
itdb_new(void)1471 Itdb_iTunesDB *itdb_new (void)
1472 {
1473     Itdb_iTunesDB *itdb;
1474 
1475 #if !GLIB_CHECK_VERSION(2, 36, 0)
1476     /* g_type_init() is obsolete with newer glib versions */
1477     static GOnce g_type_init_once = G_ONCE_INIT;
1478     g_once (&g_type_init_once, (GThreadFunc)g_type_init, NULL);
1479 #endif
1480 
1481     itdb = g_new0 (Itdb_iTunesDB, 1);
1482     itdb->priv = g_new0 (Itdb_iTunesDB_Private, 1);
1483     itdb->device = itdb_device_new ();
1484     itdb->version = 0x13;
1485     itdb->id = ((guint64)g_random_int () << 32) |
1486 	((guint64)g_random_int ());
1487     itdb->priv->pid = ((guint64)g_random_int () << 32) |
1488 	((guint64)g_random_int ());
1489     itdb->priv->lang = 0x656e;
1490     itdb->priv->platform = 1; /* Mac */
1491     return itdb;
1492 }
1493 
1494 /* Returns the type of the mhod and the length *ml. *ml is set to -1
1495  * on error (e.g. because there's no mhod at @seek). */
1496 /* A return value of -1 and no error set means that no mhod was found
1497    at @seek */
get_mhod_type(FContents * cts,glong seek,guint32 * ml)1498 static gint32 get_mhod_type (FContents *cts, glong seek, guint32 *ml)
1499 {
1500     gint32 type = -1;
1501 
1502 #if ITUNESDB_DEBUG
1503     fprintf(stderr, "get_mhod_type seek: %x\n", (int)seek);
1504 #endif
1505 
1506     if (ml) *ml = -1;
1507 
1508     if (check_header_seek (cts, "mhod", seek))
1509     {
1510 	guint32 len = get32lint (cts, seek+8);    /* total length */
1511 	if (cts->error) return -1;
1512 	if (ml) *ml = len;
1513 	type = get32lint (cts, seek+12);          /* mhod_id      */
1514 	if (cts->error) return -1;
1515     }
1516     return type;
1517 }
1518 
extract_mhod_string(FContents * cts,glong seek)1519 static char *extract_mhod_string (FContents *cts, glong seek)
1520 {
1521     gunichar2 *entry_utf16;
1522     char *entry_utf8;
1523     gint string_type;
1524     gsize len;
1525 
1526     /* type of string: 0x02: UTF8, 0x01 or 0x00: UTF16 LE */
1527     string_type = get32lint (cts, seek);
1528     len = get32lint (cts, seek+4);   /* length of string */
1529     g_return_val_if_fail (len < G_MAXUINT - 2, NULL);
1530     if (string_type != 0x02) {
1531 	/* UTF-16 string */
1532 	entry_utf16 = g_new0 (gunichar2, (len+2)/2);
1533 	if (seek_get_n_bytes (cts, (gchar *)entry_utf16, seek+16, len)) {
1534 	    fixup_little_utf16 (entry_utf16);
1535 	    entry_utf8 = utf16_to_utf8_with_partial (entry_utf16);
1536 	    g_free (entry_utf16);
1537 	} else {
1538 	    g_free (entry_utf16);
1539 	    return NULL;
1540 	}
1541     } else {
1542 	/* UTF-8 string */
1543 	entry_utf8 = g_new0 (gchar, len+1);
1544 	if (!seek_get_n_bytes (cts, entry_utf8, seek+16, len)) {
1545 	    g_free (entry_utf8);
1546 	    return NULL;
1547 	}
1548     }
1549 
1550     if ((entry_utf8 != NULL) && g_utf8_validate (entry_utf8, -1, NULL)) {
1551 	return entry_utf8;
1552     } else {
1553 	g_free (entry_utf8);
1554 	return NULL;
1555     }
1556 }
1557 
1558 /* Returns the contents of the mhod at position @mhod_seek. This can
1559    be a simple string or something more complicated as in the case for
1560    Itdb_SPLPREF OR Itdb_SPLRULES.
1561 
1562    *mhod_len is set to the total length of the mhod (-1 in case an
1563    *error occured).
1564 
1565    MHODData.valid is set to FALSE in case of any error. cts->error
1566    will be set accordingly.
1567 
1568    MHODData.type is set to the type of the mhod. The data (or a
1569    pointer to the data) will be stored in
1570    .playlist_id/.string/.chapterdata/.splp/.splrs
1571 */
1572 
get_mhod(FImport * fimp,glong mhod_seek,guint32 * ml)1573 static MHODData get_mhod (FImport *fimp, glong mhod_seek, guint32 *ml)
1574 {
1575   MHODData result;
1576   gint32 xl;
1577   guint32 mhod_len;
1578   gint32 header_length;
1579   gulong seek;
1580   FContents *cts;
1581 
1582   cts = fimp->fcontents;
1583 
1584   result.valid = FALSE;
1585   result.type = -1;
1586   g_return_val_if_fail (ml, result);
1587   *ml = -1;
1588 
1589   g_return_val_if_fail (cts, result);
1590   g_return_val_if_fail (!cts->error, result);
1591 
1592 #if ITUNESDB_DEBUG
1593   fprintf(stderr, "get_mhod seek: %ld\n", mhod_seek);
1594 #endif
1595 
1596   result.type = get_mhod_type (cts, mhod_seek, &mhod_len);
1597 
1598   if (mhod_len == -1)
1599   {
1600       if (!cts->error)
1601       {   /* set error */
1602 	  g_set_error (&cts->error,
1603 		       ITDB_FILE_ERROR,
1604 		       ITDB_FILE_ERROR_CORRUPT,
1605 		       _("iTunesDB corrupt: no MHOD at offset %ld in file '%s'."),
1606 		       mhod_seek, cts->filename);
1607       }
1608       return result;
1609   }
1610 
1611   if (!check_seek (cts, mhod_seek, mhod_len))
1612       return result;
1613 
1614 
1615   header_length = get32lint (cts, mhod_seek+4); /* header length  */
1616 
1617   seek = mhod_seek + header_length;
1618 
1619 #if ITUNESDB_DEBUG
1620   fprintf(stderr, "ml: %x type: %x\n", *ml, result.type);
1621 #endif
1622 
1623   switch ((enum MHOD_ID)result.type)
1624   {
1625   case MHOD_ID_LIBPLAYLISTINDEX:
1626       /* this is not yet supported */
1627   case MHOD_ID_PLAYLIST:
1628       /* return the position indicator */
1629       result.data.track_pos = get32lint (cts, mhod_seek+24);
1630       break;
1631   case MHOD_ID_TITLE:
1632   case MHOD_ID_PATH:
1633   case MHOD_ID_ALBUM:
1634   case MHOD_ID_ARTIST:
1635   case MHOD_ID_GENRE:
1636   case MHOD_ID_FILETYPE:
1637   case MHOD_ID_COMMENT:
1638   case MHOD_ID_CATEGORY:
1639   case MHOD_ID_COMPOSER:
1640   case MHOD_ID_GROUPING:
1641   case MHOD_ID_DESCRIPTION:
1642   case MHOD_ID_SUBTITLE:
1643   case MHOD_ID_TVSHOW:
1644   case MHOD_ID_TVEPISODE:
1645   case MHOD_ID_TVNETWORK:
1646   case MHOD_ID_ALBUMARTIST:
1647   case MHOD_ID_KEYWORDS:
1648   case MHOD_ID_SORT_ARTIST:
1649   case MHOD_ID_SORT_TITLE:
1650   case MHOD_ID_SORT_ALBUM:
1651   case MHOD_ID_SORT_ALBUMARTIST:
1652   case MHOD_ID_SORT_COMPOSER:
1653   case MHOD_ID_SORT_TVSHOW:
1654       result.data.string = extract_mhod_string (cts, seek);
1655       if (result.data.string == NULL) {
1656 	  *ml = mhod_len;
1657 	  return result;
1658       }
1659       break;
1660   case MHOD_ID_PODCASTURL:
1661   case MHOD_ID_PODCASTRSS:
1662       /* length of string */
1663       xl = mhod_len - header_length;
1664       g_return_val_if_fail (xl < G_MAXUINT - 1, result);
1665       result.data.string = g_new0 (gchar, xl+1);
1666       if (!seek_get_n_bytes (cts, result.data.string, seek, xl))
1667       {
1668 	  g_free (result.data.string);
1669 	  return result;  /* *ml==-1, result.valid==FALSE */
1670       }
1671       break;
1672   case MHOD_ID_CHAPTERDATA:
1673       result.data.chapterdata = itdb_chapterdata_new();
1674       result.data.chapterdata->unk024 = get32lint (cts, seek);
1675       result.data.chapterdata->unk028 = get32lint (cts, seek+4);
1676       result.data.chapterdata->unk032 = get32lint (cts, seek+8);
1677       seek += 12; /* get past unks */
1678       if (check_header_seek (cts, "sean", seek+4))
1679       {
1680 	  gint i;
1681 	  guint32 numchapters;
1682 	  numchapters = get32bint (cts, seek+12) - 1; /* minus 1 for hedr atom */
1683 	  seek += 20; /* move to atom data */
1684 	  for (i=0; i<numchapters; ++i)
1685 	  {
1686 	      if (check_header_seek (cts, "chap", seek+4))
1687 	      {
1688 		  guint32 length;
1689 		  guint32 childlength;
1690 		  guint32 startpos;
1691 		  guint32 children;
1692 		  gint j;
1693 		  gunichar2 *string_utf16;
1694 		  startpos = get32bint (cts, seek+8);
1695 		  children = get32bint (cts, seek+12);
1696 		  seek += 20;
1697 		  for (j=0; j<children; ++j)
1698 		  {
1699 		      childlength = get32bint (cts, seek);
1700 		      if (check_header_seek (cts, "name", seek+4))
1701 		      {
1702 			  length = get16bint (cts, seek+20);
1703 			  string_utf16 = g_new0 (gunichar2, (length+1));
1704 			  if (!seek_get_n_bytes (cts, (gchar *)string_utf16, seek+22, length*2))
1705 			  {
1706 				g_free (string_utf16);
1707 				itdb_chapterdata_free (result.data.chapterdata);
1708 				return result;  /* *ml==-1, result.valid==FALSE */
1709 			  }
1710 			  fixup_big_utf16 (string_utf16);
1711 			  itdb_chapterdata_add_chapter(result.data.chapterdata,startpos,g_utf16_to_utf8 (
1712 				string_utf16, -1, NULL, NULL, NULL));
1713 			  g_free (string_utf16);
1714 		      }
1715 		      seek += childlength;
1716 		  }
1717 	      }
1718 	  }
1719 	  if (check_header_seek (cts, "hedr", seek+4))
1720 	  {
1721 	      guint32 hedrlength = get32bint(cts, seek);
1722 	      seek += hedrlength;
1723 	  }
1724 
1725       }
1726       break;
1727   case MHOD_ID_SPLPREF:  /* Settings for smart playlist */
1728       if (!check_seek (cts, seek, 14))
1729 	  return result;  /* *ml==-1, result.valid==FALSE */
1730       result.data.splpref = g_new0 (Itdb_SPLPref, 1);
1731       result.data.splpref->liveupdate = get8int (cts, seek);
1732       result.data.splpref->checkrules = get8int (cts, seek+1);
1733       result.data.splpref->checklimits = get8int (cts, seek+2);
1734       result.data.splpref->limittype = get8int (cts, seek+3);
1735       result.data.splpref->limitsort = get8int (cts, seek+4);
1736       result.data.splpref->limitvalue = get32lint (cts, seek+8);
1737       result.data.splpref->matchcheckedonly = get8int (cts, seek+12);
1738       /* if the opposite flag is on (seek+13), set limitsort's high
1739 	 bit -- see note in itunesdb.h for more info */
1740       if (get8int (cts, seek+13))
1741 	  result.data.splpref->limitsort |= 0x80000000;
1742       break;
1743   case MHOD_ID_SPLRULES:  /* Rules for smart playlist */
1744       if (check_header_seek (cts, "SLst", seek))
1745       {
1746 	  /* !!! for some reason the SLst part is the only part of the
1747 	     iTunesDB with big-endian encoding, including UTF16
1748 	     strings */
1749 	  gint i;
1750 	  guint32 numrules;
1751 	  if (!check_seek (cts, seek, 136))
1752 	      return result;  /* *ml==-1, result.valid==FALSE */
1753 	  result.data.splrules = g_new0 (Itdb_SPLRules, 1);
1754 	  result.data.splrules->unk004 = get32bint (cts, seek+4);
1755 	  numrules = get32bint (cts, seek+8);
1756 	  result.data.splrules->match_operator = get32bint (cts, seek+12);
1757 	  seek += 136;  /* I can't find this value stored in the
1758 			   iTunesDB :-( */
1759 	  for (i=0; i<numrules; ++i)
1760 	  {
1761 	      guint32 length;
1762 	      ItdbSPLFieldType ft;
1763 	      gunichar2 *string_utf16;
1764 	      Itdb_SPLRule *splr = g_new0 (Itdb_SPLRule, 1);
1765 	      result.data.splrules->rules = g_list_append (
1766 		  result.data.splrules->rules, splr);
1767 	      if (!check_seek (cts, seek, 56))
1768 		  goto splrules_error;
1769 	      splr->field = get32bint (cts, seek);
1770 	      splr->action = get32bint (cts, seek+4);
1771 
1772 	      if (!itdb_spl_action_known (splr->action))
1773 	      {
1774 		  g_warning (_("Unknown smart rule action at %ld: %x. Trying to continue.\n"), seek, splr->action);
1775 	      }
1776 
1777 	      seek += 52;
1778 	      length = get32bint (cts, seek);
1779 	      g_return_val_if_fail (length < G_MAXUINT-2, result);
1780 
1781 	      ft = itdb_splr_get_field_type (splr);
1782 	      switch (ft)
1783 	      {
1784 	      case ITDB_SPLFT_STRING:
1785 		  string_utf16 = g_new0 (gunichar2, (length+2)/2);
1786 		  if (!seek_get_n_bytes (cts, (gchar *)string_utf16,
1787 					 seek+4, length))
1788 		  {
1789 		      g_free (string_utf16);
1790 		      goto splrules_error;
1791 		  }
1792 		  fixup_big_utf16 (string_utf16);
1793 		  splr->string = g_utf16_to_utf8 (
1794 		      string_utf16, -1, NULL, NULL, NULL);
1795 		  g_free (string_utf16);
1796 		  break;
1797 	      case ITDB_SPLFT_INT:
1798 	      case ITDB_SPLFT_DATE:
1799 	      case ITDB_SPLFT_BOOLEAN:
1800 	      case ITDB_SPLFT_PLAYLIST:
1801 	      case ITDB_SPLFT_UNKNOWN:
1802 	      case ITDB_SPLFT_BINARY_AND:
1803 		  if (length != 0x44)
1804 		  {
1805 		      g_warning (_("Length of smart playlist rule field (%d) not as expected. Trying to continue anyhow.\n"), length);
1806 		  }
1807 		  if (!check_seek (cts, seek, 72))
1808 		      goto splrules_error;
1809 		  splr->fromvalue = get64bint (cts, seek+4);
1810 		  splr->fromdate = get64bint (cts, seek+12);
1811 		  splr->fromunits = get64bint (cts, seek+20);
1812 		  splr->tovalue = get64bint (cts, seek+28);
1813 		  splr->todate = get64bint (cts, seek+36);
1814 		  splr->tounits = get64bint (cts, seek+44);
1815 		  /* ITDB_SPLFIELD_PLAYLIST seems to use these unknowns*/
1816 		  splr->unk052 = get32bint (cts, seek+52);
1817 		  splr->unk056 = get32bint (cts, seek+56);
1818 		  splr->unk060 = get32bint (cts, seek+60);
1819 		  splr->unk064 = get32bint (cts, seek+64);
1820 		  splr->unk068 = get32bint (cts, seek+68);
1821 
1822 		  if (ft == ITDB_SPLFT_DATE) {
1823 		      ItdbSPLActionType at;
1824 		      at = itdb_splr_get_action_type (splr);
1825 		      if ((at == ITDB_SPLAT_RANGE_DATE) ||
1826 			  (at == ITDB_SPLAT_DATE))
1827 		      {
1828 			  Itdb_iTunesDB *itdb = fimp->itdb;
1829 			  splr->fromvalue = device_time_mac_to_time_t (itdb->device,
1830 								     splr->fromvalue);
1831 			  splr->tovalue = device_time_mac_to_time_t (itdb->device,
1832 								   splr->tovalue);
1833 		      }
1834 		  }
1835 
1836 		  break;
1837 	      }
1838 	      seek += length+4;
1839 	  }
1840       }
1841       else
1842       {
1843 	  if (!cts->error)
1844 	  {   /* set error */
1845 	      g_set_error (&cts->error,
1846 			   ITDB_FILE_ERROR,
1847 			   ITDB_FILE_ERROR_CORRUPT,
1848 			   _("iTunesDB corrupt: no SLst at offset %ld in file '%s'."),
1849 			   seek, cts->filename);
1850 	  }
1851 	  return result;  /* *ml==-1, result.valid==FALSE */
1852       }
1853       break;
1854     splrules_error:
1855       g_list_foreach (result.data.splrules->rules,
1856 		      (GFunc)(itdb_splr_free), NULL);
1857       g_list_free (result.data.splrules->rules);
1858       g_free (result.data.splrules);
1859       return result;  /* *ml==-1, result.valid==FALSE */
1860   default:
1861       g_warning (_("Encountered unknown MHOD type (%d) while parsing the iTunesDB. Ignoring.\n\n"), result.type);
1862       *ml = mhod_len;
1863       return result;
1864   }
1865 
1866   *ml = mhod_len;
1867   result.valid = TRUE;
1868   return result;
1869 }
1870 
1871 /* Returns the value of a string type mhod. return the length of the
1872    mhod *ml, the mhod type *mty, and a string with the entry (in
1873    UTF8). After use you must free the string with g_free(). Returns
1874    NULL if no string is avaible. *ml is set to -1 in case of error and
1875    cts->error is set appropriately. */
get_mhod_string(FImport * fimp,glong seek,guint32 * ml,gint32 * mty)1876 static gchar *get_mhod_string (FImport *fimp, glong seek, guint32 *ml, gint32 *mty)
1877 {
1878     MHODData mhoddata;
1879     FContents *cts;
1880 
1881     cts = fimp->fcontents;
1882 
1883     *mty = get_mhod_type (cts, seek, ml);
1884     if (cts->error) return NULL;
1885 
1886     if (*ml != -1) switch ((enum MHOD_ID)*mty)
1887     {
1888     case MHOD_ID_TITLE:
1889     case MHOD_ID_PATH:
1890     case MHOD_ID_ALBUM:
1891     case MHOD_ID_ARTIST:
1892     case MHOD_ID_GENRE:
1893     case MHOD_ID_FILETYPE:
1894     case MHOD_ID_COMMENT:
1895     case MHOD_ID_CATEGORY:
1896     case MHOD_ID_COMPOSER:
1897     case MHOD_ID_GROUPING:
1898     case MHOD_ID_DESCRIPTION:
1899     case MHOD_ID_PODCASTURL:
1900     case MHOD_ID_PODCASTRSS:
1901     case MHOD_ID_SUBTITLE:
1902     case MHOD_ID_TVSHOW:
1903     case MHOD_ID_TVEPISODE:
1904     case MHOD_ID_TVNETWORK:
1905     case MHOD_ID_ALBUMARTIST:
1906     case MHOD_ID_KEYWORDS:
1907     case MHOD_ID_SORT_ARTIST:
1908     case MHOD_ID_SORT_TITLE:
1909     case MHOD_ID_SORT_ALBUM:
1910     case MHOD_ID_SORT_ALBUMARTIST:
1911     case MHOD_ID_SORT_COMPOSER:
1912     case MHOD_ID_SORT_TVSHOW:
1913     case MHOD_ID_ALBUM_ALBUM:
1914     case MHOD_ID_ALBUM_ARTIST:
1915     case MHOD_ID_ALBUM_ARTIST_MHII:
1916     case MHOD_ID_ALBUM_SORT_ARTIST:
1917 	mhoddata = get_mhod (fimp, seek, ml);
1918 	if ((*ml != -1) && mhoddata.valid)
1919 	    return mhoddata.data.string;
1920 	else
1921 	    return NULL;
1922     case MHOD_ID_SPLPREF:
1923     case MHOD_ID_SPLRULES:
1924     case MHOD_ID_LIBPLAYLISTINDEX:
1925     case MHOD_ID_PLAYLIST:
1926     case MHOD_ID_CHAPTERDATA:
1927     case MHOD_ID_LIBPLAYLISTJUMPTABLE:
1928 	/* these do not have a string entry */
1929 	return NULL;
1930     }
1931 #if ITUNESDB_MHIT_DEBUG
1932     fprintf (stderr, "Ignoring unknown MHOD of type %d at offset %ld\n", *mty, seek);
1933 #endif
1934     return NULL;
1935 }
1936 
1937 
1938 /* convenience function: set error for zero length hunk */
set_error_zero_length_hunk(GError ** error,glong seek,const gchar * filename)1939 static void set_error_zero_length_hunk (GError **error, glong seek,
1940 					const gchar *filename)
1941 {
1942     g_set_error (error,
1943 		 ITDB_FILE_ERROR,
1944 		 ITDB_FILE_ERROR_CORRUPT,
1945 		 _("iTunesDB corrupt: hunk length 0 for hunk at %ld in file '%s'."),
1946 		 seek, filename);
1947 }
1948 
1949 /* convenience function: set error for missing hunk */
set_error_a_not_found_in_b(GError ** error,const gchar * a,const gchar * b,glong b_seek)1950 static void set_error_a_not_found_in_b (GError **error,
1951 					const gchar *a,
1952 					const gchar *b,
1953 					glong b_seek)
1954 {
1955     g_set_error (error,
1956 		 ITDB_FILE_ERROR,
1957 		 ITDB_FILE_ERROR_CORRUPT,
1958 		 _("iTunesDB corrupt: no section '%s' found in section '%s' starting at %ld."),
1959 		 a, b, b_seek);
1960 }
1961 
1962 /* convenience function: set error if header is smaller than expected */
set_error_a_header_smaller_than_b(GError ** error,const gchar * a,guint32 b,guint32 len,glong a_seek,const gchar * filename)1963 static void set_error_a_header_smaller_than_b (GError **error,
1964 					       const gchar *a,
1965 					       guint32 b, guint32 len,
1966 					       glong a_seek,
1967 					       const gchar *filename)
1968 {
1969       g_set_error (error,
1970 		   ITDB_FILE_ERROR,
1971 		   ITDB_FILE_ERROR_CORRUPT,
1972 		   _("header length of '%s' smaller than expected (%d < %d) at offset %ld in file '%s'."),
1973 		   a, b, len, a_seek, filename);
1974 }
1975 
1976 
1977 /* finds next occurence of section @a in section b (@b_seek) starting
1978    at @start_seek
1979 */
1980 /* Return value:
1981    -1 and cts->error not set: section @a could not be found
1982    -1 and cts->error set: some error occured
1983    >=0: start of next occurence of section @a
1984 */
find_next_a_in_b(FContents * cts,const gchar * a,glong b_seek,glong start_seek)1985 static glong find_next_a_in_b (FContents *cts,
1986 			       const gchar *a,
1987 			       glong b_seek, glong start_seek)
1988 {
1989     glong b_len;
1990     glong offset, len;
1991 
1992     g_return_val_if_fail (a, -1);
1993     g_return_val_if_fail (cts, -1);
1994     g_return_val_if_fail (strlen (a) == 4, -1);
1995     g_return_val_if_fail (b_seek>=0, -1);
1996     g_return_val_if_fail (start_seek >= b_seek, -1);
1997 
1998 /*     printf ("%s: b_seek: %lx, start_seek: %lx\n", a, b_seek, start_seek); */
1999 
2000     b_len = get32lint (cts, b_seek+8);
2001     if (cts->error) return -1;
2002 
2003     offset = start_seek - b_seek;
2004     do
2005     {   /* skip headers inside the b hunk (b_len) until we find header
2006 	   @a */
2007 	len = get32lint (cts, b_seek+offset+4);
2008 	if (cts->error) return -1;
2009 	if (len == 0)
2010 	{   /* This needs to be checked, otherwise we might hang */
2011 	    set_error_zero_length_hunk (&cts->error, b_seek+offset,
2012 					cts->filename);
2013 	    return -1;
2014 	}
2015 	offset += len;
2016 /* 	printf ("offset: %lx, b_len: %lx, bseek+offset: %lx\n",  */
2017 /* 		offset, b_len, b_seek+offset); */
2018     } while ((offset < b_len-4) &&
2019 	     !check_header_seek (cts, a, b_seek+offset));
2020     if (cts->error) return -1;
2021 
2022     if (offset >= b_len)	return -1;
2023 
2024 /*     printf ("%s found at %lx\n", a, b_seek+offset); */
2025 
2026     return b_seek+offset;
2027 }
2028 
2029 
2030 
2031 
2032 /* return the position of mhsd with type @type */
2033 /* Return value:
2034      -1 if mhsd cannot be found. cts->error will not be set
2035 
2036      0 and cts->error is set if some other error occurs.
2037      Since the mhsd can never be at position 0 (a mhbd must be there),
2038      a return value of 0 always indicates an error.
2039 */
find_mhsd(FContents * cts,guint32 type)2040 static glong find_mhsd (FContents *cts, guint32 type)
2041 {
2042     guint32 i, len, mhsd_num;
2043     glong seek;
2044 
2045     len = get32lint (cts, 4);
2046     if (cts->error) return 0;
2047     /* all the headers I know are 0x68 long -- if this one is longer
2048        we can could simply ignore the additional information */
2049     /* Since 'we' (parse_fimp()) only need data from the first 32
2050        bytes, don't complain unless it's smaller than that */
2051     if (len < 32)
2052     {
2053 	g_set_error (&cts->error,
2054 		     ITDB_FILE_ERROR,
2055 		     ITDB_FILE_ERROR_CORRUPT,
2056 		     _("iTunesDB ('%s'): header length of mhsd hunk smaller than expected (%d<32). Aborting."),
2057 		     cts->filename, len);
2058 	return FALSE;
2059     }
2060 
2061     mhsd_num = get32lint (cts, 20);
2062     if (cts->error) return 0;
2063 
2064     seek = 0;
2065     for (i=0; i<mhsd_num; ++i)
2066     {
2067 	guint32 mhsd_type;
2068 
2069 	seek += len;
2070 	if (!check_header_seek (cts, "mhsd", seek))
2071 	{
2072 	    if (!cts->error)
2073 	    {   /* set error */
2074 		g_set_error (&cts->error,
2075 			     ITDB_FILE_ERROR,
2076 			     ITDB_FILE_ERROR_CORRUPT,
2077 			     _("iTunesDB '%s' corrupt: mhsd expected at %ld."),
2078 			     cts->filename, seek);
2079 	    }
2080 	    return 0;
2081 	}
2082 	len = get32lint (cts, seek+8);
2083 	if (cts->error) return 0;
2084 
2085 	mhsd_type = get32lint (cts, seek+12);
2086 	if (cts->error) return 0;
2087 
2088 	if (mhsd_type == type) return seek;
2089     }
2090     return -1;
2091 }
2092 
2093 
2094 /* sort in reverse order */
pos_comp(gconstpointer a,gconstpointer b)2095 static gint pos_comp (gconstpointer a, gconstpointer b)
2096 {
2097     const PosEntry *pa = (const PosEntry*)a;
2098     const PosEntry *pb = (const PosEntry*)b;
2099 
2100     return pb->track_pos - pa->track_pos;
2101 }
2102 
2103 
2104 /* Read and process the mhip at @seek. Return a pointer to the next
2105    possible mhip. */
2106 /* Return value: -1 if no mhip is present at @seek */
get_mhip(FImport * fimp,glong mhip_seek)2107 static glong get_mhip (FImport *fimp, glong mhip_seek)
2108 {
2109     gboolean first_entry = TRUE;
2110     FContents *cts;
2111     guint32 mhip_hlen, mhip_len, mhod_num, mhod_seek;
2112     gint32 i;
2113     gint32 mhod_type;
2114     guint32 trackid;
2115 
2116 
2117     g_return_val_if_fail (fimp, -1);
2118 
2119     cts = fimp->fcontents;
2120 
2121     if (!check_header_seek (cts, "mhip", mhip_seek))
2122     {
2123 	CHECK_ERROR (fimp, -1);
2124 	return -1;
2125     }
2126 
2127     mhip_hlen = get32lint (cts, mhip_seek+4);
2128     CHECK_ERROR (fimp, -1);
2129 
2130     if (mhip_hlen < 36)
2131     {
2132 	set_error_a_header_smaller_than_b (&fimp->error,
2133 					   "mhip",
2134 					   mhip_hlen, 36,
2135 					   mhip_seek, cts->filename);
2136 	return -1;
2137     }
2138 
2139     /* Check if entire mhip header can be read -- that way we won't
2140        have to check for read errors every time we access a single
2141        byte */
2142 
2143     check_seek (cts, mhip_seek, mhip_hlen);
2144     CHECK_ERROR (fimp, -1);
2145 
2146     mhip_len = get32lint (cts, mhip_seek+8);
2147     mhod_num = get32lint (cts, mhip_seek+12);
2148     trackid = get32lint(cts, mhip_seek+24);
2149 
2150     mhod_seek = mhip_seek + mhip_hlen;
2151 
2152     /* the mhod that follows gives us the position in the
2153        playlist (type 100). Just for flexibility, we scan all
2154        following mhods and pick the type 100 */
2155     for (i=0; i<mhod_num; ++i)
2156     {
2157 	guint32 mhod_len;
2158 
2159 	mhod_type = get_mhod_type (cts, mhod_seek, &mhod_len);
2160 	CHECK_ERROR (fimp, -1);
2161 	if (mhod_type == MHOD_ID_PLAYLIST)
2162 	{
2163 	    MHODData mhod;
2164 	    mhod = get_mhod (fimp, mhod_seek, &mhod_len);
2165 	    CHECK_ERROR (fimp, -1);
2166 	    if (mhod.valid && first_entry)
2167 	    {
2168 		PosEntry *entry = g_new(PosEntry, 1);
2169 		entry->trackid = trackid;
2170 		entry->track_pos = mhod.data.track_pos;
2171 		fimp->pos_glist = g_list_prepend (fimp->pos_glist, entry);
2172 		/* don't call this section more than once (it never
2173 		   should happen except in the case of corrupted
2174 		   iTunesDBs...) */
2175 		first_entry = FALSE;
2176 	    }
2177 	}
2178 	else
2179 	{
2180 	    if (mhod_len == -1)
2181 	    {
2182 		g_warning (_("Number of MHODs in mhip at %ld inconsistent in file '%s'."),
2183 			   mhip_seek, cts->filename);
2184 		break;
2185 	    }
2186 	}
2187 	mhod_seek += mhod_len;
2188     }
2189 
2190     /* Up to iTunesd V4.7 or so the mhip_len was set incorrectly
2191        (mhip_len == mhip_hlen). In that case we need to find the seek
2192        to the next mhip by going through all mhods.
2193     */
2194     if ((mhip_len == mhip_hlen) && (mhod_num > 0))
2195 	return mhod_seek;
2196     else
2197 	return mhip_seek+mhip_len;
2198 }
2199 
2200 
2201 
2202 
2203 /* Get a playlist. Returns the position where the next playlist should
2204    be. On error -1 is returned and fimp->error is set
2205    appropriately. */
2206 /* get_mhyp */
get_playlist(FImport * fimp,guint mhsd_type,glong mhyp_seek)2207 static glong get_playlist (FImport *fimp, guint mhsd_type, glong mhyp_seek)
2208 {
2209   guint32 i, mhipnum, mhod_num;
2210   glong nextseek, mhod_seek, mhip_seek;
2211   guint32 header_len;
2212   Itdb_Playlist *plitem = NULL;
2213   FContents *cts;
2214   GList *gl;
2215 
2216 #if ITUNESDB_DEBUG
2217   fprintf(stderr, "mhyp seek: %x\n", (int)mhyp_seek);
2218 #endif
2219   g_return_val_if_fail (fimp, -1);
2220   g_return_val_if_fail (fimp->idtree, -1);
2221   g_return_val_if_fail (fimp->pos_glist == NULL, -1);
2222 
2223   cts = fimp->fcontents;
2224 
2225   if (!check_header_seek (cts, "mhyp", mhyp_seek))
2226   {
2227       if (cts->error)
2228 	  g_propagate_error (&fimp->error, cts->error);
2229       return -1;
2230   }
2231   header_len = get32lint (cts, mhyp_seek+4); /* length of header */
2232   CHECK_ERROR (fimp, -1);
2233 
2234   if (header_len < 48)
2235   {
2236       set_error_a_header_smaller_than_b (&fimp->error,
2237 					 "mhyp",
2238 					 header_len, 48,
2239 					 mhyp_seek, cts->filename);
2240       return -1;
2241   }
2242 
2243   /* Check if entire mhyp can be read -- that way we won't have to
2244    * check for read errors every time we access a single byte */
2245 
2246   check_seek (cts, mhyp_seek, header_len);
2247   CHECK_ERROR (fimp, -1);
2248 
2249   nextseek = mhyp_seek + get32lint (cts, mhyp_seek+8);/* possible begin of next PL */
2250   mhod_num = get32lint (cts, mhyp_seek+12); /* number of MHODs we expect */
2251   mhipnum = get32lint (cts, mhyp_seek+16); /* number of tracks
2252 					       (mhips) in playlist */
2253 
2254   plitem = itdb_playlist_new (NULL, FALSE);
2255 
2256   plitem->num = mhipnum;
2257   /* Some Playlists have added 256 to their type -- I don't know what
2258      it's for, so we just ignore it for now -> & 0xff */
2259   plitem->type = get8int (cts, mhyp_seek+20);
2260   plitem->flag1 = get8int (cts, mhyp_seek+21);
2261   plitem->flag2 = get8int (cts, mhyp_seek+22);
2262   plitem->flag3 = get8int (cts, mhyp_seek+23);
2263   plitem->timestamp = get32lint (cts, mhyp_seek+24);
2264   plitem->timestamp = device_time_mac_to_time_t (fimp->itdb->device, plitem->timestamp);
2265   plitem->id = get64lint (cts, mhyp_seek+28);
2266 /*  plitem->mhodcount = get32lint (cts, mhyp_seek+36);   */
2267 /*  plitem->libmhodcount = get16lint (cts, mhyp_seek+40);*/
2268   plitem->podcastflag = get16lint (cts, mhyp_seek+42);
2269   plitem->sortorder = get32lint (cts, mhyp_seek+44);
2270   if (header_len >= 0x6C) {
2271       plitem->priv->mhsd5_type = get16lint (cts, mhyp_seek+0x50);
2272   }
2273 
2274   mhod_seek = mhyp_seek + header_len;
2275 
2276   for (i=0; i < mhod_num; ++i)
2277   {
2278       gint32 type;
2279       MHODData mhod;
2280 
2281       type = get_mhod_type (cts, mhod_seek, &header_len);
2282       CHECK_ERROR (fimp, -1);
2283       if (header_len != -1)
2284       {
2285 	  switch ((enum MHOD_ID)type)
2286 	  {
2287 	  case MHOD_ID_PLAYLIST:
2288 	      /* here we could do something about the playlist settings */
2289 	      break;
2290 	  case MHOD_ID_TITLE:
2291 	      mhod = get_mhod (fimp, mhod_seek, &header_len);
2292 	      CHECK_ERROR (fimp, -1);
2293 	      if (mhod.valid && mhod.data.string)
2294 	      {
2295 		  /* sometimes there seem to be two mhod TITLE headers */
2296 		  g_free (plitem->name);
2297 		  plitem->name = mhod.data.string;
2298 		  mhod.valid = FALSE;
2299 	      }
2300 	      break;
2301 	  case MHOD_ID_SPLPREF:
2302 	      mhod = get_mhod (fimp, mhod_seek, &header_len);
2303 	      CHECK_ERROR (fimp, -1);
2304 	      if (mhod.valid && mhod.data.splpref)
2305 	      {
2306 		  plitem->is_spl = TRUE;
2307 		  memcpy (&plitem->splpref, mhod.data.splpref,
2308 			  sizeof (Itdb_SPLPref));
2309 		  g_free (mhod.data.splpref);
2310 		  mhod.valid = FALSE;
2311 	      }
2312 	      break;
2313 	  case MHOD_ID_SPLRULES:
2314 	      mhod = get_mhod (fimp, mhod_seek, &header_len);
2315 	      CHECK_ERROR (fimp, -1);
2316 	      if (mhod.valid && mhod.data.splrules)
2317 	      {
2318 		  plitem->is_spl = TRUE;
2319 		  memcpy (&plitem->splrules, mhod.data.splrules,
2320 			  sizeof (Itdb_SPLRules));
2321 		  g_free (mhod.data.splrules);
2322 		  mhod.valid = FALSE;
2323 	      }
2324 	      break;
2325 	  case MHOD_ID_PATH:
2326 	  case MHOD_ID_ALBUM:
2327 	  case MHOD_ID_ARTIST:
2328 	  case MHOD_ID_GENRE:
2329 	  case MHOD_ID_FILETYPE:
2330 	  case MHOD_ID_COMMENT:
2331 	  case MHOD_ID_CATEGORY:
2332 	  case MHOD_ID_COMPOSER:
2333 	  case MHOD_ID_GROUPING:
2334 	  case MHOD_ID_DESCRIPTION:
2335 	  case MHOD_ID_PODCASTURL:
2336 	  case MHOD_ID_PODCASTRSS:
2337 	  case MHOD_ID_SUBTITLE:
2338 	  case MHOD_ID_TVSHOW:
2339 	  case MHOD_ID_TVEPISODE:
2340 	  case MHOD_ID_TVNETWORK:
2341 	  case MHOD_ID_ALBUMARTIST:
2342 	  case MHOD_ID_KEYWORDS:
2343 	  case MHOD_ID_CHAPTERDATA:
2344 	  case MHOD_ID_SORT_ARTIST:
2345 	  case MHOD_ID_SORT_TITLE:
2346 	  case MHOD_ID_SORT_ALBUM:
2347 	  case MHOD_ID_SORT_ALBUMARTIST:
2348 	  case MHOD_ID_SORT_COMPOSER:
2349 	  case MHOD_ID_SORT_TVSHOW:
2350 	  case MHOD_ID_ALBUM_ALBUM:
2351 	  case MHOD_ID_ALBUM_ARTIST:
2352 	  case MHOD_ID_ALBUM_ARTIST_MHII:
2353 	  case MHOD_ID_ALBUM_SORT_ARTIST:
2354 	  case MHOD_ID_LIBPLAYLISTJUMPTABLE:
2355 	      /* these are not expected here */
2356 	      break;
2357 	  case MHOD_ID_LIBPLAYLISTINDEX:
2358 	      /* this I don't know how to handle */
2359 	      break;
2360 	  }
2361 	  mhod_seek += header_len;
2362       }
2363       else
2364       {
2365 	  g_warning (_("Number of MHODs in mhyp at %ld inconsistent in file '%s'."),
2366 		     mhyp_seek, cts->filename);
2367 	  break;
2368       }
2369   }
2370 
2371   if (!plitem->name)
2372   {   /* we did not read a valid mhod TITLE header -> */
2373       /* we simply make up our own name */
2374       if (itdb_playlist_is_mpl (plitem))
2375 	  plitem->name = g_strdup (_("Master-PL"));
2376       else
2377       {
2378 	  if (itdb_playlist_is_podcasts (plitem))
2379 	      plitem->name = g_strdup (_("Podcasts"));
2380 	  else
2381 	      plitem->name = g_strdup (_("Playlist"));
2382       }
2383   }
2384 
2385 #if ITUNESDB_DEBUG
2386   fprintf(stderr, "pln: %s(%d Itdb_Tracks) \n", plitem->name, (int)plitem->num);
2387 #endif
2388 
2389   /* add new playlist */
2390   if (mhsd_type == 5) {
2391       itdb_playlist_add_mhsd5_playlist (fimp->itdb, plitem, -1);
2392   } else {
2393       itdb_playlist_add (fimp->itdb, plitem, -1);
2394   }
2395 
2396   mhip_seek = mhod_seek;
2397 
2398   /* tracks read */
2399   for (i=0; i < mhipnum; ++i)
2400   {
2401       mhip_seek = get_mhip (fimp, mhip_seek);
2402       if (mhip_seek == -1)
2403       {
2404 	  g_set_error (&fimp->error,
2405 		       ITDB_FILE_ERROR,
2406 		       ITDB_FILE_ERROR_CORRUPT,
2407 		       _("iTunesDB corrupt: number of mhip sections inconsistent in mhyp starting at %ld in file '%s'."),
2408 		       mhyp_seek, cts->filename);
2409 	  return -1;
2410       }
2411   }
2412 
2413   /* sort in reverse order */
2414   fimp->pos_glist = g_list_sort (fimp->pos_glist, pos_comp);
2415   for (gl = fimp->pos_glist; gl; gl = g_list_next (gl))
2416   {
2417       PosEntry* entry = (PosEntry*)gl->data;
2418       Itdb_Track *tr = itdb_track_id_tree_by_id (fimp->idtree, entry->trackid);
2419       if (tr)
2420       {
2421 	  /* preprend because we sorted in reverse order */
2422 	  itdb_playlist_add_track (plitem, tr, 0);
2423       }
2424       else
2425       {
2426 	  if (plitem->podcastflag == ITDB_PL_FLAG_NORM)
2427 	  {
2428 	      g_warning (_("Itdb_Track ID '%d' not found.\n"), entry->trackid);
2429 	  }
2430       }
2431       g_free(entry);
2432   }
2433 
2434   g_list_free (fimp->pos_glist);
2435   fimp->pos_glist = NULL;
2436   return nextseek;
2437 }
2438 
2439 
2440 /* returns a pointer to the next header or -1 on error. fimp->error is
2441    set appropriately. If no "mhit" header is found at the location
2442    specified, -1 is returned but no error is set. */
get_mhit(FImport * fimp,glong mhit_seek)2443 static glong get_mhit (FImport *fimp, glong mhit_seek)
2444 {
2445   Itdb_Track *track;
2446   gchar *entry_utf8;
2447   gint32 type;
2448   guint32 header_len;
2449   guint32 zip;
2450   struct playcount *playcount;
2451   guint32 i, mhod_nums;
2452   FContents *cts;
2453   glong seek = mhit_seek;
2454   gboolean free_playcount;
2455 
2456 #if ITUNESDB_DEBUG
2457   fprintf(stderr, "get_mhit seek: %x\n", (int)seek);
2458 #endif
2459 
2460   g_return_val_if_fail (fimp, -1);
2461 
2462   cts = fimp->fcontents;
2463 
2464   if (!check_header_seek (cts, "mhit", seek))
2465   {
2466       if (cts->error)
2467 	  g_propagate_error (&fimp->error, cts->error);
2468       return -1;
2469   }
2470 
2471   header_len = get32lint (cts, seek+4);
2472   CHECK_ERROR (fimp, -1);
2473 
2474   /* size of the mhit header: For dbversion <= 0x0b (iTunes 4.7 and
2475      earlier), the length is 0x9c. As of dbversion 0x0c and 0x0d
2476      (iTunes 4.7.1 - iTunes 4.9), the size is 0xf4. */
2477   if (header_len < 0x9c)
2478   {
2479       set_error_a_header_smaller_than_b (&fimp->error,
2480 					 "mhit",
2481 					 header_len, 0x9c,
2482 					 seek, cts->filename);
2483       return -1;
2484   }
2485 
2486   /* Check if entire mhit can be read -- that way we won't have to
2487    * check for read errors every time we access a single byte */
2488 
2489   check_seek (cts, seek, header_len);
2490 
2491   mhod_nums = get32lint (cts, seek+12);
2492   CHECK_ERROR (fimp, -1);
2493 
2494 
2495   track = itdb_track_new ();
2496 
2497   if (header_len >= 0x9c)
2498   {
2499       guint32 val32;
2500       track->id = get32lint(cts, seek+16);         /* iPod ID          */
2501       track->visible = get32lint (cts, seek+20);
2502       track->filetype_marker = get32lint (cts, seek+24);
2503       track->type1 = get8int (cts, seek+28);
2504       track->type2 = get8int (cts, seek+29);
2505       track->compilation = get8int (cts, seek+30);
2506       track->rating = get8int (cts, seek+31);
2507       track->time_modified = get32lint(cts, seek+32); /* time added       */
2508       track->time_modified = device_time_mac_to_time_t (fimp->itdb->device,
2509 						      track->time_modified);
2510       track->size = get32lint(cts, seek+36);       /* file size        */
2511       track->tracklen = get32lint(cts, seek+40);   /* time             */
2512       track->track_nr = get32lint(cts, seek+44);   /* track number     */
2513       track->tracks = get32lint(cts, seek+48);     /* nr of tracks     */
2514       track->year = get32lint(cts, seek+52);       /* year             */
2515       track->bitrate = get32lint(cts, seek+56);    /* bitrate          */
2516       val32 = get32lint (cts, seek+60);
2517       track->samplerate = val32 >> 16;             /* sample rate      */
2518       track->samplerate_low = val32 & 0xffff;      /* remaining bits   */
2519       track->volume = get32lint(cts, seek+64);     /* volume adjust    */
2520       track->starttime = get32lint (cts, seek+68);
2521       track->stoptime = get32lint (cts, seek+72);
2522       track->soundcheck = get32lint (cts, seek+76);/* soundcheck       */
2523       track->playcount = get32lint (cts, seek+80); /* playcount        */
2524       track->playcount2 = get32lint (cts, seek+84);
2525       track->time_played = get32lint(cts, seek+88);/* last time played */
2526       track->time_played = device_time_mac_to_time_t (fimp->itdb->device,
2527 						    track->time_played);
2528       track->cd_nr = get32lint(cts, seek+92);      /* CD nr            */
2529       track->cds = get32lint(cts, seek+96);        /* CD nr of..       */
2530       /* Apple Store/Audible User ID (for DRM'ed files only, set to 0
2531 	 otherwise). */
2532       track->drm_userid = get32lint (cts, seek+100);
2533       track->time_added = get32lint(cts, seek+104);/* last mod. time */
2534       track->time_added = device_time_mac_to_time_t (fimp->itdb->device,
2535 						   track->time_added);
2536       track->bookmark_time = get32lint (cts, seek+108);/*time bookmarked*/
2537       track->dbid = get64lint (cts, seek+112);
2538       track->checked = get8int (cts, seek+120); /*Checked/Unchecked: 0/1*/
2539       /* The rating set by the application, as opposed to the rating
2540 	 set on the iPod itself */
2541       track->app_rating = get8int (cts, seek+121);
2542       track->BPM = get16lint (cts, seek+122);
2543       track->artwork_count = get16lint (cts, seek+124);
2544       track->unk126 = get16lint (cts, seek+126);
2545       track->artwork_size = get32lint (cts, seek+128);
2546       track->unk132 = get32lint (cts, seek+132);
2547       track->samplerate2 = get32lfloat (cts, seek+136);
2548       track->time_released = get32lint (cts, seek+140);
2549       track->time_released = device_time_mac_to_time_t (fimp->itdb->device,
2550 						      track->time_released);
2551       track->unk144 = get16lint (cts, seek+144);
2552       track->explicit_flag = get16lint (cts, seek+146);
2553       track->unk148 = get32lint (cts, seek+148);
2554       track->unk152 = get32lint (cts, seek+152);
2555   }
2556   if (header_len >= 0xf4)
2557   {
2558       track->skipcount = get32lint (cts, seek+156);
2559       track->last_skipped = get32lint (cts, seek+160);
2560       track->last_skipped = device_time_mac_to_time_t (fimp->itdb->device,
2561 						     track->last_skipped);
2562       track->has_artwork = get8int (cts, seek+164);
2563       track->skip_when_shuffling = get8int (cts, seek+165);
2564       track->remember_playback_position = get8int (cts, seek+166);
2565       track->flag4 = get8int (cts, seek+167);
2566       track->dbid2 = get64lint (cts, seek+168);
2567       track->lyrics_flag = get8int (cts, seek+176);
2568       track->movie_flag = get8int (cts, seek+177);
2569       track->mark_unplayed = get8int (cts, seek+178);
2570       track->unk179 = get8int (cts, seek+179);
2571       track->unk180 = get32lint (cts, seek+180);
2572       track->pregap = get32lint (cts, seek+184);
2573       track->samplecount = get64lint (cts, seek+188);
2574       track->unk196 = get32lint (cts, seek+196);
2575       track->postgap = get32lint (cts, seek+200);
2576       track->unk204 = get32lint (cts, seek+204);
2577       track->mediatype = get32lint (cts, seek+208);
2578       track->season_nr = get32lint (cts, seek+212);
2579       track->episode_nr = get32lint (cts, seek+216);
2580       track->unk220 = get32lint (cts, seek+220);
2581       track->unk224 = get32lint (cts, seek+224);
2582       track->unk228 = get32lint (cts, seek+228);
2583       track->unk232 = get32lint (cts, seek+232);
2584       track->unk236 = get32lint (cts, seek+236);
2585       track->unk240 = get32lint (cts, seek+240);
2586   }
2587   if (header_len >= 0x148)
2588   {
2589       track->unk244 = get32lint (cts, seek+244);
2590       track->gapless_data = get32lint (cts, seek+248);
2591       track->unk252 = get32lint (cts, seek+252);
2592       track->gapless_track_flag = get16lint (cts, seek+256);
2593       track->gapless_album_flag = get16lint (cts, seek+258);
2594   }
2595   if (header_len >= 0x184)
2596   {
2597       track->mhii_link = get32lint (cts, seek+352);
2598   }
2599 
2600   track->transferred = TRUE;                   /* track is on iPod! */
2601 
2602   seek += get32lint (cts, seek+4);             /* 1st mhod starts here! */
2603   CHECK_ERROR (fimp, -1);
2604 
2605   for (i=0; i<mhod_nums; ++i)
2606   {
2607       entry_utf8 = get_mhod_string (fimp, seek, &zip, &type);
2608       CHECK_ERROR (fimp, -1);
2609       if (entry_utf8 != NULL)
2610       {
2611 	  switch ((enum MHOD_ID)type)
2612 	  {
2613 	  case MHOD_ID_TITLE:
2614 	      track->title = entry_utf8;
2615 	      break;
2616 	  case MHOD_ID_PATH:
2617 	      track->ipod_path = entry_utf8;
2618 	      break;
2619 	  case MHOD_ID_ALBUM:
2620 	      track->album = entry_utf8;
2621 	      break;
2622 	  case MHOD_ID_ARTIST:
2623 	      track->artist = entry_utf8;
2624 	      break;
2625 	  case MHOD_ID_GENRE:
2626 	      track->genre = entry_utf8;
2627 	      break;
2628 	  case MHOD_ID_FILETYPE:
2629 	      track->filetype = entry_utf8;
2630 	      break;
2631 	  case MHOD_ID_COMMENT:
2632 	      track->comment = entry_utf8;
2633 	      break;
2634 	  case MHOD_ID_CATEGORY:
2635 	      track->category = entry_utf8;
2636 	      break;
2637 	  case MHOD_ID_COMPOSER:
2638 	      track->composer = entry_utf8;
2639 	      break;
2640 	  case MHOD_ID_GROUPING:
2641 	      track->grouping = entry_utf8;
2642 	      break;
2643 	  case MHOD_ID_DESCRIPTION:
2644 	      track->description = entry_utf8;
2645 	      break;
2646 	  case MHOD_ID_PODCASTURL:
2647 	      track->podcasturl = entry_utf8;
2648 	      break;
2649 	  case MHOD_ID_PODCASTRSS:
2650 	      track->podcastrss = entry_utf8;
2651 	      break;
2652 	  case MHOD_ID_SUBTITLE:
2653 	      track->subtitle = entry_utf8;
2654 	      break;
2655 	  case MHOD_ID_TVSHOW:
2656 	      track->tvshow = entry_utf8;
2657 	      break;
2658 	  case MHOD_ID_TVEPISODE:
2659 	      track->tvepisode = entry_utf8;
2660 	      break;
2661 	  case MHOD_ID_TVNETWORK:
2662 	      track->tvnetwork = entry_utf8;
2663 	      break;
2664 	  case MHOD_ID_ALBUMARTIST:
2665 	      track->albumartist = entry_utf8;
2666 	      break;
2667 	  case MHOD_ID_KEYWORDS:
2668 	      track->keywords = entry_utf8;
2669 	      break;
2670 	  case MHOD_ID_SORT_ARTIST:
2671 	      track->sort_artist = entry_utf8;
2672 	      break;
2673 	  case MHOD_ID_SORT_TITLE:
2674 	      track->sort_title = entry_utf8;
2675 	      break;
2676 	  case MHOD_ID_SORT_ALBUM:
2677 	      track->sort_album = entry_utf8;
2678 	      break;
2679 	  case MHOD_ID_SORT_ALBUMARTIST:
2680 	      track->sort_albumartist = entry_utf8;
2681 	      break;
2682 	  case MHOD_ID_SORT_COMPOSER:
2683 	      track->sort_composer = entry_utf8;
2684 	      break;
2685 	  case MHOD_ID_SORT_TVSHOW:
2686 	      track->sort_tvshow = entry_utf8;
2687 	      break;
2688 	  case MHOD_ID_SPLPREF:
2689 	  case MHOD_ID_SPLRULES:
2690 	  case MHOD_ID_LIBPLAYLISTINDEX:
2691 	  case MHOD_ID_LIBPLAYLISTJUMPTABLE:
2692 	  case MHOD_ID_PLAYLIST:
2693 	  case MHOD_ID_CHAPTERDATA:
2694 	  case MHOD_ID_ALBUM_ALBUM:
2695 	  case MHOD_ID_ALBUM_ARTIST:
2696 	  case MHOD_ID_ALBUM_ARTIST_MHII:
2697 	  case MHOD_ID_ALBUM_SORT_ARTIST:
2698 	      g_free (entry_utf8);
2699 	      break;
2700 	  }
2701       }
2702       else
2703       {
2704 	  MHODData mhod;
2705 	  switch (type)
2706 	  {
2707 	  case MHOD_ID_CHAPTERDATA:
2708 	      mhod = get_mhod (fimp, seek, &zip);
2709 	      if (mhod.valid && mhod.data.chapterdata)
2710 	      {
2711 		  track->chapterdata = mhod.data.chapterdata;
2712 		  mhod.valid = FALSE;
2713 	      }
2714 	      break;
2715 	  default:
2716 /*
2717 	  printf ("found mhod type %d at %lx inside mhit starting at %lx\n",
2718 	  type, seek, mhit_seek);*/
2719 	      break;
2720 	  }
2721       }
2722       seek += zip;
2723   }
2724 
2725   playcount = playcount_take_next (fimp);
2726   free_playcount = TRUE;
2727   if (!playcount && fimp->pcounts2)
2728   {
2729       playcount = g_hash_table_lookup (fimp->pcounts2, &track->dbid);
2730       free_playcount = FALSE;
2731   }
2732   if (playcount)
2733   {
2734       if (playcount->rating != NO_PLAYCOUNT)
2735       {
2736 	  if (track->rating != playcount->rating)
2737 	  {
2738 	      /* backup original rating to app_rating */
2739 	      track->app_rating = track->rating;
2740 	      track->rating = playcount->rating;
2741 	  }
2742       }
2743       if (playcount->time_played)
2744 	  track->time_played = playcount->time_played;
2745 
2746       if (playcount->bookmark_time)
2747 	  track->bookmark_time = playcount->bookmark_time;
2748 
2749       track->playcount += playcount->playcount;
2750       if (playcount->playcount != 0)
2751       {   /* unmark the 'unplayed' flag */
2752 	  track->mark_unplayed = 0x01;
2753       }
2754       track->recent_playcount = playcount->playcount;
2755 
2756       track->skipcount += playcount->skipcount;
2757       track->recent_skipcount = playcount->skipcount;
2758       if (free_playcount)
2759 	  g_free (playcount);
2760   }
2761   fimp->tracks = g_list_prepend(fimp->tracks, track);
2762   return seek;
2763 }
2764 
2765 
2766 /* Called by read_OTG_playlists(): OTG playlist stored in @cts by
2767  * adding a new playlist (named @plname) with the tracks specified in
2768  * @cts. If @plname is NULL, a standard name will be substituted */
2769 /* Returns FALSE on error, TRUE on success. On error @fimp->error will
2770  * be set apropriately. */
process_OTG_file(FImport * fimp,FContents * cts,const gchar * plname)2771 static gboolean process_OTG_file (FImport *fimp, FContents *cts,
2772 				  const gchar *plname)
2773 {
2774     guint32 header_length, entry_length, entry_num;
2775 
2776     g_return_val_if_fail (fimp && cts, FALSE);
2777     g_return_val_if_fail (fimp->itdb, FALSE);
2778 
2779     if (!plname) plname = _("OTG Playlist");
2780 
2781     if (!check_header_seek (cts, "mhpo", 0))
2782     {
2783 	if (cts->error)
2784 	{
2785 	    g_propagate_error (&fimp->error, cts->error);
2786 	    return FALSE;
2787 	}
2788 	fcontents_set_reversed (cts, TRUE);
2789 	if (!check_header_seek (cts, "mhpo", 0))
2790 	{
2791 	    /* cts->error can't be set as already checked above */
2792 	    /* set error */
2793 	    g_return_val_if_fail (cts->filename, FALSE);
2794 	    g_set_error (&fimp->error,
2795 			 ITDB_FILE_ERROR,
2796 			 ITDB_FILE_ERROR_CORRUPT,
2797 			 _("Not a OTG playlist file: '%s' (missing mhpo header)."),
2798 			 cts->filename);
2799 	    return FALSE;
2800 	}
2801     }
2802     header_length = get32lint (cts, 4);
2803     CHECK_ERROR (fimp, FALSE);
2804     /* all the headers I know are 0x14 long -- if this one is
2805        longer we can simply ignore the additional information */
2806     if (header_length < 0x14)
2807     {
2808 	g_set_error (&fimp->error,
2809 		     ITDB_FILE_ERROR,
2810 		     ITDB_FILE_ERROR_CORRUPT,
2811 		     _("OTG playlist file ('%s'): header length smaller than expected (%d<20)."),
2812 		     cts->filename, header_length);
2813 	return FALSE;
2814     }
2815     entry_length = get32lint (cts, 8);
2816     CHECK_ERROR (fimp, FALSE);
2817     /* all the entries I know are 0x04 long */
2818     if (entry_length < 0x04)
2819     {
2820 	g_set_error (&fimp->error,
2821 		     ITDB_FILE_ERROR,
2822 		     ITDB_FILE_ERROR_CORRUPT,
2823 		     _("OTG playlist file ('%s'): entry length smaller than expected (%d<4)."),
2824 		     cts->filename, entry_length);
2825 	return FALSE;
2826     }
2827     /* number of entries */
2828     entry_num = get32lint (cts, 12);
2829     CHECK_ERROR (fimp, FALSE);
2830 
2831     if (entry_num > 0)
2832     {
2833 	gint i;
2834 	Itdb_Playlist *pl;
2835 
2836 	pl = itdb_playlist_new (plname, FALSE);
2837 	/* Add new playlist */
2838 	itdb_playlist_add (fimp->itdb, pl, -1);
2839 
2840 	/* Add items */
2841 	for (i=0; i<entry_num; ++i)
2842 	{
2843 	    Itdb_Track *track;
2844 	    guint32 num = get32lint (cts,
2845 				     header_length + entry_length *i);
2846 	    CHECK_ERROR (fimp, FALSE);
2847 
2848 	    track = g_list_nth_data (fimp->itdb->tracks, num);
2849 	    if (track)
2850 	    {
2851 		itdb_playlist_add_track (pl, track, -1);
2852 	    }
2853 	    else
2854 	    {
2855 		g_set_error (&fimp->error,
2856 			     ITDB_FILE_ERROR,
2857 			     ITDB_FILE_ERROR_CORRUPT,
2858 			     _("OTG playlist file '%s': reference to non-existent track (%d)."),
2859 			     cts->filename, num);
2860 		return FALSE;
2861 	    }
2862 	}
2863     }
2864     return TRUE;
2865 }
2866 
2867 
2868 
2869 
2870 /* Add the On-The-Go Playlist(s) to the database */
2871 /* The OTG-Files are located in the directory given by
2872    fimp->itdb->itdb_filename.
2873    On error FALSE is returned and fimp->error is set accordingly. */
read_OTG_playlists(FImport * fimp)2874 static gboolean read_OTG_playlists (FImport *fimp)
2875 {
2876     gchar *db[] = {"OTGPlaylistInfo", NULL};
2877     gchar *dirname, *otgname;
2878     gint i=1;
2879 
2880     g_return_val_if_fail (fimp, FALSE);
2881     g_return_val_if_fail (fimp->itdb, FALSE);
2882     g_return_val_if_fail (fimp->itdb->filename, FALSE);
2883 
2884     dirname = g_path_get_dirname (fimp->itdb->filename);
2885     otgname = itdb_resolve_path (dirname, (const gchar **)db);
2886 
2887     /* only parse if "OTGPlaylistInfo" exists */
2888     while (otgname)
2889     {
2890         FContents *cts = fcontents_read (otgname, &fimp->error);
2891         if (cts)
2892         {
2893             gchar *plname = g_strdup_printf (_("OTG Playlist %d"), i);
2894             process_OTG_file (fimp, cts, plname);
2895             g_free (plname);
2896             fcontents_free (cts);
2897         }
2898         g_free (otgname);
2899         if (fimp->error) break;
2900         db[0] = g_strdup_printf ("OTGPlaylistInfo_%d", i);
2901         otgname = itdb_resolve_path (dirname, (const gchar **)db);
2902         g_free (db[0]);
2903         ++i;
2904     }
2905     g_free (dirname);
2906     return TRUE;
2907 }
2908 
2909 
2910 /* Read the tracklist (mhlt). mhsd_seek must point to type 1 mhsd
2911    (this is treated as a programming error) */
2912 /* Return value:
2913    TRUE: import successful
2914    FALSE: error occured, fimp->error is set */
parse_tracks(FImport * fimp,glong mhsd_seek)2915 static gboolean parse_tracks (FImport *fimp, glong mhsd_seek)
2916 {
2917     FContents *cts;
2918     GList* gl;
2919     glong mhlt_seek, seek;
2920     guint32 nr_tracks, i;
2921 
2922     g_return_val_if_fail (fimp, FALSE);
2923     g_return_val_if_fail (fimp->itdb, FALSE);
2924     g_return_val_if_fail (fimp->fcontents, FALSE);
2925     g_return_val_if_fail (fimp->fcontents->filename, FALSE);
2926     g_return_val_if_fail (mhsd_seek >= 0, FALSE);
2927 
2928     cts = fimp->fcontents;
2929 
2930     g_return_val_if_fail (check_header_seek (cts, "mhsd", mhsd_seek),
2931 			  FALSE);
2932 
2933    /* The mhlt header should be the next after the mhsd header. In
2934       order to allow slight changes in the format, we skip headers
2935       until we find an mhlt inside the given mhsd */
2936 
2937     mhlt_seek = find_next_a_in_b (cts, "mhlt", mhsd_seek, mhsd_seek);
2938     CHECK_ERROR (fimp, FALSE);
2939 
2940     if (mhlt_seek == -1)
2941     {
2942 	set_error_a_not_found_in_b (&fimp->error,
2943 				    "mhlt", "mhsd", mhsd_seek);
2944 	return FALSE;
2945     }
2946 
2947     /* Now we are at the mhlt */
2948     nr_tracks = get32lint (cts, mhlt_seek+8);
2949     CHECK_ERROR (fimp, FALSE);
2950 
2951     seek = find_next_a_in_b (cts, "mhit", mhsd_seek, mhlt_seek);
2952     CHECK_ERROR (fimp, FALSE);
2953     /* seek should now point to the first mhit */
2954     for (i=0; i<nr_tracks; ++i)
2955     {
2956 	/* seek could be -1 if first mhit could not be found */
2957 	if (seek != -1)
2958 	    seek = get_mhit (fimp, seek);
2959 	if (fimp->error) return FALSE;
2960 	if (seek == -1)
2961 	{   /* this should not be -- issue warning */
2962 	    g_warning (_("iTunesDB corrupt: number of tracks (mhit hunks) inconsistent. Trying to continue.\n"));
2963 	    break;
2964 	}
2965     }
2966     for (gl=fimp->tracks; gl; gl=g_list_next(gl)) {
2967 	Itdb_Track *track = (Itdb_Track *)gl->data;
2968 	itdb_track_add (fimp->itdb, track, 0);
2969     }
2970     return TRUE;
2971 }
2972 
2973 
2974 
2975 /* Read the playlists (mhlp). mhsd_seek must point to type 2 or type 3
2976    mhsd (this is treated as a programming error) */
2977 /* Return value:
2978    TRUE: import successful
2979    FALSE: error occured, fimp->error is set */
parse_playlists(FImport * fimp,glong mhsd_seek)2980 static gboolean parse_playlists (FImport *fimp, glong mhsd_seek)
2981 {
2982     FContents *cts;
2983     glong seek, mhlp_seek;
2984     guint32 nr_playlists, i;
2985     guint mhsd_type;
2986 
2987     g_return_val_if_fail (fimp, FALSE);
2988     g_return_val_if_fail (fimp->itdb, FALSE);
2989     g_return_val_if_fail (fimp->fcontents, FALSE);
2990     g_return_val_if_fail (fimp->fcontents->filename, FALSE);
2991     g_return_val_if_fail (mhsd_seek >= 0, FALSE);
2992 
2993     cts = fimp->fcontents;
2994 
2995     g_return_val_if_fail (check_header_seek (cts, "mhsd", mhsd_seek),
2996 			  FALSE);
2997     mhsd_type = get32lint (cts, mhsd_seek+12);
2998 
2999     /* The mhlp header should be the next after the mhsd header. In
3000        order to allow slight changes in the format, we skip headers
3001        until we find an mhlp inside the given mhsd */
3002     mhlp_seek = find_next_a_in_b (cts, "mhlp", mhsd_seek, mhsd_seek);
3003     CHECK_ERROR (fimp, FALSE);
3004 
3005     if (mhlp_seek == -1)
3006     {
3007 	set_error_a_not_found_in_b (&fimp->error,
3008 				    "mhlp", "mhsd", mhsd_seek);
3009 	return FALSE;
3010     }
3011     /* Now we are at the mhlp */
3012 
3013     nr_playlists = get32lint (cts, mhlp_seek+8);
3014     CHECK_ERROR (fimp, FALSE);
3015 
3016     /* Create track-id tree for quicker track lookup */
3017     fimp->idtree = itdb_track_id_tree_create (fimp->itdb);
3018 
3019     seek = find_next_a_in_b (cts, "mhyp", mhsd_seek, mhlp_seek);
3020     CHECK_ERROR (fimp, FALSE);
3021     /* seek should now point to the first mhit */
3022     for (i=0; i<nr_playlists; ++i)
3023     {
3024 	/* seek could be -1 if first mhyp could not be found */
3025 	if (seek != -1)
3026 	    seek = get_playlist (fimp, mhsd_type, seek);
3027 	if (fimp->error) return FALSE;
3028 	if (seek == -1)
3029 	{   /* this should not be -- issue warning */
3030 	    g_warning (_("iTunesDB possibly corrupt: number of playlists (mhyp hunks) inconsistent. Trying to continue.\n"));
3031 	    break;
3032 	}
3033     }
3034 
3035     itdb_track_id_tree_destroy (fimp->idtree);
3036     fimp->idtree = NULL;
3037 
3038     return TRUE;
3039 }
3040 
parse_genius_mhsd(FImport * fimp,glong mhsd_seek)3041 static gboolean parse_genius_mhsd(FImport *fimp, glong mhsd_seek)
3042 {
3043     FContents *cts;
3044     guint32 hdrlen, mhsdlen;
3045     gint32 len;
3046     gchar *genius_cuid;
3047 
3048     g_return_val_if_fail (fimp, FALSE);
3049     g_return_val_if_fail (fimp->itdb, FALSE);
3050     g_return_val_if_fail (fimp->fcontents, FALSE);
3051     g_return_val_if_fail (fimp->fcontents->filename, FALSE);
3052     g_return_val_if_fail (mhsd_seek >= 0, FALSE);
3053 
3054     cts = fimp->fcontents;
3055 
3056     g_return_val_if_fail (check_header_seek (cts, "mhsd", mhsd_seek),
3057 			  FALSE);
3058 
3059     hdrlen = get32lint (cts, mhsd_seek+4);
3060     mhsdlen = get32lint (cts, mhsd_seek+8);
3061 
3062     len = mhsdlen - hdrlen;
3063     if (len < 0) {
3064 	return FALSE;
3065     }
3066 
3067     if (len != 32) {
3068 	g_warning(_("%s: Unexpected length %d for genius_cuid!\n"), __func__, len);
3069     }
3070 
3071     genius_cuid = g_new0(gchar, len+1);
3072     if (!seek_get_n_bytes (cts, genius_cuid, mhsd_seek+hdrlen, len)) {
3073 	g_free (genius_cuid);
3074 	return FALSE;
3075     }
3076 
3077     fimp->itdb->priv->genius_cuid = genius_cuid;
3078 
3079     return TRUE;
3080 }
3081 
looks_like_itunesdb(FImport * fimp)3082 static gboolean looks_like_itunesdb (FImport *fimp)
3083 {
3084     FContents *cts;
3085 
3086     cts = fimp->fcontents;
3087 
3088     /* Check if it looks like an iTunesDB */
3089     if (!check_header_seek (cts, "mhbd", 0))
3090     {
3091 	fcontents_set_reversed (cts, TRUE);
3092 	if (cts->error) return 0;
3093 	if (!check_header_seek (cts, "mhbd", 0))
3094 	{
3095 	    if (!cts->error)
3096 	    {   /* set error */
3097 		g_set_error (&cts->error,
3098 			     ITDB_FILE_ERROR,
3099 			     ITDB_FILE_ERROR_CORRUPT,
3100 			     _("Not a iTunesDB: '%s' (missing mhdb header)."),
3101 			     cts->filename);
3102 	    }
3103 	    return FALSE;
3104 	}
3105     }
3106     return TRUE;
3107 }
3108 
parse_fimp(FImport * fimp,gboolean compressed)3109 static gboolean parse_fimp (FImport *fimp, gboolean compressed)
3110 {
3111     glong seek=0;
3112     FContents *cts;
3113     glong mhsd_1, mhsd_2, mhsd_3, mhsd_5, mhsd_9;
3114 
3115     g_return_val_if_fail (fimp, FALSE);
3116     g_return_val_if_fail (fimp->itdb, FALSE);
3117     g_return_val_if_fail (fimp->fcontents, FALSE);
3118     g_return_val_if_fail (fimp->fcontents->filename, FALSE);
3119 
3120     if (!looks_like_itunesdb (fimp)) {
3121 	return FALSE;
3122     }
3123 
3124     if (compressed) {
3125 	itdb_zlib_check_decompress_fimp (fimp);
3126     }
3127 
3128     cts = fimp->fcontents;
3129 
3130     /* get the positions of the various mhsd */
3131     /* type 1: track list */
3132     mhsd_1 = find_mhsd (cts, 1);
3133     CHECK_ERROR (fimp, FALSE);
3134     /* type 2: standard playlist section -- Podcasts playlist will be
3135        just an ordinary playlist */
3136     mhsd_2 = find_mhsd (cts, 2);
3137     CHECK_ERROR (fimp, FALSE);
3138     /* type 3: playlist section with special version of Podcasts
3139        playlist (optional) */
3140     mhsd_3 = find_mhsd (cts, 3);
3141     CHECK_ERROR (fimp, FALSE);
3142 
3143     /* read smart playlists */
3144     mhsd_5 = find_mhsd (cts, 5);
3145     CHECK_ERROR (fimp, FALSE);
3146 
3147     /* read genius cuid mhsd */
3148     mhsd_9 = find_mhsd (cts, 9);
3149     CHECK_ERROR (fimp, FALSE);
3150 
3151     fimp->itdb->version = get32lint (cts, seek+0x10);
3152     CHECK_ERROR (fimp, FALSE);
3153     fimp->itdb->id = get64lint (cts, seek+0x18);
3154     CHECK_ERROR (fimp, FALSE);
3155     fimp->itdb->priv->platform = get16lint (cts, seek+0x20);
3156     CHECK_ERROR (fimp, FALSE);
3157     fimp->itdb->priv->unk_0x22 = get16lint (cts, seek+0x22);
3158     CHECK_ERROR (fimp, FALSE);
3159     fimp->itdb->priv->id_0x24 = get64lint (cts, seek+0x24);
3160     CHECK_ERROR (fimp, FALSE);
3161     fimp->itdb->priv->lang = get16lint (cts, seek+0x46);
3162     CHECK_ERROR (fimp, FALSE);
3163     fimp->itdb->priv->pid = get64lint (cts, seek+0x48);
3164     CHECK_ERROR (fimp, FALSE);
3165     fimp->itdb->priv->unk_0x50 = get32lint (cts, seek+0x50);
3166     CHECK_ERROR (fimp, FALSE);
3167     fimp->itdb->priv->unk_0x54 = get32lint (cts, seek+0x54);
3168     CHECK_ERROR (fimp, FALSE);
3169     fimp->itdb->tzoffset = get32lint (cts, seek+0x6c);
3170     CHECK_ERROR (fimp, FALSE);
3171     fimp->itdb->priv->audio_language = get16lint (cts, seek+0xA0);
3172     CHECK_ERROR (fimp, FALSE);
3173     fimp->itdb->priv->subtitle_language = get16lint (cts, seek+0xA2);
3174     CHECK_ERROR (fimp, FALSE);
3175     fimp->itdb->priv->unk_0xa4 = get16lint (cts, seek+0xA4);
3176     CHECK_ERROR (fimp, FALSE);
3177     fimp->itdb->priv->unk_0xa6 = get16lint (cts, seek+0xA6);
3178     CHECK_ERROR (fimp, FALSE);
3179     fimp->itdb->priv->unk_0xa8 = get16lint (cts, seek+0xA8);
3180     CHECK_ERROR (fimp, FALSE);
3181     if(fimp->itdb->priv->unk_0xa8 != 0) {
3182 	g_warning ("Unknown value for 0xa8 in header: should be 0 for uncompressed, is %d.\n", fimp->itdb->priv->unk_0xa8);
3183     }
3184 
3185     if (mhsd_1 == -1)
3186     {   /* Very bad: no type 1 mhsd which should hold the tracklist */
3187 	g_set_error (&fimp->error,
3188 		     ITDB_FILE_ERROR,
3189 		     ITDB_FILE_ERROR_CORRUPT,
3190 		     _("iTunesDB '%s' corrupt: Could not find tracklist (no mhsd type 1 section found)"),
3191 		     cts->filename);
3192 	return FALSE;
3193     }
3194 
3195     /* copy the 'reversed endian flag' */
3196     if (cts->reversed) {
3197       fimp->itdb->device->byte_order = G_BIG_ENDIAN;
3198     } else {
3199       fimp->itdb->device->byte_order = G_LITTLE_ENDIAN;
3200     }
3201 #if 0
3202     fimp->itdb->device->endianess_set = TRUE;
3203     fimp->itdb->device->endianess_reversed = cts->reversed;
3204 #endif
3205 
3206     parse_tracks (fimp, mhsd_1);
3207     if (fimp->error) return FALSE;
3208 
3209     if (mhsd_3 != -1)
3210 	parse_playlists (fimp, mhsd_3);
3211     else if (mhsd_2 != -1)
3212 	parse_playlists (fimp, mhsd_2);
3213     else
3214     {  /* Very bad: no type 2 or type 3 mhsd which should hold the
3215 	  playlists */
3216 	g_set_error (&fimp->error,
3217 		     ITDB_FILE_ERROR,
3218 		     ITDB_FILE_ERROR_CORRUPT,
3219 		     _("iTunesDB '%s' corrupt: Could not find playlists (no mhsd type 2 or type 3 sections found)"),
3220 		     cts->filename);
3221 	return FALSE;
3222     }
3223 
3224     if (mhsd_5 != -1) {
3225 	parse_playlists (fimp, mhsd_5);
3226     }
3227 
3228     if (mhsd_9 != -1) {
3229 	parse_genius_mhsd (fimp, mhsd_9);
3230     }
3231 
3232     return TRUE;
3233 }
3234 
3235 
3236 
3237 /* Set @error with standard error message */
error_no_itunes_dir(const gchar * mp,GError ** error)3238 static void error_no_itunes_dir (const gchar *mp, GError **error)
3239 {
3240     gchar *str;
3241 
3242     g_return_if_fail (mp);
3243     g_return_if_fail (error);
3244 
3245     str = g_build_filename (mp, "iPod_Control", "iTunes", NULL);
3246     g_set_error (error,
3247 		 ITDB_FILE_ERROR,
3248 		 ITDB_FILE_ERROR_NOTFOUND,
3249 		 _("iTunes directory not found: '%s' (or similar)."),
3250 		 str);
3251     g_free (str);
3252 }
3253 
3254 /* Set @error with standard error message */
error_no_music_dir(const gchar * mp,GError ** error)3255 static void error_no_music_dir (const gchar *mp, GError **error)
3256 {
3257     gchar *str;
3258 
3259     g_return_if_fail (mp);
3260     g_return_if_fail (error);
3261 
3262     str = g_build_filename (mp, "iPod_Control", "Music", NULL);
3263     g_set_error (error,
3264 		 ITDB_FILE_ERROR,
3265 		 ITDB_FILE_ERROR_NOTFOUND,
3266 		 _("Music directory not found: '%s' (or similar)."),
3267 		 str);
3268     g_free (str);
3269 }
3270 
3271 #if 0
3272 /* Set @error with standard error message */
3273 static void error_no_control_dir (const gchar *mp, GError **error)
3274 {
3275     gchar *str;
3276 
3277     g_return_if_fail (mp);
3278     g_return_if_fail (error);
3279 
3280     str = g_build_filename (mp, "iPod_Control", NULL);
3281     g_set_error (error,
3282 		 ITDB_FILE_ERROR,
3283 		 ITDB_FILE_ERROR_NOTFOUND,
3284 		 _("Control directory not found: '%s' (or similar)."),
3285 		 str);
3286     g_free (str);
3287 }
3288 #endif
3289 
3290 
3291 static gboolean
itdb_parse_internal(Itdb_iTunesDB * itdb,gboolean compressed,GError ** error)3292 itdb_parse_internal (Itdb_iTunesDB *itdb, gboolean compressed, GError **error)
3293 {
3294     FImport *fimp;
3295     gboolean success = FALSE;
3296 
3297     g_return_val_if_fail (itdb->filename != NULL, FALSE);
3298 
3299     fimp = g_new0 (FImport, 1);
3300     fimp->itdb = itdb;
3301 
3302     fimp->fcontents = fcontents_read (itdb->filename, error);
3303 
3304     if (fimp->fcontents)
3305     {
3306 	itdb_hash72_extract_hash_info (fimp->itdb->device,
3307 				       (guchar *)fimp->fcontents->contents,
3308 				       fimp->fcontents->length);
3309 	if (!playcounts_init (fimp))
3310 	{
3311 	    g_warning ("Error parsing recent playcounts");
3312 	}
3313 	if (parse_fimp (fimp, compressed))
3314 	{
3315 	    if (read_OTG_playlists (fimp))
3316 	    {
3317 		success = TRUE;
3318 	    }
3319 	}
3320     }
3321 
3322     if (fimp->error)
3323 	g_propagate_error (error, fimp->error);
3324 
3325     itdb_free_fimp (fimp);
3326 
3327     return success;
3328 }
3329 
3330 /**
3331  * itdb_parse:
3332  * @mp:     mount point of the iPod (eg "/mnt/ipod") in local encoding
3333  * @error:  return location for a #GError or NULL
3334  *
3335  * Parse the Itdb_iTunesDB of the iPod located at @mp
3336  *
3337  * Returns: a newly allocated #Itdb_iTunesDB struct holding the tracks and
3338  * the playlists present on the iPod at @mp, NULL if @mp isn't an iPod mount
3339  * point. If non-NULL, the #Itdb_iTunesDB is to be freed with itdb_free() when
3340  * it's no longer needed
3341  */
itdb_parse(const gchar * mp,GError ** error)3342 Itdb_iTunesDB *itdb_parse (const gchar *mp, GError **error)
3343 {
3344     gchar *filename;
3345     Itdb_iTunesDB *itdb = NULL;
3346     gboolean compressed = FALSE;
3347 
3348     filename = itdb_get_itunescdb_path (mp);
3349     if (!filename) {
3350 	filename = itdb_get_itunesdb_path (mp);
3351     } else {
3352 	compressed = TRUE;
3353     }
3354     if (filename)
3355     {
3356 	itdb = itdb_new ();
3357 
3358 	if (itdb)
3359 	{
3360 	    gboolean success;
3361 
3362 	    itdb_set_mountpoint (itdb, mp);
3363 	    itdb->filename = g_strdup (filename);
3364 	    success = itdb_parse_internal (itdb, compressed, error);
3365 	    if (success)
3366 	    {
3367 		/* We don't test the return value of ipod_parse_artwork_db
3368 		 * since the database content will be consistent even if
3369 		 * we fail to get the various thumbnails, we ignore the
3370 		 * error since older ipods don't have thumbnails.
3371 
3372 		 * FIXME: this probably should go into itdb_parse_file,
3373 		 * but I don't understand its purpose, and
3374 		 * ipod_parse_artwork_db needs the mountpoint field from
3375 		 * the itdb, which may not be available in the other
3376 		 * function
3377 
3378 		 * JCS: itdb_parse_file is used to read local repositories
3379 		 * (usually repositories stored in
3380 		 * ~/.gtkpod). ipod_parse_artwork_db (and the
3381 		 * corresponding artbook write function) should probably
3382 		 * be expanded to look for (write) the required files into
3383 		 * the same directory as itdb->filename in case
3384 		 * itdb->mountpoint does not exist. Because several local
3385 		 * repositories may exist in the same directory, the names
3386 		 * should be modified by the repository name.
3387 		 */
3388 		ipod_parse_artwork_db (itdb);
3389 	    }
3390 	    else
3391 	    {
3392 		itdb_free (itdb);
3393 		itdb = NULL;
3394 	    }
3395 	}
3396     }
3397     else
3398     {
3399 	g_set_error (error,
3400 		     ITDB_FILE_ERROR,
3401 		     ITDB_FILE_ERROR_NOTFOUND,
3402 		     _("Couldn't find an iPod database on %s."),
3403 		     mp);
3404     }
3405     g_free (filename);
3406     return itdb;
3407 }
3408 
3409 /**
3410  * itdb_parse_file:
3411  * @filename:   path to a file in iTunesDB format
3412  * @error:      return location for a #GError or NULL
3413  *
3414  *  Same as itunesdb_parse(), but filename is specified directly.
3415  *
3416  * Returns: a newly allocated #Itdb_iTunesDB struct holding the tracks and
3417  * the playlists present in @filename, NULL if @filename isn't a parsable
3418  * iTunesDB file. If non-NULL, the #Itdb_iTunesDB is to be freed with
3419  * itdb_free() when it's no longer needed
3420  */
itdb_parse_file(const gchar * filename,GError ** error)3421 Itdb_iTunesDB *itdb_parse_file (const gchar *filename, GError **error)
3422 {
3423     Itdb_iTunesDB *itdb;
3424     gboolean success;
3425 
3426     g_return_val_if_fail (filename, NULL);
3427 
3428     itdb = itdb_new ();
3429     itdb->filename = g_strdup (filename);
3430 
3431     success = itdb_parse_internal (itdb, FALSE, error);
3432     if (!success)
3433     {
3434 	itdb_free (itdb);
3435 	itdb = NULL;
3436     }
3437 
3438     return itdb;
3439 }
3440 
3441 
3442 /* up to here we had the functions for reading the iTunesDB               */
3443 /* ---------------------------------------------------------------------- */
3444 /* from here on we have the functions for writing the iTunesDB            */
3445 
3446 /* will expand @cts when necessary in order to accomodate @len bytes
3447    starting at @seek */
wcontents_maybe_expand(WContents * cts,gulong len,gulong seek)3448 static void wcontents_maybe_expand (WContents *cts, gulong len,
3449 				    gulong seek)
3450 {
3451     g_return_if_fail (cts);
3452 
3453     while (cts->pos+len > cts->total)
3454     {
3455 	cts->total += WCONTENTS_STEPSIZE;
3456 	cts->contents = g_realloc (cts->contents, cts->total);
3457     }
3458 }
3459 
3460 
3461 /* Write @data, @n bytes long to position @seek. Will always be
3462  * successful because glib terminates when out of memory */
put_data_seek(WContents * cts,gchar * data,gulong len,gulong seek)3463 static void put_data_seek (WContents *cts, gchar *data,
3464 			   gulong len, gulong seek)
3465 {
3466     g_return_if_fail (cts);
3467 
3468     if (len != 0)
3469     {
3470 	g_return_if_fail (data);
3471 	wcontents_maybe_expand (cts, len, seek);
3472 
3473 	memcpy (&cts->contents[seek], data, len);
3474 	/* adjust end position if necessary */
3475 	if (seek+len > cts->pos)
3476 	    cts->pos = seek+len;
3477     }
3478 }
3479 
3480 
3481 
3482 /* Write @data, @n bytes long to end of @cts. Will always be
3483  * successful because glib terminates when out of memory */
put_data(WContents * cts,gchar * data,gulong len)3484 static void put_data (WContents *cts, gchar *data, gulong len)
3485 {
3486     g_return_if_fail (cts);
3487 
3488     put_data_seek (cts, data, len, cts->pos);
3489 }
3490 
3491 
3492 /* Write @string without trailing Null to end of @cts. Will always be
3493  * successful because glib terminates when out of memory */
put_string(WContents * cts,gchar * string)3494 static void put_string (WContents *cts, gchar *string)
3495 {
3496     g_return_if_fail (cts);
3497     g_return_if_fail (string);
3498 
3499     put_data (cts, string, strlen(string));
3500 }
3501 
3502 /* Write 4-byte long @header identifcation taking into account
3503  * possible reversed endianess */
put_header(WContents * cts,gchar * header)3504 static void put_header (WContents *cts, gchar *header)
3505 {
3506     gchar rdata[4];
3507     gint i, offset, sign;
3508 
3509 
3510     g_return_if_fail (cts);
3511     g_return_if_fail (header);
3512     g_return_if_fail (strlen (header) == 4);
3513 
3514     /* reverse data for write if necessary */
3515     if (cts->reversed)
3516     {
3517 	offset = 3;
3518 	sign = -1;
3519     }
3520     else
3521     {
3522 	offset = 0;
3523 	sign = 1;
3524     }
3525     for (i=0; i<4; ++i)
3526     {
3527 	    rdata[i] = header[offset + sign*i];
3528     }
3529 
3530     put_data (cts, rdata, 4);
3531 }
3532 
3533 
3534 
3535 /* ------------------------------------------------------------
3536    Little Endian
3537    ------------------------------------------------------------ */
3538 
3539 /* Write 2-byte integer @n to @cts in little endian order. */
raw_put16lint(WContents * cts,guint16 n)3540 static void raw_put16lint (WContents *cts, guint16 n)
3541 {
3542     n = GUINT16_TO_LE (n);
3543     put_data (cts, (gchar *)&n, 2);
3544 }
3545 
3546 /* Write 3-byte integer @n to @cts in big endian order. */
raw_put24lint(WContents * cts,guint32 n)3547 static void raw_put24lint (WContents *cts, guint32 n)
3548 {
3549     gchar buf[3] ;
3550     buf[2] = (n >> 16) & 0xff ;
3551     buf[1] = (n >> 8)  & 0xff ;
3552     buf[0] = (n >> 0)  & 0xff ;
3553     put_data (cts, buf, 3);
3554 }
3555 
3556 
3557 /* Write 4-byte integer @n to @cts in little endian order. */
raw_put32lint(WContents * cts,guint32 n)3558 static void raw_put32lint (WContents *cts, guint32 n)
3559 {
3560     n = GUINT32_TO_LE (n);
3561     put_data (cts, (gchar *)&n, 4);
3562 }
3563 
3564 /* Write 4 byte floating number */
raw_put32lfloat(WContents * cts,float f)3565 static void raw_put32lfloat (WContents *cts, float f)
3566 {
3567     union
3568     {
3569 	guint32 i;
3570 	float   f;
3571     } flt;
3572 
3573     if (sizeof (float) != 4)
3574     {
3575 	raw_put32lint (cts, 0);
3576 	g_return_if_reached ();
3577     }
3578 
3579     flt.f = f;
3580     raw_put32lint (cts, flt.i);
3581 }
3582 
3583 
3584 /* Write 4-byte integer @n to @cts at position @seek in little
3585    endian order. */
raw_put32lint_seek(WContents * cts,guint32 n,gulong seek)3586 static void raw_put32lint_seek (WContents *cts, guint32 n, gulong seek)
3587 {
3588     n = GUINT32_TO_LE (n);
3589     put_data_seek (cts, (gchar *)&n, 4, seek);
3590 }
3591 
3592 
3593 /* Write 8-byte integer @n to @cts in big little order. */
raw_put64lint(WContents * cts,guint64 n)3594 static void raw_put64lint (WContents *cts, guint64 n)
3595 {
3596     n = GUINT64_TO_LE (n);
3597     put_data (cts, (gchar *)&n, 8);
3598 }
3599 
3600 
3601 /* ------------------------------------------------------------
3602    Big Endian
3603    ------------------------------------------------------------ */
3604 
3605 /* Write 2-byte integer @n to @cts in big endian order. */
raw_put16bint(WContents * cts,guint16 n)3606 static void raw_put16bint (WContents *cts, guint16 n)
3607 {
3608     n = GUINT16_TO_BE (n);
3609     put_data (cts, (gchar *)&n, 2);
3610 }
3611 
3612 /* Write 3-byte integer @n to @cts in big endian order. */
raw_put24bint(WContents * cts,guint32 n)3613 static void raw_put24bint (WContents *cts, guint32 n)
3614 {
3615     gchar buf[3] ;
3616     buf[0] = (n >> 16) & 0xff ;
3617     buf[1] = (n >> 8)  & 0xff ;
3618     buf[2] = (n >> 0)  & 0xff ;
3619     put_data (cts, buf, 3);
3620 }
3621 
3622 /* Write 4-byte integer @n to @cts in big endian order. */
raw_put32bint(WContents * cts,guint32 n)3623 static void raw_put32bint (WContents *cts, guint32 n)
3624 {
3625     n = GUINT32_TO_BE (n);
3626     put_data (cts, (gchar *)&n, 4);
3627 }
3628 
3629 
3630 /* Write 4-byte integer @n to @cts at position @seek in big
3631    endian order. */
raw_put32bint_seek(WContents * cts,guint32 n,gulong seek)3632 static void raw_put32bint_seek (WContents *cts, guint32 n, gulong seek)
3633 {
3634     n = GUINT32_TO_BE (n);
3635     put_data_seek (cts, (gchar *)&n, 4, seek);
3636 }
3637 
3638 
3639 /* Write 4 byte floating number */
raw_put32bfloat(WContents * cts,float f)3640 static void raw_put32bfloat (WContents *cts, float f)
3641 {
3642     union
3643     {
3644 	guint32 i;
3645 	float   f;
3646     } flt;
3647 
3648     if (sizeof (float) != 4)
3649     {
3650 	raw_put32bint (cts, 0);
3651 	g_return_if_reached ();
3652     }
3653 
3654     flt.f = f;
3655     raw_put32bint (cts, flt.i);
3656 }
3657 
3658 
3659 /* Write 8-byte integer @n to cts in big endian order. */
raw_put64bint(WContents * cts,guint64 n)3660 static void raw_put64bint (WContents *cts, guint64 n)
3661 {
3662     n = GUINT64_TO_BE (n);
3663     put_data (cts, (gchar *)&n, 8);
3664 }
3665 
3666 
3667 /* ------------------------------------------------------------
3668    Not Endian Dependent
3669    ------------------------------------------------------------ */
3670 
3671 /* Write 1-byte integer @n to @cts */
put8int(WContents * cts,guint8 n)3672 static void put8int (WContents *cts, guint8 n)
3673 {
3674     put_data (cts, (gchar *)&n, 1);
3675 }
3676 
3677 
3678 /* Append @n times 2-byte-long zeros */
put16_n0(WContents * cts,gulong n)3679 static void put16_n0 (WContents *cts, gulong n)
3680 {
3681     g_return_if_fail (cts);
3682 
3683     if (n>0)
3684     {
3685 	wcontents_maybe_expand (cts, 2*n, cts->pos);
3686 	memset (&cts->contents[cts->pos], 0, 2*n);
3687 	cts->pos += 2*n;
3688     }
3689 }
3690 
3691 /* Append @n times 4-byte-long zeros */
put32_n0(WContents * cts,gulong n)3692 static void put32_n0 (WContents *cts, gulong n)
3693 {
3694     g_return_if_fail (cts);
3695 
3696     if (n>0)
3697     {
3698 	wcontents_maybe_expand (cts, 4*n, cts->pos);
3699 	memset (&cts->contents[cts->pos], 0, 4*n);
3700 	cts->pos += 4*n;
3701     }
3702 }
3703 
3704 
3705 /* ------------------------------------------------------------
3706    Reversed Endian Sensitive (little endian)
3707    ------------------------------------------------------------ */
3708 
put16lint(WContents * cts,guint16 n)3709 static void put16lint (WContents *cts, guint16 n)
3710 {
3711     if (!cts->reversed)
3712 	raw_put16lint (cts, n);
3713     else
3714 	raw_put16bint (cts, n);
3715 }
3716 
3717 #if 0
3718 static void put24lint (WContents *cts, guint32 n)
3719 {
3720     if (!cts->reversed)
3721 	raw_put24lint (cts, n);
3722     else
3723 	raw_put24bint (cts, n);
3724 }
3725 #endif
put32lint(WContents * cts,guint32 n)3726 static void put32lint (WContents *cts, guint32 n)
3727 {
3728     if (!cts->reversed)
3729 	raw_put32lint (cts, n);
3730     else
3731 	raw_put32bint (cts, n);
3732 }
3733 
put32lfloat(WContents * cts,float f)3734 static void put32lfloat (WContents *cts, float f)
3735 {
3736     if (!cts->reversed)
3737 	raw_put32lfloat (cts, f);
3738     else
3739 	raw_put32bfloat (cts, f);
3740 }
3741 
put32lint_seek(WContents * cts,guint32 n,gulong seek)3742 static void put32lint_seek (WContents *cts, guint32 n, gulong seek)
3743 {
3744     if (!cts->reversed)
3745 	raw_put32lint_seek (cts, n, seek);
3746     else
3747 	raw_put32bint_seek (cts, n, seek);
3748 }
3749 
3750 
put64lint(WContents * cts,guint64 n)3751 static void put64lint (WContents *cts, guint64 n)
3752 {
3753     if (!cts->reversed)
3754 	raw_put64lint (cts, n);
3755     else
3756 	raw_put64bint (cts, n);
3757 }
3758 
3759 /* ------------------------------------------------------------
3760    Reversed Endian Sensitive (big endian)
3761    ------------------------------------------------------------ */
put16bint(WContents * cts,guint16 n)3762 static void put16bint (WContents *cts, guint16 n)
3763 {
3764     if (cts->reversed)
3765 	raw_put16lint (cts, n);
3766     else
3767 	raw_put16bint (cts, n);
3768 }
3769 
3770 #if 0
3771 static void put16bint_seek (WContents *cts, guint16 n, gulong seek)
3772 {
3773 	if (cts->reversed)
3774 		raw_put16lint_seek (cts, n, seek);
3775 	else
3776 		raw_put16bint_seek (cts, n, seek);
3777 }
3778 #endif
put24bint(WContents * cts,guint32 n)3779 static void put24bint (WContents *cts, guint32 n)
3780 {
3781     if (cts->reversed)
3782 	raw_put24lint (cts, n);
3783     else
3784 	raw_put24bint (cts, n);
3785 }
3786 
put32bint(WContents * cts,guint32 n)3787 static void put32bint (WContents *cts, guint32 n)
3788 {
3789     if (cts->reversed)
3790 	raw_put32lint (cts, n);
3791     else
3792 	raw_put32bint (cts, n);
3793 }
3794 #if 0
3795 static void put32bfloat (WContents *cts, float f)
3796 {
3797     if (cts->reversed)
3798 	raw_put32lfloat (cts, f);
3799     else
3800 	raw_put32bfloat (cts, f);
3801 }
3802 #endif
3803 
put32bint_seek(WContents * cts,guint32 n,gulong seek)3804 static void put32bint_seek (WContents *cts, guint32 n, gulong seek)
3805 {
3806     if (cts->reversed)
3807 	raw_put32lint_seek (cts, n, seek);
3808     else
3809 	raw_put32bint_seek (cts, n, seek);
3810 }
3811 
put64bint(WContents * cts,guint64 n)3812 static void put64bint (WContents *cts, guint64 n)
3813 {
3814     if (cts->reversed)
3815 	raw_put64lint (cts, n);
3816     else
3817 	raw_put64bint (cts, n);
3818 }
3819 
3820 
3821 
3822 /* Write out the mhbd header. Size will be written later */
mk_mhbd(FExport * fexp,guint32 children)3823 static void mk_mhbd (FExport *fexp, guint32 children)
3824 {
3825   WContents *cts;
3826 
3827   g_return_if_fail (fexp);
3828   g_return_if_fail (fexp->itdb);
3829   g_return_if_fail (fexp->wcontents);
3830 
3831   cts = fexp->wcontents;
3832 
3833   put_header (cts, "mhbd");
3834   put32lint (cts, 244); /* header size */
3835   put32lint (cts, -1);  /* size of whole mhdb -- fill in later */
3836   if (itdb_device_supports_compressed_itunesdb (fexp->itdb->device)) {
3837       /* 2 on iPhone 3.0 and Nano 5G, 1 on iPod Color and iPod Classic
3838 	 the iPod Classic 3G  won't work if set to 2. Tying it to compressed
3839 	 database support but that's a long shot. */
3840       put32lint (cts, 2);
3841   } else {
3842       put32lint (cts, 1);
3843   }
3844 
3845   /* Version number: 0x01: iTunes 2
3846                      0x02: iTunes 3
3847 		     0x09: iTunes 4.2
3848 		     0x0a: iTunes 4.5
3849 		     0x0b: iTunes 4.7
3850 		     0x0c: iTunes 4.71/4.8 (required for shuffle)
3851                      0x0d: iTunes 4.9
3852                      0x0e: iTunes 5
3853 		     0x0f: iTunes 6
3854 		     0x10: iTunes 6.0.1(?)
3855 		     0x11: iTunes 6.0.2
3856                      0x12 = iTunes 6.0.5.
3857 		     0x13 = iTunes 7
3858                      0x14 = iTunes 7.1
3859                      0x15 = iTunes 7.2
3860                      0x19 = iTunes 7.4
3861                      0x28 = iTunes 8.2.1
3862 		     0x2a = iTunes 9.0.1
3863 		     0x2e = iTunes 9.1
3864 		     0x30 = iTunes 9.2
3865     Be aware that newer ipods won't work if the library version number is too
3866     old
3867   */
3868   fexp->itdb->version = 0x30;
3869   put32lint (cts, fexp->itdb->version);
3870   put32lint (cts, children);
3871   put64lint (cts, fexp->itdb->id);
3872   /* 0x20 */
3873   put16lint (cts, fexp->itdb->priv->platform); /* OS type, 1 = MacOS X, 2 = Windows */
3874 
3875   /* 0x22 */
3876   put16lint (cts, fexp->itdb->priv->unk_0x22);  /* unknown */
3877   put64lint (cts, fexp->itdb->priv->id_0x24); /* unkown id */
3878   put32lint (cts, 0);  /* unknown */
3879   /* 0x30 */
3880   put16lint (cts, 0);   /* set hashing scheme to 0 for now, will be set
3881 			 * to the appropriate value in
3882 			 * itdb_device_write_checksum */
3883   put16_n0  (cts, 10);  /* unknown */
3884   /* 0x46 */
3885   put16lint (cts, fexp->itdb->priv->lang);   /* language (e.g. 'de' for German) */
3886   put64lint (cts, fexp->itdb->priv->pid);   /* library persistent ID */
3887   /* 0x50 */
3888   put32lint (cts, fexp->itdb->priv->unk_0x50);  /* unknown: seen: 0x05 for nano 3G */
3889                                                 /* seen: 0x01 for iPod Color       */
3890   put32lint (cts, fexp->itdb->priv->unk_0x54);  /* unknown: seen: 0x4d for nano 3G */
3891   				         	/* seen: 0x0f for iPod Color       */
3892   put32_n0 (cts, 5);    /* 20 bytes hash58 */
3893   put32lint (cts, fexp->itdb->tzoffset);   /* timezone offset in seconds */
3894   /* 0x70 */
3895   switch (itdb_device_get_checksum_type(fexp->itdb->device)) {
3896     case ITDB_CHECKSUM_HASHAB:
3897       put16lint (cts, 4);   /* new on 4th gen iOS devices */
3898       break;
3899     case ITDB_CHECKSUM_HASH72:
3900       put16lint (cts, 2);
3901       break;
3902     default:
3903       put16lint (cts, 0);
3904       break;
3905   }
3906   put16lint (cts, 0);   /* hash72 */
3907   put32_n0 (cts, 11);   /* hash72 */
3908   /* 0xa0 */
3909   put16lint (cts, fexp->itdb->priv->audio_language); /* audio_language */
3910   put16lint (cts, fexp->itdb->priv->subtitle_language); /* subtitle_language */
3911   put16lint (cts, fexp->itdb->priv->unk_0xa4); /* unknown */
3912   put16lint (cts, fexp->itdb->priv->unk_0xa6); /* unknown */
3913   put16lint (cts, fexp->itdb->priv->unk_0xa8); /* unknown */
3914   put8int (cts, 0);
3915   /* 0xab */
3916   put8int (cts, 0);   /* hashAB */
3917   put32_n0 (cts, 14); /* hashAB */
3918   put32_n0 (cts, 4); /* dummy space */
3919 }
3920 
3921 /* Fill in the length of a standard header */
fix_header(WContents * cts,gulong header_seek)3922 static void fix_header (WContents *cts, gulong header_seek)
3923 {
3924   put32lint_seek (cts, cts->pos-header_seek, header_seek+8);
3925 }
3926 
3927 /* Fill in the length of a short header which are common in the itunesSD */
fix_short_header(WContents * cts,gulong header_seek)3928 static void fix_short_header (WContents *cts, gulong header_seek)
3929 {
3930 	put32lint_seek (cts, cts->pos-header_seek, header_seek+4);
3931 }
3932 
3933 /* Write out the mhsd header. Size will be written later */
mk_mhsd(FExport * fexp,guint32 type)3934 static void mk_mhsd (FExport *fexp, guint32 type)
3935 {
3936   WContents *cts;
3937 
3938   g_return_if_fail (fexp);
3939   g_return_if_fail (fexp->itdb);
3940   g_return_if_fail (fexp->wcontents);
3941 
3942   cts = fexp->wcontents;
3943 
3944   put_header (cts, "mhsd");
3945   put32lint (cts, 96);   /* Headersize */
3946   put32lint (cts, -1);   /* size of whole mhsd -- fill in later */
3947   put32lint (cts, type); /* type: 1 = track, 2 = playlist */
3948   put32_n0 (cts, 20);      /* dummy space */
3949 }
3950 
3951 
3952 /* Write out the mhlt header. */
mk_mhlt(FExport * fexp,guint32 num)3953 static void mk_mhlt (FExport *fexp, guint32 num)
3954 {
3955   WContents *cts;
3956 
3957   g_return_if_fail (fexp);
3958   g_return_if_fail (fexp->itdb);
3959   g_return_if_fail (fexp->wcontents);
3960 
3961   cts = fexp->wcontents;
3962 
3963   put_header (cts, "mhlt");
3964   put32lint (cts, 92);       /* Headersize */
3965   put32lint (cts, num);      /* tracks in this itunesdb */
3966   put32_n0 (cts, 20);        /* dummy space */
3967 }
3968 
3969 
3970 /* Write out the mhit header. Size will be written later */
mk_mhit(WContents * cts,Itdb_Track * track)3971 static void mk_mhit (WContents *cts, Itdb_Track *track)
3972 {
3973   guint32 mac_time;
3974   g_return_if_fail (cts);
3975   g_return_if_fail (track);
3976 
3977   put_header (cts, "mhit");
3978   put32lint (cts, 0x248);/* header size */
3979   put32lint (cts, -1);   /* size of whole mhit -- fill in later */
3980   put32lint (cts, -1);   /* nr of mhods in this mhit -- later   */
3981   /* +0x10 */
3982   put32lint (cts, track->id); /* track index number */
3983 
3984   put32lint (cts, track->visible);
3985   put32lint (cts, track->filetype_marker);
3986   put8int (cts, track->type1);
3987   put8int (cts, track->type2);
3988   put8int   (cts, track->compilation);
3989   put8int   (cts, track->rating);
3990   /* +0x20 */
3991   mac_time = device_time_time_t_to_mac (track->itdb->device, track->time_modified);
3992   put32lint (cts, mac_time); /* timestamp               */
3993   put32lint (cts, track->size);    /* filesize                   */
3994   put32lint (cts, track->tracklen);/* length of track in ms      */
3995   put32lint (cts, track->track_nr);/* track number               */
3996   /* +0x30 */
3997   put32lint (cts, track->tracks);  /* number of tracks           */
3998   put32lint (cts, track->year);    /* the year                   */
3999   put32lint (cts, track->bitrate); /* bitrate                    */
4000   put32lint (cts, (((guint32)track->samplerate)<<16) |
4001 	     ((guint32)track->samplerate_low));
4002   /* +0x40 */
4003   put32lint (cts, track->volume);  /* volume adjust              */
4004   put32lint (cts, track->starttime);
4005   put32lint (cts, track->stoptime);
4006   put32lint (cts, track->soundcheck);
4007   /* +0x50 */
4008   put32lint (cts, track->playcount);/* playcount                 */
4009   /* track->playcount2 = track->playcount; */
4010   put32lint (cts, track->playcount2); /* playcount2, keep original value */
4011   mac_time = device_time_time_t_to_mac (track->itdb->device, track->time_played);
4012   put32lint (cts, mac_time); /* last time played       */
4013   put32lint (cts, track->cd_nr);   /* CD number                  */
4014   /* +0x60 */
4015   put32lint (cts, track->cds);     /* number of CDs              */
4016   put32lint (cts, track->drm_userid);
4017   mac_time = device_time_time_t_to_mac (track->itdb->device, track->time_added);
4018   put32lint (cts, mac_time); /* timestamp            */
4019   put32lint (cts, track->bookmark_time);
4020   /* +0x70 */
4021   put64lint (cts, track->dbid);
4022   if (track->checked)   put8int (cts, 1);
4023   else                  put8int (cts, 0);
4024   put8int (cts, track->app_rating);
4025   put16lint (cts, track->BPM);
4026   put16lint (cts, track->artwork_count);
4027   put16lint (cts, track->unk126);
4028   /* +0x80 */
4029   put32lint (cts, track->artwork_size);
4030   put32lint (cts, track->unk132);
4031   put32lfloat (cts, track->samplerate2);
4032   mac_time = device_time_time_t_to_mac (track->itdb->device, track->time_released);
4033   put32lint (cts, mac_time);
4034   /* +0x90 */
4035   put16lint (cts, track->unk144);
4036   put16lint (cts, track->explicit_flag);
4037   put32lint (cts, track->unk148);
4038   put32lint (cts, track->unk152);
4039   /* since iTunesDB version 0x0c */
4040   put32lint (cts, track->skipcount);
4041   /* +0xA0 */
4042   mac_time = device_time_time_t_to_mac (track->itdb->device, track->last_skipped);
4043   put32lint (cts, mac_time);
4044   put8int (cts, track->has_artwork);
4045   put8int (cts, track->skip_when_shuffling);
4046   put8int (cts, track->remember_playback_position);
4047   put8int (cts, track->flag4);
4048   put64lint (cts, track->dbid2);
4049   /* +0xB0 */
4050   put8int (cts, track->lyrics_flag);
4051   put8int (cts, track->movie_flag);
4052   put8int (cts, track->mark_unplayed);
4053   put8int (cts, track->unk179);
4054   put32lint (cts, track->unk180);
4055   put32lint (cts, track->pregap);
4056   put64lint (cts, track->samplecount);
4057   put32lint (cts, track->unk196);
4058   put32lint (cts, track->postgap);
4059   put32lint (cts, track->unk204);
4060   /* +0xD0 */
4061   put32lint (cts, track->mediatype);
4062   put32lint (cts, track->season_nr);
4063   put32lint (cts, track->episode_nr);
4064   put32lint (cts, track->unk220);
4065   /* +0xE0 */
4066   put32lint (cts, track->unk224);
4067   put32lint (cts, track->unk228);
4068   put32lint (cts, track->unk232);
4069   put32lint (cts, track->unk236);
4070   /* +0xF0 */
4071   put32lint (cts, track->unk240);
4072   put32lint (cts, track->unk244);
4073   put32lint (cts, track->gapless_data);
4074   put32lint (cts, track->unk252);
4075   /* +0x100 */
4076   put16lint (cts, track->gapless_track_flag);
4077   put16lint (cts, track->gapless_album_flag);
4078   put32_n0 (cts, 7);
4079   /* +0x120 */
4080   put32lint (cts, track->priv->album_id);
4081   put64lint (cts, track->itdb->priv->id_0x24); /* same as mhbd+0x24, purpose unknown */
4082   put32lint (cts, track->size); /* seems to be filesize again */
4083   /* +0x130 */
4084   put32lint (cts, 0);
4085   put64lint (cts, 0x808080808080LL);  /* what the heck is this?! */
4086   put32lint (cts, 0);
4087   /* +0x140 */
4088   put32_n0 (cts, 2);
4089   if ((track->mediatype & ITDB_MEDIATYPE_EPUB_BOOK)
4090       || (track->mediatype & ITDB_MEDIATYPE_PDF_BOOK)) {
4091       put16lint (cts, 1);
4092       put16lint (cts, 1);
4093   } else {
4094       put32lint (cts, 0);
4095   }
4096   put32_n0 (cts, 5);
4097   /* +0x160 */
4098   /* mhii_link is needed on fat nanos/ipod classic to get artwork
4099    * in the right sidepane. This matches mhii::song_id in the ArtworkDB */
4100   put32lint (cts, track->mhii_link);
4101   put32lint (cts, 0);
4102   put32lint (cts, 1);  /* unknown */
4103   put32lint (cts, 0);
4104   /* +0x170 */
4105   put32_n0 (cts, 28);
4106   /* +0x1E0 */
4107   put32lint (cts, track->priv->artist_id);
4108   put32_n0 (cts, 4);
4109   /* +0x1F4 */
4110   put32lint (cts, track->priv->composer_id); /* FIXME: this is another ID for something, composer?! */
4111   put32_n0 (cts, 20); /* padding */
4112 }
4113 
4114 
4115 /* Fill in the missing items of the mhit header:
4116    total size and number of mhods */
fix_mhit(WContents * cts,gulong mhit_seek,guint32 mhod_num)4117 static void fix_mhit (WContents *cts, gulong mhit_seek, guint32 mhod_num)
4118 {
4119   g_return_if_fail (cts);
4120 
4121   /* size of whole mhit */
4122   put32lint_seek (cts, cts->pos-mhit_seek, mhit_seek+8);
4123   /* nr of mhods */
4124   put32lint_seek (cts, mhod_num, mhit_seek+12);
4125 }
4126 
4127 /* Returns:
4128    - track->sort_artist if track->sort_artist is not NULL
4129 
4130    - 'Artist, The' if track->sort_artist is NULL and track->artist of
4131      of type 'The Artist'.
4132 
4133    - NULL if track->sort_artist is NULL and track->artist is not of
4134      type 'The Artist'.
4135 
4136    You must g_free() the returned string */
get_sort_artist(Itdb_Track * track)4137 static gchar *get_sort_artist (Itdb_Track *track)
4138 {
4139     g_return_val_if_fail (track, NULL);
4140 
4141     if (track->sort_artist && *track->sort_artist)
4142     {
4143 	return g_strdup (track->sort_artist);
4144     }
4145 
4146     if (!(track->artist && *track->artist))
4147     {
4148 	return NULL;
4149     }
4150 
4151     /* check if artist is of type 'The Artist' */
4152     if (g_ascii_strncasecmp ("The ", track->artist, 4) == 0)
4153     {   /* return 'Artist, The', followed by five 0x01 chars
4154 	   (analogous to iTunes) */
4155 	return g_strdup_printf ("%s, The%c%c%c%c%c",
4156 				track->artist+4,
4157 				0x01, 0x01, 0x01, 0x01, 0x01);
4158     }
4159 
4160     return NULL;
4161 }
4162 
4163 
4164 
4165 
mhod52_sort_title(const struct mhod52track * a,const struct mhod52track * b)4166 static gint mhod52_sort_title (const struct mhod52track *a, const struct mhod52track *b)
4167 {
4168     return strcmp (a->title, b->title);
4169 }
4170 
4171 
mhod52_sort_album(const struct mhod52track * a,const struct mhod52track * b)4172 static gint mhod52_sort_album (const struct mhod52track *a, const struct mhod52track *b)
4173 {
4174     gint result;
4175 
4176     result = strcmp (a->album, b->album);
4177     if (result == 0)
4178 	result = a->cd_nr - b->cd_nr;
4179     if (result == 0)
4180 	result = a->track_nr - b->track_nr;
4181     if (result == 0)
4182 	result = strcmp (a->title, b->title);
4183     return result;
4184 }
4185 
mhod52_sort_artist(struct mhod52track * a,struct mhod52track * b)4186 static gint mhod52_sort_artist (struct mhod52track *a, struct mhod52track *b)
4187 {
4188     gint result;
4189 
4190     result = strcmp (a->artist, b->artist);
4191     if (result == 0)
4192 	result = strcmp (a->album, b->album);
4193     if (result == 0)
4194 	result = a->cd_nr - b->cd_nr;
4195     if (result == 0)
4196 	result = a->track_nr - b->track_nr;
4197     if (result == 0)
4198 	result = strcmp (a->title, b->title);
4199     return result;
4200 }
4201 
mhod52_sort_genre(struct mhod52track * a,struct mhod52track * b)4202 static gint mhod52_sort_genre (struct mhod52track *a, struct mhod52track *b)
4203 {
4204     gint result;
4205 
4206     result = strcmp (a->genre, b->genre);
4207     if (result == 0)
4208 	result = strcmp (a->artist, b->artist);
4209     if (result == 0)
4210 	result = strcmp (a->album, b->album);
4211     if (result == 0)
4212 	result = a->cd_nr - b->cd_nr;
4213     if (result == 0)
4214 	result = a->track_nr - b->track_nr;
4215     if (result == 0)
4216 	result = strcmp (a->title, b->title);
4217     return result;
4218 }
4219 
mhod52_sort_composer(struct mhod52track * a,struct mhod52track * b)4220 static gint mhod52_sort_composer (struct mhod52track *a, struct mhod52track *b)
4221 {
4222     gint result;
4223 
4224     result = strcmp (a->composer, b->composer);
4225     if (result == 0)
4226 	result = strcmp (a->album, b->album);
4227     if (result == 0)
4228 	result = a->cd_nr - b->cd_nr;
4229     if (result == 0)
4230 	result = a->track_nr - b->track_nr;
4231     if (result == 0)
4232 	result = strcmp (a->title, b->title);
4233     return result;
4234 }
4235 
4236 
4237 /* Return the first alpha-numeric character in the string
4238    Return upper-case for alpha, return 0 for all numbers */
4239 
jump_table_letter(gchar * p)4240 static gunichar2 jump_table_letter (gchar *p)
4241 {
4242     gunichar chr = 0;
4243     gboolean found_alnum_chars = FALSE;
4244 
4245     g_return_val_if_fail (p != NULL, '0');
4246     g_return_val_if_fail (g_utf8_validate (p, -1, NULL), '0');
4247 
4248     while (*p != '\0') {
4249         chr = g_utf8_get_char (p);
4250         if (g_unichar_isalnum (chr))
4251         {
4252 	    found_alnum_chars = TRUE;
4253             break;
4254         }
4255         p = g_utf8_find_next_char (p, NULL);
4256     }
4257 
4258     if (!found_alnum_chars) {
4259         /* Return number 0 if no alphanumerics in string */
4260         return '0';
4261     }
4262 
4263     if (g_unichar_isalpha(chr))
4264     {
4265         gunichar2 utf16chr;
4266         gunichar upperchr;
4267         gunichar2 *str;
4268         GError *err = NULL;
4269 
4270 	upperchr = g_unichar_toupper (chr);
4271         str = g_ucs4_to_utf16 (&upperchr, 1, NULL, NULL, &err);
4272 	if (err != NULL) {
4273 	    fprintf (stderr, "Error in UCS4 to UTF16 conversion: %s, original unichar: %x, toupper unichar: %x\n", err->message, chr, upperchr);
4274 	    g_error_free (err);
4275 	    return '0';
4276 	}
4277 	else
4278         {
4279 	    utf16chr = str[0];
4280 	    g_free (str);
4281 	    return utf16chr;
4282 	}
4283     }
4284 
4285     return '0';		/* Return number 0 for any digit */
4286 }
4287 
4288 
4289 /* Create a new list containing all tracks but with collate keys
4290    instead of the actual strings (title, artist, album, genre,
4291    composer) */
mhod52_make_collate_keys(GList * tracks)4292 static GList *mhod52_make_collate_keys (GList *tracks)
4293 {
4294     gint numtracks = g_list_length (tracks);
4295     GList *gl, *coltracks = NULL;
4296     gint index=0;
4297 
4298     for (gl=tracks; gl; gl=gl->next)
4299     {
4300 	gchar *str;
4301 	struct mhod52track *ct;
4302 	Itdb_Track *tr = gl->data;
4303 	g_return_val_if_fail (tr, NULL);
4304 
4305 	ct = g_new0 (struct mhod52track, 1);
4306 	coltracks = g_list_prepend (coltracks, ct);
4307 
4308 	/* album */
4309 	if (tr->sort_album && *tr->sort_album)
4310 	{
4311 	    ct->album = g_utf8_collate_key (tr->sort_album, -1);
4312 	    ct->letter_album = jump_table_letter (tr->sort_album);
4313 	}
4314 	else if (tr->album)
4315 	{
4316 	    ct->album = g_utf8_collate_key (tr->album, -1);
4317 	    ct->letter_album = jump_table_letter (tr->album);
4318 	}
4319 	else
4320 	{
4321 	    ct->album = g_strdup ("");
4322 	    ct->letter_album = '0';
4323 	}
4324 
4325 	/* title */
4326 	if (tr->sort_title && *tr->sort_title)
4327 	{
4328 	    ct->title = g_utf8_collate_key (tr->sort_title, -1);
4329 	    ct->letter_title = jump_table_letter (tr->sort_title);
4330 	}
4331 	else if (tr->title)
4332 	{
4333 	    ct->title = g_utf8_collate_key (tr->title, -1);
4334 	    ct->letter_title = jump_table_letter (tr->title);
4335 	}
4336 	else
4337 	{
4338 	    ct->title = g_strdup ("");
4339 	    ct->letter_title = '0';
4340 	}
4341 
4342 	/* artist */
4343 	str = get_sort_artist (tr);
4344 	if (str)
4345 	{
4346 	    ct->artist = g_utf8_collate_key (str, -1);
4347 	    ct->letter_artist = jump_table_letter (str);
4348 	    g_free (str);
4349 	}
4350 	else if (tr->artist)
4351 	{
4352 	    ct->artist = g_utf8_collate_key (tr->artist, -1);
4353 	    ct->letter_artist = jump_table_letter (tr->artist);
4354 	}
4355 	else
4356 	{
4357 	    ct->artist = g_strdup ("");
4358 	    ct->letter_artist = '0';
4359 	}
4360 
4361 	/* genre */
4362 	if (tr->genre)
4363 	{
4364 	    ct->genre = g_utf8_collate_key (tr->genre, -1);
4365 	    ct->letter_genre = jump_table_letter (tr->genre);
4366 	}
4367 	else
4368 	{
4369 	    ct->genre = g_strdup ("");
4370 	    ct->letter_genre = '0';
4371 	}
4372 
4373 	/* composer */
4374 	if (tr->sort_composer && *tr->sort_composer)
4375 	{
4376 	    ct->composer = g_utf8_collate_key (tr->sort_composer, -1);
4377 	    ct->letter_composer = jump_table_letter (tr->sort_composer);
4378 	}
4379 	else if (tr->composer)
4380 	{
4381 	    ct->composer = g_utf8_collate_key (tr->composer, -1);
4382 	    ct->letter_composer = jump_table_letter (tr->composer);
4383 	}
4384 	else
4385 	{
4386 	    ct->composer = g_strdup ("");
4387 	    ct->letter_composer = '0';
4388 	}
4389 
4390 	ct->track_nr = tr->track_nr;
4391 	ct->cd_nr = tr->cd_nr;
4392 	ct->numtracks = numtracks;
4393 	ct->index = index++;
4394     }
4395     return coltracks;
4396 }
4397 
4398 
4399 /* Free all memory used up by the collate keys */
mhod52_free_collate_keys(GList * coltracks)4400 static void mhod52_free_collate_keys (GList *coltracks)
4401 {
4402     GList *gl;
4403 
4404     for (gl=coltracks; gl; gl=gl->next)
4405     {
4406 	struct mhod52track *ct = gl->data;
4407 	g_return_if_fail (ct);
4408 	g_free (ct->album);
4409 	g_free (ct->title);
4410 	g_free (ct->artist);
4411 	g_free (ct->genre);
4412 	g_free (ct->composer);
4413 	g_free (ct);
4414     }
4415     g_list_free (coltracks);
4416 }
4417 
4418 static void
itdb_chapterdata_build_chapter_blob_internal(WContents * cts,Itdb_Chapterdata * chapterdata)4419 itdb_chapterdata_build_chapter_blob_internal (WContents *cts,
4420                                               Itdb_Chapterdata *chapterdata)
4421 {
4422     gulong atom_len_seek;
4423     gint numchapters;
4424     GList *ch_gl = NULL;
4425     /* printf("[%s] -- inserting into \"chapter\"\n", __func__); */
4426 
4427     numchapters = g_list_length (chapterdata->chapters);
4428 
4429     put32lint (cts, chapterdata->unk024); /* unknown */
4430     put32lint (cts, chapterdata->unk028); /* unknown */
4431     put32lint (cts, chapterdata->unk032); /* unknown */
4432     atom_len_seek = cts->pos; /* needed to fix length */
4433     put32bint (cts, -1);            /* total length of sean atom, fix later  */
4434     put_header (cts, "sean");
4435     put32bint (cts, 1);             /* unknown     */
4436     put32bint (cts, numchapters+1); /* children    */
4437     put32bint (cts, 0);             /* unknown     */
4438     for (ch_gl=chapterdata->chapters; ch_gl; ch_gl=ch_gl->next)
4439     {
4440 	gunichar2 *title_utf16;
4441 	Itdb_Chapter *chapter = ch_gl->data;
4442 	glong len;
4443 	title_utf16 = g_utf8_to_utf16 (chapter->chaptertitle, -1, NULL, &len, NULL);
4444 	fixup_big_utf16 (title_utf16);
4445 	put32bint (cts, 42+2*len); /* total length  */
4446 	put_header (cts, "chap");
4447 	put32bint (cts, chapter->startpos); /* should we check if startpos=0 here? */
4448 	put32bint (cts, 1);        /* children */
4449 	put32bint (cts, 0);        /* unknown  */
4450 	put32bint (cts, 22+2*len); /* length   */
4451 	put_header (cts, "name");
4452 	put32bint (cts, 1);        /* unknown  */
4453 	put32_n0 (cts, 2);         /* unknown  */
4454 	put16bint (cts, len);
4455 	put_data (cts, (gchar *)title_utf16, 2*len);
4456 	g_free (title_utf16);
4457     }
4458     put32bint (cts, 28); /* size     */
4459     put_header (cts, "hedr");
4460     put32bint (cts, 1);  /* unknown  */
4461     put32bint (cts, 0);  /* children */
4462     put32_n0 (cts, 2);   /* unknown  */
4463     put32bint (cts, 1);  /* unknown  */
4464 
4465     put32bint_seek (cts, cts->pos-atom_len_seek, atom_len_seek); /* size     */
4466 }
4467 
4468 /* Write out one mhod header.
4469      type: see enum of MHMOD_IDs;
4470      data: utf8 string for text items
4471            position indicator for MHOD_ID_PLAYLIST
4472            Itdb_SPLPref for MHOD_ID_SPLPREF
4473 	   Itdb_SPLRules for MHOD_ID_SPLRULES */
mk_mhod(FExport * fexp,MHODData * mhod)4474 static void mk_mhod (FExport *fexp, MHODData *mhod)
4475 {
4476   WContents *cts = fexp->wcontents;
4477 
4478   g_return_if_fail (cts);
4479   g_return_if_fail (mhod->valid);
4480 
4481   switch ((enum MHOD_ID)mhod->type)
4482   {
4483   case MHOD_ID_TITLE:
4484   case MHOD_ID_PATH:
4485   case MHOD_ID_ALBUM:
4486   case MHOD_ID_ARTIST:
4487   case MHOD_ID_GENRE:
4488   case MHOD_ID_FILETYPE:
4489   case MHOD_ID_COMMENT:
4490   case MHOD_ID_CATEGORY:
4491   case MHOD_ID_COMPOSER:
4492   case MHOD_ID_GROUPING:
4493   case MHOD_ID_DESCRIPTION:
4494   case MHOD_ID_SUBTITLE:
4495   case MHOD_ID_TVSHOW:
4496   case MHOD_ID_TVEPISODE:
4497   case MHOD_ID_TVNETWORK:
4498   case MHOD_ID_ALBUMARTIST:
4499   case MHOD_ID_KEYWORDS:
4500   case MHOD_ID_SORT_ARTIST:
4501   case MHOD_ID_SORT_TITLE:
4502   case MHOD_ID_SORT_ALBUM:
4503   case MHOD_ID_SORT_ALBUMARTIST:
4504   case MHOD_ID_SORT_COMPOSER:
4505   case MHOD_ID_SORT_TVSHOW:
4506   case MHOD_ID_ALBUM_ALBUM:
4507   case MHOD_ID_ALBUM_ARTIST:
4508   case MHOD_ID_ALBUM_SORT_ARTIST:
4509   case MHOD_ID_ALBUM_ARTIST_MHII:
4510       g_return_if_fail (mhod->data.string);
4511       /* normal iTunesDBs seem to take utf16 strings), endian-inversed
4512 	 iTunesDBs seem to take utf8 strings */
4513       if (!cts->reversed)
4514       {
4515 	  /* convert to utf16  */
4516 	  glong len;
4517 	  gunichar2 *entry_utf16 = g_utf8_to_utf16 (mhod->data.string, -1,
4518 						    NULL, &len, NULL);
4519 	  fixup_little_utf16 (entry_utf16);
4520 	  put_header (cts, "mhod");   /* header                     */
4521 	  put32lint (cts, 24);        /* size of header             */
4522 	  put32lint (cts, sizeof (gunichar2)*len+40);  /* size of header + body      */
4523 	  put32lint (cts, mhod->type);/* type of the mhod           */
4524 	  put32_n0 (cts, 2);          /* unknown                    */
4525 	  /* end of header, start of data */
4526 	  put32lint (cts, 1);         /* string type UTF16          */
4527 	  put32lint (cts, sizeof (gunichar2)*len);     /* size of string             */
4528 	  put32lint (cts, 1);         /* unknown, but is set to 1 */
4529 	  put32lint (cts, 0);
4530 	  put_data (cts, (gchar *)entry_utf16, sizeof (gunichar2)*len);/* the string */
4531 	  g_free (entry_utf16);
4532       }
4533       else
4534       {
4535 	  guint32 len = strlen (mhod->data.string);
4536 	  put_header (cts, "mhod");   /* header                     */
4537 	  put32lint (cts, 24);        /* size of header             */
4538 	  put32lint (cts, len+40);    /* size of header + body      */
4539 	  put32lint (cts, mhod->type);/* type of the mhod           */
4540 	  put32_n0 (cts, 2);          /* unknown                    */
4541 	  /* end of header, start of data */
4542 	  put32lint (cts, 2);         /* string type UTF 8          */
4543 	  put32lint (cts, len);       /* size of string             */
4544 	  put8int (cts, 1);           /* unknown                    */
4545 	  put8int (cts, 0);           /* unknown                    */
4546 	  put8int (cts, 0);           /* unknown                    */
4547 	  put8int (cts, 0);           /* unknown                    */
4548 	  put32lint (cts, 0);         /* unknown                    */
4549 	  put_string (cts, mhod->data.string);/* the string         */
4550       }
4551       break;
4552   case MHOD_ID_PODCASTURL:
4553   case MHOD_ID_PODCASTRSS:
4554       g_return_if_fail (mhod->data.string);
4555       {
4556 	  guint32 len = strlen (mhod->data.string);
4557 	  put_header (cts, "mhod");   /* header                     */
4558 	  put32lint (cts, 24);        /* size of header             */
4559 	  put32lint (cts, 24+len);    /* size of header + data      */
4560 	  put32lint (cts, mhod->type);/* type of the mhod           */
4561 	  put32_n0 (cts, 2);          /* unknown                    */
4562 	  put_string (cts, mhod->data.string);/* the string         */
4563       }
4564       break;
4565   case MHOD_ID_PLAYLIST:
4566       put_header (cts, "mhod");   /* header                     */
4567       put32lint (cts, 24);        /* size of header             */
4568       put32lint (cts, 44);        /* size of header + body      */
4569       put32lint (cts, mhod->type); /* type of the entry          */
4570       put32_n0 (cts, 2);          /* unknown                    */
4571       /* end of header, start of data */
4572       put32lint (cts, mhod->data.track_pos);/* position of track in playlist */
4573       put32_n0 (cts, 4);             /* unknown                    */
4574       break;
4575   case MHOD_ID_CHAPTERDATA:
4576       g_return_if_fail (mhod->data.chapterdata);
4577       {
4578 	  gulong header_seek = cts->pos; /* needed to fix length */
4579 	  put_header (cts, "mhod");       /* header      */
4580 	  put32lint (cts, 24);            /* header size */
4581 	  put32lint (cts, -1);            /* total length, fix later  */
4582 	  put32lint (cts, mhod->type);    /* entry type  */
4583 	  put32_n0 (cts, 2);              /* unknown     */
4584 	  itdb_chapterdata_build_chapter_blob_internal (cts, mhod->data.chapterdata);
4585 	  fix_header (cts, header_seek);
4586       }
4587       break;
4588   case MHOD_ID_SPLPREF:
4589       g_return_if_fail (mhod->data.splpref);
4590       put_header (cts, "mhod");  /* header                 */
4591       put32lint (cts, 24);       /* size of header         */
4592       put32lint (cts, 96);       /* size of header + body  */
4593       put32lint (cts, mhod->type);/* type of the entry      */
4594       put32_n0 (cts, 2);         /* unknown                */
4595       /* end of header, start of data */
4596       put8int (cts, mhod->data.splpref->liveupdate);
4597       put8int (cts, mhod->data.splpref->checkrules? 1:0);
4598       put8int (cts, mhod->data.splpref->checklimits);
4599       put8int (cts, mhod->data.splpref->limittype);
4600       put8int (cts, mhod->data.splpref->limitsort & 0xff);
4601       put8int (cts, 0);          /* unknown                */
4602       put8int (cts, 0);          /* unknown                */
4603       put8int (cts, 0);          /* unknown                */
4604       put32lint (cts, mhod->data.splpref->limitvalue);
4605       put8int (cts, mhod->data.splpref->matchcheckedonly);
4606       /* for the following see note at definitions of limitsort
4607 	 types in itunesdb.h */
4608       put8int (cts, (mhod->data.splpref->limitsort & 0x80000000) ? 1:0);
4609       put8int (cts, 0);          /* unknown                */
4610       put8int (cts, 0);          /* unknown                */
4611       put32_n0 (cts, 14);        /* unknown                */
4612       break;
4613   case MHOD_ID_SPLRULES:
4614       g_return_if_fail (mhod->data.splrules);
4615       {
4616 	  gulong header_seek = cts->pos; /* needed to fix length */
4617 	  GList *gl;
4618 	  gint numrules = g_list_length (mhod->data.splrules->rules);
4619 
4620 	  put_header (cts, "mhod");  /* header                   */
4621 	  put32lint (cts, 24);       /* size of header           */
4622 	  put32lint (cts, -1);       /* total length, fix later  */
4623 	  put32lint (cts, mhod->type);/* type of the entry        */
4624 	  put32_n0 (cts, 2);         /* unknown                  */
4625 	  /* end of header, start of data */
4626 	  /* For some reason this is the only part of the iTunesDB
4627 	     that uses big endian */
4628 	  put_header (cts, "SLst");  /* header                   */
4629 	  put32bint (cts, mhod->data.splrules->unk004); /* unknown*/
4630 	  put32bint (cts, numrules);
4631 	  put32bint (cts, mhod->data.splrules->match_operator);
4632 	  put32_n0 (cts, 30);            /* unknown              */
4633 	  /* end of header, now follow the rules */
4634 	  for (gl=mhod->data.splrules->rules; gl; gl=gl->next)
4635 	  {
4636 	      Itdb_SPLRule *splr = gl->data;
4637 	      ItdbSPLFieldType ft;
4638 	      glong len;
4639 	      gunichar2 *entry_utf16;
4640 	      g_return_if_fail (splr);
4641 	      ft = itdb_splr_get_field_type (splr);
4642 /*	      printf ("%p: field: %d ft: %d\n", splr, splr->field, ft);*/
4643 	      itdb_splr_validate (splr);
4644 	      put32bint (cts, splr->field);
4645 	      put32bint (cts, splr->action);
4646 	      put32_n0 (cts, 11);          /* unknown              */
4647 	      switch (ft)
4648 	      {
4649 	      case ITDB_SPLFT_STRING:
4650 		  /* write string-type rule */
4651 		  len = 0;
4652 		  entry_utf16 = NULL;
4653 		  /* splr->string may be NULL */
4654 		  if (splr->string)
4655 		      entry_utf16 = g_utf8_to_utf16 (splr->string,
4656 						     -1,NULL,&len,NULL);
4657 		  fixup_big_utf16 (entry_utf16);
4658 		  put32bint (cts, sizeof (gunichar2)*len); /* length of string     */
4659 		  put_data (cts, (gchar *)entry_utf16, sizeof (gunichar2)*len);
4660 		  g_free (entry_utf16);
4661 		  break;
4662 	      case ITDB_SPLFT_DATE:
4663 	      case ITDB_SPLFT_INT:
4664 	      case ITDB_SPLFT_BOOLEAN:
4665 	      case ITDB_SPLFT_PLAYLIST:
4666 	      case ITDB_SPLFT_UNKNOWN:
4667 	      case ITDB_SPLFT_BINARY_AND: {
4668 		  guint64 fromvalue;
4669 		  guint64 tovalue;
4670 
4671 		  fromvalue = splr->fromvalue;
4672 		  tovalue = splr->tovalue;
4673 
4674 		  if (ft == ITDB_SPLFT_DATE) {
4675 		      ItdbSPLActionType at;
4676 		      at = itdb_splr_get_action_type (splr);
4677 		      if ((at == ITDB_SPLAT_RANGE_DATE) ||
4678 			  (at == ITDB_SPLAT_DATE))
4679 		      {
4680 			  Itdb_iTunesDB *itdb = fexp->itdb;
4681 			  fromvalue = device_time_time_t_to_mac (itdb->device, fromvalue);
4682 			  tovalue = device_time_time_t_to_mac (itdb->device, tovalue);
4683 		      }
4684 		  }
4685 
4686 		  /* write non-string-type rule */
4687 		  put32bint (cts, 0x44); /* length of data        */
4688 		  /* data */
4689 		  put64bint (cts, fromvalue);
4690 		  put64bint (cts, splr->fromdate);
4691 		  put64bint (cts, splr->fromunits);
4692 		  put64bint (cts, tovalue);
4693 		  put64bint (cts, splr->todate);
4694 		  put64bint (cts, splr->tounits);
4695 		  put32bint (cts, splr->unk052);
4696 		  put32bint (cts, splr->unk056);
4697 		  put32bint (cts, splr->unk060);
4698 		  put32bint (cts, splr->unk064);
4699 		  put32bint (cts, splr->unk068);
4700 		  break;
4701 	      }
4702 	      }
4703 	  }
4704 	  /* insert length of mhod junk */
4705 	  fix_header (cts, header_seek);
4706       }
4707       break;
4708   case MHOD_ID_LIBPLAYLISTINDEX:
4709       g_return_if_fail (mhod->data.mhod52coltracks);
4710       g_return_if_fail (mhod->data.mhod52coltracks->data);
4711       {
4712 	  struct mhod52track *ct = mhod->data.mhod52coltracks->data;
4713 	  gint numtracks = ct->numtracks;
4714 	  GList *gl;
4715 	  gpointer compfunc = NULL;
4716  	  gunichar2 sortkey = 0;
4717           gunichar2 lastsortkey = 0;
4718 	  guint32 mhod53index = 0;
4719  	  struct mhod53_entry *m53 = NULL;
4720 
4721 	  /* sort list */
4722 	  switch (mhod->mhod52sorttype)
4723 	  {
4724 	  case MHOD52_SORTTYPE_TITLE:
4725 	      compfunc = mhod52_sort_title;
4726 	      break;
4727 	  case MHOD52_SORTTYPE_ALBUM:
4728 	      compfunc = mhod52_sort_album;
4729 	      break;
4730 	  case MHOD52_SORTTYPE_ARTIST:
4731 	      compfunc = mhod52_sort_artist;
4732 	      break;
4733 	  case MHOD52_SORTTYPE_GENRE:
4734 	      compfunc = mhod52_sort_genre;
4735 	      break;
4736 	  case MHOD52_SORTTYPE_COMPOSER:
4737 	      compfunc = mhod52_sort_composer;
4738 	      break;
4739 	  }
4740 	  g_return_if_fail (compfunc);
4741 
4742 	  /* sort the tracks */
4743 	  mhod->data.mhod52coltracks = g_list_sort (mhod->data.mhod52coltracks,
4744 						    compfunc);
4745 	  /* Write the MHOD */
4746 	  put_header (cts, "mhod");         /* header                     */
4747 	  put32lint (cts, 24);              /* size of header             */
4748 	  put32lint (cts, 4*numtracks+72);  /* size of header + body      */
4749 	  put32lint (cts, mhod->type);      /* type of the mhod           */
4750 	  put32_n0 (cts, 2);                /* unknown                    */
4751 	  /* end of header, start of data */
4752 	  put32lint (cts, mhod->mhod52sorttype);   /* sort type     */
4753 	  put32lint (cts, numtracks);       /* number of entries          */
4754 	  put32_n0 (cts, 10);               /* unknown                    */
4755 	  for (gl=mhod->data.mhod52coltracks; gl; gl=gl->next)
4756 	  {
4757 	      ct = gl->data;
4758 	      g_return_if_fail (ct);
4759 	      put32lint (cts, ct->index);
4760 
4761 	      /* This is for the type 53s */
4762 	      switch (mhod->mhod52sorttype)
4763 	      {
4764 		case MHOD52_SORTTYPE_TITLE:
4765 		sortkey = ct->letter_title;
4766 		break;
4767 	      case MHOD52_SORTTYPE_ALBUM:
4768 		sortkey = ct->letter_album;
4769 		break;
4770 	      case MHOD52_SORTTYPE_ARTIST:
4771 		sortkey = ct->letter_artist;
4772 		break;
4773 	      case MHOD52_SORTTYPE_GENRE:
4774 		sortkey = ct->letter_genre;
4775 		break;
4776 	      case MHOD52_SORTTYPE_COMPOSER:
4777 		sortkey = ct->letter_composer;
4778 		break;
4779 	      }
4780 
4781 	    if (sortkey != lastsortkey) {
4782 	      m53 = g_new0 (struct mhod53_entry, 1);
4783 	      m53->letter = sortkey;
4784 	      m53->start = mhod53index;
4785 	      m53->count = 0;
4786 	      mhod->mhod53_list = g_list_append (mhod->mhod53_list, m53);
4787 	      lastsortkey = sortkey;
4788 	    }
4789 	    mhod53index++;
4790 	    g_assert (m53 != NULL);
4791 	    m53->count++;
4792 	  }
4793       }
4794       break;
4795   case MHOD_ID_LIBPLAYLISTJUMPTABLE:
4796       {
4797           GList *gl;
4798           guint32 numentries;
4799           struct mhod53_entry *m53;
4800 
4801           numentries = g_list_length (mhod->mhod53_list);
4802           put_header (cts, "mhod");				/* header */
4803           put32lint (cts, 24);				/* size of header */
4804           put32lint (cts, 12*numentries+40);		/* size of header + body */
4805           put32lint (cts, mhod->type);			/* type of the mhod */
4806           put32_n0 (cts, 2);				/* unknown */
4807           put32lint (cts, mhod->mhod52sorttype);    	/* sort type */
4808           put32lint (cts, numentries);			/* number of entries */
4809           put32_n0 (cts, 2);				/* unknown */
4810 
4811           for (gl=mhod->mhod53_list; gl; gl=gl->next)
4812           {
4813               m53 = gl->data;
4814               put16lint (cts, m53->letter);
4815               put16lint (cts, 0);
4816               put32lint (cts, m53->start);
4817               put32lint (cts, m53->count);
4818           }
4819       }
4820       break;
4821   }
4822 }
4823 
mk_mhia(gpointer key,gpointer value,gpointer user_data)4824 static void mk_mhia (gpointer key, gpointer value, gpointer user_data)
4825 {
4826   FExport *fexp;
4827   WContents *cts;
4828   Itdb_Track *track;
4829   Itdb_Item_Id *id;
4830   MHODData mhod;
4831   guint mhod_num;
4832   gulong mhia_seek;
4833 
4834   track = (Itdb_Track *)key;
4835   g_return_if_fail (track != NULL);
4836 
4837   id = (Itdb_Item_Id *)value;
4838   g_return_if_fail (id != NULL);
4839 
4840   fexp = (FExport *)user_data;
4841   g_return_if_fail (fexp);
4842   g_return_if_fail (fexp->wcontents);
4843   cts = fexp->wcontents;
4844   mhia_seek = cts->pos;
4845 
4846   put_header (cts, "mhia");        		/* header                   */
4847   put32lint (cts, 88);             		/* size of header           */
4848   put32lint (cts, -1);             		/* total size -> later */
4849   put32lint (cts, 2);		   		/* number of children mhods */
4850   put32lint (cts, id->id);		   	/* album id */
4851   put64lint (cts, id->sql_id);   		/* id used in the sqlite DB */
4852   put32lint (cts, 2);		   		/* unknown */
4853   put32_n0 (cts, 14); 				/* padding */
4854 
4855   mhod.valid = TRUE;
4856   mhod_num = 0;
4857   if (track->album && *track->album) {
4858       mhod.type = MHOD_ID_ALBUM_ALBUM;
4859       mhod.data.string = track->album;
4860       mk_mhod (fexp, &mhod);
4861       ++mhod_num;
4862   }
4863 
4864   if (track->albumartist && *track->albumartist) {
4865       mhod.type = MHOD_ID_ALBUM_ARTIST;
4866       mhod.data.string = track->albumartist;
4867       mk_mhod (fexp, &mhod);
4868       ++mhod_num;
4869   } else if (track->artist && *track->artist) {
4870       mhod.type = MHOD_ID_ALBUM_ARTIST;
4871       mhod.data.string = track->artist;
4872       mk_mhod (fexp, &mhod);
4873       ++mhod_num;
4874   }
4875 
4876   if (track->sort_albumartist && *track->sort_albumartist) {
4877       mhod.type = MHOD_ID_ALBUM_SORT_ARTIST;
4878       mhod.data.string = track->sort_albumartist;
4879       mk_mhod (fexp, &mhod);
4880       ++mhod_num;
4881   } else if (track->sort_artist && *track->sort_artist) {
4882       mhod.type = MHOD_ID_ALBUM_SORT_ARTIST;
4883       mhod.data.string = track->sort_artist;
4884       mk_mhod (fexp, &mhod);
4885       ++mhod_num;
4886   }
4887   fix_mhit (cts, mhia_seek, mhod_num);
4888 }
4889 
mk_mhla(FExport * fexp)4890 static void mk_mhla (FExport *fexp)
4891 {
4892   WContents *cts;
4893 
4894   g_return_if_fail (fexp);
4895   g_return_if_fail (fexp->wcontents);
4896   g_return_if_fail (fexp->albums);
4897 
4898   cts = fexp->wcontents;
4899 
4900   put_header (cts, "mhla");        /* header                   */
4901   put32lint (cts, 92);             /* size of header           */
4902   /* albums on iPod */
4903   put32lint (cts, g_hash_table_size (fexp->albums));
4904   put32_n0 (cts, 20);               /* dummy space              */
4905   g_hash_table_foreach (fexp->albums, mk_mhia, fexp);
4906 }
4907 
4908 /* Write out the mhlp header. Size will be written later */
mk_mhlp(FExport * fexp)4909 static void mk_mhlp (FExport *fexp)
4910 {
4911   WContents *cts;
4912 
4913   g_return_if_fail (fexp);
4914   g_return_if_fail (fexp->wcontents);
4915 
4916   cts = fexp->wcontents;
4917 
4918   put_header (cts, "mhlp");        /* header                   */
4919   put32lint (cts, 92);             /* size of header           */
4920   /* playlists on iPod (including main!) */
4921   put32lint (cts, -1); /* filled in later */
4922   put32_n0 (cts, 20);               /* dummy space              */
4923 }
4924 
mk_mhii(gpointer key,gpointer value,gpointer user_data)4925 static void mk_mhii (gpointer key, gpointer value, gpointer user_data)
4926 {
4927   FExport *fexp;
4928   WContents *cts;
4929   Itdb_Track *track;
4930   Itdb_Item_Id *id;
4931   MHODData mhod;
4932   guint mhod_num;
4933   gulong mhii_seek;
4934 
4935   track = (Itdb_Track *)key;
4936   g_return_if_fail (track != NULL);
4937 
4938   id = (Itdb_Item_Id *)value;
4939   g_return_if_fail (id != NULL);
4940 
4941   fexp = (FExport *)user_data;
4942   g_return_if_fail (fexp);
4943   g_return_if_fail (fexp->wcontents);
4944   cts = fexp->wcontents;
4945   mhii_seek = cts->pos;
4946 
4947   put_header (cts, "mhii");        		/* header                   */
4948   put32lint (cts, 80);             		/* size of header           */
4949   put32lint (cts, -1);             		/* total size -> later */
4950   put32lint (cts, 1);		   		/* number of children mhods */
4951   put32lint (cts, id->id);			/* artist id */
4952   put64lint (cts, id->sql_id);			/* artist SQL id */
4953   put32lint (cts, 2);		   		/* unknown */
4954   put32_n0 (cts, 12); 				/* padding */
4955 
4956   mhod.valid = TRUE;
4957   mhod_num = 0;
4958 
4959   if (track->artist && *track->artist) {
4960       mhod.type = MHOD_ID_ALBUM_ARTIST_MHII;
4961       mhod.data.string = track->artist;
4962       mk_mhod (fexp, &mhod);
4963       ++mhod_num;
4964   }
4965 
4966   fix_mhit (cts, mhii_seek, mhod_num);
4967 }
4968 
mk_mhli(FExport * fexp)4969 static void mk_mhli (FExport *fexp)
4970 {
4971   WContents *cts;
4972 
4973   g_return_if_fail (fexp);
4974   g_return_if_fail (fexp->wcontents);
4975   g_return_if_fail (fexp->artists);
4976 
4977   cts = fexp->wcontents;
4978 
4979   put_header (cts, "mhli");        /* header                   */
4980   put32lint (cts, 92);             /* size of header           */
4981   /* albums on iPod (including main!) */
4982   put32lint (cts, g_hash_table_size (fexp->artists));
4983   put32_n0 (cts, 20);               /* dummy space              */
4984   g_hash_table_foreach (fexp->artists, mk_mhii, fexp);
4985 }
4986 
4987 /* Write out the long MHOD_ID_PLAYLIST mhod header.
4988    This seems to be an itunespref thing.. dunno know this
4989    but if we set everything to 0, itunes doesn't show any data
4990    even if you drag an mp3 to your ipod: nothing is shown, but itunes
4991    will copy the file!
4992    .. so we create a hardcoded-pref.. this will change in future
4993    Seems to be a Preferences mhod, every PL has such a thing
4994    FIXME !!! */
mk_long_mhod_id_playlist(FExport * fexp,Itdb_Playlist * pl)4995 static void mk_long_mhod_id_playlist (FExport *fexp, Itdb_Playlist *pl)
4996 {
4997   WContents *cts;
4998 
4999   g_return_if_fail (fexp);
5000   g_return_if_fail (fexp->wcontents);
5001   g_return_if_fail (pl);
5002 
5003   cts = fexp->wcontents;
5004 
5005   put_header (cts, "mhod");          /* header                   */
5006   put32lint (cts, 0x18);             /* size of header  ?        */
5007   put32lint (cts, 0x0288);           /* size of header + body    */
5008   put32lint (cts, MHOD_ID_PLAYLIST); /* type of the entry        */
5009   put32_n0 (cts, 6);
5010   put32lint (cts, 0x010084);         /* ? */
5011   put32lint (cts, 0x05);             /* ? */
5012   put32lint (cts, 0x09);             /* ? */
5013   put32lint (cts, 0x03);             /* ? */
5014   put32lint (cts, 0x120001);         /* ? */
5015   put32_n0 (cts, 3);
5016   put32lint (cts, 0xc80002);         /* ? */
5017   put32_n0 (cts, 3);
5018   put32lint (cts, 0x3c000d);         /* ? */
5019   put32_n0 (cts, 3);
5020   put32lint (cts, 0x7d0004);         /* ? */
5021   put32_n0 (cts, 3);
5022   put32lint (cts, 0x7d0003);         /* ? */
5023   put32_n0 (cts, 3);
5024   put32lint (cts, 0x640008);         /* ? */
5025   put32_n0 (cts, 3);
5026   put32lint (cts, 0x640017);         /* ? */
5027   put32lint (cts, 0x01);             /* bool? (visible? / colums?) */
5028   put32_n0 (cts, 2);
5029   put32lint (cts, 0x500014);         /* ? */
5030   put32lint (cts, 0x01);             /* bool? (visible?) */
5031   put32_n0 (cts, 2);
5032   put32lint (cts, 0x7d0015);         /* ? */
5033   put32lint (cts, 0x01);             /* bool? (visible?) */
5034   put32_n0 (cts, 114);
5035 }
5036 
5037 
5038 
5039 /* Header for new PL item */
5040 /* @pos: position in playlist */
mk_mhip(FExport * fexp,guint32 childcount,guint32 podcastgroupflag,guint32 podcastgroupid,guint32 trackid,guint32 timestamp,guint32 podcastgroupref)5041 static void mk_mhip (FExport *fexp,
5042 		     guint32 childcount,
5043 		     guint32 podcastgroupflag,
5044 		     guint32 podcastgroupid,
5045 		     guint32 trackid,
5046 		     guint32 timestamp,
5047 		     guint32 podcastgroupref)
5048 {
5049   WContents *cts;
5050 
5051   g_return_if_fail (fexp);
5052   g_return_if_fail (fexp->wcontents);
5053 
5054   cts = fexp->wcontents;
5055 
5056   put_header (cts, "mhip");
5057   put32lint (cts, 76);                              /*  4 */
5058   put32lint (cts, -1);  /* fill in later */         /*  8 */
5059   put32lint (cts, childcount);                      /* 12 */
5060   put32lint (cts, podcastgroupflag);                /* 16 */
5061   put32lint (cts, podcastgroupid);                  /* 20 */
5062   put32lint (cts, trackid);                         /* 24 */
5063   timestamp = device_time_time_t_to_mac (fexp->itdb->device, timestamp);
5064   put32lint (cts, timestamp);                       /* 28 */
5065   put32lint (cts, podcastgroupref);                 /* 32 */
5066   put32_n0 (cts, 10);                               /* 36 */
5067 }
5068 
5069 
5070 /* Write first mhsd hunk. Return FALSE in case of error and set
5071  * fexp->error */
write_mhsd_tracks(FExport * fexp)5072 static gboolean write_mhsd_tracks (FExport *fexp)
5073 {
5074     GList *gl;
5075     gulong mhsd_seek;
5076     WContents *cts;
5077 
5078     g_return_val_if_fail (fexp, FALSE);
5079     g_return_val_if_fail (fexp->itdb, FALSE);
5080     g_return_val_if_fail (fexp->wcontents, FALSE);
5081 
5082     cts = fexp->wcontents;
5083 
5084     mhsd_seek = cts->pos;      /* get position of mhsd header  */
5085     mk_mhsd (fexp, 1);         /* write header: type 1: tracks */
5086     /* write header with nr. of tracks */
5087     mk_mhlt (fexp, g_list_length (fexp->itdb->tracks));
5088     for (gl=fexp->itdb->tracks; gl; gl=gl->next)  /* Write each track */
5089     {
5090 	Itdb_Track *track = gl->data;
5091 	guint32 mhod_num = 0;
5092 	gulong mhit_seek = cts->pos;
5093 	MHODData mhod;
5094 
5095 	g_return_val_if_fail (track, FALSE);
5096 
5097 	mhod.valid = TRUE;
5098 
5099 	mk_mhit (cts, track);
5100 	if (track->title && *track->title)
5101 	{
5102 	    mhod.type = MHOD_ID_TITLE;
5103 	    mhod.data.string = track->title;
5104 	    mk_mhod (fexp, &mhod);
5105 	    ++mhod_num;
5106 	}
5107 	if (track->artist && *track->artist)
5108 	{
5109 	    mhod.type = MHOD_ID_ARTIST;
5110 	    mhod.data.string = track->artist;
5111 	    mk_mhod (fexp, &mhod);
5112 	    ++mhod_num;
5113 	}
5114 	if (track->album && *track->album)
5115 	{
5116 	    mhod.type = MHOD_ID_ALBUM;
5117 	    mhod.data.string = track->album;
5118 	    mk_mhod (fexp, &mhod);
5119 	    ++mhod_num;
5120 	}
5121 	if (track->filetype && *track->filetype)
5122 	{
5123 	    mhod.type = MHOD_ID_FILETYPE;
5124 	    mhod.data.string = track->filetype;
5125 	    mk_mhod (fexp, &mhod);
5126 	    ++mhod_num;
5127 	}
5128 	if (track->comment && *track->comment)
5129 	{
5130 	    mhod.type = MHOD_ID_COMMENT;
5131 	    mhod.data.string = track->comment;
5132 	    mk_mhod (fexp, &mhod);
5133 	    ++mhod_num;
5134 	}
5135 	if (track->ipod_path && *track->ipod_path)
5136 	{
5137 	    mhod.type = MHOD_ID_PATH;
5138 	    mhod.data.string = track->ipod_path;
5139 	    mk_mhod (fexp, &mhod);
5140 	    ++mhod_num;
5141 	}
5142 	if (track->genre && *track->genre)
5143 	{
5144 	    mhod.type = MHOD_ID_GENRE;
5145 	    mhod.data.string = track->genre;
5146 	    mk_mhod (fexp, &mhod);
5147 	    ++mhod_num;
5148 	}
5149 	if (track->category && *track->category)
5150 	{
5151 	    mhod.type = MHOD_ID_CATEGORY;
5152 	    mhod.data.string = track->category;
5153 	    mk_mhod (fexp, &mhod);
5154 	    ++mhod_num;
5155 	}
5156 	if (track->composer && *track->composer)
5157 	{
5158 	    mhod.type = MHOD_ID_COMPOSER;
5159 	    mhod.data.string = track->composer;
5160 	    mk_mhod (fexp, &mhod);
5161 	    ++mhod_num;
5162 	}
5163 	if (track->grouping && *track->grouping)
5164 	{
5165 	    mhod.type = MHOD_ID_GROUPING;
5166 	    mhod.data.string = track->grouping;
5167 	    mk_mhod (fexp, &mhod);
5168 	    ++mhod_num;
5169 	}
5170 	if (track->description && *track->description)
5171 	{
5172 	    mhod.type = MHOD_ID_DESCRIPTION;
5173 	    mhod.data.string = track->description;
5174 	    mk_mhod (fexp, &mhod);
5175 	    ++mhod_num;
5176 	}
5177 	if (track->subtitle && *track->subtitle)
5178 	{
5179 	    mhod.type = MHOD_ID_SUBTITLE;
5180 	    mhod.data.string = track->subtitle;
5181 	    mk_mhod (fexp, &mhod);
5182 	    ++mhod_num;
5183 	}
5184 	if (track->tvshow && *track->tvshow)
5185 	{
5186 	    mhod.type = MHOD_ID_TVSHOW;
5187 	    mhod.data.string = track->tvshow;
5188 	    mk_mhod (fexp, &mhod);
5189 	    ++mhod_num;
5190 	}
5191 	if (track->tvepisode && *track->tvepisode)
5192 	{
5193 	    mhod.type = MHOD_ID_TVEPISODE;
5194 	    mhod.data.string = track->tvepisode;
5195 	    mk_mhod (fexp, &mhod);
5196 	    ++mhod_num;
5197 	}
5198 	if (track->tvnetwork && *track->tvnetwork)
5199 	{
5200 	    mhod.type = MHOD_ID_TVNETWORK;
5201 	    mhod.data.string = track->tvnetwork;
5202 	    mk_mhod (fexp, &mhod);
5203 	    ++mhod_num;
5204 	}
5205 	if (track->albumartist && *track->albumartist)
5206 	{
5207 	    mhod.type = MHOD_ID_ALBUMARTIST;
5208 	    mhod.data.string = track->albumartist;
5209 	    mk_mhod (fexp, &mhod);
5210 	    ++mhod_num;
5211 	}
5212 	if (track->keywords && *track->keywords)
5213 	{
5214 	    mhod.type = MHOD_ID_KEYWORDS;
5215 	    mhod.data.string = track->keywords;
5216 	    mk_mhod (fexp, &mhod);
5217 	    ++mhod_num;
5218 	}
5219 	if (track->podcasturl && *track->podcasturl)
5220 	{
5221 	    mhod.type = MHOD_ID_PODCASTURL;
5222 	    mhod.data.string = track->podcasturl;
5223 	    mk_mhod (fexp, &mhod);
5224 	    ++mhod_num;
5225 	}
5226 	if (track->podcastrss && *track->podcastrss)
5227 	{
5228 	    mhod.type = MHOD_ID_PODCASTRSS;
5229 	    mhod.data.string = track->podcastrss;
5230 	    mk_mhod (fexp, &mhod);
5231 	    ++mhod_num;
5232 	}
5233 	if (track->sort_artist && *track->sort_artist)
5234 	{
5235 	    mhod.type = MHOD_ID_SORT_ARTIST;
5236 	    mhod.data.string = track->sort_artist;
5237 	    mk_mhod (fexp, &mhod);
5238 	    ++mhod_num;
5239 	}
5240 	if (track->sort_title && *track->sort_title)
5241 	{
5242 	    mhod.type = MHOD_ID_SORT_TITLE;
5243 	    mhod.data.string = track->sort_title;
5244 	    mk_mhod (fexp, &mhod);
5245 	    ++mhod_num;
5246 	}
5247 	if (track->sort_album && *track->sort_album)
5248 	{
5249 	    mhod.type = MHOD_ID_SORT_ALBUM;
5250 	    mhod.data.string = track->sort_album;
5251 	    mk_mhod (fexp, &mhod);
5252 	    ++mhod_num;
5253 	}
5254 	if (track->sort_albumartist && *track->sort_albumartist)
5255 	{
5256 	    mhod.type = MHOD_ID_SORT_ALBUMARTIST;
5257 	    mhod.data.string = track->sort_albumartist;
5258 	    mk_mhod (fexp, &mhod);
5259 	    ++mhod_num;
5260 	}
5261 	if (track->sort_composer && *track->sort_composer)
5262 	{
5263 	    mhod.type = MHOD_ID_SORT_COMPOSER;
5264 	    mhod.data.string = track->sort_composer;
5265 	    mk_mhod (fexp, &mhod);
5266 	    ++mhod_num;
5267 	}
5268 	if (track->sort_tvshow && *track->sort_tvshow)
5269 	{
5270 	    mhod.type = MHOD_ID_SORT_TVSHOW;
5271 	    mhod.data.string = track->sort_tvshow;
5272 	    mk_mhod (fexp, &mhod);
5273 	    ++mhod_num;
5274 	}
5275 	if (track->chapterdata && track->chapterdata->chapters)
5276 	{
5277 	    mhod.type = MHOD_ID_CHAPTERDATA;
5278 	    mhod.data.chapterdata = track->chapterdata;
5279 	    mk_mhod (fexp, &mhod);
5280 	    ++mhod_num;
5281 	}
5282         /* Fill in the missing items of the mhit header */
5283 	fix_mhit (cts, mhit_seek, mhod_num);
5284     }
5285     fix_header (cts, mhsd_seek);
5286     return TRUE;
5287 }
5288 
5289 
5290 /* write out the mhip/mhod pairs for playlist @pl. @mhyp_seek points
5291    to the start of the corresponding mhyp, but is not used yet. */
write_playlist_mhips(FExport * fexp,Itdb_Playlist * pl,glong mhyp_seek)5292 static gboolean write_playlist_mhips (FExport *fexp,
5293 				      Itdb_Playlist *pl,
5294 				      glong mhyp_seek)
5295 {
5296     GList *gl;
5297     WContents *cts;
5298     guint32 i=0;
5299     guint32 mhip_num;
5300 
5301     g_return_val_if_fail (fexp, FALSE);
5302     g_return_val_if_fail (fexp->itdb, FALSE);
5303     g_return_val_if_fail (fexp->wcontents, FALSE);
5304     g_return_val_if_fail (pl, FALSE);
5305 
5306     cts = fexp->wcontents;
5307 
5308     for (gl=pl->members; gl; gl=gl->next)
5309     {
5310 	Itdb_Track *track = gl->data;
5311 	glong mhip_seek = cts->pos;
5312 	MHODData mhod;
5313 
5314 	g_return_val_if_fail (track, FALSE);
5315 
5316 	mk_mhip (fexp, 1, 0, 0, track->id, 0, 0);
5317 	mhod.valid = TRUE;
5318 	mhod.type = MHOD_ID_PLAYLIST;
5319 	mhod.data.track_pos = i;
5320 	mk_mhod (fexp, &mhod);
5321 	/* note: with iTunes 4.9 the mhod is counted as a child to
5322 	   mhip, so we have put the total length of the mhip and mhod
5323 	   into the mhip header */
5324 	fix_header (cts, mhip_seek);
5325 	++i;
5326     }
5327 
5328     /* set number of mhips */
5329     mhip_num = g_list_length (pl->members);
5330     put32lint_seek (cts, mhip_num, mhyp_seek+16);
5331 
5332     return TRUE;
5333 }
5334 
5335 
5336 static void free_memberlist (gpointer data);
5337 static void write_one_podcast_group (gpointer key, gpointer value, gpointer userdata);
free_memberlist(gpointer data)5338 static void free_memberlist (gpointer data)
5339 {
5340     GList **memberlist = data;
5341     if (memberlist)
5342     {
5343 	g_list_free (*memberlist);
5344 	g_free (memberlist);
5345     }
5346 }
write_one_podcast_group(gpointer key,gpointer value,gpointer userdata)5347 static void write_one_podcast_group (gpointer key, gpointer value,
5348    			             gpointer userdata)
5349 {
5350     gchar *album = key;
5351     GList **memberlist = value;
5352     FExport *fexp = userdata;
5353     GList *gl;
5354     WContents *cts;
5355     glong mhip_seek;
5356     guint32 groupid;
5357     MHODData mhod;
5358 
5359     g_return_if_fail (album);
5360     g_return_if_fail (memberlist);
5361     g_return_if_fail (fexp);
5362     g_return_if_fail (fexp->itdb);
5363     g_return_if_fail (fexp->wcontents);
5364 
5365     cts = fexp->wcontents;
5366     mhip_seek = cts->pos;
5367 
5368     groupid = fexp->next_id++;
5369     mk_mhip (fexp, 1, 256, groupid, 0, 0, 0);
5370 
5371     mhod.valid = TRUE;
5372     mhod.type = MHOD_ID_TITLE;
5373     mhod.data.string = album;
5374     mk_mhod (fexp, &mhod);
5375     fix_header (cts, mhip_seek);
5376 
5377     /* write members */
5378     for (gl=*memberlist; gl; gl=gl->next)
5379     {
5380 	Itdb_Track *track = gl->data;
5381 	guint32 mhip_id;
5382 
5383 	g_return_if_fail (track);
5384 
5385 	mhip_seek = cts->pos;
5386 	mhip_id = fexp->next_id++;
5387 	mk_mhip (fexp, 1, 0, mhip_id, track->id, 0, groupid);
5388 	mhod.type = MHOD_ID_PLAYLIST;
5389 	mhod.data.track_pos = mhip_id;
5390 	mk_mhod (fexp, &mhod);
5391 	fix_header (cts, mhip_seek);
5392     }
5393 }
5394 
5395 
5396 
5397 /* write out the mhip/mhod pairs for the podcast playlist
5398    @pl. @mhyp_seek points to the start of the corresponding mhyp, but
5399    is not used yet. */
write_podcast_mhips(FExport * fexp,Itdb_Playlist * pl,glong mhyp_seek)5400 static gboolean write_podcast_mhips (FExport *fexp,
5401 				     Itdb_Playlist *pl,
5402 				     glong mhyp_seek)
5403 {
5404     GList *gl;
5405     WContents *cts;
5406     guint32 mhip_num;
5407     GHashTable *album_hash;
5408 
5409     g_return_val_if_fail (fexp, FALSE);
5410     g_return_val_if_fail (fexp->itdb, FALSE);
5411     g_return_val_if_fail (fexp->wcontents, FALSE);
5412     g_return_val_if_fail (pl, FALSE);
5413 
5414     cts = fexp->wcontents;
5415 
5416     /* Create a list with all available album names because we have to
5417        group the podcasts according to albums */
5418     album_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
5419 					NULL, free_memberlist);
5420     for (gl=pl->members; gl; gl=gl->next)
5421     {
5422 	GList **memberlist;
5423 	gchar *album;
5424 	Itdb_Track *track = gl->data;
5425 	g_return_val_if_fail (track, FALSE);
5426 
5427 	if (track->album)
5428 	    album = track->album;
5429 	else
5430 	    album = "";
5431 
5432 	memberlist = g_hash_table_lookup (album_hash, album);
5433 
5434 	if (!memberlist)
5435 	{
5436 	    memberlist = g_new0 (GList *, 1);
5437 	    g_hash_table_insert (album_hash, album, memberlist);
5438 	}
5439 	*memberlist = g_list_append (*memberlist, track);
5440     }
5441 
5442     g_hash_table_foreach (album_hash, write_one_podcast_group, fexp);
5443 
5444     /* set number of mhips */
5445     mhip_num = g_list_length (pl->members)+g_hash_table_size (album_hash);
5446     put32lint_seek (cts, mhip_num, mhyp_seek+16);
5447 
5448     g_hash_table_destroy (album_hash);
5449 
5450     return TRUE;
5451 }
5452 
5453 /* Write one type 52 and the corresponding type 53 mhod */
mk_mhod52(enum MHOD52_SORTTYPE mhod52sorttype,FExport * fexp,MHODData * mhod)5454 static void mk_mhod52 (enum MHOD52_SORTTYPE mhod52sorttype, FExport *fexp,
5455                        MHODData *mhod)
5456 {
5457     mhod->mhod52sorttype = mhod52sorttype;
5458     mhod->type = MHOD_ID_LIBPLAYLISTINDEX;
5459     mk_mhod (fexp, mhod);
5460 }
5461 
mk_mhod53(enum MHOD52_SORTTYPE mhod52sorttype,FExport * fexp,MHODData * mhod)5462 static void mk_mhod53 (enum MHOD52_SORTTYPE mhod52sorttype, FExport *fexp,
5463                        MHODData *mhod)
5464 {
5465     mhod->type = MHOD_ID_LIBPLAYLISTJUMPTABLE;
5466     mk_mhod (fexp, mhod);
5467     g_list_foreach (mhod->mhod53_list, (GFunc)g_free, NULL);
5468     g_list_free (mhod->mhod53_list);
5469     mhod->mhod53_list = NULL;
5470 }
5471 
5472 
5473 /* corresponds to mk_mhyp */
5474 /* mhsd_type: 2: write normal playlists
5475               3: write podcast playlist in special format */
5476 /* Return FALSE in case of error and set fexp->error */
write_playlist(FExport * fexp,Itdb_Playlist * pl,guint32 mhsd_type)5477 static gboolean write_playlist (FExport *fexp,
5478 				Itdb_Playlist *pl, guint32 mhsd_type)
5479 {
5480     gulong mhyp_seek;
5481     WContents *cts;
5482     gboolean result = TRUE;
5483     MHODData mhod;
5484     gint mhodnum;
5485     guint32 mac_time;
5486 
5487     g_return_val_if_fail (fexp, FALSE);
5488     g_return_val_if_fail (fexp->itdb, FALSE);
5489     g_return_val_if_fail (fexp->wcontents, FALSE);
5490     g_return_val_if_fail (pl, FALSE);
5491 
5492     cts = fexp->wcontents;
5493     mhyp_seek = cts->pos;
5494 
5495 #if ITUNESDB_DEBUG
5496   fprintf(stderr, "Playlist: %s (%d tracks)\n", pl->name, g_list_length (pl->members));
5497 #endif
5498 
5499     put_header (cts, "mhyp");      /* header                    */
5500     put32lint (cts, 108);          /* length		        */
5501     put32lint (cts, -1);           /* size -> later             */
5502     mhodnum = 2;
5503     if (pl->is_spl)
5504     {   /* 2 more SPL MHODs */
5505 	mhodnum += 2;
5506     }
5507     else
5508     {
5509 	if ((pl->type == ITDB_PL_TYPE_MPL) && pl->members)
5510 	{   /* 5 more MHOD 52 lists and 5 more MHOD 53 lists*/
5511 	    mhodnum += 10;
5512 	}
5513     }
5514     put32lint (cts, mhodnum);      /* nr of mhods               */
5515     /* number of tracks in plist */
5516     put32lint (cts, -1);           /* number of mhips -> later  */
5517     put8int (cts, pl->type);       /* 1 = main, 0 = visible     */
5518     put8int (cts, pl->flag1);      /* unknown                   */
5519     put8int (cts, pl->flag2);      /* unknown                   */
5520     put8int (cts, pl->flag3);      /* unknown                   */
5521     mac_time = device_time_time_t_to_mac (fexp->itdb->device, pl->timestamp);
5522     put32lint (cts, mac_time);     /* some timestamp            */
5523     put64lint (cts, pl->id);       /* 64 bit ID                 */
5524     put32lint (cts, 0);            /* unknown, always 0?        */
5525     put16lint (cts, 1);            /* string mhod count (1)     */
5526     put16lint (cts, pl->podcastflag);
5527     put32lint (cts, pl->sortorder);
5528     if (mhsd_type == 5) {
5529         put32_n0 (cts, 8);            /* ?                         */
5530         /* +0x50 */
5531         put16lint (cts, pl->priv->mhsd5_type);
5532         put16lint (cts, pl->priv->mhsd5_type); /* same as 0x50 ? */
5533         /* +0x54 */
5534         if ((pl->priv->mhsd5_type == ITDB_PLAYLIST_MHSD5_MOVIE_RENTALS)
5535              || (pl->priv->mhsd5_type == ITDB_PLAYLIST_MHSD5_RINGTONES)){
5536             put32lint (cts, 1); /* unknown, 1 for Movie rentals + Ringtones */
5537         } else {
5538             put32lint (cts, 0); /* 0 otherwise */
5539         }
5540         put32_n0 (cts, 5); /* ? */
5541 
5542 
5543     } else {
5544         put32_n0 (cts, 15);            /* ?                         */
5545     }
5546 
5547     mhod.valid = TRUE;
5548     mhod.type = MHOD_ID_TITLE;
5549     mhod.data.string = pl->name;
5550     mk_mhod (fexp, &mhod);
5551     mk_long_mhod_id_playlist (fexp, pl);
5552 
5553     if ((pl->type == ITDB_PL_TYPE_MPL) && pl->members)
5554     {   /* write out the MHOD 52 and MHOD 53 lists */
5555 	/* We have to sort all tracks five times. To speed this up,
5556 	   translate the utf8 keys into collate_keys and use the
5557 	   faster strcmp() for comparison */
5558 	mhod.valid = TRUE;
5559 	mhod.data.mhod52coltracks = mhod52_make_collate_keys (pl->members);
5560 	mhod.mhod53_list = NULL;
5561 
5562 	mk_mhod52 (MHOD52_SORTTYPE_TITLE, fexp, &mhod);
5563 	mk_mhod53 (MHOD52_SORTTYPE_TITLE, fexp, &mhod);
5564 	mk_mhod52 (MHOD52_SORTTYPE_ARTIST, fexp, &mhod);
5565 	mk_mhod53 (MHOD52_SORTTYPE_ARTIST, fexp, &mhod);
5566 	mk_mhod52 (MHOD52_SORTTYPE_ALBUM, fexp, &mhod);
5567 	mk_mhod53 (MHOD52_SORTTYPE_ALBUM, fexp, &mhod);
5568 	mk_mhod52 (MHOD52_SORTTYPE_GENRE, fexp, &mhod);
5569 	mk_mhod53 (MHOD52_SORTTYPE_GENRE, fexp, &mhod);
5570 	mk_mhod52 (MHOD52_SORTTYPE_COMPOSER, fexp, &mhod);
5571 	mk_mhod53 (MHOD52_SORTTYPE_COMPOSER, fexp, &mhod);
5572 
5573 	mhod52_free_collate_keys (mhod.data.mhod52coltracks);
5574     }
5575     else  if (pl->is_spl)
5576     {  /* write the smart rules */
5577 	mhod.type = MHOD_ID_SPLPREF;
5578 	mhod.data.splpref = &pl->splpref;
5579 	mk_mhod (fexp, &mhod);
5580 
5581 	mhod.type = MHOD_ID_SPLRULES;
5582 	mhod.data.splrules = &pl->splrules;
5583 	mk_mhod (fexp, &mhod);
5584     }
5585 
5586     if (itdb_playlist_is_podcasts(pl) && (mhsd_type == 3))
5587     {
5588 	/* write special podcast playlist */
5589 	result = write_podcast_mhips (fexp, pl, mhyp_seek);
5590     }
5591     else if (mhsd_type == 5)
5592     {
5593 	/* set number of mhips to 0 for mhsd_type 5 */
5594 	put32lint_seek (cts, 0, mhyp_seek+16);
5595     }
5596     else
5597     {
5598 	/* write standard playlist hard-coded tracks */
5599 	result = write_playlist_mhips (fexp, pl, mhyp_seek);
5600     }
5601     fix_header (cts, mhyp_seek);
5602     return result;
5603 }
5604 
5605 
5606 /* Expects the master playlist to be the first in the list */
5607 /* Return FALSE in case of error and set fexp->error */
write_mhsd_playlists(FExport * fexp,guint32 mhsd_type)5608 static gboolean write_mhsd_playlists (FExport *fexp, guint32 mhsd_type)
5609 {
5610     GList *gl;
5611     glong mhsd_seek;
5612     glong mhlp_seek;
5613     WContents *cts;
5614     int plcnt = 0;
5615     GList *playlists;
5616 
5617     g_return_val_if_fail (fexp, FALSE);
5618     g_return_val_if_fail (fexp->itdb, FALSE);
5619     g_return_val_if_fail (fexp->wcontents, FALSE);
5620     g_return_val_if_fail ((mhsd_type == 2) || (mhsd_type == 3) || (mhsd_type == 5), FALSE);
5621 
5622     cts = fexp->wcontents;
5623     mhsd_seek = cts->pos;      /* get position of mhsd header */
5624     mk_mhsd (fexp, mhsd_type); /* write header */
5625     /* write header with nr. of playlists */
5626     mhlp_seek = cts->pos;
5627     mk_mhlp (fexp);
5628     if (mhsd_type == 5) {
5629         playlists = fexp->itdb->priv->mhsd5_playlists;
5630     } else {
5631         playlists = fexp->itdb->playlists;
5632     }
5633     for (gl=playlists; gl; gl=gl->next)
5634     {
5635 	Itdb_Playlist *pl = gl->data;
5636 	g_return_val_if_fail (pl, FALSE);
5637 
5638 	write_playlist (fexp, pl, mhsd_type);
5639 	plcnt++;
5640 	if (fexp->error)  return FALSE;
5641     }
5642 
5643     put32lint_seek (cts, plcnt, mhlp_seek+8);
5644 
5645     fix_header (cts, mhsd_seek);
5646     return TRUE;
5647 }
5648 
write_mhsd_albums(FExport * fexp)5649 static gboolean write_mhsd_albums (FExport *fexp)
5650 {
5651     gulong mhsd_seek;
5652     WContents *cts;
5653 
5654     g_return_val_if_fail (fexp, FALSE);
5655     g_return_val_if_fail (fexp->itdb, FALSE);
5656     g_return_val_if_fail (fexp->wcontents, FALSE);
5657 
5658     cts = fexp->wcontents;
5659     mhsd_seek = cts->pos;      	/* get position of mhsd header */
5660     mk_mhsd (fexp, 4); 		/* write header */
5661     mk_mhla (fexp);
5662     fix_header (cts, mhsd_seek);
5663     return TRUE;
5664 }
5665 
write_mhsd_artists(FExport * fexp)5666 static gboolean write_mhsd_artists (FExport *fexp)
5667 {
5668     gulong mhsd_seek;
5669     WContents *cts;
5670 
5671     g_return_val_if_fail (fexp, FALSE);
5672     g_return_val_if_fail (fexp->itdb, FALSE);
5673     g_return_val_if_fail (fexp->wcontents, FALSE);
5674 
5675     cts = fexp->wcontents;
5676     mhsd_seek = cts->pos;      	/* get position of mhsd header */
5677     mk_mhsd (fexp, 8); 		/* write header */
5678     mk_mhli (fexp);
5679     fix_header (cts, mhsd_seek);
5680     return TRUE;
5681 }
5682 
write_mhsd_type6(FExport * fexp)5683 static gboolean write_mhsd_type6 (FExport *fexp)
5684 {
5685     gulong mhsd_seek;
5686     WContents *cts;
5687 
5688     g_return_val_if_fail (fexp, FALSE);
5689     g_return_val_if_fail (fexp->itdb, FALSE);
5690     g_return_val_if_fail (fexp->wcontents, FALSE);
5691 
5692     cts = fexp->wcontents;
5693     mhsd_seek = cts->pos;      	/* get position of mhsd header */
5694     mk_mhsd (fexp, 6); 		/* write header */
5695     mk_mhlt (fexp, 0);	/* for now, produce an empty set */
5696     fix_header (cts, mhsd_seek);
5697     return TRUE;
5698 }
5699 
write_genius_mhsd(FExport * fexp)5700 static gboolean write_genius_mhsd (FExport *fexp)
5701 {
5702     gulong mhsd_seek;
5703     WContents *cts;
5704 
5705     g_return_val_if_fail (fexp, FALSE);
5706     g_return_val_if_fail (fexp->itdb, FALSE);
5707     g_return_val_if_fail (fexp->wcontents, FALSE);
5708 
5709     if (!fexp->itdb->priv->genius_cuid) {
5710         return TRUE;
5711     }
5712     cts = fexp->wcontents;
5713     mhsd_seek = cts->pos;      	/* get position of mhsd header */
5714     mk_mhsd (fexp, 9); 		/* write header */
5715     put_string (cts, fexp->itdb->priv->genius_cuid);
5716     fix_header (cts, mhsd_seek);
5717     return TRUE;
5718 }
5719 
write_mhsd_type10(FExport * fexp)5720 static gboolean write_mhsd_type10 (FExport *fexp)
5721 {
5722     gulong mhsd_seek;
5723     WContents *cts;
5724 
5725     g_return_val_if_fail (fexp, FALSE);
5726     g_return_val_if_fail (fexp->itdb, FALSE);
5727     g_return_val_if_fail (fexp->wcontents, FALSE);
5728 
5729     cts = fexp->wcontents;
5730     mhsd_seek = cts->pos;      	/* get position of mhsd header */
5731     mk_mhsd (fexp, 10); 		/* write header */
5732     mk_mhlt (fexp, 0);	/* for now, produce an empty set */
5733     fix_header (cts, mhsd_seek);
5734     return TRUE;
5735 }
5736 
5737 /* create a WContents structure */
wcontents_new(const gchar * filename)5738 static WContents *wcontents_new (const gchar *filename)
5739 {
5740     WContents *cts;
5741 
5742     g_return_val_if_fail (filename, NULL);
5743 
5744     cts = g_new0 (WContents, 1);
5745     cts->filename = g_strdup (filename);
5746 
5747     return cts;
5748 }
5749 
5750 
5751 /* write the contents of WContents. Return FALSE on error and set
5752  * cts->error accordingly. */
wcontents_write(WContents * cts)5753 static gboolean wcontents_write (WContents *cts)
5754 {
5755     g_return_val_if_fail (cts, FALSE);
5756     g_return_val_if_fail (cts->filename, FALSE);
5757 
5758     cts->error = NULL;
5759     return g_file_set_contents (cts->filename, cts->contents,
5760                                 cts->pos, &cts->error);
5761 }
5762 
5763 
5764 /* Free memory associated with WContents @cts */
wcontents_free(WContents * cts)5765 static void wcontents_free (WContents *cts)
5766 {
5767     if (cts)
5768     {
5769 	g_free (cts->filename);
5770 	g_free (cts->contents);
5771 	/* must not g_error_free (cts->error) because the error was
5772 	   propagated -> might free the error twice */
5773 	g_free (cts);
5774     }
5775 }
5776 
safe_str_equal(gconstpointer v1,gconstpointer v2)5777 static gboolean safe_str_equal (gconstpointer v1, gconstpointer v2)
5778 {
5779     if ((v1 == NULL) || (v2 == NULL)) {
5780         return (v1 == v2);
5781     } else {
5782         return g_str_equal (v1, v2);
5783     }
5784 }
5785 
itdb_album_hash(gconstpointer v)5786 static guint itdb_album_hash (gconstpointer v)
5787 {
5788   Itdb_Track *track = (Itdb_Track *)v;
5789   if (track->album != NULL) {
5790     return g_str_hash (track->album);
5791   } else {
5792     return 0;
5793   }
5794   g_assert_not_reached ();
5795 }
5796 
itdb_album_equal(gconstpointer v1,gconstpointer v2)5797 static gboolean itdb_album_equal (gconstpointer v1, gconstpointer v2)
5798 {
5799   Itdb_Track *track1 = (Itdb_Track *)v1;
5800   Itdb_Track *track2 = (Itdb_Track *)v2;
5801   gboolean same_show;
5802   gboolean same_album;
5803 
5804   /*album is the same if:
5805    * tvshow is the same, and
5806    * album and albumartist are the same, or
5807    * albumartist is null and artist and album are the same
5808    */
5809   same_show = safe_str_equal (track1->tvshow, track2->tvshow);
5810   if (!same_show) {
5811       return FALSE;
5812   }
5813 
5814   same_album = safe_str_equal (track1->album, track2->album);
5815   if (!same_album) {
5816       return FALSE;
5817   }
5818 
5819   if ((track1->albumartist != NULL) && (track2->albumartist != NULL)) {
5820       return safe_str_equal (track1->albumartist, track2->albumartist);
5821   } else {
5822       return safe_str_equal (track1->artist, track2->artist);
5823   }
5824 }
5825 
add_new_id(GHashTable * ids,Itdb_Track * track,guint new_id)5826 static void add_new_id (GHashTable *ids, Itdb_Track *track, guint new_id)
5827 {
5828     Itdb_Item_Id *id;
5829 
5830     id  = g_new0 (Itdb_Item_Id, 1);
5831     id->id = new_id;
5832     id->sql_id = ((guint64)g_random_int () << 32) | ((guint64)g_random_int ());
5833 
5834     g_hash_table_insert (ids, track, id);
5835 }
5836 
itdb_artist_hash(gconstpointer v)5837 static guint itdb_artist_hash (gconstpointer v)
5838 {
5839   Itdb_Track *track = (Itdb_Track *)v;
5840   if (track->artist != NULL) {
5841     return g_str_hash (track->artist);
5842   } else {
5843     return 0;
5844   }
5845 }
5846 
itdb_artist_equal(gconstpointer v1,gconstpointer v2)5847 static gboolean itdb_artist_equal (gconstpointer v1, gconstpointer v2)
5848 {
5849   Itdb_Track *track1 = (Itdb_Track *)v1;
5850   Itdb_Track *track2 = (Itdb_Track *)v2;
5851 
5852   return safe_str_equal (track1->artist, track2->artist);
5853 }
5854 
itdb_composer_hash(gconstpointer v)5855 static guint itdb_composer_hash (gconstpointer v)
5856 {
5857   Itdb_Track *track = (Itdb_Track *)v;
5858   if (track->composer != NULL) {
5859     return g_str_hash (track->composer);
5860   } else {
5861     return 0;
5862   }
5863 }
5864 
itdb_composer_equal(gconstpointer v1,gconstpointer v2)5865 static gboolean itdb_composer_equal (gconstpointer v1, gconstpointer v2)
5866 {
5867   Itdb_Track *track1 = (Itdb_Track *)v1;
5868   Itdb_Track *track2 = (Itdb_Track *)v2;
5869 
5870   return safe_str_equal (track1->composer, track2->composer);
5871 }
5872 
5873 /* - reassign the iPod IDs
5874    - make sure the itdb->tracks are in the same order as the mpl
5875    - assign album IDs to write the MHLA
5876 */
prepare_itdb_for_write(FExport * fexp)5877 static void prepare_itdb_for_write (FExport *fexp)
5878 {
5879     GList *gl;
5880     GList *pl;
5881     Itdb_iTunesDB *itdb;
5882     Itdb_Playlist *mpl;
5883     Itdb_Playlist *playlist;
5884     guint album_id = 1;
5885     guint artist_id = 1;
5886     guint composer_id = 1;
5887 
5888     g_return_if_fail (fexp);
5889     itdb = fexp->itdb;
5890     g_return_if_fail (itdb);
5891 
5892     /* Arrange itdb->tracks in the same order as mpl->members
5893        (otherwise On-The-Go Playlists will not show the correct
5894        content. Unfortunately we cannot just copy mpl->members to
5895        itdb->tracks because podcasts do not show up in the MPL. */
5896 
5897     mpl = itdb_playlist_mpl (itdb);
5898     g_return_if_fail (mpl);
5899 
5900     for (gl=g_list_last(mpl->members); gl; gl=gl->prev)
5901     {
5902 	GList *link;
5903 	Itdb_Track *track = gl->data;
5904 	g_return_if_fail (track);
5905 	link = g_list_find (itdb->tracks, track);
5906 	g_return_if_fail (link);
5907 
5908 	/* move this track to the beginning of itdb_tracks */
5909 	itdb->tracks = g_list_delete_link (itdb->tracks, link);
5910 	itdb->tracks = g_list_prepend (itdb->tracks, track);
5911     }
5912 
5913     fexp->next_id = FIRST_IPOD_ID;
5914 
5915     g_assert (fexp->albums == NULL);
5916     fexp->albums = g_hash_table_new_full (itdb_album_hash, itdb_album_equal,
5917 					  NULL, g_free);
5918 
5919     g_assert (fexp->artists == NULL);
5920     fexp->artists = g_hash_table_new_full (itdb_artist_hash, itdb_artist_equal,
5921 					   NULL, g_free);
5922 
5923     g_assert (fexp->composers == NULL);
5924     fexp->composers = g_hash_table_new_full (itdb_composer_hash, itdb_composer_equal,
5925 					     NULL, g_free);
5926 
5927     /* assign unique IDs and create sort keys */
5928     for (gl=itdb->tracks; gl; gl=gl->next)
5929     {
5930 	Itdb_Track *track = gl->data;
5931 	Itdb_Item_Id *id;
5932 
5933 	g_return_if_fail (track);
5934 	track->id = fexp->next_id++;
5935 
5936 	if (track->album != NULL) {
5937 		/* album ids are used when writing the mhla header */
5938 		id = g_hash_table_lookup (fexp->albums, track);
5939 		if (id != NULL) {
5940 		    track->priv->album_id = id->id;
5941 		} else {
5942 		    add_new_id (fexp->albums, track, album_id);
5943 		    track->priv->album_id = album_id;
5944 		    album_id++;
5945 		}
5946 	}
5947 
5948 	if (track->artist != NULL) {
5949 		/* artist ids are used when writing the mhli header */
5950 		id = g_hash_table_lookup (fexp->artists, track);
5951 		if (id != NULL) {
5952 		    track->priv->artist_id = id->id;
5953 		} else {
5954 		    add_new_id (fexp->artists, track, artist_id);
5955 		    track->priv->artist_id = artist_id;
5956 		    artist_id++;
5957 		}
5958 	}
5959 
5960 	if (track->composer != NULL) {
5961 		id = g_hash_table_lookup (fexp->composers, track);
5962 		if (id != NULL) {
5963 		    track->priv->composer_id = id->id;
5964 		} else {
5965 		    add_new_id (fexp->composers, track, composer_id);
5966 		    track->priv->composer_id = composer_id;
5967 		    composer_id++;
5968 		}
5969 	}
5970     }
5971 
5972     /* Make sure playlist->num is correct */
5973     for (pl = itdb->playlists; pl; pl = pl->next) {
5974 	playlist = pl->data;
5975 	g_return_if_fail (playlist);
5976 	playlist->num = itdb_playlist_tracks_number (playlist);
5977     }
5978 }
5979 
itdb_write_file_internal(Itdb_iTunesDB * itdb,const gchar * filename,GError ** error)5980 static gboolean itdb_write_file_internal (Itdb_iTunesDB *itdb,
5981 					  const gchar *filename,
5982 					  GError **error)
5983 {
5984     FExport *fexp;
5985     gulong mhbd_seek = 0;
5986     WContents *cts;
5987     gboolean result = TRUE;
5988     guint32 num_mhsds;
5989 
5990     g_return_val_if_fail (itdb, FALSE);
5991     g_return_val_if_fail (itdb->device, FALSE);
5992     g_return_val_if_fail (filename || itdb->filename, FALSE);
5993 
5994     if (!filename) filename = itdb->filename;
5995 
5996     /* set endianess flag */
5997     if (!itdb->device->byte_order)
5998 	itdb_device_autodetect_endianess (itdb->device);
5999 
6000     fexp = g_new0 (FExport, 1);
6001     fexp->itdb = itdb;
6002     fexp->wcontents = wcontents_new (filename);
6003     cts = fexp->wcontents;
6004 
6005     cts->reversed = (itdb->device->byte_order == G_BIG_ENDIAN);
6006 
6007     prepare_itdb_for_write (fexp);
6008 
6009 #if HAVE_GDKPIXBUF
6010     /* only write ArtworkDB if we deal with an iPod
6011        FIXME: figure out a way to store the artwork data when storing
6012        to local directories. At the moment it's the application's task
6013        to handle this. */
6014     /* The ArtworkDB must be written after the call to
6015      * prepare_itdb_for_write since it needs Itdb_Track::id to be set
6016      * to its final value to write properly on nano video/ipod classics
6017      */
6018     if (itdb_device_supports_artwork (itdb->device)) {
6019         ipod_write_artwork_db (itdb);
6020     }
6021 #endif
6022 
6023     /* default mhsd count */
6024     num_mhsds = 8; /* eight mhsds */
6025 
6026     /* if genius_cuid present, we have one more */
6027     if (fexp->itdb->priv->genius_cuid) {
6028 	num_mhsds++;
6029     }
6030 
6031     mk_mhbd (fexp, num_mhsds);
6032 
6033     /* write tracklist (mhsd type 1) */
6034     if (!fexp->error && !write_mhsd_tracks (fexp)) {
6035 	g_set_error (&fexp->error,
6036 		     ITDB_FILE_ERROR,
6037 		     ITDB_FILE_ERROR_ITDB_CORRUPT,
6038 		     _("Error writing list of tracks (mhsd type 1)"));
6039 	goto err;
6040     }
6041 
6042     /* write special podcast version mhsd (mhsd type 3) */
6043     if (!fexp->error && !write_mhsd_playlists (fexp, 3)) {
6044 	g_set_error (&fexp->error,
6045 		     ITDB_FILE_ERROR,
6046 		     ITDB_FILE_ERROR_ITDB_CORRUPT,
6047 		     _("Error writing special podcast playlists (mhsd type 3)"));
6048 	goto err;
6049     }
6050     /* write standard playlist mhsd (mhsd type 2) */
6051     if (!fexp->error && !write_mhsd_playlists (fexp, 2)) {
6052 	g_set_error (&fexp->error,
6053 		     ITDB_FILE_ERROR,
6054 		     ITDB_FILE_ERROR_ITDB_CORRUPT,
6055 		     _("Error writing standard playlists (mhsd type 2)"));
6056 	goto err;
6057     }
6058 
6059     /* write albums (mhsd type 4) */
6060     if (!write_mhsd_albums (fexp)) {
6061 	g_set_error (&fexp->error,
6062 		     ITDB_FILE_ERROR,
6063 		     ITDB_FILE_ERROR_ITDB_CORRUPT,
6064 		     _("Error writing list of albums (mhsd type 4)"));
6065 	goto err;
6066     }
6067     /* write artists (mhsd type 8) */
6068     if (!fexp->error && !write_mhsd_artists (fexp)) {
6069 	g_set_error (&fexp->error,
6070 		     ITDB_FILE_ERROR,
6071 		     ITDB_FILE_ERROR_ITDB_CORRUPT,
6072 		     _("Error writing list of artists (mhsd type 8)"));
6073 	goto err;
6074     }
6075 
6076     /* write empty mhsd type 6, whatever it is */
6077     if (!fexp->error && !write_mhsd_type6 (fexp)) {
6078 	g_set_error (&fexp->error,
6079 		     ITDB_FILE_ERROR,
6080 		     ITDB_FILE_ERROR_ITDB_CORRUPT,
6081 		     _("Error writing mhsd type 6"));
6082 	goto err;
6083     }
6084 
6085     /* write empty mhsd type 10, whatever it is */
6086     if (!fexp->error && !write_mhsd_type10 (fexp)) {
6087 	g_set_error (&fexp->error,
6088 		     ITDB_FILE_ERROR,
6089 		     ITDB_FILE_ERROR_ITDB_CORRUPT,
6090 		     _("Error writing mhsd type 10"));
6091 	goto err;
6092     }
6093 
6094     /* write mhsd5 playlists */
6095     if (!fexp->error && !write_mhsd_playlists (fexp, 5)) {
6096 	g_set_error (&fexp->error,
6097 		     ITDB_FILE_ERROR,
6098 		     ITDB_FILE_ERROR_ITDB_CORRUPT,
6099 		     _("Error writing mhsd5 playlists"));
6100 	goto err;
6101     }
6102 
6103     if (fexp->itdb->priv->genius_cuid) {
6104 	/* write genius cuid (mhsd type 9) */
6105 	if (!fexp->error && !write_genius_mhsd (fexp)) {
6106 	    g_set_error (&fexp->error,
6107 		    ITDB_FILE_ERROR,
6108 		    ITDB_FILE_ERROR_ITDB_CORRUPT,
6109 		    _("Error writing mhsd type 9"));
6110 	    goto err;
6111 	}
6112     }
6113 
6114     fix_header (cts, mhbd_seek);
6115 
6116     if (itdb_device_supports_compressed_itunesdb (itdb->device)) {
6117 	if (!itdb_zlib_check_compress_fexp (fexp)) {
6118 	    goto err;
6119 	}
6120     }
6121 
6122     /* Set checksum (ipods require it starting from Classic and Nano Video) */
6123     itdb_device_write_checksum (itdb->device,
6124 				(unsigned char *)fexp->wcontents->contents,
6125 				fexp->wcontents->pos,
6126 				&fexp->error);
6127     if (fexp->error) {
6128 	    goto err;
6129     }
6130 
6131     if (itdb_device_supports_sqlite_db (itdb->device)) {
6132 	if (itdb_sqlite_generate_itdbs(fexp) != 0) {
6133 	    goto err;
6134 	}
6135     }
6136 
6137     if (itdb_device_is_shuffle (itdb->device)) {
6138         /* iPod Shuffle uses a simplified database in addition to the
6139 	 * iTunesDB */
6140         if (!itdb_shuffle_write (itdb, &fexp->error)) {
6141 		goto err;
6142 	}
6143     }
6144 
6145     if (!fexp->error)
6146     {
6147 	if (wcontents_write (cts)) {
6148 	    playcounts_reset (itdb_get_mountpoint (itdb));
6149 	} else {
6150 	    g_propagate_error (&fexp->error, cts->error);
6151 	}
6152     }
6153 err:
6154     if (fexp->error)
6155     {
6156 	g_propagate_error (error, fexp->error);
6157 	result = FALSE;
6158     }
6159     wcontents_free (cts);
6160     if (fexp->albums != NULL) {
6161 	g_hash_table_destroy (fexp->albums);
6162     }
6163     if (fexp->artists != NULL) {
6164 	g_hash_table_destroy (fexp->artists);
6165     }
6166     g_free (fexp);
6167     if (result == TRUE)
6168     {
6169 	gchar *fn = g_strdup (filename);
6170 	g_free (itdb->filename);
6171 	itdb->filename = fn;
6172     }
6173 
6174     /* make sure all buffers are flushed as some people tend to
6175        disconnect as soon as gtkpod returns */
6176     itdb_fsync ();
6177 
6178     return result;
6179 }
6180 
6181 /**
6182  * itdb_write_file:
6183  * @itdb:       the #Itdb_iTunesDB to save
6184  * @filename:   filename to save @itdb to
6185  * @error:      return location for a #GError or NULL
6186  *
6187  * Write the content of @itdb to @filename. If @filename is NULL, it attempts
6188  * to write to @itdb->filename.
6189  *
6190  * Returns: TRUE if all went well, FALSE otherwise
6191  */
6192 
itdb_write_file(Itdb_iTunesDB * itdb,const gchar * filename,GError ** error)6193 gboolean itdb_write_file (Itdb_iTunesDB *itdb, const gchar *filename,
6194 			  GError **error)
6195 {
6196     return itdb_write_file_internal (itdb, filename, error);
6197 }
6198 
6199 /**
6200  * itdb_write:
6201  * @itdb:   the #Itdb_iTunesDB to write to disk
6202  * @error:  return location for a #GError or NULL
6203  *
6204  * Write out an iTunesDB. It reassigns unique IDs to all tracks.
6205  * An existing "Play Counts" file is renamed to "Play Counts.bak" if
6206  * the export was successful.
6207  * An existing "OTGPlaylistInfo" file is removed if the export was
6208  * successful.
6209  *
6210  * Returns: TRUE on success, FALSE on error, in which case @error is
6211  * set accordingly.
6212  */
itdb_write(Itdb_iTunesDB * itdb,GError ** error)6213 gboolean itdb_write (Itdb_iTunesDB *itdb, GError **error)
6214 {
6215     gchar *itunes_filename, *itunes_path;
6216     gboolean result = FALSE;
6217 
6218     g_return_val_if_fail (itdb, FALSE);
6219     g_return_val_if_fail (itdb_get_mountpoint (itdb), FALSE);
6220 
6221     /* Now handling regular iPod or iPhone/iPod Touch */
6222 
6223     /* First, let's try to write the .ithmb files containing the artwork data
6224      * since this operation modifies the 'artwork_count' and 'artwork_size'
6225      * field in the Itdb_Track contained in the database.
6226      * Errors happening during that operation are considered non fatal since
6227      * they shouldn't corrupt the main database.
6228      */
6229     itunes_path = itdb_get_itunes_dir (itdb_get_mountpoint (itdb));
6230 
6231     if(!itunes_path)
6232     {
6233 	error_no_itunes_dir (itdb_get_mountpoint (itdb), error);
6234 	return FALSE;
6235     }
6236 
6237     if (itdb_device_supports_compressed_itunesdb (itdb->device)) {
6238 	itunes_filename = g_build_filename (itunes_path, "iTunesCDB", NULL);
6239     } else {
6240 	itunes_filename = g_build_filename (itunes_path, "iTunesDB", NULL);
6241     }
6242 
6243     itdb_start_sync (itdb);
6244 
6245     result = itdb_write_file_internal (itdb, itunes_filename, error);
6246     g_free (itunes_filename);
6247 
6248     if (result && itdb_device_supports_compressed_itunesdb (itdb->device)) {
6249 	/* the iPhone gets confused when it has both an iTunesCDB and a non
6250 	 * empty iTunesDB
6251 	 */
6252 	itunes_filename = g_build_filename (itunes_path, "iTunesDB", NULL);
6253 	g_file_set_contents(itunes_filename, NULL, 0, NULL);
6254 	g_free (itunes_filename);
6255     }
6256 
6257     g_free (itunes_path);
6258 
6259     if (result != FALSE)
6260     {
6261 	/* Write SysInfo file if it has changed */
6262 	if (itdb->device->sysinfo_changed)
6263 	{
6264 	    itdb_device_write_sysinfo (itdb->device, error);
6265 	}
6266 	result = itdb_rename_files (itdb_get_mountpoint (itdb), error);
6267     }
6268 
6269     /* make sure all buffers are flushed as some people tend to
6270        disconnect as soon as gtkpod returns */
6271     itdb_fsync ();
6272 
6273     itdb_stop_sync (itdb);
6274 
6275     return result;
6276 }
6277 
6278 /**
6279  * itdb_start_sync:
6280  * @itdb:   the #Itdb_iTunesDB that is being sync'ed
6281  *
6282  * Hints libgpod that a series of file copies/database saves/... is about
6283  * to start. On regular iPods, this does nothing (but is safe to be used),
6284  * but on iPhones and iPod Touch this makes sure the "Sync in progress" screen
6285  * is shown for the whole duration of the writing process.
6286  *
6287  * Calls to itdb_start_sync must be paired with calls to itdb_stop_sync. Nesting
6288  * is allowed.
6289  *
6290  * Returns: TRUE on success, FALSE on error
6291  */
itdb_start_sync(Itdb_iTunesDB * itdb)6292 gboolean itdb_start_sync (Itdb_iTunesDB *itdb)
6293 {
6294     g_return_val_if_fail (itdb != NULL, FALSE);
6295     g_return_val_if_fail (itdb->device != NULL, FALSE);
6296 
6297 #ifdef HAVE_LIBIMOBILEDEVICE
6298     if (itdb->device->iphone_sync_context != NULL) {
6299 	itdb->device->iphone_sync_nest_level++;
6300 	return TRUE;
6301     }
6302     if (itdb_device_is_iphone_family (itdb->device)) {
6303 	int sync_status;
6304 	sync_status = itdb_iphone_start_sync (itdb->device,
6305 		                              &itdb->device->iphone_sync_context);
6306 	if (sync_status == 0) {
6307 	    return TRUE;
6308 	} else {
6309 	    return FALSE;
6310 	}
6311     }
6312 #endif
6313 
6314     return TRUE;
6315 }
6316 
6317 /**
6318  * itdb_stop_sync:
6319  * @itdb:   the #Itdb_iTunesDB that is being sync'ed
6320  *
6321  * Hints libgpod that the series of file copies/database saves/... started
6322  * with itdb_start_sync is finished. On regular iPods, this does nothing
6323  * (but is safe to be used). On iPhones and iPod Touch this will hide the
6324  * "Sync in progress" screen.
6325  *
6326  * Calls to itdb_stop_sync must be paired with calls to itdb_start_sync. Nesting
6327  * is allowed, and only the final itdb_stop_sync will actually stop the sync.
6328  *
6329  * Returns: TRUE on success, FALSE on error
6330  */
itdb_stop_sync(Itdb_iTunesDB * itdb)6331 gboolean itdb_stop_sync (Itdb_iTunesDB *itdb)
6332 {
6333     g_return_val_if_fail (itdb != NULL, FALSE);
6334     g_return_val_if_fail (itdb->device != NULL, FALSE);
6335 
6336 #ifdef HAVE_LIBIMOBILEDEVICE
6337     if (itdb->device->iphone_sync_nest_level) {
6338 	itdb->device->iphone_sync_nest_level--;
6339 	return TRUE;
6340     }
6341     if (itdb_device_is_iphone_family (itdb->device)) {
6342 	int sync_status;
6343 	if (itdb->device->iphone_sync_context == NULL) {
6344 	    g_warning ("Trying to unlock an already unlocked device");
6345 	    return FALSE;
6346 	}
6347 	sync_status = itdb_iphone_stop_sync (itdb->device->iphone_sync_context);
6348 	itdb->device->iphone_sync_context = NULL;
6349 	if (sync_status != 0) {
6350 	    return FALSE;
6351 	}
6352     }
6353 #endif
6354     return TRUE;
6355 }
6356 
6357 /* from here on we have the functions for writing the iTunesDB          */
6358 /* -------------------------------------------------------------------- */
6359 /* up to here we had the functions for writing the iTunesSD             */
6360 
6361 /*
6362 |  Copyright (C) 2005 Jorg Schuler <jcsjcs at users.sourceforge.net>
6363 |  Part of the gtkpod project.
6364 |
6365 |  Based on itunessd.c written by Steve Wahl for gtkpod-0.88:
6366 |
6367 |  Copyright 2005 Steve Wahl <steve at pro-ns dot net>
6368 |
6369 |  This file contains routines to create the iTunesSD file, as
6370 |  used by the ipod shuffle.
6371 |
6372 |  Like itunesdb.c, it is derived from the perl script "mktunes.pl"
6373 |  (part of the gnupod-tools collection) written by Adrian
6374 |  Ulrich <pab at blinkenlights.ch>.
6375 |
6376 |  Small(?) portions derived from itunesdb.c, so Jorg Schuler probably
6377 |  has some copyright ownership in this file as well.
6378 |
6379 |  The code contained in this file is free software; you can redistribute
6380 |  it and/or modify it under the terms of the GNU Lesser General Public
6381 |  License as published by the Free Software Foundation; either version
6382 |  2.1 of the License, or (at your option) any later version.
6383 |
6384 |  This file is distributed in the hope that it will be useful,
6385 |  but WITHOUT ANY WARRANTY; without even the implied warranty of
6386 |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
6387 |  Lesser General Public License for more details.
6388 |
6389 |  You should have received a copy of the GNU Lesser General Public
6390 |  License along with this code; if not, write to the Free Software
6391 |  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
6392 |
6393 |  iTunes and iPod are trademarks of Apple
6394 |
6395 |  This product is not supported/written/published by Apple!
6396 |
6397 */
6398 
6399 /* notes:
6400 
6401   All software currently seems to write iTunesDB as well as iTunesSD
6402   on the iPod shuffle.  I assume that reading from the iTunesSD file
6403   is not necessary.  The iTunesStats file is different, but I leave
6404   that for another day.
6405 
6406   The iTunesSD file format is as follows (taken from WikiPodLinux, feb
6407   '05):
6408 
6409     Offset Field         Bytes   Value
6410      (hex)               (dec)
6411 
6412   iTunesSD header (occurs once, at beginning of file):
6413 
6414     00      num_songs     3       number of song entries in the file
6415 
6416     03      unknown       3       always(?) 0x010600
6417     06      header size   3       size of the header (0x12, 18 bytes)
6418     09      unknown       3       possibly zero padding
6419 
6420   iTunesSD song entry format (occurs once for each song)
6421 
6422    000      size of entry 3       always(?) 0x00022e (558 bytes)
6423    003      unk1          3       unknown, always(?) 0x5aa501
6424    006      starttime     3       Start Time, in 256 ms increments
6425                                   e.g. 60s = 0xea (234 dec)
6426    009      unk2          3       unknown (always 0?)
6427    00C      unk3          3       unknown, some relationship to starttime
6428    00F      stoptime      3       Stop Time, also in 256 ms increments.
6429                                   Zero means play to end of file.
6430    012      unk4          3       Unknown.
6431    015      unk5          3       Unknown, but associated with stoptime?
6432    018      volume        3       Volume - ranges from 0x00 (-100%) to 0x64
6433                                   (0%) to 0xc8 (100%)
6434    01B      file_type     3       0x01 = MP3, 0x02 = AAC, 0x04=WAV
6435    01E      unk6          3       unknown (always 0x200?)
6436    021      filename      522     filename of the song, padded at the end
6437                                   with 0's.  Note: forward slashes are used
6438                                   here, not colons like in the iTunesDB --
6439                                   for example,
6440                                   "/iPod_Control/Music/F00/Song.mp3"
6441    22B      shuffleflag   1       If this value is 0x00, the song will be
6442                                   skipped while the player is in shuffle
6443                                   mode.  Any other value will allow it to be
6444                                   played in both normal and shuffle modes.
6445                                   iTunes 4.7.1 sets this to 0 for audio books.
6446    22C      bookmarkflag  1       If this flag is 0x00, the song will not be
6447                                   bookmarkable (i.e. its playback position
6448                                   won't be saved when switching to a different
6449                                   song). Any other value wil make it
6450                                   Bookmarkable.  Unlike hard drive based iPods,
6451                                   all songs can be marked as bookmarkable,
6452                                   not just .m4b and .aa
6453    22D      unknownflag   1       unknown, always? 0x00.
6454 
6455 All integers in the iTunesSD file are in BIG endian form...
6456 
6457 */
6458 
6459 /**
6460  * itdb_shuffle_write:
6461  * @itdb:   the #Itdb_iTunesDB to write to disk
6462  * @error:  return location for a #GError or NULL
6463  *
6464  * Write out an iTunesSD for the Shuffle.
6465  *
6466  * First reassigns unique IDs to all tracks.  An existing "Play
6467  * Counts" file is renamed to "Play Counts.bak" if the export was
6468  * successful.  An existing "OTGPlaylistInfo" file is removed if the
6469  * export was successful.  @itdb->mountpoint must point to the mount
6470  * point of the iPod, e.g. "/mnt/ipod" and be in local encoding.
6471  *
6472  * Returns: TRUE on success, FALSE on error, in which case @error is
6473  * set accordingly.
6474  */
itdb_shuffle_write(Itdb_iTunesDB * itdb,GError ** error)6475 gboolean itdb_shuffle_write (Itdb_iTunesDB *itdb, GError **error)
6476 {
6477     gchar *itunes_filename, *itunes_path;
6478     gboolean result = FALSE;
6479 
6480     g_return_val_if_fail (itdb, FALSE);
6481     g_return_val_if_fail (itdb_get_mountpoint (itdb), FALSE);
6482 
6483     itunes_path = itdb_get_itunes_dir (itdb_get_mountpoint (itdb));
6484 
6485     if(!itunes_path)
6486     {
6487 	gchar *str = g_build_filename (itdb_get_mountpoint (itdb),
6488 				       "iPod_Control", "iTunes", NULL);
6489 	g_set_error (error,
6490 		     ITDB_FILE_ERROR,
6491 		     ITDB_FILE_ERROR_NOTFOUND,
6492 		     _("Path not found: '%s' (or similar)."),
6493 		     str);
6494 	g_free (str);
6495 	return FALSE;
6496     }
6497 
6498     itunes_filename = g_build_filename (itunes_path, "iTunesSD", NULL);
6499 
6500     result = itdb_shuffle_write_file (itdb, itunes_filename, error);
6501 
6502     g_free(itunes_filename);
6503     g_free(itunes_path);
6504 
6505     if (result == TRUE)
6506 	result = itdb_rename_files (itdb_get_mountpoint (itdb), error);
6507 
6508     /* make sure all buffers are flushed as some people tend to
6509        disconnect as soon as gtkpod returns */
6510     itdb_fsync ();
6511 
6512     return result;
6513 }
6514 
6515 /* helper function */
haystack(gchar * filetype,gchar ** desclist)6516 static gboolean haystack (gchar *filetype, gchar **desclist)
6517 {
6518     gchar **dlp;
6519     if (!filetype || !desclist) return FALSE;
6520     for (dlp=desclist; *dlp; ++dlp)
6521     {
6522         if (strstr (filetype, *dlp)) return TRUE;
6523     }
6524     return FALSE;
6525 }
6526 
6527 /* Converts a standard itunesDB filetype to the one used in itunesSD */
6528 /* TODO: Find the values for other filetypes */
convert_filetype(gchar * filetype)6529 static guint convert_filetype (gchar *filetype)
6530 {
6531 	guint32 stype;
6532 	/* Type 0x01 Default value */
6533 	/* gchar *mp3_desc[] = {"MPEG", "MP3", "mpeg", "mp3", NULL}; */
6534 	/* Type 0x02 */
6535 	gchar *mp4_desc[] = {"AAC", "MP4", "M4A", "aac", "mp4", "m4a", NULL};
6536 	/* Type 0x04 */
6537 	gchar *wav_desc[] = {"WAV", "wav", NULL};
6538 
6539 	/* Default to mp3 */
6540 	stype=0x01;
6541 
6542 	if (haystack (filetype, mp4_desc))
6543 		stype=0x02;
6544 	else if (haystack (filetype, wav_desc))
6545 		stype=0x04;
6546 
6547 	return stype;
6548 }
6549 
6550 /* Write out the rths header, the iTunesSD track header */
write_rths(WContents * cts,Itdb_Track * track)6551 static gboolean write_rths (WContents *cts, Itdb_Track *track)
6552 {
6553 	gulong rths_seek;
6554 	gint padlength;
6555 	gchar *path;
6556 
6557 	g_return_val_if_fail (cts, FALSE);
6558 	g_return_val_if_fail (track, FALSE);
6559 
6560 	rths_seek=cts->pos;
6561 
6562 	/* Prep the path */
6563 	/* Correct this if the path field changes length */
6564 	path = g_strndup (track->ipod_path, 256);
6565 	g_strdelimit (path, ":", '/');
6566 
6567 	put_header (cts, "rths");
6568 	put32lint (cts, -1); /* Length of header to be added later */
6569 	put32lint (cts, track->starttime); /* Start pos in ms */
6570 	put32lint (cts, track->stoptime); /* Stop pos in ms */
6571 	put32lint (cts, track->volume); /* Volume gain */
6572 	put32lint (cts, convert_filetype (track->filetype)); /* Filetype see
6573 								convert filetype*/
6574 	/* If the length of this field changes make sure to correct the
6575 	   g_strndup above */
6576 	put_string (cts, path); /* Path to the song */
6577 	for (padlength = 256-strlen (path); padlength > 0; padlength--)
6578 		put8int (cts, 0);
6579 
6580 	put32lint (cts, track->bookmark_time); /* Bookmark time */
6581 	/* Don't Skip on shuffle */
6582 	/* This field has the exact opposite value as in the iTunesDB */
6583 	/* 1 when you want to skip, 0 when you don't want to skip. Causes a
6584          * playlist to be skipped if all songs in that playlist have this set */
6585 	put8int (cts, !track->skip_when_shuffling);
6586 	put8int (cts, track->remember_playback_position); /* Remember playing pos */
6587 	/* put8int (cts, ); In uninterruptable album */
6588 	put8int (cts, 0); /* Best guess its gapless album */
6589 	put8int (cts, 0); /* Unknown */
6590 	put32lint (cts, track->pregap); /* Pregap */
6591 	put32lint (cts, track->postgap); /* Postgap */
6592 	put32lint (cts, (guint32) track->samplecount); /* Number of Samples */
6593 	put32_n0 (cts, 1); /* Unknown */
6594 	put32lint (cts, track->gapless_data); /* Gapless Data */
6595 	put32_n0 (cts, 1); /* Unknown */
6596 	put32lint (cts, track->priv->album_id); /* Album ID */
6597 	put16lint (cts, track->track_nr); /* Track number */
6598 	put16lint (cts, track->cd_nr); /* Disk number */
6599 	put32_n0 (cts, 2); /* Unknown */
6600 	put64lint (cts, track->dbid); /* Voiceover Filename also dbid*/
6601 	put32lint (cts, track->priv->artist_id); /* Artist ID */
6602 	put32_n0 (cts, 8); /* Unknown */
6603 
6604 	fix_short_header (cts, rths_seek);
6605 
6606 	g_free (path);
6607 
6608 	return TRUE;
6609 }
6610 
6611 /* Write out the hths header, the iTunesSD track list header*/
write_hths(FExport * fexp)6612 static gboolean write_hths (FExport *fexp)
6613 {
6614 	gulong hths_seek, track_seek;
6615 	WContents *cts;
6616 	GList *gl;
6617 	guint32 trackcnt;
6618 	guint32 nonstdtrackcnt;
6619 
6620 	g_return_val_if_fail (fexp, FALSE);
6621 	g_return_val_if_fail (fexp->itdb, FALSE);
6622 	g_return_val_if_fail (fexp->wcontents, FALSE);
6623 
6624 	cts = fexp->wcontents;
6625 	hths_seek = cts->pos;
6626 	trackcnt = itdb_tracks_number(fexp->itdb);
6627 	nonstdtrackcnt = 0; /* Counts the number of audiobook and podcast tracks */
6628 
6629 	/* Add Track list offset to bdhs header */
6630 	/* If the bdhs header changes make sure the seek value is still correct */
6631 	put32lint_seek(cts, cts->pos, 36);
6632 
6633 	put_header (cts, "hths");
6634 	put32lint (cts, -1); /* Length of header to be added later */
6635 	put32lint (cts, trackcnt); /* Number of tracks */
6636 	put32_n0 (cts, 2); /* Unknown */
6637 	track_seek=cts->pos; /* Put the offset of the first track here */
6638 	put32_n0 (cts, trackcnt); /* The offset for each track to be added later */
6639 
6640 	fix_short_header (cts, hths_seek);
6641 
6642 	/* Add each tracks header */
6643 	for(gl=fexp->itdb->tracks;gl;gl=gl->next)
6644 	{
6645 		Itdb_Track *track = gl->data;
6646 
6647 		/* Add this tracks offset then add the track */
6648 		put32lint_seek(cts, cts->pos, track_seek);
6649 		g_return_val_if_fail (write_rths(cts, track), FALSE);
6650 
6651 		if ((track->mediatype & ITDB_MEDIATYPE_AUDIOBOOK) ||
6652 		    (track->mediatype & ITDB_MEDIATYPE_PODCAST))
6653 			nonstdtrackcnt++;
6654 
6655 		/* Go to the offset for the next track */
6656 		track_seek += 4;
6657 	}
6658 	/* Add the number of nonpodcasts to bdhs header */
6659 	/* If the bdhs header changes make sure the seek value is still correct */
6660 	put32lint_seek(cts, trackcnt-nonstdtrackcnt, 32);
6661 	return TRUE;
6662 }
6663 
6664 /* Write out the lphs header, the iTunesSD playlist header */
write_lphs(WContents * cts,Itdb_Playlist * pl)6665 static gboolean write_lphs (WContents *cts, Itdb_Playlist *pl)
6666 {
6667 	gulong lphs_seek;
6668 	GList *tl, *tracks, *current_track;
6669 	Itdb_Track *tr, *ctr;
6670 	guint64 id;
6671 	guint32 stype;
6672 	guint32 tracknum;
6673 	guint32 nonstdtrackcnt;
6674 
6675 	g_return_val_if_fail (pl, FALSE);
6676 	g_return_val_if_fail (pl->itdb, FALSE);
6677 	g_return_val_if_fail (cts, FALSE);
6678 
6679 	lphs_seek = cts->pos;
6680 	tracks = pl->itdb->tracks;
6681 	nonstdtrackcnt = 0; /* The number of audiobook and podcast tracks*/
6682 	/* Change the playlist itunesDB type into a itunesSD type */
6683 	/* 1 is for the master playlist
6684 	   2 is for a normal playlist
6685 	   3 is for the podcast playlist
6686 	   4 is for an audiobook playlist */
6687 	if (itdb_playlist_is_mpl (pl))
6688 		stype = 1;
6689 	else if (itdb_playlist_is_podcasts (pl))
6690 		stype = 3;
6691 	else if (itdb_playlist_is_audiobooks (pl))
6692 		stype = 4;
6693 	else /* Everything else we treat as normal */
6694 		stype = 2;
6695 
6696 	put_header (cts, "lphs");
6697 	put32lint (cts, -1); /* Length of header to be written later */
6698 	put32lint (cts, pl->num); /* Number of tracks in this playlist */
6699 	put32lint (cts, -1); /* The number of non podcasts or audiobooks to be added later */
6700 
6701 	if (stype == 1)
6702 		put32_n0 (cts, 2); /* The voiceover for the master is at 0 */
6703 	else
6704 		put64lint (cts, pl->id); /* Voiceover filename also Playlist ID */
6705 
6706 	put32lint (cts, stype); /* Type of playlist */
6707 	put32_n0 (cts, 4); /* Unknown */
6708 	if (tracks) /* It makes no sense to do the following if there is no tracks */
6709 	{
6710 		/* Walk the playlist and find and write out the track number
6711 		   of each track in it */
6712 		for( tl = pl -> members; tl; tl = tl->next)
6713 		{
6714 			tracknum = 0;
6715 			current_track= tracks;
6716 			tr = tl->data;
6717 			id = tr->dbid;
6718 			ctr = current_track->data;
6719 			/* Count the number of podcasts and audiobooks for later use */
6720 			if ((tr->mediatype & ITDB_MEDIATYPE_AUDIOBOOK) ||
6721 			    (tr->mediatype & ITDB_MEDIATYPE_PODCAST))
6722 				nonstdtrackcnt++;
6723 
6724 			while( id != ctr->dbid)
6725 			{
6726 				tracknum ++;
6727 				current_track = current_track->next;
6728 				g_return_val_if_fail (current_track, FALSE);
6729 				ctr = current_track->data;
6730 			}
6731 			put32lint (cts, tracknum);
6732 		}
6733 	}
6734 
6735 	put32lint_seek (cts, pl->num - nonstdtrackcnt, lphs_seek+12);
6736 	fix_short_header (cts, lphs_seek);
6737 	return TRUE;
6738 
6739 }
6740 
6741 /* Write out the hphs header, the iTunesSD playlist list header */
write_hphs(FExport * fexp)6742 static gboolean write_hphs (FExport *fexp)
6743 {
6744 	gulong hphs_seek, playlist_seek;
6745 	WContents *cts;
6746 	GList *gl;
6747 	guint16 playlistcnt;
6748 	guint16 podcastscnt;
6749 	guint16 mastercnt;
6750 	guint16 audiobookscnt;
6751 
6752 	g_return_val_if_fail (fexp, FALSE);
6753 	g_return_val_if_fail (fexp->itdb, FALSE);
6754 	g_return_val_if_fail (fexp->wcontents, FALSE);
6755 
6756 	cts = fexp->wcontents;
6757 	hphs_seek = cts->pos;
6758 	playlistcnt = 0; /* Number of nonempty playlists, this may not be the same
6759 			    as what itdb_playlists_number returns*/
6760 	podcastscnt = 0; /* Number of podcast playlists should be 1 if one exists*/
6761 	mastercnt = 0; /* Number of master playlists should be 1 */
6762 	audiobookscnt = 0; /* Number of audiobook playlists */
6763 
6764 	/* We have to walk the playlist list before we can write the needed counts */
6765 	for (gl = fexp->itdb->playlists; gl; gl = gl->next) {
6766 		Itdb_Playlist *pl = gl->data;
6767 
6768 		if (!pl->members) {
6769 		  /* If the playlist has no members skip it */
6770 		  continue;
6771 		}
6772 		/* Otherwise count it and count its type */
6773 		playlistcnt++;
6774 
6775 		if (itdb_playlist_is_mpl (pl)){
6776 			mastercnt++;
6777 		}
6778 		else if (itdb_playlist_is_podcasts (pl)) {
6779 			podcastscnt++;
6780 		}
6781 		else if (itdb_playlist_is_audiobooks (pl)) {
6782 			audiobookscnt++;
6783 		}
6784 	}
6785 
6786 	/* Add nonempty playlist count to bdhs */
6787 	put32lint_seek (cts, playlistcnt, 16);
6788 
6789 	/* Add the Playlist Header Offset */
6790 	put32lint_seek (cts, cts->pos, 40);
6791 
6792 	put_header (cts, "hphs");
6793 	put32lint (cts, -1); /* Length of header to be added later */
6794 	put16lint (cts, playlistcnt); /* Number of playlists */
6795 	put16_n0 (cts, 1); /* Unknown */
6796 	put16lint (cts, playlistcnt-podcastscnt); /* Number of non podcast playlists
6797 						    there should be a maximum of 1
6798 						    podcast playlist. */
6799 	put16lint (cts, mastercnt); /* Number of master playlists there should be a
6800 				       maximum of 1 master playlist */
6801 	put16lint (cts, playlistcnt-audiobookscnt); /* Number of non audiobook playlists */
6802 	put16_n0 (cts, 1);
6803 
6804 	playlist_seek = cts->pos;
6805 	put32_n0 (cts, playlistcnt); /* Offsets for each playlist */
6806 
6807 	fix_short_header (cts, hphs_seek);
6808 
6809 	for (gl = fexp->itdb->playlists; gl; gl = gl->next)
6810 	{
6811 		Itdb_Playlist *pl = gl->data;
6812 
6813 		if (!pl->members) {
6814 			continue;
6815 		}
6816 
6817 		/* Write this playlist's header offset */
6818 		put32lint_seek (cts, cts->pos, playlist_seek);
6819 
6820 		g_return_val_if_fail (write_lphs (cts, pl),FALSE);
6821 
6822 		/* Move to the field for the next header */
6823 		playlist_seek += 4;
6824 	}
6825 	return TRUE;
6826 }
6827 
6828 /* Write out the bdhs header, the main iTunesSD header */
write_bdhs(FExport * fexp)6829 static gboolean write_bdhs (FExport *fexp)
6830 {
6831 	WContents *cts;
6832 	gulong bdhs_seek;
6833 	guint32 trackcnt;
6834 
6835 	g_return_val_if_fail (fexp, FALSE);
6836 	g_return_val_if_fail (fexp->itdb, FALSE);
6837 	g_return_val_if_fail (fexp->wcontents, FALSE);
6838 
6839 	cts = fexp->wcontents;
6840 	bdhs_seek = cts->pos;
6841 	trackcnt = itdb_tracks_number (fexp->itdb);
6842 
6843 	put_header (cts, "bdhs");
6844 	put32lint (cts, 0x02000003); /* Unknown */
6845 	put32lint (cts, -1); /* Length of header to be added later*/
6846 	put32lint (cts, trackcnt); /* Number of tracks */
6847 	put32_n0 (cts, 1); /* Number of nonempty playlists to be written later
6848 			      in write_hphs see the note below! */
6849 	put32_n0 (cts, 2); /* Unknown */
6850 	/* TODO: Parse the max volume */
6851 	put8int (cts, 0); /* Limit max volume currently ignored and set to off */
6852 	/* TODO: Find another source of the voiceover option */
6853 	put8int (cts, 1); /* Voiceover currently ignored and set to on */
6854 	put16_n0 (cts, 1); /* Unknown */
6855 
6856 	/* NOTE: If the bdhs header changes the offsets of these fields may change
6857 	   make sure you correct the field offset in their respective tags */
6858 
6859 	put32lint (cts, -1); /* Number of tracks excluding podcasts and audiobooks
6860 				added in write_hths */
6861 	put32lint (cts, -1); /* Track Header Offset added in write_hths */
6862 	put32lint (cts, -1); /* Playlist Header Offset added in write_hphs */
6863 
6864 	put32_n0 (cts, 5); /* Unknown */
6865 
6866 	fix_header(cts, bdhs_seek);
6867 
6868 	return TRUE;
6869 }
6870 
is_shuffle_2g(Itdb_Device * device)6871 static gboolean is_shuffle_2g (Itdb_Device *device)
6872 {
6873     return (itdb_device_get_shadowdb_version (device) == ITDB_SHADOW_DB_V1);
6874 }
6875 
6876 /**
6877  * itdb_shuffle_write_file:
6878  * @itdb:       the #Itdb_iTunesDB to write to disk
6879  * @filename:   file to write to, cannot be NULL
6880  * @error:      return location for a #GError or NULL
6881  *
6882  * Do the actual writing to the iTunesSD
6883  *
6884  * Returns: TRUE on success, FALSE on error, in which case @error is
6885  * set accordingly.
6886  */
itdb_shuffle_write_file(Itdb_iTunesDB * itdb,const gchar * filename,GError ** error)6887 gboolean itdb_shuffle_write_file (Itdb_iTunesDB *itdb,
6888 				  const gchar *filename, GError **error)
6889 {
6890     FExport *fexp;
6891     GList *gl;
6892     WContents *cts;
6893     gboolean result = TRUE;;
6894 
6895     g_return_val_if_fail (itdb, FALSE);
6896     g_return_val_if_fail (filename, FALSE);
6897     g_return_val_if_fail (itdb->device, FALSE);
6898 
6899     /* Set endianess flag just in case */
6900     if (!itdb->device->byte_order)
6901 	    itdb_device_autodetect_endianess (itdb->device);
6902 
6903     fexp = g_new0 (FExport, 1);
6904     fexp->itdb = itdb;
6905     fexp->wcontents = wcontents_new (filename);
6906     cts = fexp->wcontents;
6907 
6908     cts->reversed = (itdb->device->byte_order == G_BIG_ENDIAN);
6909 
6910     prepare_itdb_for_write (fexp);
6911 
6912     if (is_shuffle_2g (itdb->device)) {
6913 	/* Older iTunesSD format */
6914 	put24bint (cts, itdb_tracks_number (itdb));
6915 	put24bint (cts, 0x010600);
6916 	put24bint (cts, 0x12);	/* size of header */
6917 	put24bint (cts, 0x0);	/* padding? */
6918 	put24bint (cts, 0x0);
6919 	put24bint (cts, 0x0);
6920 
6921 	for (gl=itdb->tracks; gl; gl=gl->next)
6922 	{
6923 	    Itdb_Track *tr = gl->data;
6924 	    gchar *path;
6925 	    gunichar2 *path_utf16;
6926 	    glong pathlen;
6927 
6928 	    g_return_val_if_fail (tr, FALSE);
6929 
6930 	    put24bint (cts, 0x00022e);
6931 	    put24bint (cts, 0x5aa501);
6932 	    /* starttime is in 256 ms incr. for shuffle */
6933 	    put24bint (cts, tr->starttime / 256);
6934 	    put24bint (cts, 0);
6935 	    put24bint (cts, 0);
6936 	    put24bint (cts, tr->stoptime / 256);
6937 	    put24bint (cts, 0);
6938 	    put24bint (cts, 0);
6939 	    /* track->volume ranges from -255 to +255 */
6940 	    /* we want 0 - 200 */
6941 	    put24bint (cts, ((tr->volume + 255) * 201) / 511);
6942 
6943 	    /* The next one should be 0x01 for MP3,
6944 	     ** 0x02 for AAC, and 0x04 for WAV, but I can't find
6945 	     ** a suitable indicator within the track structure? */
6946 	    /* JCS: let's do heuristic on tr->filetype which would contain
6947 	       "MPEG audio file", "AAC audio file", "Protected AAC audio
6948 	       file", "AAC audio book file", "WAV audio file" (or similar
6949 	       if not written by gtkpod) */
6950 
6951 	    put24bint (cts, convert_filetype (tr->filetype));
6952 
6953 	    put24bint (cts, 0x200);
6954 
6955 	    path = g_strdup (tr->ipod_path);
6956 	    /* shuffle uses forward slash separator, not colon */
6957 	    g_strdelimit (path, ":", '/');
6958 	    path_utf16 = g_utf8_to_utf16 (path, -1, NULL, &pathlen, NULL);
6959 	    if (pathlen > 261) pathlen = 261;
6960 	    fixup_little_utf16 (path_utf16);
6961 	    put_data (cts, (gchar *)path_utf16, sizeof (gunichar2)*pathlen);
6962 	    /* pad to 522 bytes */
6963 	    put16_n0 (cts, 261-pathlen);
6964 	    g_free(path);
6965 	    g_free(path_utf16);
6966 
6967 	    put8int (cts, tr->skip_when_shuffling); /* Is the song used in shuffle mode */
6968 	    put8int (cts, tr->remember_playback_position);   /* Is the song bookmarkable */
6969 	    put8int (cts, 0);
6970 	}
6971     }else {
6972 	    /* Newer iTunesSD format */
6973 	    /* Add the main Header */
6974 	    write_bdhs(fexp);
6975 
6976 	    /* Add the Tracks Header */
6977 	    if (!write_hths(fexp)){
6978 		g_set_error(&fexp->error,
6979 			    ITDB_FILE_ERROR,
6980 			    ITDB_FILE_ERROR_CORRUPT,
6981 			    _("Error writing list of tracks (hths)"));
6982 		goto serr;
6983 	    }
6984 
6985 	    /* Add the Playlist Header */
6986 	    if(!fexp->error && !write_hphs(fexp)){
6987 		g_set_error(&fexp->error,
6988 			    ITDB_FILE_ERROR,
6989 			    ITDB_FILE_ERROR_CORRUPT,
6990 			    _("Error writing playlists (hphs)"));
6991 		goto serr;
6992 	}
6993     }
6994      if (!fexp->error)
6995     {
6996 	if (!wcontents_write (cts))
6997 	    g_propagate_error (&fexp->error, cts->error);
6998      }
6999 serr:
7000      if (fexp->error)
7001      {
7002 	  g_propagate_error (error, fexp->error);
7003 	  result = FALSE;
7004      }
7005     wcontents_free (cts);
7006     g_free (fexp);
7007 
7008     /* make sure all buffers are flushed as some people tend to
7009        disconnect as soon as gtkpod returns */
7010     itdb_fsync ();
7011 
7012     return result;
7013 }
7014 
7015 /*------------------------------------------------------------------*\
7016  *                                                                  *
7017  *                  Other file/filename stuff                       *
7018  *                                                                  *
7019 \*------------------------------------------------------------------*/
7020 
7021 /**
7022  * itdb_rename_files:
7023  * @mp:     mount point of the iPod
7024  * @error:  return location for a #GError or NULL
7025  *
7026  * Renames/removes some files on the iPod (Playcounts, OTG
7027  * semaphore). May have to be called if you write the iTunesDB not
7028  * directly to the iPod but to some other location and then manually
7029  * copy the file from there to the iPod.
7030  *
7031  * Returns: FALSE on error and sets @error accordingly
7032  */
itdb_rename_files(const gchar * mp,GError ** error)7033 gboolean itdb_rename_files (const gchar *mp, GError **error)
7034 {
7035     const gchar *db_plc_o[] = {"Play Counts", NULL};
7036     const gchar *db_otg[] = {"OTGPlaylistInfo", NULL};
7037     const gchar *db_shu[] = {"iTunesShuffle", NULL};
7038     const gchar *db_ist[] = {"iTunesStats", NULL};
7039     gchar *itunesdir;
7040     gchar *plcname_o;
7041     gchar *plcname_n;
7042     gchar *otgname;
7043     gchar *shuname;
7044     gchar *istname;
7045     gboolean result = TRUE;
7046 
7047     g_return_val_if_fail (mp, FALSE);
7048 
7049     itunesdir = itdb_get_itunes_dir (mp);
7050 
7051     if(!itunesdir)
7052     {
7053 	error_no_itunes_dir (mp, error);
7054 	return FALSE;
7055     }
7056 
7057     plcname_o = itdb_resolve_path (itunesdir, db_plc_o);
7058     plcname_n = g_build_filename (itunesdir,
7059 					 "Play Counts.bak", NULL);
7060     otgname = itdb_resolve_path (itunesdir, db_otg);
7061     shuname = itdb_resolve_path (itunesdir, db_shu);
7062     istname = itdb_resolve_path (itunesdir, db_ist);
7063 
7064     /* rename "Play Counts" to "Play Counts.bak" */
7065     if (plcname_o)
7066     {
7067 	if (rename (plcname_o, plcname_n) == -1)
7068 	{   /* an error occured */
7069 	    g_set_error (error,
7070 			 G_FILE_ERROR,
7071 			 g_file_error_from_errno (errno),
7072 			 _("Error renaming '%s' to '%s' (%s)."),
7073 			 plcname_o, plcname_n, g_strerror (errno));
7074 	    result = FALSE;
7075 	}
7076     }
7077 
7078     /* remove "OTGPlaylistInfo" (the iPod will remove the remaining
7079      * files */
7080     if (otgname)
7081     {
7082 	if (unlink (otgname) == -1)
7083 	{
7084 	    if (error && !*error)
7085 	    {   /* don't overwrite previous error */
7086 	    g_set_error (error,
7087 			 G_FILE_ERROR,
7088 			 g_file_error_from_errno (errno),
7089 			 _("Error removing '%s' (%s)."),
7090 			 otgname, g_strerror (errno));
7091 	    }
7092 	    result = FALSE;
7093 	}
7094     }
7095 
7096     /* remove some Shuffle files */
7097     if (shuname)
7098     {
7099 	if (unlink (shuname) == -1)
7100 	{
7101 	    if (error && !*error)
7102 	    {   /* don't overwrite previous error */
7103 	    g_set_error (error,
7104 			 G_FILE_ERROR,
7105 			 g_file_error_from_errno (errno),
7106 			 _("Error removing '%s' (%s)."),
7107 			 shuname, g_strerror (errno));
7108 	    }
7109 	    result = FALSE;
7110 	}
7111     }
7112 
7113     /* remove some Shuffle files */
7114     if (istname)
7115     {
7116 	if (unlink (istname) == -1)
7117 	{
7118 	    if (error && !*error)
7119 	    {   /* don't overwrite previous error */
7120 	    g_set_error (error,
7121 			 G_FILE_ERROR,
7122 			 g_file_error_from_errno (errno),
7123 			 _("Error removing '%s' (%s)."),
7124 			 istname, g_strerror (errno));
7125 	    }
7126 	    result = FALSE;
7127 	}
7128     }
7129 
7130     g_free (plcname_o);
7131     g_free (plcname_n);
7132     g_free (otgname);
7133     g_free (shuname);
7134     g_free (istname);
7135     g_free (itunesdir);
7136 
7137     return result;
7138 }
7139 
7140 
7141 /**
7142  * itdb_filename_fs2ipod:
7143  * @filename: a 'PC-style' filename (eg /iPod_Control/Music/f00/test.mp3)
7144  *
7145  * Convert string from casual PC file name to iPod iTunesDB format
7146  * using ':' instead of G_DIR_SEPARATOR_S (i.e. slashes on Unix-like
7147  * systems). @ipod_file is modified in place.
7148  */
itdb_filename_fs2ipod(gchar * ipod_file)7149 void itdb_filename_fs2ipod (gchar *ipod_file)
7150 {
7151     g_strdelimit (ipod_file, G_DIR_SEPARATOR_S, ':');
7152 }
7153 
7154 /**
7155  * itdb_filename_ipod2fs:
7156  * @ipod_file: a 'PC-style' filename (eg /iPod_Control/Music/f00/test.mp3)
7157  *
7158  * Convert string from from iPod iTunesDB format to casual PC file
7159  * name using G_DIR_SEPARATOR (ie slashes on Unix-like systems)
7160  * instead of ':'.  @ipod_file is modified in place.
7161  */
itdb_filename_ipod2fs(gchar * ipod_file)7162 void itdb_filename_ipod2fs (gchar *ipod_file)
7163 {
7164     g_strdelimit (ipod_file, ":", G_DIR_SEPARATOR);
7165 }
7166 
7167 /**
7168  * itdb_set_mountpoint:
7169  * @itdb:   an #Itdb_iTunesDB
7170  * @mp:     new mount point
7171  *
7172  * Sets the mountpoint of @itdb. Always use this function to set the
7173  * mountpoint of an #Itdb_iTunesDB as it will reset the number of
7174  * available /iPod_Control/Music/F.. dirs. It doesn't attempt to parse
7175  * an iPod database that may be present on the iPod at @mp.
7176  *
7177  * <note><para>Calling this function removes the artwork in the
7178  * #Itdb_iTunesDB database using this #Itdb_Device which was read from the
7179  * iPod.</para></note>.
7180  *
7181  * Since: 0.1.3
7182  */
itdb_set_mountpoint(Itdb_iTunesDB * itdb,const gchar * mp)7183 void itdb_set_mountpoint (Itdb_iTunesDB *itdb, const gchar *mp)
7184 {
7185     g_return_if_fail (itdb);
7186     g_return_if_fail (itdb->device);
7187 
7188     itdb_device_set_mountpoint (itdb->device, mp);
7189     itdb->device->musicdirs = 0;
7190 }
7191 
7192 /**
7193  * itdb_get_mountpoint:
7194  * @itdb: an #Itdb_iTunesDB
7195  *
7196  * Retrieve a reference to the mountpoint of @itdb
7197  *
7198  * Returns: the @itdb mountpoint, this string shouldn't be freed
7199  * nor modified
7200  *
7201  * Since: 0.4.0
7202  */
itdb_get_mountpoint(Itdb_iTunesDB * itdb)7203 const gchar *itdb_get_mountpoint (Itdb_iTunesDB *itdb)
7204 {
7205     g_return_val_if_fail (itdb, NULL);
7206     g_return_val_if_fail (itdb->device, NULL);
7207     return itdb->device->mountpoint;
7208 }
7209 
7210 /* Retrieve a reference to the mountpoint */
itdb_photodb_get_mountpoint(Itdb_PhotoDB * photodb)7211 const gchar *itdb_photodb_get_mountpoint (Itdb_PhotoDB *photodb)
7212 {
7213     g_return_val_if_fail (photodb, NULL);
7214     g_return_val_if_fail (photodb->device, NULL);
7215     return photodb->device->mountpoint;
7216 }
7217 
7218 /**
7219  * itdb_musicdirs_number:
7220  * @itdb: an #Itdb_iTunesDB
7221  *
7222  * Determine the number of F.. directories in iPod_Control/Music.
7223  *
7224  * If @itdb->musicdirs is already set, simply return the previously
7225  * determined number. Otherwise count the directories first and set
7226  * @itdb->musicdirs.
7227  *
7228  * Returns: max number of directories in iPod_Control/Music
7229  *
7230  * Since: 0.1.3
7231  */
itdb_musicdirs_number(Itdb_iTunesDB * itdb)7232 gint itdb_musicdirs_number (Itdb_iTunesDB *itdb)
7233 {
7234     g_return_val_if_fail (itdb, 0);
7235     g_return_val_if_fail (itdb->device, 0);
7236 
7237     return itdb_device_musicdirs_number (itdb->device);
7238 }
7239 
7240 /**
7241  * itdb_cp_get_dest_filename:
7242  * @track:      track to transfer or NULL
7243  * @mountpoint: mountpoint of your iPod or NULL
7244  * @filename:   the source file
7245  * @error:      return location for a #GError or NULL
7246  *
7247  * Creates a valid filename on the iPod where @filename can be copied.
7248  *
7249  * You must provide either @track or @mountpoint. Providing @track is
7250  * not thread-safe (accesses track->itdb->device and may even write to
7251  * track->itdb->device). Providing @mountpoint is thread-safe but
7252  * slightly slower because the number of music directories is counted
7253  * each time the function is called.
7254  *
7255  * You can use itdb_cp() to copy the track to the iPod or implement
7256  * your own copy function. After the file was copied you have to call
7257  * itdb_cp_finalize() to obtain relevant update information for
7258  * #Itdb_Track.
7259  *
7260  * Returns: a valid filename on the iPod where @filename can be
7261  * copied or NULL in case of an error. In that case @error is set
7262  * accordingly. You must free the filename when it is no longer
7263  * needed.
7264  *
7265  * Since: 0.5.0
7266  */
itdb_cp_get_dest_filename(Itdb_Track * track,const gchar * mountpoint,const gchar * filename,GError ** error)7267 gchar *itdb_cp_get_dest_filename (Itdb_Track *track,
7268                                   const gchar *mountpoint,
7269 				  const gchar *filename,
7270 				  GError **error)
7271 {
7272     gchar *ipod_fullfile = NULL;
7273 
7274     /* either supply mountpoint or track */
7275     g_return_val_if_fail (mountpoint || track, NULL);
7276     /* if mountpoint is not set, track->itdb is required */
7277     g_return_val_if_fail (mountpoint || track->itdb, NULL);
7278     g_return_val_if_fail (filename, NULL);
7279 
7280     if (!mountpoint)
7281     {
7282 	mountpoint = itdb_get_mountpoint (track->itdb);
7283     }
7284 
7285     if (!mountpoint)
7286     {
7287 	g_set_error (error,
7288 		     ITDB_FILE_ERROR,
7289 		     ITDB_FILE_ERROR_NOTFOUND,
7290 		     _("Mountpoint not set."));
7291 	return NULL;
7292     }
7293 
7294     /* If track->ipod_path exists, we use that one instead. */
7295     if (track)
7296     {
7297 	ipod_fullfile = itdb_filename_on_ipod (track);
7298     }
7299 
7300     if (!ipod_fullfile)
7301     {
7302 	gint dir_num, musicdirs_number;
7303 	gchar *dest_components[] = {NULL, NULL, NULL};
7304 	gchar *parent_dir_filename, *music_dir;
7305 	gchar *original_suffix;
7306 	gchar dir_num_str[6];
7307 	gint32 oops = 0;
7308 	gint32 rand = g_random_int_range (0, 899999); /* 0 to 900000 */
7309 
7310 	music_dir = itdb_get_music_dir (mountpoint);
7311 	if (!music_dir)
7312 	{
7313 	    error_no_music_dir (mountpoint, error);
7314 	    return NULL;
7315 	}
7316 
7317 	if (track)
7318 	{
7319 	    musicdirs_number = itdb_musicdirs_number (track->itdb);
7320 	}
7321 	else
7322 	{
7323 	    musicdirs_number = itdb_musicdirs_number_by_mountpoint (mountpoint);
7324 	}
7325 	if (musicdirs_number <= 0)
7326 	{
7327 	    g_set_error (error,
7328 			 ITDB_FILE_ERROR,
7329 			 ITDB_FILE_ERROR_NOTFOUND,
7330 			 _("No 'F..' directories found in '%s'."),
7331 			 music_dir);
7332 	    g_free (music_dir);
7333 	    return NULL;
7334 	}
7335 
7336 	dir_num = g_random_int_range (0, musicdirs_number);
7337 
7338 	g_snprintf (dir_num_str, 6, "F%02d", dir_num);
7339 	dest_components[0] = dir_num_str;
7340 
7341 	parent_dir_filename =
7342 	    itdb_resolve_path (music_dir, (const gchar **)dest_components);
7343 	if(parent_dir_filename == NULL)
7344 	{
7345 	    /* Can't find the F%02d directory */
7346 	    gchar *str = g_build_filename (music_dir,
7347 					   dest_components[0], NULL);
7348 	    g_set_error (error,
7349 			 ITDB_FILE_ERROR,
7350 			 ITDB_FILE_ERROR_NOTFOUND,
7351 			 _("Path not found: '%s'."),
7352 			 str);
7353 	    g_free (str);
7354 	    g_free (music_dir);
7355 	    return NULL;
7356 	}
7357 
7358 	/* we need the original suffix of pcfile to construct a correct ipod
7359 	   filename */
7360 	original_suffix = strrchr (filename, '.');
7361 	/* If there is no ".mp3", ".m4a" etc, set original_suffix to empty
7362 	   string. Note: the iPod will most certainly ignore this file... */
7363 	if (!original_suffix) original_suffix = "";
7364 
7365 	/* use lower-case version of extension as some iPods seem to
7366 	   choke on upper-case extension. */
7367 	original_suffix = g_ascii_strdown (original_suffix, -1);
7368 
7369 	do
7370 	{   /* we need to loop until we find an unused filename */
7371 	    dest_components[1] =
7372 		g_strdup_printf("libgpod%06d%s",
7373 				rand + oops, original_suffix);
7374 	    ipod_fullfile = itdb_resolve_path (
7375 		parent_dir_filename,
7376 		(const gchar **)&dest_components[1]);
7377 	    if(ipod_fullfile)
7378 	    {   /* already exists -- try next */
7379 		g_free(ipod_fullfile);
7380 		ipod_fullfile = NULL;
7381 	    }
7382 	    else
7383 	    {   /* found unused file -- build filename */
7384 		ipod_fullfile = g_build_filename (parent_dir_filename,
7385 						  dest_components[1], NULL);
7386 	    }
7387 	    g_free (dest_components[1]);
7388 	    ++oops;
7389 	} while (!ipod_fullfile);
7390 	g_free(parent_dir_filename);
7391 	g_free (music_dir);
7392 	g_free (original_suffix);
7393     }
7394 
7395     return ipod_fullfile;
7396 }
7397 
7398 /**
7399  * itdb_cp_finalize:
7400  * @track:          track to update or NULL
7401  * @mountpoint:     mountpoint of your iPod or NULL
7402  * @dest_filename:  the name of the file on the iPod copied to
7403  * @error:          return location for a #GError or NULL
7404  *
7405  * Updates information in @track necessary for the iPod.
7406  *
7407  * You must supply either @track or @mountpoint. If @track == NULL, a
7408  * new track structure is created that must be freed with
7409  * itdb_track_free() when it is no longer needed.
7410  *
7411  * The following @track fields are updated:
7412  *
7413  * <itemizedlist>
7414  *   <listitem>
7415  *       ipod_path
7416  *   </listitem>
7417  *   <listitem>
7418  *       filetype_marker
7419  *   </listitem>
7420  *   <listitem>
7421  *       transferred
7422  *   </listitem>
7423  *   <listitem>
7424  *       size
7425  *   </listitem>
7426  * </itemizedlist>
7427  *
7428  * Returns: on success a pointer to the #Itdb_Track item passed
7429  * or a new #Itdb_Track item if @track was NULL. In the latter case
7430  * you must free the memory using itdb_track_free() when the item is
7431  * no longer used. If an error occurs NULL is returned and @error is
7432  * set accordingly. Errors occur when @dest_filename cannot be
7433  * accessed or the mountpoint is not set.
7434  *
7435  * Since: 0.5.0
7436  */
itdb_cp_finalize(Itdb_Track * track,const gchar * mountpoint,const gchar * dest_filename,GError ** error)7437 Itdb_Track *itdb_cp_finalize (Itdb_Track *track,
7438 			      const gchar *mountpoint,
7439 			      const gchar *dest_filename,
7440 			      GError **error)
7441 {
7442     const gchar *suffix;
7443     Itdb_Track *use_track;
7444     gint i, mplen;
7445     struct stat statbuf;
7446 
7447     /* either supply mountpoint or track */
7448     g_return_val_if_fail (mountpoint || track, NULL);
7449     /* if mountpoint is not set, track->itdb is required */
7450     g_return_val_if_fail (mountpoint || track->itdb, NULL);
7451     g_return_val_if_fail (dest_filename, NULL);
7452 
7453     if (!mountpoint)
7454     {
7455 	mountpoint = itdb_get_mountpoint (track->itdb);
7456     }
7457 
7458     if (!mountpoint)
7459     {
7460 	g_set_error (error,
7461 		     ITDB_FILE_ERROR,
7462 		     ITDB_FILE_ERROR_NOTFOUND,
7463 		     _("Mountpoint not set."));
7464 	return NULL;
7465     }
7466 
7467     if (stat (dest_filename, &statbuf) == -1)
7468     {
7469 	g_set_error (error,
7470 		     G_FILE_ERROR,
7471 		     g_file_error_from_errno (errno),
7472 		     _("'%s' could not be accessed (%s)."),
7473 		     dest_filename, g_strerror (errno));
7474 	return NULL;
7475     }
7476 
7477     if (strlen (mountpoint) >= strlen (dest_filename))
7478     {
7479 	g_set_error (error,
7480 		     ITDB_FILE_ERROR,
7481 		     ITDB_FILE_ERROR_CORRUPT,
7482 		     _("Destination file '%s' does not appear to be on the iPod mounted at '%s'."),
7483 		     dest_filename, mountpoint);
7484 	return NULL;
7485     }
7486 
7487     if (!track)
7488     {
7489 	use_track = itdb_track_new ();
7490     }
7491     else
7492     {
7493 	use_track = track;
7494     }
7495 
7496     use_track->transferred = TRUE;
7497     use_track->size = statbuf.st_size;
7498 
7499     /* we need the original suffix of pcfile to construct a correct ipod
7500        filename */
7501     suffix = strrchr (dest_filename, '.');
7502     /* If there is no ".mp3", ".m4a" etc, set original_suffix to empty
7503        string. Note: the iPod will most certainly ignore this file... */
7504     if (!suffix) suffix = ".";
7505 
7506     /* set filetype from the suffix, e.g. '.mp3' -> 'MP3 ' */
7507     use_track->filetype_marker = 0;
7508     for (i=1; i<=4; ++i)   /* start with i=1 to skip the '.' */
7509     {
7510 	use_track->filetype_marker = use_track->filetype_marker << 8;
7511 	if (strlen (suffix) > i)
7512 	    use_track->filetype_marker |= g_ascii_toupper (suffix[i]);
7513 	else
7514 	    use_track->filetype_marker |= ' ';
7515     }
7516 
7517     /* now extract filepath for use_track->ipod_path from ipod_fullfile */
7518     /* ipod_path must begin with a '/' */
7519     g_free (use_track->ipod_path);
7520     mplen = strlen (mountpoint); /* length of mountpoint in bytes */
7521     if (dest_filename[mplen] == G_DIR_SEPARATOR)
7522     {
7523 	use_track->ipod_path = g_strdup (&dest_filename[mplen]);
7524     }
7525     else
7526     {
7527 	use_track->ipod_path = g_strdup_printf ("%c%s", G_DIR_SEPARATOR,
7528 						&dest_filename[mplen]);
7529     }
7530     /* convert to iPod type */
7531     itdb_filename_fs2ipod (use_track->ipod_path);
7532 
7533     return use_track;
7534 }
7535 
7536 /**
7537  * itdb_cp_track_to_ipod:
7538  * @track:      the #Itdb_Track to copy (containing @filename metadata)
7539  * @filename:   the source file
7540  * @error:      return location for a #GError or NULL
7541  *
7542  * Copy one track to the iPod. The PC filename is @filename
7543  * and is taken literally.
7544  *
7545  * The mountpoint of the iPod (in local encoding) must have been set
7546  * with itdb_set_mountpoint() (done automatically when reading an
7547  * iTunesDB).
7548  *
7549  * If @track->transferred is set to TRUE, nothing is done. Upon
7550  * successful transfer @track->transferred is set to TRUE.
7551  *
7552  * For storage, the directories "F00 ... Fnn" will be used randomly.
7553  *
7554  * The filename is constructed as "libgpod@random_number" and copied
7555  * to @track->ipod_path. If this file already exists, @random_number
7556  * is adjusted until an unused filename is found.
7557  *
7558  * If @track->ipod_path is already set, this one will be used
7559  * instead. If a file with this name already exists, it will be
7560  * overwritten.
7561  *
7562  * @track->filetype_marker is set according to the filename extension
7563  *
7564  * Returns: TRUE on success, FALSE on error, in which case @error is
7565  * set accordingly.
7566  */
itdb_cp_track_to_ipod(Itdb_Track * track,const gchar * filename,GError ** error)7567 gboolean itdb_cp_track_to_ipod (Itdb_Track *track,
7568 				const gchar *filename, GError **error)
7569 {
7570     gchar *dest_filename;
7571     gboolean result = FALSE;
7572 
7573     g_return_val_if_fail (track, FALSE);
7574     g_return_val_if_fail (track->itdb, FALSE);
7575     g_return_val_if_fail (itdb_get_mountpoint (track->itdb), FALSE);
7576     g_return_val_if_fail (filename, FALSE);
7577 
7578     if(track->transferred)  return TRUE; /* nothing to do */
7579 
7580     dest_filename = itdb_cp_get_dest_filename (track, NULL, filename, error);
7581 
7582     if (dest_filename)
7583     {
7584 	if (itdb_cp (filename, dest_filename, error))
7585 	{
7586 	    if (itdb_cp_finalize (track, NULL, dest_filename, error))
7587 	    {
7588 		result = TRUE;
7589 	    }
7590 	}
7591 	g_free (dest_filename);
7592     }
7593 
7594     return result;
7595 }
7596 
7597 /**
7598  * itdb_filename_on_ipod:
7599  * @track: an #Itdb_Track
7600  *
7601  * Get the full iPod filename as stored in @track.
7602  *
7603  * <note>
7604  * NULL is returned when the file does not exist.
7605  * </note>
7606  *
7607  * <note>
7608  * This code works around a problem on some systems (see
7609  * itdb_resolve_path()) and might return a filename with different
7610  * case than the original filename. Don't copy it back to @track if
7611  * you can avoid it.
7612  * </note>
7613  *
7614  * Returns: full filename to @track on the iPod or NULL if no
7615  * filename is set in @track. Must be freed with g_free() after use.
7616  */
itdb_filename_on_ipod(Itdb_Track * track)7617 gchar *itdb_filename_on_ipod (Itdb_Track *track)
7618 {
7619     gchar *result = NULL;
7620     gchar *buf;
7621     const gchar *mp;
7622 
7623     g_return_val_if_fail (track, NULL);
7624 
7625     if (!track->ipod_path || !*track->ipod_path)
7626     {   /* No filename set */
7627 	return NULL;
7628     }
7629 
7630     g_return_val_if_fail (track->itdb, NULL);
7631 
7632     if (!itdb_get_mountpoint (track->itdb)) return NULL;
7633 
7634     mp = itdb_get_mountpoint (track->itdb);
7635 
7636     buf = g_strdup (track->ipod_path);
7637     itdb_filename_ipod2fs (buf);
7638     result = g_build_filename (mp, buf, NULL);
7639     g_free (buf);
7640 
7641     if (!g_file_test (result, G_FILE_TEST_EXISTS))
7642     {
7643 	gchar **components = g_strsplit (track->ipod_path,":",10);
7644 	g_free (result);
7645 	result = itdb_resolve_path (mp, (const gchar **)components);
7646 	g_strfreev (components);
7647     }
7648 
7649     return result;
7650 }
7651 
7652 /* Use open instead of fopen.  fwrite is really slow on the Mac. */
7653 /**
7654  * itdb_cp:
7655  * @from_file:  source file
7656  * @to_file:    destination file
7657  * @error:      return location for a #GError or NULL
7658  *
7659  * Copy file @from_file to @to_file.
7660  *
7661  * Returns: TRUE on success, FALSE on error, in which case @error is
7662  * set accordingly.
7663  */
itdb_cp(const gchar * from_file,const gchar * to_file,GError ** error)7664 gboolean itdb_cp (const gchar *from_file, const gchar *to_file,
7665 		  GError **error)
7666 {
7667 #ifndef O_BINARY
7668 #define O_BINARY 0
7669 #endif
7670     gchar *data;
7671     glong bread, bwrite;
7672     int file_in = -1;
7673     int file_out = -1;
7674 
7675 #if ITUNESDB_DEBUG
7676     fprintf(stderr, "Entered itunesdb_cp: '%s', '%s'\n", from_file, to_file);
7677 #endif
7678 
7679     g_return_val_if_fail (from_file, FALSE);
7680     g_return_val_if_fail (to_file, FALSE);
7681 
7682     data = g_malloc (ITUNESDB_COPYBLK);
7683 
7684     file_in = g_open (from_file, O_RDONLY | O_BINARY, 0);
7685     if (file_in < 0)
7686     {
7687 	g_set_error (error,
7688 		     G_FILE_ERROR,
7689 		     g_file_error_from_errno (errno),
7690 		     _("Error opening '%s' for reading (%s)."),
7691 		     from_file, g_strerror (errno));
7692 	goto err_out;
7693     }
7694 
7695     file_out =  g_open (to_file, O_CREAT|O_WRONLY|O_TRUNC|O_BINARY, 0777);
7696     if (file_out < 0)
7697     {
7698 	g_set_error (error,
7699 		     G_FILE_ERROR,
7700 		     g_file_error_from_errno (errno),
7701 		     _("Error opening '%s' for writing (%s)."),
7702 		     to_file, g_strerror (errno));
7703 	goto err_out;
7704     }
7705 
7706     do {
7707 	bread = read (file_in, data, ITUNESDB_COPYBLK);
7708 #if ITUNESDB_DEBUG
7709 	fprintf(stderr, "itunesdb_cp: read %ld bytes\n", bread);
7710 #endif
7711 	if (bread < 0)
7712 	{
7713 	    /* error -- not end of file! */
7714 	    g_set_error (error,
7715 			 G_FILE_ERROR,
7716 			 g_file_error_from_errno (errno),
7717 			 _("Error while reading from '%s' (%s)."),
7718 			 from_file, g_strerror (errno));
7719 	    goto err_out;
7720 	}
7721 	else
7722 	{
7723 	    bwrite = write (file_out, data, bread);
7724 #if ITUNESDB_DEBUG
7725 	    fprintf(stderr, "itunesdb_cp: wrote %ld bytes\n", bwrite);
7726 #endif
7727 	    if (bwrite != bread)
7728 	    {
7729 		g_set_error (error,
7730 			     G_FILE_ERROR,
7731 			     g_file_error_from_errno (errno),
7732 			     _("Error while writing to '%s' (%s)."),
7733 			     to_file, g_strerror (errno));
7734 		goto err_out;
7735 	    }
7736 	}
7737     } while (bread != 0);
7738 
7739     if (close (file_in) != 0)
7740     {
7741 	file_in = -1;
7742 	g_set_error (error,
7743 		     G_FILE_ERROR,
7744 		     g_file_error_from_errno (errno),
7745 		     _("Error when closing '%s' (%s)."),
7746 		     from_file, g_strerror (errno));
7747 	goto err_out;
7748     }
7749     if (close (file_out) != 0)
7750     {
7751 	file_out = -1;
7752 	g_set_error (error,
7753 		     G_FILE_ERROR,
7754 		     g_file_error_from_errno (errno),
7755 		     _("Error when closing '%s' (%s)."),
7756 		     to_file, g_strerror (errno));
7757 	goto err_out;
7758     }
7759     g_free (data);
7760     return TRUE;
7761 
7762   err_out:
7763     if (file_in >= 0)  close (file_in);
7764     if (file_out >= 0) close (file_out);
7765     g_unlink (to_file);
7766     g_free (data);
7767     return FALSE;
7768 }
7769 
7770 /**
7771  * itdb_get_control_dir:
7772  * @mountpoint: the iPod mountpoint
7773  *
7774  * Get the i*_Control directory. Observed values are 'iPod_Control'
7775  * for standard iPods and 'iTunes/iTunes_Control' for mobile
7776  * applications.
7777  *
7778  * Returns: path to the control dir or NULL if non-existent. Must
7779  * g_free() after use.
7780  *
7781  * Since: 0.4.0
7782  */
itdb_get_control_dir(const gchar * mountpoint)7783 gchar *itdb_get_control_dir (const gchar *mountpoint)
7784 {
7785     gchar *p_iphone[] = {"iTunes_Control", NULL};
7786     gchar *p_ipod[] = {"iPod_Control", NULL};
7787     gchar *p_mobile[] = {"iTunes", "iTunes_Control", NULL};
7788     /* Use an array with all possibilities, so further extension will
7789        be easy. It is important that checking for the iTunes_Control
7790        directory be done before the iPod_Control directory because
7791        some devices actually have both directories. The iTunes_Control
7792        directory is correct in these cases. This happens for devices
7793        that Apple shipped with the iPod_Control as the control directory
7794        and later switched to iTunes_Control instead. iTunes appears to
7795        remove files from the old iPod_Control directory but leave the
7796        directory structure intact. Finding this empty directory structure
7797        first will result in a failure to find files. */
7798     gchar **paths[] = {p_iphone, p_ipod, p_mobile, NULL};
7799     gchar ***ptr;
7800     gchar *result = NULL;
7801 
7802     g_return_val_if_fail (mountpoint, NULL);
7803 
7804     for (ptr=paths; *ptr && !result; ++ptr)
7805     {
7806 	result = itdb_resolve_path (mountpoint, (const gchar **)*ptr);
7807     }
7808     return result;
7809 }
7810 
7811 /**
7812  * itdb_get_dir:
7813  * @mountpoint: the iPod mountpoint
7814  * @dir:        a directory
7815  *
7816  * Retrieve the directory @dir by first calling itdb_get_control_dir()
7817  * and then adding @dir
7818  *
7819  * Returns: path to @dir or NULL if non-existent. Must g_free()
7820  * after use.
7821  */
itdb_get_dir(const gchar * mountpoint,const gchar * dir)7822 static gchar *itdb_get_dir (const gchar *mountpoint, const gchar *dir)
7823 {
7824     gchar *control_dir;
7825     gchar *result = NULL;
7826 
7827     g_return_val_if_fail (mountpoint, NULL);
7828     g_return_val_if_fail (dir, NULL);
7829 
7830     control_dir = itdb_get_control_dir (mountpoint);
7831     if (control_dir)
7832     {
7833 	const gchar *p_dir[] = {NULL, NULL};
7834 	p_dir[0] = dir;
7835 	result = itdb_resolve_path (control_dir, p_dir);
7836 	g_free (control_dir);
7837     }
7838     return result;
7839 }
7840 
7841 /**
7842  * itdb_get_path:
7843  * @dir:    a directory
7844  * @file:   a file
7845  *
7846  * Retrieve a path to the @file in @dir
7847  *
7848  * Returns: path to the @file or NULL if non-existent. Must g_free()
7849  * after use.
7850  *
7851  * Since: 0.4.0
7852  */
itdb_get_path(const gchar * dir,const gchar * file)7853 gchar *itdb_get_path (const gchar *dir, const gchar *file)
7854 {
7855     const gchar *p_file[] = {NULL, NULL};
7856 
7857     g_return_val_if_fail (dir, NULL);
7858 
7859     p_file[0] = file;
7860 
7861     return itdb_resolve_path (dir, p_file);
7862 }
7863 
7864 /**
7865  * itdb_get_itunes_dir:
7866  * @mountpoint: the iPod mountpoint
7867  *
7868  * Retrieve the iTunes directory (containing the iTunesDB) by first
7869  * calling itdb_get_control_dir() and then adding 'iTunes'
7870  *
7871  * Returns: path to the iTunes directory or NULL if non-existent.
7872  * Must g_free() after use.
7873  *
7874  * Since: 0.4.0
7875  */
itdb_get_itunes_dir(const gchar * mountpoint)7876 gchar *itdb_get_itunes_dir (const gchar *mountpoint)
7877 {
7878     g_return_val_if_fail (mountpoint, NULL);
7879 
7880     return itdb_get_dir (mountpoint, "iTunes");
7881 }
7882 
7883 /**
7884  * itdb_get_music_dir:
7885  * @mountpoint: the iPod mountpoint
7886  *
7887  * Retrieve the Music directory (containing the Fnn dirs) by first
7888  * calling itdb_get_control_dir() and then adding 'Music'
7889  *
7890  * Returns: path to the Music directory or NULL if
7891  * non-existent. Must g_free() after use.
7892  */
itdb_get_music_dir(const gchar * mountpoint)7893 gchar *itdb_get_music_dir (const gchar *mountpoint)
7894 {
7895     g_return_val_if_fail (mountpoint, NULL);
7896 
7897     return itdb_get_dir (mountpoint, "Music");
7898 }
7899 
7900 /**
7901  * itdb_get_device_dir:
7902  * @mountpoint: the iPod mountpoint
7903  *
7904  * Retrieve the Device directory (containing the SysInfo file) by
7905  * first calling itdb_get_control_dir() and then adding 'Device'
7906  *
7907  * Returns: path to the Device directory or NULL if
7908  * non-existent. Must g_free() after use.
7909  *
7910  * Since: 0.4.0
7911  */
itdb_get_device_dir(const gchar * mountpoint)7912 gchar *itdb_get_device_dir (const gchar *mountpoint)
7913 {
7914     g_return_val_if_fail (mountpoint, NULL);
7915 
7916     return itdb_get_dir (mountpoint, "Device");
7917 }
7918 
7919 /**
7920  * itdb_get_artwork_dir:
7921  * @mountpoint: the iPod mountpoint
7922  *
7923  * Retrieve the Artwork directory (containing the ArtworDB) by
7924  * first calling itdb_get_control_dir() and then adding 'Artwork'
7925  *
7926  * Returns: path to the Artwork directory or NULL if
7927  * non-existent. Must g_free() after use.
7928  *
7929  * Since: 0.4.0
7930  */
itdb_get_artwork_dir(const gchar * mountpoint)7931 gchar *itdb_get_artwork_dir (const gchar *mountpoint)
7932 {
7933     g_return_val_if_fail (mountpoint, NULL);
7934 
7935     return itdb_get_dir (mountpoint, "Artwork");
7936 }
7937 
7938 /**
7939  * itdb_get_itunesdb_path:
7940  * @mountpoint: the iPod mountpoint
7941  *
7942  * Retrieve a path to the iTunesDB
7943  *
7944  * Returns: path to the iTunesDB or NULL if non-existent. Must g_free()
7945  * after use.
7946  *
7947  * Since: 0.4.0
7948  */
itdb_get_itunesdb_path(const gchar * mountpoint)7949 gchar *itdb_get_itunesdb_path (const gchar *mountpoint)
7950 {
7951     gchar *itunes_dir, *path=NULL;
7952 
7953     g_return_val_if_fail (mountpoint, NULL);
7954 
7955     itunes_dir = itdb_get_itunes_dir (mountpoint);
7956 
7957     if (itunes_dir)
7958     {
7959 	path = itdb_get_path (itunes_dir, "iTunesCDB");
7960 	if(!path) {
7961 	    path = itdb_get_path (itunes_dir, "iTunesDB");
7962 	}
7963 	g_free (itunes_dir);
7964     }
7965 
7966     return path;
7967 }
7968 
7969 /**
7970  * itdb_get_itunescdb_path:
7971  * @mountpoint: the iPod mountpoint
7972  *
7973  * Retrieve a path to the iTunesCDB. The iTunesCDB is a compressed version
7974  * of the iTunesDB which can be found on iPhones/iPod Touch with firmware 3.0
7975  *
7976  *
7977  * Returns: path to the iTunesCDB or NULL if non-existent. Must g_free()
7978  * after use.
7979  *
7980  * Since: 0.4.0
7981  */
itdb_get_itunescdb_path(const gchar * mountpoint)7982 gchar *itdb_get_itunescdb_path (const gchar *mountpoint)
7983 {
7984     gchar *itunes_dir, *path=NULL;
7985 
7986     g_return_val_if_fail (mountpoint, NULL);
7987 
7988     itunes_dir = itdb_get_itunes_dir (mountpoint);
7989 
7990     if (itunes_dir)
7991     {
7992 	path = itdb_get_path (itunes_dir, "iTunesCDB");
7993 	g_free (itunes_dir);
7994     }
7995 
7996     return path;
7997 }
7998 
7999 /**
8000  * itdb_get_itunessd_path:
8001  * @mountpoint: the iPod mountpoint
8002  *
8003  * Retrieve a path to the iTunesSD
8004  *
8005  * Returns: path to the iTunesSD or NULL if non-existent. Must g_free()
8006  * after use.
8007  *
8008  * Since: 0.4.0
8009  */
itdb_get_itunessd_path(const gchar * mountpoint)8010 gchar *itdb_get_itunessd_path (const gchar *mountpoint)
8011 {
8012     gchar *itunes_dir, *path=NULL;
8013 
8014     g_return_val_if_fail (mountpoint, NULL);
8015 
8016     itunes_dir = itdb_get_itunes_dir (mountpoint);
8017 
8018     if (itunes_dir)
8019     {
8020 	path = itdb_get_path (itunes_dir, "iTunesSD");
8021 	g_free (itunes_dir);
8022     }
8023 
8024     return path;
8025 }
8026 
8027 /**
8028  * itdb_get_artworkdb_path:
8029  * @mountpoint: the iPod mountpoint
8030  *
8031  * Retrieve a path to the ArtworkDB
8032  *
8033  * Returns: path to the ArtworkDB or NULL if non-existent. Must g_free()
8034  * after use.
8035  *
8036  * Since: 0.4.0
8037  */
itdb_get_artworkdb_path(const gchar * mountpoint)8038 gchar *itdb_get_artworkdb_path (const gchar *mountpoint)
8039 {
8040     gchar *itunes_dir, *path=NULL;
8041 
8042     g_return_val_if_fail (mountpoint, NULL);
8043 
8044     itunes_dir = itdb_get_artwork_dir (mountpoint);
8045 
8046     if (itunes_dir)
8047     {
8048 	path = itdb_get_path (itunes_dir, "ArtworkDB");
8049 	g_free (itunes_dir);
8050     }
8051 
8052     return path;
8053 }
8054 
8055 
8056 /*------------------------------------------------------------------*\
8057  *                                                                  *
8058  *                       Timestamp stuff                            *
8059  *                                                                  *
8060 \*------------------------------------------------------------------*/
8061 
8062 /**
8063  * itdb_time_get_mac_time:
8064  *
8065  * Gets the current time in a format appropriate for storing in the libgpod
8066  * data structures
8067  *
8068  * Returns: current time
8069  *
8070  * Deprecated: kept for compatibility with older code, directly use
8071  * g_get_current_time() or time(NULL) instead
8072  */
itdb_time_get_mac_time(void)8073 time_t itdb_time_get_mac_time (void)
8074 {
8075     GTimeVal time;
8076 
8077     g_get_current_time (&time);
8078 
8079     return time.tv_sec;
8080 }
8081 
8082 /**
8083  * itdb_time_mac_to_host:
8084  * @time: time expressed in libgpod format
8085  *
8086  * Converts a timestamp from libgpod format to host system timestamp.
8087  *
8088  * Returns: timestamp for the host system
8089  *
8090  * Deprecated: It's been kept for compatibility with older code, but this
8091  * function is now a no-op
8092  */
itdb_time_mac_to_host(time_t time)8093 time_t itdb_time_mac_to_host (time_t time)
8094 {
8095     return time;
8096 }
8097 
8098 /**
8099  * itdb_time_host_to_mac:
8100  * @time: time expressed in host unit
8101  *
8102  * Convert host system timestamp to libgpod format timestamp
8103  *
8104  * Returns: a libgpod timestamp
8105  *
8106  * Deprecated: It's been kept for compatibility with older code, but this
8107  * function is now a no-op
8108  */
itdb_time_host_to_mac(time_t time)8109 time_t itdb_time_host_to_mac (time_t time)
8110 {
8111     return time;
8112 }
8113 
8114 /**
8115  * itdb_init_ipod:
8116  * @mountpoint:   the iPod mountpoint
8117  * @model_number: the iPod model number, can be NULL
8118  * @ipod_name:    the name to give to the iPod. Will be displayed in
8119  *                gtkpod or itunes
8120  * @error:        return location for a #GError or NULL
8121  *
8122  * Initialise an iPod device from scratch. The function attempts to
8123  * create a blank database, complete with master playlist and device
8124  * information as well as the directory structure required for the
8125  * type of iPod.
8126  * @model_number is used to tell libgpod about the exact iPod
8127  * model, which is needed for proper artwork writing. @model_number can be
8128  * found from the table returned by itdb_device_get_ipod_info_table (for
8129  * example). On recent distros with iPods released
8130  * in the last few years (starting with the iPod Color), it should be fine
8131  * to pass in a NULL @model_number while still getting artwork writing.
8132  *
8133  * Returns: TRUE when successful, FALSE if a failure has occurred.
8134  *
8135  * Since: 0.4.0
8136  */
itdb_init_ipod(const gchar * mountpoint,const gchar * model_number,const gchar * ipod_name,GError ** error)8137 gboolean itdb_init_ipod (const gchar *mountpoint,
8138 			 const gchar *model_number,
8139 			 const gchar *ipod_name,
8140 			 GError **error)
8141 {
8142 	gboolean writeok;
8143 	Itdb_iTunesDB *itdb = NULL;
8144 	Itdb_Playlist *mpl = NULL;
8145 	gchar *path;
8146 
8147 	g_return_val_if_fail (mountpoint, FALSE);
8148 
8149 	/* Create new blank itdb database for writing to iPod */
8150 	itdb = itdb_new();
8151 
8152 	/* Assign iPod device reference to new database */
8153 	itdb_set_mountpoint(itdb, mountpoint);
8154 
8155 	/* Insert model_number into sysinfo file if present
8156 	 * The model number can be extracted in a couple of ways:
8157 	 *		- use the read_sysinfo_file function
8158 	 *		- use libipoddevice and hal to get the model
8159 	 *		  (as far as I know, libipoddevice will also
8160 	 *		  read the sysinfo file, complemented by some
8161 	 *		  guessing).
8162 	 */
8163 	if (model_number)
8164 	{
8165 	    itdb_device_set_sysinfo (itdb->device,
8166 				     "ModelNumStr", model_number);
8167 	}
8168 
8169 	/* Create the remaining directories resident on blank ipod */
8170 	writeok = itdb_create_directories(itdb->device, error);
8171 	if(! writeok)
8172 	{
8173 		return FALSE;
8174 	}
8175 
8176 	/* Create a new playlist with the desired name of the ipod
8177 	 * and set it as the mpl */
8178 	if (ipod_name == NULL)
8179 	{
8180 	    mpl = itdb_playlist_new(_("iPod"), FALSE);
8181 	}
8182 	else
8183 	{
8184 	    mpl = itdb_playlist_new(ipod_name, FALSE);
8185 	}
8186 	itdb_playlist_set_mpl(mpl);
8187 	itdb_playlist_add(itdb, mpl, -1);
8188 
8189 	/* Write both the iTunesDB and iTunesSD files to the new ipod,
8190 	 * unless they already exist */
8191 	path = itdb_get_itunesdb_path (mountpoint);
8192 	if (!path)
8193 	{
8194 	    writeok = itdb_write(itdb, error);
8195 	    if(! writeok)
8196 	    {
8197 	        itdb_free (itdb);
8198 		return FALSE;
8199 	    }
8200 	}
8201 	g_free (path);
8202 
8203 	/* If model is a shuffle or the model is undetermined,
8204 	 * ie. @model_number is NULL, then create the itunesSD database
8205 	 */
8206 	if(!model_number || itdb_device_is_shuffle (itdb->device))
8207 	{
8208 	    path = itdb_get_itunessd_path (mountpoint);
8209 	    if (!path)
8210 	    {
8211 		writeok = itdb_shuffle_write(itdb, error);
8212 		if(! writeok)
8213 		{
8214 		    itdb_free (itdb);
8215 		    return FALSE;
8216 		}
8217 	    }
8218 	    g_free (path);
8219 	}
8220 	itdb_free (itdb);
8221 	return TRUE;
8222 }
8223 
8224 /**
8225  * itdb_chapterdata_build_chapter_blob:
8226  * @chapterdata: Itdb_Chapterdata pointer of chapter data to be encoded
8227  *
8228  * Creates an iTunesDB binary blob of chapter data from @chapterdata.
8229  * This helper function is used by both mk_mhod() in itdb_itunesdb.c
8230  * and mk_Extras() in itdb_sqlite.c, so take care when updating to
8231  * maintain compatibility with both chapterdata blobs.
8232  *
8233  * NOTE: Caller must call g_byte_array_free(chapter_blob, TRUE) on the
8234  * returned chapter_blob
8235  *
8236  * Returns: a binary itdb blob of chapter data (to be freed by the caller)
8237  *
8238  * Since: 0.7.95
8239  */
itdb_chapterdata_build_chapter_blob(Itdb_Chapterdata * chapterdata,gboolean reversed)8240 G_GNUC_INTERNAL GByteArray *itdb_chapterdata_build_chapter_blob(Itdb_Chapterdata *chapterdata, gboolean reversed)
8241 {
8242     WContents *cts;
8243     GByteArray * chapter_blob = NULL;
8244 
8245     cts = wcontents_new("");
8246     cts->reversed = reversed;
8247     cts->pos = 0;
8248 
8249     itdb_chapterdata_build_chapter_blob_internal (cts, chapterdata);
8250 
8251     chapter_blob = g_byte_array_sized_new(cts->pos);
8252     g_byte_array_append(chapter_blob, (guint8 *)cts->contents, cts->pos);
8253     wcontents_free(cts);
8254     return chapter_blob;
8255 }
8256 
8257 
8258 /*------------------------------------------------------------------*\
8259  *                                                                  *
8260  *             Some functions to access Itdb_DB safely              *
8261  *                                                                  *
8262 \*------------------------------------------------------------------*/
8263 G_GNUC_INTERNAL
db_get_itunesdb(Itdb_DB * db)8264 Itdb_iTunesDB *db_get_itunesdb (Itdb_DB *db)
8265 {
8266     g_return_val_if_fail (db, NULL);
8267     g_return_val_if_fail (db->db_type == DB_TYPE_ITUNES, NULL);
8268 
8269     return db->db.itdb;
8270 }
8271 
8272 G_GNUC_INTERNAL
db_get_photodb(Itdb_DB * db)8273 Itdb_PhotoDB *db_get_photodb (Itdb_DB *db)
8274 {
8275     g_return_val_if_fail (db, NULL);
8276     g_return_val_if_fail (db->db_type == DB_TYPE_PHOTO, NULL);
8277 
8278     return db->db.photodb;
8279 }
8280 
8281 G_GNUC_INTERNAL
db_get_device(Itdb_DB * db)8282 Itdb_Device *db_get_device(Itdb_DB *db)
8283 {
8284     g_return_val_if_fail (db, NULL);
8285 
8286     switch (db->db_type) {
8287     case DB_TYPE_ITUNES:
8288 	g_return_val_if_fail (db_get_itunesdb(db), NULL);
8289 	return db_get_itunesdb(db)->device;
8290     case DB_TYPE_PHOTO:
8291 	g_return_val_if_fail (db_get_photodb(db), NULL);
8292 	return db_get_photodb(db)->device;
8293     }
8294     g_return_val_if_reached (NULL);
8295 }
8296 
8297 G_GNUC_INTERNAL
db_get_mountpoint(Itdb_DB * db)8298 gchar *db_get_mountpoint(Itdb_DB *db)
8299 {
8300     Itdb_Device *device;
8301     g_return_val_if_fail (db, NULL);
8302 
8303     device = db_get_device (db);
8304     g_return_val_if_fail (device, NULL);
8305 
8306     return device->mountpoint;
8307 }
8308 
itdb_device_get_control_dir(const Itdb_Device * device)8309 static gchar *itdb_device_get_control_dir (const Itdb_Device *device)
8310 {
8311     Itdb_IpodInfo const *info = NULL;
8312     char *podpath;
8313 
8314     podpath = itdb_get_control_dir (device->mountpoint);
8315     if (podpath != NULL) {
8316         return podpath;
8317     }
8318 
8319     /* The device doesn't already have an iPod_Control directory, let's try
8320      * to get which one is appropriate to use
8321      */
8322     info = itdb_device_get_ipod_info (device);
8323 
8324     if (itdb_device_is_shuffle (device)) {
8325         podpath = g_build_filename (device->mountpoint, "iPod_Control", NULL);
8326     } else if (itdb_device_is_iphone_family (device)) {
8327         podpath = g_build_filename (device->mountpoint,"iTunes_Control", NULL);
8328     } else if (info->ipod_model == ITDB_IPOD_MODEL_MOBILE_1) {
8329         podpath = g_build_filename (device->mountpoint,
8330                                     "iTunes", "iTunes_Control",
8331                                     NULL);
8332     } else {
8333         podpath = g_build_filename (device->mountpoint, "iPod_Control", NULL);
8334     }
8335 
8336     return podpath;
8337 }
8338 
8339 
8340 /*------------------------------------------------------------------*\
8341  *                                                                  *
8342  *             Create iPod directory hierarchy                      *
8343  *                                                                  *
8344 \*------------------------------------------------------------------*/
itdb_create_directories(Itdb_Device * device,GError ** error)8345 static gboolean itdb_create_directories (Itdb_Device *device, GError **error)
8346 {
8347     const gchar *mp;
8348     gboolean result;
8349     gchar *pbuf;
8350     gint i, dirnum;
8351     gchar *podpath = NULL;
8352     gchar *model_number;
8353     Itdb_IpodInfo const *info = NULL;
8354 
8355     g_return_val_if_fail (device, FALSE);
8356 
8357     mp = device->mountpoint;
8358     info = itdb_device_get_ipod_info (device);
8359 
8360     g_return_val_if_fail (mp, FALSE);
8361 
8362     /* Construct the Control directory */
8363     pbuf = itdb_device_get_control_dir (device);
8364     if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8365     {
8366 	if (g_mkdir_with_parents(pbuf, 0777) != 0)
8367 	{
8368 	    goto error_dir;
8369 	}
8370     }
8371     g_free (pbuf);
8372     podpath = itdb_get_control_dir (mp);
8373     if (!podpath) {
8374         goto error_dir;
8375     }
8376 
8377     /* Construct the Music directory inside the Control directory */
8378     pbuf = g_build_filename (podpath, "Music", NULL);
8379     if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8380     {
8381 	if((g_mkdir(pbuf, 0777) != 0))
8382 	{
8383 	    goto error_dir;
8384 	}
8385     }
8386     g_free (pbuf);
8387 
8388     /* Construct the iTunes directory inside the Control directory */
8389     pbuf = g_build_filename (podpath, "iTunes", NULL);
8390     if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8391     {
8392 	if((g_mkdir(pbuf, 0777) != 0))
8393 	{
8394 	    goto error_dir;
8395 	}
8396     }
8397     g_free (pbuf);
8398 
8399     /* Build Artwork directory only for devices requiring artwork
8400      * (assume that 'unknown models' are new and will support
8401      * artwork) */
8402     if (itdb_device_supports_artwork(device) ||
8403 	(info->ipod_model == ITDB_IPOD_MODEL_UNKNOWN))
8404     {
8405 	pbuf = g_build_filename (podpath, "Artwork", NULL);
8406 	if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8407 	{
8408 	    if((g_mkdir(pbuf, 0777) != 0)) {
8409 		goto error_dir;
8410 	    }
8411 	}
8412 	g_free (pbuf);
8413     }
8414 
8415     /* Build Photo directory only for devices requiring it */
8416     if (itdb_device_supports_photo(device) ||
8417 	(info->ipod_model == ITDB_IPOD_MODEL_UNKNOWN))
8418     {
8419 	pbuf = g_build_filename (mp, "Photos", "Thumbs", NULL);
8420 	if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8421 	{
8422 	    if (g_mkdir_with_parents(pbuf, 0777) != 0)
8423 	    {
8424 		goto error_dir;
8425 	    }
8426 	}
8427 	g_free (pbuf);
8428     }
8429 
8430     /* Build the directories that hold the music files */
8431     dirnum = info->musicdirs;
8432     if (dirnum == 0)
8433     {
8434 	guint64 capacity, free_space;
8435 	if (itdb_device_get_storage_info(device, &capacity, &free_space)) {
8436 	    gdouble size = ((gdouble)capacity) / 1073741824;
8437 	    if (size < 20)  dirnum = 20;
8438 	    else            dirnum = 50;
8439 	} else {
8440 	    dirnum = 20;
8441 	}
8442     }
8443 
8444     for(i = 0; i < dirnum; i++)
8445     {
8446 	gchar *num = g_strdup_printf ("F%02d", i);
8447 	pbuf = g_build_filename (podpath, "Music", num, NULL);
8448 	g_free (num);
8449 	if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8450 	{
8451 	    if((g_mkdir(pbuf, 0777) != 0))
8452 	    {
8453 		goto error_dir;
8454 	    }
8455 	}
8456 	g_free (pbuf);
8457     }
8458 
8459     /* Build Calendar directory for models requiring it */
8460     if (!itdb_device_is_iphone_family (device)
8461         && !itdb_device_is_shuffle (device))
8462     {
8463 	pbuf = g_build_filename (mp, "Calendars", NULL);
8464 	if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8465 	{
8466 	    if((g_mkdir(pbuf, 0777) != 0))
8467 	    {
8468 		goto error_dir;
8469 	    }
8470 	}
8471 	g_free (pbuf);
8472 
8473 	/* Build Contacts directory for models requiring it */
8474 	pbuf = g_build_filename (mp, "Contacts", NULL);
8475 	if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8476 	{
8477 	    if((g_mkdir(pbuf, 0777) != 0))
8478 	    {
8479 		goto error_dir;
8480 	    }
8481 	}
8482 	g_free (pbuf);
8483 
8484 	pbuf = g_build_filename (mp, "Notes", NULL);
8485 	if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8486 	{
8487 	    if((g_mkdir(pbuf, 0777) != 0))
8488 	    {
8489 		goto error_dir;
8490 	    }
8491 	}
8492 	g_free (pbuf);
8493     }
8494 
8495     /* Construct a Device directory file for special models */
8496     pbuf = g_build_filename (podpath, "Device", NULL);
8497     if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8498     {
8499         if((g_mkdir(pbuf, 0777) != 0))
8500         {
8501             goto error_dir;
8502         }
8503     }
8504     g_free (pbuf);
8505 
8506     model_number = itdb_device_get_sysinfo (device, "ModelNumStr");
8507     /* Construct a SysInfo file */
8508     if (model_number && (strlen (model_number) != 0))
8509     {
8510         pbuf = NULL;
8511         if (!itdb_device_write_sysinfo (device, error))
8512         {
8513             g_free (model_number);
8514             goto error_dir;
8515         }
8516     }
8517     g_free (model_number);
8518     pbuf = NULL;
8519 
8520   error_dir:
8521     if (pbuf)
8522     {
8523 	g_set_error (error, 0, -1,
8524 		     _("Problem creating iPod directory or file: '%s'."),
8525 		     pbuf);
8526 	result = FALSE;
8527     } else
8528     {
8529 	result = TRUE;
8530     }
8531     g_free (pbuf);
8532     g_free (podpath);
8533     return result;
8534 }
8535