mplib_s.c

Go to the documentation of this file.
00001 /*
00002  * mplib - a library that enables you to edit ID3 tags
00003  *
00004  * Copyright (c) 2001,2002,2003,2004,2005,2006 Stefan Podkowinski
00005  *               2006                          Michal Kowalczuk
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions are met:
00010  *
00011  * * Redistributions of source code must retain the above copyright notice, this
00012  *   list of conditions and the following disclaimer.
00013  * * Redistributions in binary form must reproduce the above copyright notice,
00014  *   this list of conditions and the following disclaimer in the documentation
00015  *   and/or other materials provided with the distribution.
00016  * * Neither the name of the author nor the names of its contributors
00017  *   may be used to endorse or promote products derived from this software
00018  *   without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030  * POSSIBILITY OF SUCH DAMAGE.
00031  */
00032 
00033 #ifdef HAVE_CONFIG_H
00034 # include "config.h"
00035 #endif
00036 
00037 #if STDC_HEADERS
00038 # include <stdlib.h>
00039 # include <string.h>
00040 #elif HAVE_STRINGS_H
00041 # include <strings.h>
00042 #endif /*STDC_HEADERS*/
00043 
00044 #if HAVE_UNISTD_H
00045 # include <unistd.h>
00046 # include <sys/types.h>
00047 #endif
00048 
00049 
00050 #include <errno.h>
00051 #include <stdio.h>
00052 #include <fcntl.h>
00053 #include <ctype.h>
00054 #include <sys/stat.h>
00055 
00056 #include "mplib_s.h"
00057 #include "xmalloc.h"
00058 
00059 
00060 
00061 /******************************/
00062 /*      Static functions           */
00063 /******************************/
00064 
00065 static id3v1_tag*
00066 id3v1_get_tag(size_t (*read_func)(void *, size_t, void *),
00067                 off_t (*lseek_func)(off_t, int, void *),
00068                 void *arg)
00069 {
00070         id3v1_tag *tag;
00071         char *c;
00072         
00073         tag = XMALLOCD0(id3v1_tag, "id3v1_get_tag:tag");
00074         
00075         c = (char *)xmallocd(3, "id3v1_get_tag:c");
00076         
00077         if(lseek_func(-128L, SEEK_END, arg) == -1) goto exit_on_error;
00078         if(read_func(c, 3, arg) < 3) goto exit_on_error;
00079         if(strncmp(c, "TAG", 3) != 0) goto exit_on_error;
00080 
00081         tag->title = (char *)xmallocd(31, "id3v1_get_tag:tag->title");
00082         if(read_func(tag->title, 30, arg) < 30) goto exit_on_error;
00083         if(tag->title[0] == 0 || id3_is_only_space(tag->title, 30)) {
00084             xfree(tag->title);
00085             tag->title = NULL;
00086         } else tag->title[30] = 0;
00087         
00088         tag->artist = (char*)xmallocd(31, "id3v1_get_tag:tag->artist");
00089         if(read_func(tag->artist, 30, arg) < 30) goto exit_on_error;
00090         if(tag->artist[0] == 0 || id3_is_only_space(tag->artist, 30)) {
00091             xfree(tag->artist);
00092             tag->artist = NULL;
00093         } else tag->artist[30] = 0;
00094         
00095         tag->album = (char*)xmallocd(31, "id3v1_get_tag:tag->album");
00096         if(read_func(tag->album, 30, arg) < 30) goto exit_on_error;
00097         if(tag->album[0] == 0 || id3_is_only_space(tag->album, 30)) {
00098             xfree(tag->album);
00099             tag->album = NULL;
00100         } else tag->album[30] = 0;
00101         
00102         tag->year = (char*)xmallocd(5, "id3v1_get_tag:tag->year");
00103         if(read_func(tag->year, 4, arg) < 4) goto exit_on_error;
00104         if(tag->year[0] == 0 || id3_is_only_space(tag->year, 4)) {
00105             xfree(tag->year);
00106             tag->year = NULL;
00107         } else tag->year[4] = 0;
00108         
00109         tag->comment = (char*)xmallocd(31, "id3v1_get_tag:tag->comment");
00110         if(read_func(tag->comment, 30, arg) < 30) goto exit_on_error;
00111         tag->comment[30] = 0;
00112         
00113         if(read_func(&(tag->genre), 1, arg) < 1) goto exit_on_error;
00114         
00115         /* Looking for v1.1 track info */
00116         if(tag->comment && tag->comment[28] == 0 && tag->comment[29] != 0)
00117         {
00118                 tag->track = tag->comment[29];
00119                 tag->comment[29] = 0;
00120         } 
00121         else 
00122         {
00123                 tag->track = 0;
00124         }
00125         
00126         /* Set comment to NULL if not set - this happens at this point because */
00127         /* there maybe a track info anyway */
00128         if(tag->comment[0] == 0 || id3_is_only_space(tag->comment, 28)) {
00129             xfree(tag->comment);
00130             tag->comment = NULL;
00131         }
00132         
00133         xfree(c);
00134         return tag;
00135 
00136         exit_on_error:
00137         
00138         xfree(c);
00139         id3v1_free_tag(tag);
00140         return NULL;
00141 }
00142 
00143 static int 
00144 id3v1_add_tag(int fd, id3v1_tag *tag) 
00145 {
00146         int i, j;
00147         void *blank, *set;
00148         char *b_tag, *b_tag_start;
00149         
00150         blank = xmallocd0(30, "id3v1_add_tag:blank");
00151         set = xmallocd(30, "id3v1_add_tag:set");
00152         memset(set, 0xFF, 30);
00153         b_tag = b_tag_start = (char *)xmallocd0(128, "id3v1_add_tag:b_tag");
00154         
00155         strncpy(b_tag, "TAG", 3); b_tag += 3;
00156         
00157         if(tag->title) 
00158         {
00159                 j = strlen(tag->title);
00160                 strncpy(b_tag, tag->title, j); b_tag += j;
00161                 i = 30 - j;
00162                 if(i > 0) 
00163                 {
00164                         strncpy(b_tag, blank, i); b_tag += i;
00165                 }
00166         }
00167         else 
00168         {
00169                 strncpy(b_tag, blank, 30); b_tag += 30;
00170         }
00171         
00172         if(tag->artist) 
00173         {
00174                 j = strlen(tag->artist);
00175                 strncpy(b_tag, tag->artist, j); b_tag += j;
00176                 i = 30 - j;
00177                 if(i > 0) 
00178                 {
00179                         strncpy(b_tag, blank, i); b_tag += i;
00180                 }
00181         } 
00182         else
00183         {
00184                 strncpy(b_tag, blank, 30); b_tag += 30;
00185         }
00186         
00187         if(tag->album)
00188         {
00189                 j = strlen(tag->album);
00190                 strncpy(b_tag, tag->album, j); b_tag += j;
00191                 i = 30 - j;
00192                 if(i > 0)
00193                 {
00194                         strncpy(b_tag, blank, i); b_tag += i;
00195                 }
00196         }
00197         else
00198         {
00199                 strncpy(b_tag, blank, 30); b_tag += 30;
00200         }
00201         
00202         if(tag->year) 
00203         {
00204                 j = strlen(tag->year);
00205                 strncpy(b_tag, tag->year, j); b_tag += j;
00206                 i = 4 - j;
00207                 if(i > 0) 
00208                 {
00209                         strncpy(b_tag, blank, i); b_tag += i;
00210                 }
00211         }
00212         else 
00213         {
00214                 strncpy(b_tag, blank, 4); b_tag += 4;
00215         }
00216         
00217         if(tag->comment)
00218         {
00219                 int hastrack = 0;
00220                 j = strlen(tag->comment);
00221                 if(tag->track > 0) hastrack = 1;
00222                 if(hastrack && j > 28) 
00223                 {
00224                         strncpy(b_tag, tag->comment, 28); b_tag += 28;
00225                 }
00226                 else
00227                 {
00228                         strncpy(b_tag, tag->comment, j); b_tag += j;
00229                         i = ((tag->track > 0) ? 28 : 30) - j;
00230                 }
00231                 if(i > 0)
00232                 {
00233                         strncpy(b_tag, blank, i); b_tag += i;
00234                 }
00235         } 
00236         else 
00237         { 
00238                 strncpy(b_tag, blank, (tag->track > 0) ? 28 : 30); 
00239                 b_tag += (tag->track > 0) ? 28 : 30;
00240         }
00241         
00242         if(tag->track > 0) 
00243         {
00244                 strncpy(b_tag, blank, 1); b_tag += 1;
00245                 strncpy(b_tag, &(tag->track), 1); b_tag += 1;
00246         }
00247         if(tag->genre != 0xFF) 
00248         {
00249                 strncpy(b_tag, &(tag->genre), 1); b_tag += 1;
00250         }
00251         else 
00252         {
00253                 strncpy(b_tag, set, 1); b_tag += 1;
00254         }
00255         
00256         j = 0;
00257         
00258         if(lseek(fd, 0L, SEEK_END) != -1)
00259         {
00260             if(write(fd, b_tag - 128, 128) < 128) j = 1;
00261         }
00262         else j = 1;
00263 
00264         xfree(b_tag_start);
00265         xfree(blank);
00266         xfree(set);
00267         
00268         return j;
00269 }
00270 
00271 static int 
00272 id3v2_add_tag(int fd, id3v2_tag *tag, id3v2_tag *old)
00273 {
00274         unsigned char *btag, *btag_start;
00275         unsigned char flag = 0;
00276         int i, j;
00277         char *b_tag, *b_tag_start, *d;
00278         id3v2_frame_list *frame_list;
00279         id3v2_frame *frame;
00280 
00281         /* at first we are going to write the tags raw data into
00282         the btag byte array */
00283         btag = btag_start = xmallocd0(tag->header->total_tag_size,
00284                             "id3v2_add_tag:btag");
00285         strncpy(btag, "ID3", 3); btag += 3;
00286         *btag = (char)tag->header->version_minor; btag += 1;
00287         *btag = (char)tag->header->version_revision; btag += 1;
00288         flag |= ((tag->header->unsyncronization & 1) << 7);
00289         flag |= ((tag->header->has_extended_header & 1) << 6);
00290         flag |= ((tag->header->is_experimental & 1) << 5);
00291         flag |= ((tag->header->has_footer & 1) << 4);
00292         memcpy(btag, &flag, 1); btag += 1;
00293         
00294         if(old)
00295         {
00296                 i = old->header->total_tag_size - 10;
00297                 if(old->header->has_footer) i -= 10;
00298         }
00299         else
00300         {
00301                 i = tag->header->total_tag_size - 10;
00302                 if(tag->header->has_footer) i -= 10;
00303                 /* add padding to total size we mean to store on disk */
00304                 /* mplib does not use any kind of padding internaly */
00305                 i += 1024;
00306         }
00307         
00308         d = id3_sync32(i); 
00309         btag[0] = d[0];
00310         btag[1] = d[1];
00311         btag[2] = d[2];
00312         btag[3] = d[3];
00313         xfree(d);
00314         btag += 4;
00315         
00316         if(tag->header->has_extended_header)
00317         {
00318                 d = id3_sync32(tag->header->extended_header->size);
00319                 btag[0] = d[0];
00320                 btag[1] = d[1];
00321                 btag[2] = d[2];
00322                 btag[3] = d[3];
00323                 xfree(d);
00324                 btag += 4;
00325                 
00326                 *btag = (char)tag->header->extended_header->no_flag_bytes; btag += 1;
00327                 flag = ((tag->header->extended_header->is_update & 1) << 6);
00328                 flag |= ((tag->header->extended_header->crc_data_present & 1) << 5);
00329                 flag |= ((tag->header->extended_header->restrictions & 1) << 4);
00330                 memcpy(btag, &flag, 1); btag += 1;
00331                 if(tag->header->extended_header->is_update) 
00332                 {
00333                         btag[0] = 0; btag += 1;
00334                 }
00335                 if(tag->header->extended_header->crc_data_present) 
00336                 {
00337                         int length = tag->header->extended_header->crc_data_length ? tag->header->extended_header->crc_data_length : 5;
00338                         *btag = (char)length; btag += 1;
00339                         memcpy(btag, tag->header->extended_header->crc_data, length); btag += 1;
00340                 }
00341                 if(tag->header->extended_header->restrictions) 
00342                 {
00343                         int length = tag->header->extended_header->restrictions_data_length ? tag->header->extended_header->restrictions_data_length : 5;
00344                         *btag = (char)length; btag += 1;
00345                         memcpy(btag, tag->header->extended_header->restrictions_data, length); btag += 1;
00346                 }
00347         }
00348         
00349         frame_list = tag->frame_list;
00350         while(frame_list) {
00351                 int j;
00352                 frame = frame_list->data;
00353                 
00354                 strncpy(btag, frame->frame_id, 4); btag += 4;
00355                 d = id3_sync32(frame->data_size);
00356                 btag[0] = d[0];
00357                 btag[1] = d[1];
00358                 btag[2] = d[2];
00359                 btag[3] = d[3];
00360                 xfree(d);
00361                 btag += 4;
00362                 memcpy(btag, &frame->status_flag, 1); btag += 1;
00363                 memcpy(btag, &frame->format_flag, 1); btag += 1;
00364                 
00365                 memcpy(btag, frame->data, frame->data_size); btag += frame->data_size;
00366                 
00367                 frame_list = frame_list->next;
00368         }
00369         
00370         /* XXX footer not supported yet */
00371         
00372         /* if an old tag was provided it is desired to overwrite it */
00373         /* else this is a brand new tag */
00374         if(old) {  
00375                 FILE *file;
00376                 void *ptr = xmallocd0(old->header->total_tag_size - tag->header->total_tag_size, "id3v2_add_tag:ptr");
00377                 if(!(file = fdopen(fd, "r+b")))
00378                 {
00379                     xfree(ptr);
00380                     goto exit_on_error;
00381                 }
00382                 
00383                 fseek(file, 0, SEEK_SET);
00384                 if(fwrite(btag_start, tag->header->total_tag_size, 1, file) < 1)
00385                 {
00386                     xfree(ptr);
00387                     goto exit_on_error;
00388                 }
00389                 
00390                 /* write padding till end of old tag */
00391                 if(fwrite(ptr, old->header->total_tag_size - tag->header->total_tag_size, 1, file) < 1) {
00392                     xfree(ptr);
00393                     goto exit_on_error;
00394                 }
00395                 
00396                 fflush(file);
00397                 xfree(ptr);
00398                                 
00399         } else {
00400                 FILE *file, *tmp;
00401                 int read;
00402                 void *ptr, *blank;
00403                 unsigned char *c;
00404 
00405                 ptr = xmallocd(4096, "id3v2_add_tag:ptr");
00406                 blank = xmallocd0(1024, "id3v2_add_tag:blank");
00407 
00408                 file = fdopen(fd, "r+b");
00409                 tmp = tmpfile();
00410                 if(!(file && tmp)) 
00411                 {
00412                     fflush(file);
00413                     fclose(tmp);
00414                     xfree(ptr);
00415                     xfree(blank);
00416                     goto exit_on_error;
00417                 }
00418 
00419                 fseek(file, 0, SEEK_SET);
00420                 fseek(tmp, 0, SEEK_SET);
00421 
00422                 /* write tag in tmp file */
00423                 fwrite(btag_start, tag->header->total_tag_size, 1, tmp);
00424 
00425                 /* Write 1024b padding */
00426                 fwrite(blank, 1024, 1, tmp);
00427 
00428                 /* write rest of file */
00429                 while(!feof(file))
00430                 {
00431                         read = fread(ptr, 1, 4096, file);
00432                         if(fwrite(ptr, 1, read, tmp) != read && !feof(file))
00433                         {
00434                             fflush(file);
00435                             fclose(tmp);
00436                             xfree(ptr);
00437                             xfree(blank);
00438                             goto exit_on_error;
00439                         }
00440                 }
00441 
00442                 fflush(tmp);
00443 
00444                 fseek(file, 0, SEEK_SET);
00445                 fseek(tmp, 0, SEEK_SET);
00446                 while(!feof(tmp))
00447                 {
00448                         read = fread(ptr, 1, 4096, tmp);
00449                         if(fwrite(ptr, 1, read, file) != read && !feof(tmp))
00450                         {
00451                             fflush(file);
00452                             fclose(tmp);
00453                             xfree(ptr);
00454                             xfree(blank);
00455                             goto exit_on_error;
00456                         }
00457                 }
00458 
00459                 fflush(file);
00460                 fclose(tmp);
00461 
00462                 xfree(ptr);
00463                 xfree(blank);
00464             }
00465 
00466         xfree(btag_start);
00467         return 0;
00468 
00469  exit_on_error:
00470         xfree(btag_start);
00471         return MP_EERROR;
00472 }
00473 
00474 static int
00475 id3v1_del_tag(int fd) 
00476 {
00477         int nlength;
00478         unsigned char *c;
00479         struct stat fs;
00480         
00481         if(fstat(fd, &fs)) return 1;
00482         
00483         if(fs.st_size < 128) return 1; /* Hardly a valid mpeg file.. */
00484 
00485         c = (char *)xmallocd(3, "id3v1_del_tag:c");
00486         if(lseek(fd, -128L, SEEK_END) == -1) goto exit_on_error;
00487         if(read(fd, c, 3) < 3) goto exit_on_error;
00488         if(strncmp(c, "TAG", 3)) goto exit_on_error;
00489         xfree(c);
00490                 
00491         nlength = fs.st_size - 128;
00492                 
00493         if(ftruncate(fd, nlength)) return 1;  
00494                 
00495         return 0;
00496 
00497  exit_on_error:
00498         xfree(c);
00499         return 1;
00500 }
00501 
00502 
00503 static int
00504 id3v2_del_tag(int fd, id3v2_tag *t)
00505 {
00506         unsigned char *c;
00507         long tag_len, file_len;
00508         FILE *file, *tmp;
00509         int read;
00510         void *ptr;
00511         id3v2_tag *tfound = NULL;;
00512         file_arg arg;
00513         
00514         if(!t)
00515         {
00516             arg.fd = fd;
00517             t = id3v2_get_tag(read_file, lseek_file, &arg);
00518             if(!t) return 0;
00519             else tfound = t;
00520         }
00521         
00522         ptr =  xmallocd(4096, "id3v2_del_tag:ptr");
00523         
00524         tag_len = t->header->total_tag_size;
00525         file_len = lseek(fd, 0, SEEK_END);
00526         if(file_len < 1 || tag_len < 1) goto exit_on_error;
00527         
00528         /* use os system buffering */
00529         file = fdopen(fd, "r+b");
00530         tmp = tmpfile();
00531         if(!(file && tmp)) goto exit_on_error;
00532         
00533         fseek(file, tag_len, SEEK_SET);
00534         fseek(tmp, 0, SEEK_SET);
00535         while(!feof(file))
00536         {
00537                 read = fread(ptr, 1, 4096, file);
00538                 if(fwrite(ptr, 1, read, tmp) != read && !feof(file))
00539                     goto exit_on_error;
00540         }
00541         
00542         fflush(tmp);
00543         
00544         fseek(file, 0, SEEK_SET);
00545         fseek(tmp, 0, SEEK_SET);
00546         while(!feof(tmp))
00547         {
00548                 read = fread(ptr, 1, 4096, tmp);
00549                 if(fwrite(ptr, 1, read, file) != read && !feof(tmp)) 
00550                     goto exit_on_error;
00551         }
00552         
00553         fclose(tmp);
00554         xfree(ptr);
00555         if(tfound) id3v2_free_tag(tfound);
00556         return 0;
00557 
00558  exit_on_error:
00559         fclose(tmp);
00560         xfree(ptr);
00561         if(tfound) id3v2_free_tag(tfound);
00562         return 1;
00563 }
00564 
00565 
00566 static int
00567 id3v1_truncate_tag(id3v1_tag *tag)
00568 {
00569         int notrunc = 0;
00570         int len = 0;
00571         void *ptr;
00572         
00573         if(tag->title && (len = strlen(tag->title)) > 30) 
00574         {
00575                 realloc(tag->title, 31);
00576                 tag->title[30] = 0;
00577         }
00578         
00579         if(tag->artist && (len = strlen(tag->artist)) > 30) 
00580         {
00581                 realloc(tag->artist, 31);
00582                 tag->artist[30] = 0;
00583         }
00584         
00585         if(tag->album && (len = strlen(tag->album)) > 30) 
00586         {
00587                 realloc(tag->album, 31);
00588                 tag->album[30] = 0;
00589         }
00590         
00591         if(tag->year && (len = strlen(tag->year)) > 4) 
00592         {
00593                 realloc(tag->title, 5);
00594                 tag->title[4] = 0;
00595         }
00596         
00597         if(tag->comment) 
00598         {
00599                 int max = (tag->track > 0) ? 28 : 30;
00600                 if((len = strlen(tag->comment)) > max) 
00601                 {
00602                         realloc(tag->comment, max + 1);
00603                         tag->comment[max] = 0;
00604                 }
00605         }
00606         
00607         return notrunc;
00608 }
00609 
00610 static int
00611 id3_is_only_space(char *str, int strlen) 
00612 {
00613         int i = 0;
00614         
00615         while(i < strlen)
00616         {
00617                 if(str[i] != 0x20) return 0;
00618                 i++;
00619         }
00620         
00621         return 1;
00622 }
00623 
00624 static id3v2_tag*
00625 id3v2_get_tag(size_t (*read_func)(void *, size_t, void *),
00626                 off_t (*lseek_func)(off_t, int, void *),
00627                 void *arg)
00628 {
00629         unsigned char *c;
00630         id3v2_header *header;
00631         id3v2_frame_list *frame_list;
00632         id3v2_frame *frame;
00633         id3v2_tag *tag = NULL;
00634         int i;
00635         
00636         if(lseek_func(0L, SEEK_SET, arg) == -1) return NULL;
00637         
00638         c = (unsigned char*)xmallocd0(1024, "id3v2_get_tag:c");
00639         
00640         if(read_func(c, 10, arg) < 10) goto exit_on_error;
00641 
00642         c[10] = 0;
00643         
00644         if(strncmp(c, "ID3", 3)) goto exit_on_error;
00645 
00646         header = XMALLOCD0(id3v2_header, "id3v2_get_tag:header");
00647         header->version_minor = c[3];
00648         header->version_revision = c[4];
00649         header->flags = c[5];
00650         header->unsyncronization = (c[5] & 128) >> 7;
00651         header->has_extended_header = (c[5] & 64) >> 6;
00652         header->is_experimental = (c[5] & 32) >> 5;
00653         header->has_footer = (c[5] & 16) >> 4;
00654         
00655         header->total_tag_size = id3_unsync32(c, 6) + 10;
00656         if(header->has_footer) header->total_tag_size += 10;
00657         
00658         tag = XMALLOCD0(id3v2_tag, "id3v2_get_tag:tag");
00659         
00660         /* check if version is supported */
00661         if(c[3] != 3 && c[3] != 4)
00662         {
00663                 xfree(c);
00664                 tag->header = header;
00665                 tag->frame_list = NULL;
00666                 return tag;
00667         }
00668 
00669         frame_list = XMALLOCD0(id3v2_frame_list, "id3v2_get_tag:frame_list");
00670         frame_list->start = frame_list;
00671         
00672         /* assigning header and frame list to tag */
00673         tag->header = header;
00674         tag->frame_list = frame_list;
00675 
00676         if(header->has_extended_header)
00677         {
00678                 id3v2_extended_header *xt_header = XMALLOCD0(id3v2_extended_header,
00679                                                    "id3v2_get_tag:id3v2_extended_header");
00680                 
00681                 header->extended_header = xt_header;
00682                 
00683                 read_func(c, 4, arg); /* get length of extended header */
00684                 xt_header->size = id3_unsync32(c, 0);
00685                 
00686                 read_func(c, 1, arg); /* get number of flags */
00687                 xt_header->no_flag_bytes = (c[0] > 0) ? c[0] : 1;
00688                 
00689                 read_func(c, xt_header->no_flag_bytes, arg); /* get flag bytes */
00690                 xt_header->is_update = (c[0] & 64) >> 6;
00691                 xt_header->crc_data_present = (c[0] & 32) >> 5;
00692                 xt_header->restrictions = (c[0] & 16) >> 4;
00693                 
00694                 /* Flag data */
00695                 if(xt_header->is_update) read_func(c, 1, arg); /* Data length ind. is 0 -skip */
00696                 if(xt_header->crc_data_present) {
00697                         read_func(c, 1, arg); /* data length - shoud be 5 */
00698                         if(*c != 5) goto exit_on_error; /*  else things might
00699                         break badly */
00700                         xt_header->crc_data_length = *c;
00701                         xt_header->crc_data = xmallocd0(*c, "id3v2_get_tag:xt_header->crc_data");
00702                         read_func(xt_header->crc_data, *c, arg);
00703                 }
00704                 if(xt_header->restrictions) {
00705                         read_func(c, 1, arg); /* data length - shoud be 1 */
00706                         if(*c != 1) goto exit_on_error;
00707                         xt_header->restrictions_data_length = *c;
00708                         xt_header->restrictions_data = xmallocd0(*c,
00709                                                        "id3v2_get_tag:xt_header->restrictions_data");
00710                         read_func(xt_header->restrictions_data, *c, arg);
00711                 }
00712         }
00713 
00714         /* Read frames */
00715         while(lseek_func(0L, SEEK_CUR, arg) < header->total_tag_size)
00716         {
00717                 int hasEnc = 0, hasLang = 0, d;
00718                 unsigned int data_size;
00719                 
00720                 read_func(c, 10, arg); /* Read header */
00721                 
00722                 /* break if padding is reached - this should never happen here.. */
00723                 if(c[0] == 0 && c[1] == 0 && c[2] ==  0 && c[3] == 0) break;
00724                 
00725                 /* Check if possible id is alpha numeric */
00726                 if(!isalnum(c[0]) || !isalnum(c[1]) || !isalnum(c[2]) || !isalnum(c[3])) break;
00727                 
00728                 data_size = id3_unsync32(c, 4);
00729 
00730                 /* Check if data_size hasn't impossible value */
00731                 if (data_size > header->total_tag_size) break;
00732                 
00733                 frame = XMALLOCD(id3v2_frame, "id3v2_get_tag:frame");
00734                 frame->frame_id = xmallocd(4, "id3v2_get_tag:frame->frame_id");
00735                 strncpy(frame->frame_id, c, 4);
00736                 frame->data_size = data_size;
00737                 frame->status_flag = c[8];
00738                 frame->format_flag = c[9];
00739                 
00740                 /* Getting frame content */
00741                 frame->data = xmallocd(frame->data_size, "id3v2_get_tag:frame->data_size");
00742                 read_func(frame->data, frame->data_size, arg);
00743                 
00744                 /* Add frame to list */
00745                 if(frame_list->data)
00746                 {
00747                         frame_list->next = XMALLOCD(id3v2_frame_list, "id3v2_get_tag:frame_list->next");
00748                         frame_list->next->start = frame_list->start;
00749                         frame_list = frame_list->next;
00750                         frame_list->next = NULL;
00751                 }
00752                 frame_list->data = frame;
00753         }
00754         
00755         
00756         xfree(c);
00757         return tag;
00758         
00759         exit_on_error:
00760         
00761         xfree(c);
00762         id3v2_free_tag(tag);
00763         return NULL;
00764 }
00765 
00766 static char **
00767 id3v2_get_names(id3v2_tag *tag)
00768 {
00769         id3v2_frame *frame;
00770         id3v2_frame_list *frame_list;
00771         char **clist;
00772         int i;
00773         
00774         if(!tag->frame_list) return NULL;
00775         
00776         frame_list = tag->frame_list;
00777         
00778         i =  id3_get_no_frames(tag);
00779         clist = xmallocd(sizeof(char*) * i+1, "id3v2_get_names:clist");
00780         clist[i] = 0;
00781         
00782         for(i = 0; frame_list; i++)
00783         {
00784                 if(!frame_list->data) continue;
00785                 frame = frame_list->data;
00786                 
00787                 if(!frame->frame_id) continue;
00788                 clist[i] = xmallocd(5, "id3v2_get_names:clist[i]");
00789                 strncpy(clist[i], frame->frame_id, 4);
00790                 clist[i][4] = 0;
00791                 frame_list = frame_list->next;
00792         }
00793         return clist;
00794 }
00795 
00796 
00797 static id3_content*
00798 id3v1_get_content(id3v1_tag *tag, int field)
00799 {
00800         int i;
00801         char *c;
00802         id3_content *ret;
00803         
00804                 switch(field)
00805                 {
00806                         case MP_ARTIST:
00807                                 if(!tag->artist)
00808                                 {
00809                                         errno = MP_EFNF;
00810                                         return NULL;
00811                                 }
00812                                 return mp_assemble_text_content(tag->artist, ISO_8859_1);                       
00813                         
00814                         case MP_TITLE:
00815                                 if(!tag->title)
00816                                 {
00817                                         errno = MP_EFNF;
00818                                         return NULL;
00819                                 }
00820                                 return mp_assemble_text_content(tag->title, ISO_8859_1);                        
00821                         
00822                         case MP_ALBUM:
00823                                 if(!tag->album)
00824                                 {
00825                                         errno = MP_EFNF;
00826                                         return NULL;
00827                                 }
00828                                 return mp_assemble_text_content(tag->album, ISO_8859_1);
00829                                 
00830                         case MP_YEAR:
00831                                 if(!tag->year)
00832                                 {
00833                                         errno = MP_EFNF;
00834                                         return NULL;
00835                                 }
00836                                 return mp_assemble_text_content(tag->year, ISO_8859_1);
00837                         
00838                         case MP_COMMENT:
00839                                 if(!tag->comment)
00840                                 {
00841                                         errno = MP_EFNF;
00842                                         return NULL;
00843                                 }
00844                                 return mp_assemble_comment_content(tag->comment, NULL, ISO_8859_1, NULL);
00845                                 
00846                         case MP_GENRE:
00847                                 if(tag->genre < GLL)
00848                                 {
00849                                         return mp_assemble_text_content(genre_list[tag->genre], ISO_8859_1);
00850                                 }
00851                                 else 
00852                                 {
00853                                         errno = MP_EFNF;
00854                                         return NULL;
00855                                 }
00856                                         
00857                         case MP_TRACK:
00858                                 if(!tag->track) 
00859                                 {
00860                                         errno = MP_EFNF;
00861                                         return NULL;
00862                                 }
00863                                                 
00864                                 if(tag->track < 10) i = 2;
00865                                 else if(tag->track < 100) i = 3;
00866                                 else i = 4;
00867                                 c = xmallocd(i, "id3v1_get_content:c");
00868                                 snprintf(c, i, "%d", tag->track);
00869                                 ret = mp_assemble_text_content(c, ISO_8859_1);
00870                                 xfree(c);
00871                                 return ret;
00872                                                 
00873                         default:
00874                                 errno = MP_EFNF;
00875                                 return NULL;
00876                 }
00877 }
00878 
00879 static id3_content*
00880 id3v2_get_content_at_pos(id3v2_tag *tag, const char *field, int pos)
00881 {
00882         id3v2_frame_list *frame_list;
00883         id3v2_frame *frame;
00884         int i, found, j;
00885         
00886         if(!tag->frame_list || !field) 
00887         {
00888                 errno = MP_EERROR;
00889                 return NULL;
00890         }
00891         
00892         frame_list = tag->frame_list;
00893         
00894         for(i = 0, found = 0; frame_list; i++, frame_list = frame_list->next)
00895         {
00896                 if(!frame_list->data) continue;
00897                 frame = frame_list->data;
00898                 
00899                 if(!frame->frame_id || !frame->data) continue;
00900                 if(strncmp(frame->frame_id, field, 4)) continue;
00901                 
00902                 if(found == pos)
00903                 {
00904                         id3_content *ret = XMALLOCD0(id3_content,
00905                                            "id3v2_get_content_at_pos:ret");
00906                         if(frame_is_compressed(frame)) ret->compressed = 1;
00907                         else ret->compressed = 0;
00908                         
00909                         if(frame_is_encrypted(frame)) ret->encrypted = 1;
00910                         else ret->encrypted = 0;
00911                         
00912                         ret->length = frame->data_size;
00913                         ret->data = xmallocd(frame->data_size, "id3v2_get_content_at_pos:ret->data");
00914                         ret->data = memcpy(ret->data, frame->data, frame->data_size);
00915                         
00916                         return ret;
00917                 }
00918                 
00919                 found++;
00920         }
00921         
00922         errno = MP_EFNF;
00923         return NULL;
00924 }
00925 
00926 static long
00927 id3_sync(unsigned char* data, long length)
00928 {
00929         int i;
00930         
00931         for(i = 0; i < length - 1; i++)
00932         {
00933                 if(((data[i] & 0xFF) == 0xFF && (data[i+1] & 0xE0) == 0xE0) ||
00934                         (i+2 < length && (data[i] & 0xFF) == 0xFF && data[i+1] == 0 && data[i+2] != 0))
00935                         {
00936                                 
00937                                 realloc(data, length + 1); length++;
00938                                 memmove(data + i+2, data + i+1, length - i - 2);
00939                                 memset(data + i+1, 0, 1);
00940                         } 
00941         }
00942         
00943         return length;
00944 }
00945 
00946 static long
00947 id3_unsync(unsigned char* data, long length)
00948 {
00949         /* TODO */
00950         /* this function is supposed to make syncsafe values normal again */
00951         /* we don't need this yet, because there are no fields supported that will */
00952         /* have the unsynchronization scheme applied */
00953         
00954         
00955 }
00956 
00957 static unsigned int
00958 id3_unsync32(unsigned char* c, int start)
00959 {
00960         return c[start+3] + (c[start+2] << 7) + (c[start+1] << 14) + (c[start] << 21);
00961 }
00962 
00963 int
00964 id3_get_no_frames(id3v2_tag *tag)
00965 {
00966         int i;
00967         id3v2_frame_list *frame_list = tag->frame_list;
00968         
00969         for(i = 0; frame_list; i++)
00970                 frame_list = frame_list->next;  
00971                 
00972                 return i;
00973 }
00974 
00975 static unsigned char *
00976 id3_sync32(unsigned int i)
00977 {
00978         unsigned char *c = (unsigned char *)xmallocd0(4, "id3_sync32:c");
00979         
00980         c[3] = (i & 0x7f);
00981         c[2] = ((i & 0x80) >> 7) | (((i & 0x7f00) >> 8) << 1);
00982         c[1] = ((i & 0x8000) >> 15) | (((i & 0x7f0000) >> 16) << 1);
00983         c[0] = ((i & 0x800000) >> 23) | (((i & 0x7f000000) >> 24) << 1);
00984         
00985         return c;
00986 }
00987 
00988 id3v2_frame* 
00989 id3_lookup_frame(id3v2_frame_list *list, const char *field, const int pos)
00990 {
00991         int cur = 0;
00992         
00993         if(!list || !field) return NULL;
00994         
00995         do
00996         {
00997                 if(!strcmp(list->data->frame_id, field))
00998                 {
00999                         if(cur == pos) return list->data;
01000                         cur++;
01001                 }
01002         } while((list = list->next));
01003         
01004         return NULL;
01005 }
01006 
01007 int 
01008 id3_remove_frame(id3v2_frame_list *list, id3v2_frame *frame)
01009 {
01010         if(!list || !frame) return MP_EERROR;
01011         
01012         /* Frame is first element in list */
01013         if(list->data == frame)
01014         {
01015                 xfree(list->data);
01016                 list->next->start = list->next;
01017                 xfree(list);
01018                 return 0;
01019         }
01020         
01021         /* Iterate through others */
01022         do
01023         {
01024                 if(list->next->data == frame)
01025                 {
01026                         list->next = list->next->next;
01027                         xfree(frame);
01028                         return 0;
01029                 }
01030         } while((list = list->next));
01031         
01032         return 1;
01033 }
01034 
01035 
01036 int 
01037 id3_add_frame(id3v2_frame_list *list, char *field, char *new_value, int len)
01038 {
01039         id3v2_frame *frame;
01040         char *new_valuecp;
01041         long len_sync;
01042         
01043         if(!list || !new_value || !field || strlen(field) != 4) return MP_EERROR;
01044         
01045         // make a copy of the given value to include it in the frame
01046         new_valuecp = xmallocd(len,
01047                       "id3_add_frame:new_valuecp");
01048         memcpy(new_valuecp, new_value, len);
01049         new_value = new_valuecp;
01050 
01051         /* make sync safe */
01052         len_sync = id3_sync(new_value, len);
01053         
01054         frame = XMALLOCD(id3v2_frame, "id3_add_frame:frame");
01055         frame->frame_id = xmallocd(4, "id3_add_frame:frame->frame_id");
01056         strncpy(frame->frame_id, field, 4);
01057         frame->data = new_value;
01058         frame->status_flag = 0;
01059         
01060         if(len != len_sync) frame->format_flag = 64;
01061         else frame->format_flag = 0;
01062         frame->data_size = len_sync;
01063         
01064         /* Empty list */
01065         if(!list->data)
01066         {
01067                 list->data = frame;
01068                 return 0;
01069         }
01070         
01071         /* iterate to last element */
01072         while(list->next) list = list->next;
01073         
01074         list->next = XMALLOCD(id3v2_frame_list, "id3_add_frame:list->next");
01075         list->next->start = list->start;
01076         list = list->next;
01077         list->next = NULL;
01078         list->data = frame;
01079         
01080         return 0;
01081 }
01082 
01083 
01084 #define BUF_SIZE 4096
01085 
01086 static int
01087 id3_lseek_syncword(int fd)
01088 {
01089         unsigned char *data = (unsigned char*) xmallocd(BUF_SIZE, "id3_lseek_syncword:data");
01090         int ret;
01091 
01092         // Reset the reading offset of the fd
01093         lseek(fd, SEEK_SET, 0);
01094 
01095         if(read(fd, data, BUF_SIZE) < 1)
01096         {
01097                 xfree(data);
01098                 return 0; /* return false on EOF */
01099         }
01100         
01101         ret = id3_lseek_syncword_r(fd, data, 0);
01102         xfree(data);
01103         return ret;
01104 }
01105 
01106 static int
01107 id3_lseek_syncword_r(int fd, unsigned char *data, int checked)
01108 {
01109         unsigned char lastchar;
01110         int i;
01111         
01112         for(i = 0; i + 1 < BUF_SIZE; i++)
01113         {
01114                 if(((data[i] & 0xFF)== 0xFF) && (((data[i+1] & 0xF0) == 0xF0) || ((data[i+1] == 0xE0) & 0xE0)))
01115                 {
01116                         lseek(fd, checked + i, SEEK_SET);
01117                         return 0;
01118                 }
01119         }
01120         
01121         lastchar = data[BUF_SIZE - 1];
01122         
01123         if(read(fd, data, BUF_SIZE) < 1) return 0; /* return false on EOF */
01124         
01125         if(((lastchar & 0xFF)== 0xFF) && ((data[0] & 0xE0)== 0xE0))
01126         {
01127                 lseek(fd, checked + BUF_SIZE - 1, SEEK_SET);
01128                 return 0;
01129         }
01130         
01131         return id3_lseek_syncword_r(fd, data, checked + BUF_SIZE);
01132 }
01133 
01134 static id3_tag* 
01135 id3v1_alloc_tag(void)
01136 {
01137         id3_tag *tag = XMALLOCD(id3_tag, "id3v1_alloc_tag:tag");
01138         id3v1_tag *v1 = XMALLOCD0(id3v1_tag, "id3v1_alloc_tag:v1");
01139         
01140         tag->tag = v1;
01141         tag->version = 1;
01142         
01143         v1->genre = 0xFF;
01144         
01145         return tag;
01146 }
01147 
01148 static id3_tag* 
01149 id3v2_alloc_tag(void)
01150 {
01151         id3_tag *tag = XMALLOCD(id3_tag, "id3v2_alloc_tag:tag");
01152         id3v2_tag *v2 = XMALLOCD0(id3v2_tag, "id3v2_alloc_tag:v2");
01153         
01154         tag->tag = v2;
01155         tag->version = 2;
01156         
01157         return tag;
01158 }
01159 
01160 static void
01161 id3v1_free_tag(id3v1_tag* v1)
01162 {
01163         xfree(v1->artist);
01164         xfree(v1->album);
01165         xfree(v1->title);
01166         xfree(v1->year);
01167         xfree(v1->comment);
01168         xfree(v1);
01169 }
01170 
01171 static void
01172 id3v2_free_tag(id3v2_tag* v2)
01173 {
01174         id3v2_frame_list* doomed;
01175         id3v2_frame *frame;
01176 
01177         if(!v2) return;
01178         
01179         xfree(v2->header->extended_header);
01180         xfree(v2->header);
01181         
01182         if(!v2->frame_list) 
01183         {
01184                 xfree(v2);
01185                 return;
01186         }
01187         
01188         /* Freeing frames */
01189         do
01190         {
01191                 frame = (id3v2_frame*)v2->frame_list->data;
01192                 if(frame)
01193                 {
01194                         if(frame->frame_id) xfree(frame->frame_id);
01195                         if(frame->data) xfree(frame->data);
01196                 }
01197                 xfree(v2->frame_list->data);
01198 
01199                 doomed = v2->frame_list->next;
01200                 xfree(v2->frame_list);
01201                 
01202         } while((v2->frame_list = doomed));
01203         
01204         xfree(v2);
01205 }
01206 
01207 size_t read_mem(void *dest, size_t nbytes, void *varg) {
01208         size_t count;
01209         mem_arg *arg = (mem_arg *)varg;
01210 
01211         if (arg->act - arg->buf + nbytes <= arg->size) {
01212                 memcpy(dest, arg->act, nbytes);
01213                 arg->act += nbytes;
01214 
01215                 return nbytes;
01216         } else {
01217                 count = arg->size - (arg->act - arg->buf);
01218                 memcpy(dest, arg->act, count);
01219                 arg->act += count;
01220 
01221                 return count;
01222         }
01223 }
01224 
01225 size_t read_file(void *dest, size_t nbytes, void *varg) {
01226         file_arg *arg = (file_arg *)varg;
01227 
01228         return read(arg->fd, dest, nbytes);
01229 }
01230 
01231 off_t lseek_mem(off_t offset, int whence, void *varg) {
01232         mem_arg *arg = (mem_arg *)varg;
01233         off_t retval;
01234         
01235         if (!arg->buf) {
01236                 errno = EBADF;
01237                 return (off_t)-1;
01238         }
01239 
01240         switch (whence) {
01241                 case SEEK_SET:
01242                         if (offset >= 0)
01243                                 arg->act = arg->buf + offset;
01244                         else goto exit_on_error;
01245                         break;
01246                 case SEEK_CUR:
01247                         if (arg->act + offset >= arg->buf)
01248                                 arg->act += offset;
01249                         else goto exit_on_error;
01250                         break;
01251                 case SEEK_END:
01252                         if (arg->buf + arg->size + offset >= arg->buf)
01253                                 arg->act = arg->buf + arg->size + offset;
01254                         else goto exit_on_error;
01255                         break;
01256                 default:
01257                         goto exit_on_error;
01258         }
01259 
01260         return (arg->act - arg->buf);
01261 
01262 exit_on_error:
01263         errno = EINVAL;
01264         return (off_t)-1;
01265 }
01266 
01267 off_t lseek_file(off_t offset, int whence, void *varg) {
01268         file_arg *arg = (file_arg *)varg;
01269 
01270         return lseek(arg->fd, offset, whence);
01271 }
01272