1 /* EasyTAG - Tag editor for audio files
2 * Copyright (C) 2014-2015 David King <amigadave@amigadave.com>
3 * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21
22 #include <glib/gi18n.h>
23 #include <string.h>
24 #include <errno.h>
25
26 #include "id3_tag.h"
27 #include "ape_tag.h"
28 #include "picture.h"
29 #include "easytag.h"
30 #include "genres.h"
31 #include "setting.h"
32 #include "misc.h"
33 #include "et_core.h"
34 #include "charset.h"
35
36 #ifdef ENABLE_MP3
37
38 #ifdef ENABLE_ID3LIB
39 #include <id3.h>
40 #include "id3lib/id3_bugfix.h"
41 #endif
42
43 /****************
44 * Declarations *
45 ****************/
46 #define ID3V2_MAX_STRING_LEN 4096
47 #define MULTIFIELD_SEPARATOR " - "
48
49
50 #ifdef ENABLE_ID3LIB
51
52 /**************
53 * Prototypes *
54 **************/
55 static gchar *Id3tag_Get_Error_Message (ID3_Err error);
56 static void Id3tag_Prepare_ID3v1 (ID3Tag *id3_tag);
57 static gchar *Id3tag_Rules_For_ISO_Fields (const gchar *string,
58 const gchar *from_codeset,
59 const gchar *to_codeset);
60 static gchar *Id3tag_Get_Field (const ID3Frame *id3_frame,
61 ID3_FieldID id3_fieldid);
62 static ID3_TextEnc Id3tag_Set_Field (const ID3Frame *id3_frame,
63 ID3_FieldID id3_fieldid,
64 const gchar *string);
65
66 ID3_C_EXPORT size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename);
67 ID3_C_EXPORT size_t ID3Field_GetASCII_1 (const ID3Field *field, char *buffer, size_t maxChars, size_t itemNum);
68 ID3_C_EXPORT size_t ID3Field_GetUNICODE_1 (const ID3Field *field, unicode_t *buffer, size_t maxChars, size_t itemNum);
69
70 static gboolean id3tag_check_if_id3lib_is_buggy (GError **error);
71
72
73
74 /*************
75 * Functions *
76 *************/
77
78 /* et_id3_error_quark:
79 *
80 * Quark for EtID3Error domain.
81 *
82 * Returns: a GQuark for the EtID3Error domain
83 */
84 GQuark
et_id3_error_quark(void)85 et_id3_error_quark (void)
86 {
87 return g_quark_from_static_string ("et-id3-error-quark");
88 }
89
90 /**
91 * et_id3tag_get_tpos_from_file_tag:
92 * @FileTag: File_Tag to get disc_number and disc_total from
93 *
94 * This function will return TPOS from FileTag.
95 * Returns: a newly allocated string, should be freed using g_free.
96 */
97 gchar *
et_id3tag_get_tpos_from_file_tag(const File_Tag * FileTag)98 et_id3tag_get_tpos_from_file_tag (const File_Tag *FileTag)
99 {
100 GString *gstring;
101 const gchar *p;
102
103 gstring = g_string_new ("");
104
105 if (!FileTag->disc_number)
106 {
107 return g_string_free (gstring, FALSE);
108 }
109
110 p = FileTag->disc_number;
111
112 while (*p)
113 {
114 if (!g_ascii_isdigit (*p))
115 {
116 break;
117 }
118
119 g_string_append_c (gstring, *p);
120 p++;
121 }
122
123 if (FileTag->disc_total && *FileTag->disc_total)
124 {
125 g_string_append_c (gstring, '/');
126 p = FileTag->disc_total;
127
128 while (*p)
129 {
130 if (!g_ascii_isdigit (*p))
131 {
132 break;
133 }
134
135 g_string_append_c (gstring, *p);
136 p++;
137 }
138 }
139
140 return g_string_free (gstring, FALSE);
141 }
142
143 /*
144 * Write the ID3 tags to the file. Returns TRUE on success, else 0.
145 */
146 static gboolean
id3tag_write_file_v23tag(const ET_File * ETFile,GError ** error)147 id3tag_write_file_v23tag (const ET_File *ETFile,
148 GError **error)
149 {
150 const File_Tag *FileTag;
151 const gchar *filename;
152 const gchar *filename_utf8;
153 gchar *basename_utf8;
154 GFile *file;
155 ID3Tag *id3_tag = NULL;
156 ID3_Err error_strip_id3v1 = ID3E_NoError;
157 ID3_Err error_strip_id3v2 = ID3E_NoError;
158 ID3_Err error_update_id3v1 = ID3E_NoError;
159 ID3_Err error_update_id3v2 = ID3E_NoError;
160 gboolean success = TRUE;
161 gint number_of_frames;
162 gboolean has_title = FALSE;
163 gboolean has_artist = FALSE;
164 gboolean has_album_artist= FALSE;
165 gboolean has_album = FALSE;
166 gboolean has_disc_number = FALSE;
167 gboolean has_year = FALSE;
168 gboolean has_track = FALSE;
169 gboolean has_genre = FALSE;
170 gboolean has_comment = FALSE;
171 gboolean has_composer = FALSE;
172 gboolean has_orig_artist = FALSE;
173 gboolean has_copyright = FALSE;
174 gboolean has_url = FALSE;
175 gboolean has_encoded_by = FALSE;
176 gboolean has_picture = FALSE;
177 //gboolean has_song_len = FALSE;
178 static gboolean flag_first_check = TRUE;
179 static gboolean flag_id3lib_bugged = TRUE;
180
181 ID3Frame *id3_frame;
182 ID3Field *id3_field;
183 //gchar *string;
184 gchar *string1;
185 EtPicture *pic;
186
187 g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
188 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
189
190 // When writing the first MP3 file, we check if the version of id3lib of the
191 // system doesn't contain a bug when writting Unicode tags
192 if (flag_first_check
193 && g_settings_get_boolean (MainSettings, "id3v2-enable-unicode"))
194 {
195 flag_first_check = FALSE;
196 flag_id3lib_bugged = id3tag_check_if_id3lib_is_buggy (NULL);
197 }
198
199 FileTag = (File_Tag *)ETFile->FileTag->data;
200 filename = ((File_Name *)ETFile->FileNameCur->data)->value;
201 filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
202
203 file = g_file_new_for_path (filename);
204
205 /* This is a protection against a bug in id3lib that enters an infinite
206 * loop with corrupted MP3 files (files containing only zeroes) */
207 if (!et_id3tag_check_if_file_is_valid (file, error))
208 {
209 if (error)
210 {
211 g_debug ("Error while checking if ID3 tag is valid: %s",
212 (*error)->message);
213 }
214
215 g_clear_error (error);
216 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, "%s",
217 _("Corrupted file"));
218 g_object_unref (file);
219 return FALSE;
220 }
221
222 /* We get again the tag from the file to keep also unused data (by EasyTAG), then
223 * we replace the changed data */
224 if ((id3_tag = ID3Tag_New ()) == NULL)
225 {
226 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
227 g_strerror (ENOMEM));
228 g_object_unref (file);
229 return FALSE;
230 }
231
232 #ifdef G_OS_WIN32
233 /* On Windows, id3lib expects filenames to be in the system codepage. */
234 {
235 gchar *locale_filename;
236
237 locale_filename = g_win32_locale_filename_from_utf8 (filename);
238
239 if (!locale_filename)
240 {
241 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, "%s",
242 g_strerror (EINVAL));
243 ID3Tag_Delete (id3_tag);
244 g_object_unref (file);
245 return FALSE;
246 }
247
248 ID3Tag_Link (id3_tag, locale_filename);
249
250 g_free (locale_filename);
251 }
252 #else
253 ID3Tag_Link (id3_tag, filename);
254 #endif
255
256 /* Set padding when tag was changed, for faster writing */
257 ID3Tag_SetPadding(id3_tag,TRUE);
258
259 basename_utf8 = g_path_get_basename (filename_utf8);
260
261 /*********
262 * Title *
263 *********/
264 // To avoid problem with a corrupted field, we remove it before to create a new one.
265 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TITLE)) )
266 ID3Tag_RemoveFrame(id3_tag,id3_frame);
267
268 if (!et_str_empty (FileTag->title))
269 {
270 id3_frame = ID3Frame_NewID(ID3FID_TITLE);
271 ID3Tag_AttachFrame(id3_tag,id3_frame);
272 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->title);
273 has_title = TRUE;
274 }
275
276
277 /**********
278 * Artist *
279 **********/
280 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_LEADARTIST)) )
281 ID3Tag_RemoveFrame(id3_tag,id3_frame);
282
283 if (!et_str_empty (FileTag->artist))
284 {
285 id3_frame = ID3Frame_NewID(ID3FID_LEADARTIST);
286 ID3Tag_AttachFrame(id3_tag,id3_frame);
287 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->artist);
288 has_artist = TRUE;
289 }
290
291 /****************
292 * Album Artist *
293 ***************/
294 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_BAND)) )
295 ID3Tag_RemoveFrame(id3_tag,id3_frame);
296
297 if (!et_str_empty (FileTag->album_artist))
298 {
299 id3_frame = ID3Frame_NewID(ID3FID_BAND);
300 ID3Tag_AttachFrame(id3_tag,id3_frame);
301 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->album_artist);
302 has_album_artist = TRUE;
303 }
304
305 /*********
306 * Album *
307 *********/
308 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ALBUM)) )
309 ID3Tag_RemoveFrame(id3_tag,id3_frame);
310
311 if (!et_str_empty (FileTag->album))
312 {
313 id3_frame = ID3Frame_NewID(ID3FID_ALBUM);
314 ID3Tag_AttachFrame(id3_tag,id3_frame);
315 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->album);
316 has_album = TRUE;
317 }
318
319
320 /*****************************
321 * Part of set and Set Total *
322 *****************************/
323 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_PARTINSET)) )
324 ID3Tag_RemoveFrame(id3_tag,id3_frame);
325
326 if (!et_str_empty (FileTag->disc_number))
327 {
328 id3_frame = ID3Frame_NewID (ID3FID_PARTINSET);
329 ID3Tag_AttachFrame (id3_tag, id3_frame);
330 string1 = et_id3tag_get_tpos_from_file_tag (FileTag);
331 Id3tag_Set_Field (id3_frame, ID3FN_TEXT, string1);
332 g_free (string1);
333 has_disc_number = TRUE;
334 }
335
336
337 /********
338 * Year *
339 ********/
340 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_YEAR)) )
341 ID3Tag_RemoveFrame(id3_tag,id3_frame);
342
343 if (!et_str_empty (FileTag->year))
344 {
345 id3_frame = ID3Frame_NewID(ID3FID_YEAR);
346 ID3Tag_AttachFrame(id3_tag,id3_frame);
347 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->year);
348 has_year = TRUE;
349 }
350
351
352 /*************************
353 * Track and Total Track *
354 *************************/
355 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TRACKNUM)) )
356 ID3Tag_RemoveFrame(id3_tag,id3_frame);
357
358 if (!et_str_empty (FileTag->track))
359 {
360 id3_frame = ID3Frame_NewID(ID3FID_TRACKNUM);
361 ID3Tag_AttachFrame(id3_tag,id3_frame);
362
363 if (!et_str_empty (FileTag->track_total))
364 string1 = g_strconcat(FileTag->track,"/",FileTag->track_total,NULL);
365 else
366 string1 = g_strdup(FileTag->track);
367
368 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, string1);
369 g_free(string1);
370 has_track = TRUE;
371 }
372
373
374 /*********
375 * Genre *
376 *********
377 * Genre is written like this :
378 * - "(<genre_id>)" -> "(3)"
379 * - "(<genre_id>)<refinement>" -> "(3)EuroDance"
380 */
381 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_CONTENTTYPE)) )
382 ID3Tag_RemoveFrame(id3_tag,id3_frame);
383
384 if (!et_str_empty (FileTag->genre))
385 {
386 gchar *genre_string_tmp;
387 guchar genre_value;
388
389 id3_frame = ID3Frame_NewID(ID3FID_CONTENTTYPE);
390 ID3Tag_AttachFrame(id3_tag,id3_frame);
391
392 genre_value = Id3tag_String_To_Genre(FileTag->genre);
393 // If genre not defined don't write genre value between brackets! (priority problem noted with some tools)
394 if ((genre_value == ID3_INVALID_GENRE)
395 || g_settings_get_boolean (MainSettings, "id3v2-text-only-genre"))
396 {
397 genre_string_tmp = g_strdup_printf("%s",FileTag->genre);
398 }
399 else
400 {
401 genre_string_tmp = g_strdup_printf("(%d)",genre_value);
402 }
403
404 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, genre_string_tmp);
405 g_free(genre_string_tmp);
406 has_genre = TRUE;
407 }
408
409
410 /***********
411 * Comment *
412 ***********/
413 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COMMENT)) )
414 ID3Tag_RemoveFrame(id3_tag,id3_frame);
415
416 if (!et_str_empty (FileTag->comment))
417 {
418 id3_frame = ID3Frame_NewID(ID3FID_COMMENT);
419 ID3Tag_AttachFrame(id3_tag,id3_frame);
420 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->comment);
421 // These 2 following fields allow synchronisation between id3v2 and id3v1 tags with id3lib
422 // Disabled as when using unicode, the comment field stay in ISO.
423 //Id3tag_Set_Field(id3_frame, ID3FN_DESCRIPTION, "ID3v1 Comment");
424 //Id3tag_Set_Field(id3_frame, ID3FN_LANGUAGE, "XXX");
425 has_comment = TRUE;
426 }
427
428
429 /************
430 * Composer *
431 ************/
432 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COMPOSER)) )
433 ID3Tag_RemoveFrame(id3_tag,id3_frame);
434
435 if (!et_str_empty (FileTag->composer))
436 {
437 id3_frame = ID3Frame_NewID(ID3FID_COMPOSER);
438 ID3Tag_AttachFrame(id3_tag,id3_frame);
439 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->composer);
440 has_composer = TRUE;
441 }
442
443
444 /*******************
445 * Original artist *
446 *******************/
447 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ORIGARTIST)) )
448 ID3Tag_RemoveFrame(id3_tag,id3_frame);
449
450 if (!et_str_empty (FileTag->orig_artist))
451 {
452 id3_frame = ID3Frame_NewID(ID3FID_ORIGARTIST);
453 ID3Tag_AttachFrame(id3_tag,id3_frame);
454 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->orig_artist);
455 has_orig_artist = TRUE;
456 }
457
458
459 /*************
460 * Copyright *
461 *************/
462 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COPYRIGHT)) )
463 ID3Tag_RemoveFrame(id3_tag,id3_frame);
464
465 if (!et_str_empty (FileTag->copyright))
466 {
467 id3_frame = ID3Frame_NewID(ID3FID_COPYRIGHT);
468 ID3Tag_AttachFrame(id3_tag,id3_frame);
469 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->copyright);
470 has_copyright = TRUE;
471 }
472
473
474 /*******
475 * URL *
476 *******/
477 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_WWWUSER)) )
478 ID3Tag_RemoveFrame(id3_tag,id3_frame);
479
480 if (!et_str_empty (FileTag->url))
481 {
482 id3_frame = ID3Frame_NewID(ID3FID_WWWUSER);
483 ID3Tag_AttachFrame(id3_tag,id3_frame);
484 Id3tag_Set_Field(id3_frame, ID3FN_URL, FileTag->url);
485 has_composer = TRUE;
486 }
487
488
489 /**************
490 * Encoded by *
491 **************/
492 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ENCODEDBY)) )
493 ID3Tag_RemoveFrame(id3_tag,id3_frame);
494
495 if (!et_str_empty (FileTag->encoded_by))
496 {
497 id3_frame = ID3Frame_NewID(ID3FID_ENCODEDBY);
498 ID3Tag_AttachFrame(id3_tag,id3_frame);
499 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->encoded_by);
500 has_encoded_by = TRUE;
501 }
502
503
504 /***********
505 * Picture *
506 ***********/
507 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_PICTURE)) )
508 ID3Tag_RemoveFrame(id3_tag,id3_frame);
509
510 has_picture = FALSE;
511
512 for (pic = FileTag->picture; pic != NULL; pic = pic->next)
513 {
514 Picture_Format format = Picture_Format_From_Data(pic);
515
516 id3_frame = ID3Frame_NewID(ID3FID_PICTURE);
517 ID3Tag_AttachFrame(id3_tag,id3_frame);
518
519 switch (format)
520 {
521 case PICTURE_FORMAT_JPEG:
522 if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_MIMETYPE)))
523 ID3Field_SetASCII(id3_field, Picture_Mime_Type_String(format));
524 if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_IMAGEFORMAT)))
525 ID3Field_SetASCII(id3_field, "JPG");
526 break;
527
528 case PICTURE_FORMAT_PNG:
529 if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_MIMETYPE)))
530 ID3Field_SetASCII(id3_field, Picture_Mime_Type_String(format));
531 if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_IMAGEFORMAT)))
532 ID3Field_SetASCII(id3_field, "PNG");
533 break;
534 case PICTURE_FORMAT_GIF:
535 if ((id3_field = ID3Frame_GetField (id3_frame,
536 ID3FN_MIMETYPE)))
537 {
538 ID3Field_SetASCII (id3_field,
539 Picture_Mime_Type_String (format));
540 }
541
542 if ((id3_field = ID3Frame_GetField (id3_frame,
543 ID3FN_IMAGEFORMAT)))
544 {
545 /* I could find no reference for what ID3FN_IMAGEFORMAT
546 * should contain, so this is a guess. */
547 ID3Field_SetASCII (id3_field, "GIF");
548 }
549 break;
550 case PICTURE_FORMAT_UNKNOWN:
551 default:
552 break;
553 }
554
555 if ((id3_field = ID3Frame_GetField(id3_frame, ID3FN_PICTURETYPE)))
556 ID3Field_SetINT(id3_field, pic->type);
557
558 if (pic->description)
559 Id3tag_Set_Field(id3_frame, ID3FN_DESCRIPTION, pic->description);
560
561 if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_DATA)))
562 {
563 gconstpointer data;
564 gsize data_size;
565
566 data = g_bytes_get_data (pic->bytes, &data_size);
567 ID3Field_SetBINARY (id3_field, data, data_size);
568 }
569
570 has_picture = TRUE;
571 }
572
573
574 /*********************************
575 * File length (in milliseconds) *
576 *********************************/
577 /* Don't write this field, not useful? *
578 while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_SONGLEN)) )
579 ID3Tag_RemoveFrame(id3_tag,id3_frame);
580 if (ETFile->ETFileInfo && ((ET_File_Info *)ETFile->ETFileInfo)->duration > 0 )
581 {
582 gchar *string;
583
584 id3_frame = ID3Frame_NewID(ID3FID_SONGLEN);
585 ID3Tag_AttachFrame(id3_tag,id3_frame);
586
587 string = g_strdup_printf("%d",((ET_File_Info *)ETFile->ETFileInfo)->duration * 1000);
588 Id3tag_Set_Field(id3_frame, ID3FN_TEXT, string);
589 g_free(string);
590 has_song_len = TRUE;
591 }*/
592
593
594 /******************************
595 * Delete an APE tag if found *
596 ******************************/
597 {
598 // Delete the APE tag (create a dummy ETFile for the Ape_Tag_... function)
599 ET_File *ETFile_tmp = ET_File_Item_New();
600 File_Name *FileName_tmp = et_file_name_new ();
601 File_Tag *FileTag_tmp = et_file_tag_new();
602 // Same file...
603 FileName_tmp->value = g_strdup(filename);
604 FileName_tmp->value_utf8 = g_strdup(filename_utf8); // Not necessary to fill 'value_ck'
605 ETFile_tmp->FileNameList = g_list_append(NULL,FileName_tmp);
606 ETFile_tmp->FileNameCur = ETFile_tmp->FileNameList;
607 // With empty tag...
608 ETFile_tmp->FileTagList = g_list_append(NULL,FileTag_tmp);
609 ETFile_tmp->FileTag = ETFile_tmp->FileTagList;
610 ape_tag_write_file_tag (ETFile_tmp, NULL);
611 ET_Free_File_List_Item(ETFile_tmp);
612 }
613
614
615 /*********************************
616 * Update id3v1.x and id3v2 tags *
617 *********************************/
618 /* Get the number of frames into the tag, cause if it is
619 * equal to 0, id3lib-3.7.12 doesn't update the tag */
620 number_of_frames = ID3Tag_NumFrames(id3_tag);
621
622 /* If all fields (managed in the UI) are empty and option id3-strip-empty
623 * is set to TRUE, we strip the ID3v1.x and ID3v2 tags. Else, write ID3v2
624 * and/or ID3v1. */
625 if (g_settings_get_boolean (MainSettings, "id3-strip-empty")
626 && !has_title && !has_artist && !has_album_artist && !has_album && !has_year && !has_track
627 && !has_genre && !has_composer && !has_orig_artist && !has_copyright && !has_url
628 && !has_encoded_by && !has_picture && !has_comment && !has_disc_number)//&& !has_song_len )
629 {
630 error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1);
631 error_strip_id3v2 = ID3Tag_Strip(id3_tag,ID3TT_ID3V2);
632 /* Check error messages */
633 if (error_strip_id3v1 == ID3E_NoError && error_strip_id3v2 == ID3E_NoError)
634 {
635 g_debug (_("Removed tag of ‘%s’"), basename_utf8);
636 }
637 else
638 {
639 if (error_strip_id3v1 != ID3E_NoError)
640 {
641 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
642 _("Error while removing ID3v1 tag of ‘%s’: %s"),
643 basename_utf8,
644 Id3tag_Get_Error_Message (error_strip_id3v1));
645 }
646
647 if (error_strip_id3v2 != ID3E_NoError)
648 {
649 g_clear_error (error);
650 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
651 _("Error while removing ID3v2 tag of ‘%s’: %s"),
652 basename_utf8,
653 Id3tag_Get_Error_Message (error_strip_id3v2));
654 }
655
656 success = FALSE;
657 }
658
659 }
660 else
661 {
662 /* It's better to remove the id3v1 tag before, to synchronize it with the
663 * id3v2 tag (else id3lib doesn't do it correctly)
664 */
665 error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1);
666
667 /*
668 * ID3v2 tag
669 */
670 if (g_settings_get_boolean (MainSettings, "id3v2-enabled")
671 && number_of_frames != 0)
672 {
673 error_update_id3v2 = ID3Tag_UpdateByTagType(id3_tag,ID3TT_ID3V2);
674 if (error_update_id3v2 != ID3E_NoError)
675 {
676 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
677 _("Error while updating ID3v2 tag of ‘%s’: %s"),
678 basename_utf8,
679 Id3tag_Get_Error_Message (error_update_id3v2));
680 success = FALSE;
681 }
682 }
683 else
684 {
685 error_strip_id3v2 = ID3Tag_Strip(id3_tag,ID3TT_ID3V2);
686 if (error_strip_id3v2 != ID3E_NoError)
687 {
688 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
689 _("Error while removing ID3v2 tag of ‘%s’: %s"),
690 basename_utf8,
691 Id3tag_Get_Error_Message (error_strip_id3v2));
692 success = FALSE;
693 }
694 }
695
696 /*
697 * ID3v1 tag
698 * Must be set after ID3v2 or ID3Tag_UpdateByTagType cause damage to unicode strings
699 */
700 // id3lib writes incorrectly the ID3v2 tag if unicode used when writing ID3v1 tag
701 if (g_settings_get_boolean (MainSettings, "id3v1-enabled")
702 && number_of_frames != 0)
703 {
704 // By default id3lib converts id3tag to ISO-8859-1 (single byte character set)
705 // Note : converting UTF-16 string (two bytes character set) to ISO-8859-1
706 // remove only the second byte => a strange string appears...
707 Id3tag_Prepare_ID3v1(id3_tag);
708
709 error_update_id3v1 = ID3Tag_UpdateByTagType(id3_tag,ID3TT_ID3V1);
710 if (error_update_id3v1 != ID3E_NoError)
711 {
712 g_clear_error (error);
713 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
714 _("Error while updating ID3v1 tag of ‘%s’: %s"),
715 basename_utf8,
716 Id3tag_Get_Error_Message (error_update_id3v1));
717 success = FALSE;
718 }
719 }else
720 {
721 error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1);
722 if (error_strip_id3v1 != ID3E_NoError)
723 {
724 g_clear_error (error);
725 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
726 _("Error while removing ID3v1 tag of ‘%s’: %s"),
727 basename_utf8,
728 Id3tag_Get_Error_Message (error_strip_id3v1));
729 success = FALSE;
730 }
731 }
732 }
733
734 /* Do a one-time check that the id3lib version is not buggy. */
735 if (g_settings_get_boolean (MainSettings, "id3v2-enabled")
736 && number_of_frames != 0 && success)
737 {
738 /* See known problem on the top : [ 1016290 ] Unicode16 writing bug.
739 * When we write the tag in Unicode, we try to check if it was
740 * correctly written. So to test it : we read again the tag, and then
741 * compare with the previous one. We check up to find an error (as only
742 * some characters are affected).
743 * If the patch to id3lib was applied to fix the problem (tested
744 * by id3tag_check_if_id3lib_is_buggy) we didn't make the following
745 * test => OK */
746 if (flag_id3lib_bugged
747 && g_settings_get_boolean (MainSettings,
748 "id3v2-enable-unicode"))
749 {
750 File_Tag *FileTag_tmp = et_file_tag_new ();
751
752 if (id3tag_read_file_tag (file, FileTag_tmp, NULL) == TRUE
753 && et_file_tag_detect_difference (FileTag,
754 FileTag_tmp) == TRUE)
755 {
756 flag_id3lib_bugged = FALSE; /* Report the error only once. */
757 success = FALSE;
758 g_set_error (error, ET_ID3_ERROR,
759 ET_ID3_ERROR_BUGGY_ID3LIB, "%s",
760 _("Buggy id3lib"));
761 }
762
763 et_file_tag_free (FileTag_tmp);
764 }
765 }
766
767 /* Free allocated data */
768 ID3Tag_Delete(id3_tag);
769 g_object_unref (file);
770 g_free(basename_utf8);
771
772 return success;
773 }
774
775
Id3tag_Get_Error_Message(ID3_Err error)776 gchar *Id3tag_Get_Error_Message(ID3_Err error)
777 {
778 switch (error)
779 {
780 case ID3E_NoError:
781 return _("No error reported");
782 case ID3E_NoMemory:
783 return _("No available memory");
784 case ID3E_NoData:
785 return _("No data to parse");
786 case ID3E_BadData:
787 return _("Improperly formatted data");
788 case ID3E_NoBuffer:
789 return _("No buffer to write to");
790 case ID3E_SmallBuffer:
791 return _("Buffer is too small");
792 case ID3E_InvalidFrameID:
793 return _("Invalid frame ID");
794 case ID3E_FieldNotFound:
795 return _("Requested field not found");
796 case ID3E_UnknownFieldType:
797 return _("Unknown field type");
798 case ID3E_TagAlreadyAttached:
799 return _("Tag is already attached to a file");
800 case ID3E_InvalidTagVersion:
801 return _("Invalid tag version");
802 case ID3E_NoFile:
803 return _("No file to parse");
804 case ID3E_ReadOnly:
805 return _("Attempting to write to a read-only file");
806 case ID3E_zlibError:
807 return _("Error in compression/uncompression");
808 default:
809 return _("Unknown error message");
810 }
811
812 }
813
814
815
816 /*
817 * As the ID3Tag_Link function of id3lib-3.8.0pre2 returns the ID3v1 tags
818 * when a file has both ID3v1 and ID3v2 tags, we first try to explicitely
819 * get the ID3v2 tags with ID3Tag_LinkWithFlags and, if we cannot get them,
820 * fall back to the ID3v1 tags.
821 * (Written by Holger Schemel).
822 */
ID3Tag_Link_1(ID3Tag * id3tag,const char * filename)823 ID3_C_EXPORT size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename)
824 {
825 size_t offset;
826
827 #ifdef G_OS_WIN32
828 /* On Windows, id3lib expects filenames to be in the system codepage. */
829 gchar *locale_filename;
830
831 locale_filename = g_win32_locale_filename_from_utf8 (filename);
832
833 if (!locale_filename)
834 {
835 g_debug ("Error converting filename '%s' to system codepage",
836 filename);
837 return 0;
838 }
839 #endif
840
841 # if (0) // Link the file with the both tags may cause damage to unicode strings
842 //# if ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) && (ID3LIB_PATCH >= 1) ) // Same test used in Id3tag_Read_File_Tag to use ID3Tag_HasTagType
843 /* No problem of priority, so we link the file with the both tags
844 * to manage => ID3Tag_HasTagType works correctly */
845 #ifdef G_OS_WIN32
846 offset = ID3Tag_LinkWithFlags (id3tag, locale_filename,
847 ID3TT_ID3V1 | ID3TT_ID3V2);
848 #else
849 offset = ID3Tag_LinkWithFlags (id3tag, filename,
850 ID3TT_ID3V1 | ID3TT_ID3V2);
851 #endif
852 # elif ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) )
853 /* Version 3.8.0pre2 gives priority to tag id3v1 instead of id3v2, so we
854 * try to fix it by linking the file with the id3v2 tag first. This bug
855 * was fixed in the final version of 3.8.0 but we can't know it... */
856 /* First, try to get the ID3v2 tags */
857 #ifdef G_OS_WIN32
858 offset = ID3Tag_LinkWithFlags (id3tag, locale_filename, ID3TT_ID3V2);
859 #else
860 offset = ID3Tag_LinkWithFlags (id3tag, filename, ID3TT_ID3V2);
861 #endif
862
863 if (offset == 0)
864 {
865 /* No ID3v2 tags available => try to get the ID3v1 tags */
866 #ifdef G_OS_WIN32
867 offset = ID3Tag_LinkWithFlags (id3tag, locale_filename,
868 ID3TT_ID3V1);
869 #else
870 offset = ID3Tag_LinkWithFlags (id3tag, filename, ID3TT_ID3V1);
871 #endif
872 }
873 # else
874 /* Function 'ID3Tag_LinkWithFlags' is not defined up to id3lib-.3.7.13 */
875 #ifdef G_OS_WIN32
876 offset = ID3Tag_Link (id3tag, locale_filename);
877 #else
878 offset = ID3Tag_Link (id3tag, filename);
879 #endif
880 # endif
881 //g_print("ID3 TAG SIZE: %d\t%s\n",offset,g_path_get_basename(filename));
882
883 #ifdef G_OS_WIN32
884 g_free (locale_filename);
885 #endif
886
887 return offset;
888 }
889
890
891 /*
892 * As the ID3Field_GetASCII function differs with the version of id3lib, we must redefine it.
893 */
ID3Field_GetASCII_1(const ID3Field * field,char * buffer,size_t maxChars,size_t itemNum)894 ID3_C_EXPORT size_t ID3Field_GetASCII_1(const ID3Field *field, char *buffer, size_t maxChars, size_t itemNum)
895 {
896
897 /* Defined by id3lib: ID3LIB_MAJOR_VERSION, ID3LIB_MINOR_VERSION, ID3LIB_PATCH_VERSION
898 * Defined by autoconf: ID3LIB_MAJOR, ID3LIB_MINOR, ID3LIB_PATCH
899 *
900 * <= 3.7.12 : first item num is 1 for ID3Field_GetASCII
901 * = 3.7.13 : first item num is 0 for ID3Field_GetASCII
902 * >= 3.8.0 : doesn't need item num for ID3Field_GetASCII
903 */
904 //g_print("id3lib version: %d.%d.%d\n",ID3LIB_MAJOR,ID3LIB_MINOR,ID3LIB_PATCH);
905 # if (ID3LIB_MAJOR >= 3)
906 // (>= 3.x.x)
907 # if (ID3LIB_MINOR <= 7)
908 // (3.0.0 to 3.7.x)
909 # if (ID3LIB_PATCH >= 13)
910 // (>= 3.7.13)
911 return ID3Field_GetASCII(field,buffer,maxChars,itemNum);
912 # else
913 return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1);
914 # endif
915 # else
916 // (>= to 3.8.0)
917 //return ID3Field_GetASCII(field,buffer,maxChars);
918 return ID3Field_GetASCIIItem(field,buffer,maxChars,itemNum);
919 # endif
920 # else
921 // Not tested (< 3.x.x)
922 return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1);
923 # endif
924 }
925
926
927
928 /*
929 * As the ID3Field_GetUNICODE function differs with the version of id3lib, we must redefine it.
930 */
ID3Field_GetUNICODE_1(const ID3Field * field,unicode_t * buffer,size_t maxChars,size_t itemNum)931 ID3_C_EXPORT size_t ID3Field_GetUNICODE_1 (const ID3Field *field, unicode_t *buffer, size_t maxChars, size_t itemNum)
932 {
933
934 /* Defined by id3lib: ID3LIB_MAJOR_VERSION, ID3LIB_MINOR_VERSION, ID3LIB_PATCH_VERSION
935 * Defined by autoconf: ID3LIB_MAJOR, ID3LIB_MINOR, ID3LIB_PATCH
936 *
937 * <= 3.7.12 : first item num is 1 for ID3Field_GetUNICODE
938 * = 3.7.13 : first item num is 0 for ID3Field_GetUNICODE
939 * >= 3.8.0 : doesn't need item num for ID3Field_GetUNICODE
940 */
941 //g_print("id3lib version: %d.%d.%d\n",ID3LIB_MAJOR,ID3LIB_MINOR,ID3LIB_PATCH);
942 # if (ID3LIB_MAJOR >= 3)
943 // (>= 3.x.x)
944 # if (ID3LIB_MINOR <= 7)
945 // (3.0.0 to 3.7.x)
946 # if (ID3LIB_PATCH >= 13)
947 // (>= 3.7.13)
948 return ID3Field_GetUNICODE(field,buffer,maxChars,itemNum);
949 # else
950 return ID3Field_GetUNICODE(field,buffer,maxChars,itemNum+1);
951 # endif
952 # else
953 // (>= to 3.8.0)
954 return ID3Field_GetUNICODE(field,buffer,maxChars);
955 // ID3Field_GetUNICODEItem always return 0 with id3lib3.8.3, it is bug in size_t D3_FieldImpl::Get()
956 //return ID3Field_GetUNICODEItem(field,buffer,maxChars,itemNum);
957 # endif
958 # else
959 // Not tested (< 3.x.x)
960 return ID3Field_GetUNICODE(field,buffer,maxChars,itemNum+1);
961 # endif
962 }
963
964
965
966
967 /*
968 * Source : "http://www.id3.org/id3v2.4.0-structure.txt"
969 *
970 * Frames that allow different types of text encoding contains a text
971 * encoding description byte. Possible encodings:
972 *
973 * $00 ISO-8859-1 [ISO-8859-1]. Terminated with $00.
974 * $01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM ($FF FE
975 * or $FE FF). All strings in the same frame SHALL have the same
976 * byteorder. Terminated with $00 00.
977 * $02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM.
978 * Terminated with $00 00.
979 * $03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.
980 *
981 * For example :
982 * T P E 1 . . . . . . . ? ? J . o . n . . G . i . n . d . i . c . k .
983 * Hex : 54 50 45 31 00 00 00 19 00 00 01 ff fe 4a 00 6f 00 6e 00 20 00 47 00 69 00 6e 00 64 00 6e 00 63 00 6b 00
984 * ^
985 * |___ UTF-16
986 */
987 /*
988 * Read the content (ID3FN_TEXT, ID3FN_URL, ...) of the id3_field of the
989 * id3_frame, and convert the string if needed to UTF-8.
990 */
Id3tag_Get_Field(const ID3Frame * id3_frame,ID3_FieldID id3_fieldid)991 gchar *Id3tag_Get_Field (const ID3Frame *id3_frame, ID3_FieldID id3_fieldid)
992 {
993 ID3Field *id3_field = NULL;
994 ID3Field *id3_field_encoding = NULL;
995 size_t num_chars = 0;
996 gchar *string = NULL, *string1 = NULL;
997
998 //g_print("Id3tag_Get_Field - ID3Frame '%s'\n",ID3FrameInfo_ShortName(ID3Frame_GetID(id3_frame)));
999
1000 if ( (id3_field = ID3Frame_GetField(id3_frame,id3_fieldid)) )
1001 {
1002 ID3_TextEnc enc = ID3TE_NONE;
1003
1004 // Data of the field must be a TEXT (ID3FTY_TEXTSTRING)
1005 if (ID3Field_GetType(id3_field) != ID3FTY_TEXTSTRING)
1006 {
1007 g_critical ("%s",
1008 "Id3tag_Get_Field() must be used only for fields containing text");
1009 return NULL;
1010 }
1011
1012 /*
1013 * We prioritize the encoding of the field. If the encoding of the field
1014 * is ISO-8859-1, it can be read with another single byte encoding.
1015 */
1016 // Get encoding from content of file...
1017 id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC);
1018 if (id3_field_encoding)
1019 enc = ID3Field_GetINT(id3_field_encoding);
1020 // Else, get encoding from the field
1021 //enc = ID3Field_GetEncoding(id3_field);
1022
1023 if (enc != ID3TE_UTF16 && enc != ID3TE_UTF8) // Encoding is ISO-8859-1?
1024 {
1025 /* Override with another character set? */
1026 if (g_settings_get_boolean (MainSettings,
1027 "id3-override-read-encoding"))
1028 {
1029 /* Encoding set by user to ???. */
1030 gint id3v1v2_charset;
1031 const gchar *charset;
1032
1033 id3v1v2_charset = g_settings_get_enum (MainSettings,
1034 "id3v1v2-charset");
1035 charset = et_charset_get_name_from_index (id3v1v2_charset);
1036 if (strcmp (charset, "ISO-8859-1") == 0)
1037 {
1038 enc = ID3TE_ISO8859_1;
1039 }
1040 else if (strcmp (charset, "UTF-16BE") == 0
1041 || strcmp (charset, "UTF-16LE") == 0)
1042 {
1043 enc = ID3TE_UTF16;
1044 }
1045 else if (strcmp (charset, "UTF-8") == 0)
1046 {
1047 enc = ID3TE_UTF8;
1048 }
1049 else if (ID3Field_IsEncodable (id3_field))
1050 {
1051 string = g_malloc0 (ID3V2_MAX_STRING_LEN + 1);
1052 num_chars = ID3Field_GetASCII_1 (id3_field, string,
1053 ID3V2_MAX_STRING_LEN, 0);
1054 string1 = convert_string (string, charset, "UTF-8", FALSE);
1055 /* Override to a non-standard character encoding. */
1056 goto out;
1057 }
1058 }
1059 }
1060
1061 // Some fields, as URL, aren't encodable, so there were written using ISO characters.
1062 if ( !ID3Field_IsEncodable(id3_field) )
1063 {
1064 enc = ID3TE_ISO8859_1;
1065 }
1066
1067 // Action according the encoding...
1068 switch ( enc )
1069 {
1070 case ID3TE_ISO8859_1:
1071 string = g_malloc0 (ID3V2_MAX_STRING_LEN + 1);
1072 num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0);
1073 string1 = convert_string(string,"ISO-8859-1","UTF-8",FALSE);
1074 break;
1075
1076 case ID3TE_UTF8: // Shouldn't work with id3lib 3.8.3 (supports only ID3v2.3, not ID3v2.4)
1077 string = g_malloc0 (ID3V2_MAX_STRING_LEN+1);
1078 num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0);
1079 //string1 = convert_string(string,"UTF-8","UTF-8",FALSE); // Nothing to do
1080 if (g_utf8_validate(string,-1,NULL))
1081 string1 = g_strdup(string);
1082 break;
1083
1084 case ID3TE_UTF16:
1085 // Id3lib (3.8.3 at least) always returns Unicode strings in UTF-16BE.
1086 case ID3TE_UTF16BE:
1087 string = g_malloc0(sizeof(unicode_t)*ID3V2_MAX_STRING_LEN+1);
1088 num_chars = ID3Field_GetUNICODE_1(id3_field,(unicode_t *)string,ID3V2_MAX_STRING_LEN,0);
1089 // "convert_string_1" as we need to pass length for UTF-16
1090 string1 = convert_string_1(string,num_chars,"UTF-16BE","UTF-8",FALSE);
1091 break;
1092
1093 case ID3TE_NONE:
1094 case ID3TE_NUMENCODINGS:
1095 default:
1096 string = g_malloc0 (4 * ID3V2_MAX_STRING_LEN + 1);
1097 num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0);
1098
1099 if (g_utf8_validate (string, -1, NULL))
1100 {
1101 string1 = g_strdup (string);
1102 }
1103 else
1104 {
1105 GError *error = NULL;
1106
1107 string1 = g_locale_to_utf8 (string, -1, NULL, NULL,
1108 &error);
1109
1110 if (string1 == NULL)
1111 {
1112 g_debug ("Error converting string from locale to UTF-8 encoding: %s",
1113 error->message);
1114 g_error_free (error);
1115 }
1116 }
1117 break;
1118 }
1119 }
1120 //g_print(">>ID:%d >'%s' (string1:'%s') (num_chars:%d)\n",ID3Field_GetINT(id3_field_encoding),string,string1,num_chars);
1121
1122 out:
1123 /* In case the conversion fails, try character fix. */
1124 if (num_chars && !string1)
1125 {
1126 gchar *escaped_str = g_strescape(string, NULL);
1127 g_debug ("Id3tag_Get_Field: Trying to fix string '%s'…", escaped_str);
1128 g_free(escaped_str);
1129
1130 string1 = g_filename_display_name (string);
1131
1132 /* TODO: Set a GError instead. */
1133 if (!string1)
1134 {
1135 g_warning ("%s", "Error converting ID3 tag field encoding");
1136 }
1137 }
1138 g_free(string);
1139
1140 return string1;
1141 }
1142
1143
1144 /*
1145 * Set the content (ID3FN_TEXT, ID3FN_URL, ...) of the id3_field of the
1146 * id3_frame. Check also if the string must be written from UTF-8 (gtk2) in the
1147 * ISO-8859-1 encoding or UTF-16.
1148 *
1149 * Return the encoding used as if UTF-16 was used, we musn't write the ID3v1 tag.
1150 *
1151 * Known problem with id3lib
1152 * - [ 1016290 ] Unicode16 writing bug
1153 * http://sourceforge.net/tracker/index.php?func=detail&aid=1016290&group_id=979&atid=300979
1154 * For example with Latin-1 characters (like éöäüß) not saved correctly
1155 * in Unicode, due to id3lib (for "é" it will write "E9 FF" instead of "EF 00")
1156 */
1157 /*
1158 * OLD NOTE : PROBLEM with ID3LIB
1159 * - [ 1074169 ] Writing ID3v1 tag breaks Unicode string in v2 tags
1160 * http://sourceforge.net/tracker/index.php?func=detail&aid=1074169&group_id=979&atid=100979
1161 * => don't write id3v1 tag if Unicode is used, up to patch applied
1162 * - [ 1073951 ] Added missing Field Encoding functions to C wrapper
1163 * http://sourceforge.net/tracker/index.php?func=detail&aid=1073951&group_id=979&atid=300979
1164 */
1165 ID3_TextEnc
Id3tag_Set_Field(const ID3Frame * id3_frame,ID3_FieldID id3_fieldid,const gchar * string)1166 Id3tag_Set_Field (const ID3Frame *id3_frame,
1167 ID3_FieldID id3_fieldid,
1168 const gchar *string)
1169 {
1170 ID3Field *id3_field = NULL;
1171 ID3Field *id3_field_encoding = NULL;
1172 gchar *string_converted = NULL;
1173
1174 if ( (id3_field = ID3Frame_GetField(id3_frame,id3_fieldid)) )
1175 {
1176 ID3_TextEnc enc = ID3TE_NONE;
1177
1178 // Data of the field must be a TEXT (ID3FTY_TEXTSTRING)
1179 if (ID3Field_GetType(id3_field) != ID3FTY_TEXTSTRING)
1180 {
1181 g_critical ("%s",
1182 "Id3tag_Set_Field() must be used only for fields containing text");
1183 return ID3TE_NONE;
1184 }
1185
1186 /* We prioritize the rule selected in options. If the encoding of the
1187 * field is ISO-8859-1, we can write it to another single byte encoding.
1188 */
1189 if (g_settings_get_boolean (MainSettings, "id3v2-enable-unicode"))
1190 {
1191 // Check if we can write the tag using ISO-8859-1 instead of UTF-16...
1192 if ( (string_converted = g_convert(string, strlen(string), "ISO-8859-1",
1193 "UTF-8", NULL, NULL ,NULL)) )
1194 {
1195 enc = ID3TE_ISO8859_1;
1196 g_free(string_converted);
1197 }else
1198 {
1199 // Force to UTF-16 as UTF-8 isn't supported
1200 enc = ID3TE_UTF16;
1201 }
1202 }
1203 else
1204 {
1205 gint id3v2_charset;
1206 const gchar *charset;
1207
1208 id3v2_charset = g_settings_get_enum (MainSettings,
1209 "id3v2-no-unicode-charset");
1210 charset = et_charset_get_name_from_index (id3v2_charset);
1211
1212 /* Other encoding selected. Encoding set by user to ???. */
1213 if (strcmp (charset, "ISO-8859-1") == 0)
1214 {
1215 enc = ID3TE_ISO8859_1;
1216 }
1217 else if (strcmp (charset, "UTF-16BE") == 0
1218 || strcmp (charset, "UTF-16LE") == 0)
1219 {
1220 enc = ID3TE_UTF16;
1221 }
1222 else if (strcmp (charset, "UTF-8") == 0)
1223 {
1224 enc = ID3TE_UTF8;
1225 }
1226 else if (ID3Field_IsEncodable (id3_field))
1227 {
1228 goto override;
1229 }
1230 }
1231
1232 // Some fields, as URL, aren't encodable, so there were written using ISO characters!
1233 if ( !ID3Field_IsEncodable(id3_field) )
1234 {
1235 enc = ID3TE_ISO8859_1;
1236 }
1237
1238 // Action according the encoding...
1239 switch ( enc )
1240 {
1241 case ID3TE_ISO8859_1:
1242 // Write into ISO-8859-1
1243 //string_converted = convert_string(string,"UTF-8","ISO-8859-1",TRUE);
1244 string_converted = Id3tag_Rules_For_ISO_Fields(string,"UTF-8","ISO-8859-1");
1245 ID3Field_SetEncoding(id3_field,ID3TE_ISO8859_1); // Not necessary for ISO-8859-1, but better to precise if field has another encoding...
1246 ID3Field_SetASCII(id3_field,string_converted);
1247 g_free(string_converted);
1248
1249 id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC);
1250 if (id3_field_encoding)
1251 ID3Field_SetINT(id3_field_encoding,ID3TE_ISO8859_1);
1252
1253 return ID3TE_ISO8859_1;
1254 break;
1255
1256 /*** Commented as it doesn't work with id3lib 3.8.3 :
1257 *** - it writes a strange UTF-8 string (2 bytes per character. Second char is FF) with a BOM
1258 *** - it set the frame encoded to UTF-8 : "$03" which is OK
1259 case ID3TE_UTF8: // Shouldn't work with id3lib 3.8.3 (supports only ID3v2.3, not ID3v2.4)
1260 ID3Field_SetEncoding(id3_field,ID3TE_UTF8);
1261 ID3Field_SetASCII(id3_field,string);
1262
1263 id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC);
1264 if (id3_field_encoding)
1265 ID3Field_SetINT(id3_field_encoding,ID3TE_UTF8);
1266
1267 return ID3TE_UTF8;
1268 break;
1269 ***/
1270
1271 case ID3TE_UTF16:
1272 //case ID3TE_UTF16BE:
1273
1274 /* See known problem on the top : [ 1016290 ] Unicode16 writing bug */
1275 // Write into UTF-16
1276 string_converted = convert_string_1(string, strlen(string), "UTF-8",
1277 "UTF-16BE", FALSE);
1278
1279 // id3lib (3.8.3 at least) always takes big-endian input for Unicode
1280 // fields, even if the field is set little-endian.
1281 ID3Field_SetEncoding(id3_field,ID3TE_UTF16);
1282 ID3Field_SetUNICODE(id3_field,(const unicode_t*)string_converted);
1283 g_free(string_converted);
1284
1285 id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC);
1286 if (id3_field_encoding)
1287 ID3Field_SetINT(id3_field_encoding,ID3TE_UTF16);
1288
1289 return ID3TE_UTF16;
1290 break;
1291
1292 override:
1293 case ID3TE_UTF16BE:
1294 case ID3TE_UTF8:
1295 case ID3TE_NUMENCODINGS:
1296 case ID3TE_NONE:
1297 default:
1298 {
1299 //string_converted = convert_string(string,"UTF-8",charset,TRUE);
1300 gint id3v2_charset;
1301 const gchar *charset;
1302
1303 id3v2_charset = g_settings_get_enum (MainSettings,
1304 "id3v2-no-unicode-charset");
1305 charset = et_charset_get_name_from_index (id3v2_charset);
1306
1307 string_converted = Id3tag_Rules_For_ISO_Fields (string,
1308 "UTF-8",
1309 charset);
1310 ID3Field_SetEncoding(id3_field,ID3TE_ISO8859_1);
1311 ID3Field_SetASCII(id3_field,string_converted);
1312 g_free(string_converted);
1313
1314 id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC);
1315 if (id3_field_encoding)
1316 ID3Field_SetINT(id3_field_encoding,ID3TE_ISO8859_1);
1317
1318 return ID3TE_NONE;
1319 break;
1320 }
1321 }
1322 }
1323
1324 return ID3TE_NONE;
1325 }
1326
1327
1328 /*
1329 * By default id3lib converts id3tag to ISO-8859-1 (single byte character set)
1330 * Note : converting UTF-16 string (two bytes character set) to ISO-8859-1
1331 * remove only the second byte => a strange string appears...
1332 */
Id3tag_Prepare_ID3v1(ID3Tag * id3_tag)1333 void Id3tag_Prepare_ID3v1 (ID3Tag *id3_tag)
1334 {
1335 ID3Frame *frame;
1336 ID3Field *id3_field_encoding;
1337 ID3Field *id3_field_text;
1338
1339 if ( id3_tag != NULL )
1340 {
1341 ID3TagIterator *id3_tag_iterator;
1342 size_t num_chars = 0;
1343 gchar *string, *string1, *string_converted;
1344
1345 id3_tag_iterator = ID3Tag_CreateIterator(id3_tag);
1346 while ( NULL != (frame = ID3TagIterator_GetNext(id3_tag_iterator)) )
1347 {
1348 ID3_TextEnc enc = ID3TE_ISO8859_1;
1349 ID3_FrameID frameid;
1350
1351 frameid = ID3Frame_GetID(frame);
1352
1353 if (frameid != ID3FID_TITLE
1354 && frameid != ID3FID_LEADARTIST
1355 && frameid != ID3FID_BAND
1356 && frameid != ID3FID_ALBUM
1357 && frameid != ID3FID_YEAR
1358 && frameid != ID3FID_TRACKNUM
1359 && frameid != ID3FID_CONTENTTYPE
1360 && frameid != ID3FID_COMMENT)
1361 continue;
1362
1363 id3_field_encoding = ID3Frame_GetField(frame, ID3FN_TEXTENC);
1364 if (id3_field_encoding != NULL)
1365 enc = ID3Field_GetINT(id3_field_encoding);
1366 id3_field_text = ID3Frame_GetField(frame, ID3FN_TEXT);
1367
1368 /* The frames in ID3TE_ISO8859_1 are already converted to the selected
1369 * single-byte character set if used. So we treat only Unicode frames */
1370 if ( (id3_field_text != NULL)
1371 && (enc != ID3TE_ISO8859_1) )
1372 {
1373 gint id3v1_charset;
1374 const gchar *charset;
1375
1376 /* Read UTF-16 frame. */
1377 string = g_malloc0(sizeof(unicode_t)*ID3V2_MAX_STRING_LEN+1);
1378 num_chars = ID3Field_GetUNICODE_1(id3_field_text,(unicode_t *)string,ID3V2_MAX_STRING_LEN,0);
1379 // "convert_string_1" as we need to pass length for UTF-16
1380 string1 = convert_string_1(string,num_chars,"UTF-16BE","UTF-8",FALSE);
1381
1382 id3v1_charset = g_settings_get_enum (MainSettings,
1383 "id3v1-charset");
1384 charset = et_charset_get_name_from_index (id3v1_charset);
1385
1386 string_converted = Id3tag_Rules_For_ISO_Fields (string1,
1387 "UTF-8",
1388 charset);
1389
1390 if (string_converted)
1391 {
1392 ID3Field_SetEncoding(id3_field_text,ID3TE_ISO8859_1); // Not necessary for ISO-8859-1
1393 ID3Field_SetASCII(id3_field_text,string_converted);
1394 ID3Field_SetINT(id3_field_encoding,ID3TE_ISO8859_1);
1395 g_free(string_converted);
1396 }
1397 g_free(string);
1398 g_free(string1);
1399 }
1400 }
1401 ID3TagIterator_Delete(id3_tag_iterator);
1402 }
1403 }
1404
1405 /*
1406 * This function must be used for tag fields containing ISO data.
1407 * We use specials functionalities of iconv : //TRANSLIT and //IGNORE (the last
1408 * one doesn't work on my system) to force conversion to the target encoding.
1409 */
1410 gchar *
Id3tag_Rules_For_ISO_Fields(const gchar * string,const gchar * from_codeset,const gchar * to_codeset)1411 Id3tag_Rules_For_ISO_Fields (const gchar *string,
1412 const gchar *from_codeset,
1413 const gchar *to_codeset)
1414 {
1415 gchar *string_converted = NULL;
1416 EtTagEncoding iconv_option;
1417
1418 g_return_val_if_fail (string != NULL && from_codeset != NULL
1419 && to_codeset != NULL, NULL);
1420
1421 iconv_option = g_settings_get_enum (MainSettings, "id3v1-encoding-option");
1422
1423 switch (iconv_option)
1424 {
1425 default:
1426 case ET_TAG_ENCODING_NONE:
1427 string_converted = convert_string (string, from_codeset,
1428 to_codeset, TRUE);
1429 break;
1430 case ET_TAG_ENCODING_TRANSLITERATE:
1431 {
1432 /* iconv_open (3):
1433 * When the string "//TRANSLIT" is appended to tocode,
1434 * transliteration is activated. This means that when a character
1435 * cannot be represented in the target character set, it can be
1436 * approximated through one or several similarly looking
1437 * characters.
1438 */
1439 gchar *to_enc = g_strconcat (to_codeset, "//TRANSLIT", NULL);
1440 string_converted = convert_string (string, from_codeset, to_enc,
1441 TRUE);
1442 g_free (to_enc);
1443 break;
1444 }
1445 case ET_TAG_ENCODING_IGNORE:
1446 {
1447 /* iconv_open (3):
1448 * When the string "//IGNORE" is appended to tocode, characters
1449 * that cannot be represented in the target character set will be
1450 * silently discarded.
1451 */
1452 gchar *to_enc = g_strconcat (to_codeset, "//IGNORE", NULL);
1453 string_converted = convert_string (string, from_codeset, to_enc,
1454 TRUE);
1455 g_free (to_enc);
1456 break;
1457 }
1458 }
1459
1460 return string_converted;
1461 }
1462
1463 /*
1464 * Some files which contains only zeroes create an infinite loop in id3lib...
1465 * To generate a file with zeroes : dd if=/dev/zero bs=1M count=6 of=test-corrupted-mp3-zero-contend.mp3
1466 */
1467 gboolean
et_id3tag_check_if_file_is_valid(GFile * file,GError ** error)1468 et_id3tag_check_if_file_is_valid (GFile *file, GError **error)
1469 {
1470 unsigned char tmp[256];
1471 unsigned char tmp0[256];
1472 gssize bytes_read;
1473 gboolean valid = FALSE;
1474 GFileInputStream *file_istream;
1475
1476 g_return_val_if_fail (file != NULL, FALSE);
1477
1478 file_istream = g_file_read (file, NULL, error);
1479
1480 if (!file_istream)
1481 {
1482 g_assert (error == NULL || *error != NULL);
1483 return valid;
1484 }
1485
1486 memset (&tmp0, 0, 256);
1487
1488 /* Keep reading until EOF. */
1489 while ((bytes_read = g_input_stream_read (G_INPUT_STREAM (file_istream),
1490 tmp, 256, NULL, error)) != 0)
1491 {
1492 if (bytes_read == -1)
1493 {
1494 /* Error in reading file. */
1495 g_assert (error == NULL || *error != NULL);
1496 g_object_unref (file_istream);
1497 return valid;
1498 }
1499
1500 /* Break out of the loop if there is a non-zero byte in the file. */
1501 if (memcmp (tmp, tmp0, bytes_read) != 0)
1502 {
1503 valid = TRUE;
1504 break;
1505 }
1506 }
1507
1508 g_object_unref (file_istream);
1509
1510 /* The error was not set by g_input_stream_read(), so the file must be
1511 * empty. */
1512 if (!valid)
1513 {
1514 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
1515 "Input truncated or empty");
1516 }
1517
1518 return valid;
1519 }
1520
1521
1522 /*
1523 * Function to detect if id3lib isn't bugged when writting to Unicode
1524 * Returns TRUE if bugged, else FALSE
1525 */
1526 static gboolean
id3tag_check_if_id3lib_is_buggy(GError ** error)1527 id3tag_check_if_id3lib_is_buggy (GError **error)
1528 {
1529 GFile *file;
1530 GFileIOStream *iostream = NULL;
1531 GOutputStream *ostream = NULL;
1532 guchar tmp[16] = {0xFF, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1533 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1534 ID3Tag *id3_tag = NULL;
1535 gchar *path;
1536 gchar *result = NULL;
1537 ID3Frame *id3_frame;
1538 gboolean use_unicode;
1539 gsize bytes_written;
1540 const gchar test_str[] = "\xe5\x92\xbb";
1541
1542 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1543
1544 /* Create a temporary file. */
1545 file = g_file_new_tmp ("easytagXXXXXX.mp3", &iostream, error);
1546
1547 if (!file)
1548 {
1549 /* TODO: Investigate whether error can be unset in this case. */
1550 if (!error)
1551 {
1552 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, "%s",
1553 _("Error while creating temporary file"));
1554 }
1555
1556 return FALSE;
1557 }
1558
1559 /* Set data in the file. */
1560 ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
1561
1562 if (!g_output_stream_write_all (G_OUTPUT_STREAM (ostream), tmp,
1563 sizeof (tmp), &bytes_written, NULL,
1564 error))
1565 {
1566 g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %" G_GSIZE_FORMAT
1567 " bytes of data were written", bytes_written, sizeof (tmp));
1568
1569 g_object_unref (file);
1570 g_object_unref (iostream);
1571
1572 return FALSE;
1573 }
1574
1575 g_output_stream_close (G_OUTPUT_STREAM (ostream), NULL, NULL);
1576 g_object_unref (iostream);
1577
1578 /* Save state of switches as we must force to Unicode before writing.
1579 * FIXME! */
1580 g_settings_delay (MainSettings);
1581 use_unicode = g_settings_get_boolean (MainSettings,
1582 "id3v2-enable-unicode");
1583 g_settings_set_boolean (MainSettings, "id3v2-enable-unicode", TRUE);
1584
1585 id3_tag = ID3Tag_New();
1586 path = g_file_get_path (file);
1587 ID3Tag_Link_1 (id3_tag, path);
1588
1589 // Create a new 'title' field for testing
1590 id3_frame = ID3Frame_NewID(ID3FID_TITLE);
1591 ID3Tag_AttachFrame(id3_tag,id3_frame);
1592 /* Test a string that exposes an id3lib bug when converted to UTF-16.
1593 * http://sourceforge.net/p/id3lib/patches/64/ */
1594 Id3tag_Set_Field (id3_frame, ID3FN_TEXT, test_str);
1595
1596 // Update the tag
1597 ID3Tag_UpdateByTagType(id3_tag,ID3TT_ID3V2);
1598 ID3Tag_Delete(id3_tag);
1599
1600 g_settings_set_boolean (MainSettings, "id3v2-enable-unicode", use_unicode);
1601 g_settings_revert (MainSettings);
1602
1603 id3_tag = ID3Tag_New();
1604 ID3Tag_Link_1 (id3_tag, path);
1605 // Read the written field
1606 if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TITLE)) )
1607 {
1608 result = Id3tag_Get_Field(id3_frame,ID3FN_TEXT);
1609 }
1610
1611 ID3Tag_Delete(id3_tag);
1612 g_free (path);
1613 g_file_delete (file, NULL, NULL);
1614
1615 g_object_unref (file);
1616
1617 /* Same string found? if yes => not buggy. */
1618 if (result && strcmp (result, test_str) != 0)
1619 {
1620 g_free (result);
1621 return TRUE;
1622 }
1623
1624 g_free (result);
1625 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s",
1626 "Buggy id3lib detected");
1627 return FALSE;
1628 }
1629
1630 #endif /* ENABLE_ID3LIB */
1631
1632
1633 /*
1634 * Write tag according the version selected by the user
1635 */
1636 gboolean
id3tag_write_file_tag(const ET_File * ETFile,GError ** error)1637 id3tag_write_file_tag (const ET_File *ETFile,
1638 GError **error)
1639 {
1640 #ifdef ENABLE_ID3LIB
1641 if (g_settings_get_boolean (MainSettings, "id3v2-version-4"))
1642 {
1643 return id3tag_write_file_v24tag (ETFile, error);
1644 }
1645 else
1646 {
1647 return id3tag_write_file_v23tag (ETFile, error);
1648 }
1649 #else
1650 return id3tag_write_file_v24tag (ETFile, error);
1651 #endif /* !ENABLE_ID3LIB */
1652 }
1653
1654 #endif /* ENABLE_MP3 */
1655
1656
1657 // Placed out #ifdef ENABLE_MP3 as not dependant of id3lib, and used in CDDB
1658 /*
1659 * Returns the corresponding genre value of the input string (for ID3v1.x),
1660 * else returns 0xFF (unknown genre, but not invalid).
1661 */
1662 guchar
Id3tag_String_To_Genre(const gchar * genre)1663 Id3tag_String_To_Genre (const gchar *genre)
1664 {
1665 guint i;
1666
1667 if (genre != NULL)
1668 {
1669 for (i=0; i<=GENRE_MAX; i++)
1670 if (strcasecmp(genre,id3_genres[i])==0)
1671 return (guchar)i;
1672 }
1673 return (guchar)0xFF;
1674 }
1675
1676
1677 /*
1678 * Returns the name of a genre code if found
1679 * Three states for genre code :
1680 * - defined (0 to GENRE_MAX)
1681 * - undefined/unknown (GENRE_MAX+1 to ID3_INVALID_GENRE-1)
1682 * - invalid (>ID3_INVALID_GENRE)
1683 */
1684 const gchar *
Id3tag_Genre_To_String(unsigned char genre_code)1685 Id3tag_Genre_To_String (unsigned char genre_code)
1686 {
1687 if (genre_code>=ID3_INVALID_GENRE) /* empty */
1688 return "";
1689 else if (genre_code>GENRE_MAX) /* unknown tag */
1690 return "Unknown";
1691 else /* known tag */
1692 return id3_genres[genre_code];
1693 }
1694