1 /* discdb.c
2  *
3  * Based on code from libcdaudio 0.5.0 (Copyright (C)1998 Tony Arcieri)
4  *
5  * All changes Copyright (c) 1998-2004  Mike Oliphant <grip@nostatic.org>
6  *
7  *   http://www.nostatic.org/grip
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  *
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #if defined(__sun__)
30 #include <strings.h>
31 #endif
32 #include <ctype.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <string.h>
36 #include <pwd.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <curl/curl.h>
40 #include <curl/easy.h>
41 #include "cddev.h"
42 #include "discdb.h"
43 #include "grip_id3.h"
44 #include "common.h"
45 #include "config.h"
46 
47 extern char *Program;
48 static char *StrConvertEncoding(char *str,const char *from,
49                                 const char *to,int max_len);
50 gboolean DiscDBUTF8Validate(const DiscInfo *disc,const DiscData *data);
51 static void DiscDBConvertEncoding(DiscInfo *disc,DiscData *data,
52                                   const char *from,const char *to);
53 static int DiscDBSum(int val);
54 static char *DiscDBReadLine(char **dataptr);
55 static GString *DiscDBMakeURI(DiscDBServer *server,DiscDBHello *hello,
56 			      char *cmd);
57 static char *DiscDBMakeRequest(DiscDBServer *server,DiscDBHello *hello,
58 			       char *cmd);
59 static void DiscDBProcessLine(char *inbuffer,DiscData *data,
60 			    int numtracks);
61 static void DiscDBWriteLine(char *header,int num,char *data,FILE *outfile,
62                             char *encoding);
63 
64 static char *discdb_genres[]={"unknown","blues","classical","country",
65 			    "data","folk","jazz","misc","newage",
66 			    "reggae","rock","soundtrack"};
67 
68 /* DiscDB sum function */
69 
DiscDBSum(int val)70 static int DiscDBSum(int val)
71 {
72   char *bufptr, buf[16];
73   int ret = 0;
74 
75   g_snprintf(buf,16,"%lu",(unsigned long int)val);
76 
77   for(bufptr = buf; *bufptr != '\0'; bufptr++)
78      ret += (*bufptr - '0');
79 
80    return ret;
81 }
82 
83 /* Produce DiscDB ID for CD currently in CD-ROM */
84 
DiscDBDiscid(DiscInfo * disc)85 unsigned int DiscDBDiscid(DiscInfo *disc)
86 {
87   int index, tracksum = 0, discid;
88 
89   if(!disc->have_info) CDStat(disc,TRUE);
90 
91   for(index = 0; index < disc->num_tracks; index++)
92     tracksum += DiscDBSum(disc->track[index].start_pos.mins * 60 +
93 			disc->track[index].start_pos.secs);
94 
95   discid = (disc->length.mins * 60 + disc->length.secs) -
96     (disc->track[0].start_pos.mins * 60 + disc->track[0].start_pos.secs);
97 
98   return (tracksum % 0xFF) << 24 | discid << 8 | disc->num_tracks;
99 }
100 
101 /* Convert numerical genre to text */
102 
DiscDBGenre(int genre)103 char *DiscDBGenre(int genre)
104 {
105   if(genre>11) return("unknown");
106 
107   return discdb_genres[genre];
108 }
109 
110 /* Convert genre from text form into an integer value */
111 
DiscDBGenreValue(char * genre)112 int DiscDBGenreValue(char *genre)
113 {
114   int pos;
115 
116   for(pos=0;pos<12;pos++)
117     if(!strcmp(genre,discdb_genres[pos])) return pos;
118 
119   return 0;
120 }
121 
122 /* Read a single line from the buffer and move the pointer along */
123 
DiscDBReadLine(char ** dataptr)124 static char *DiscDBReadLine(char **dataptr)
125 {
126   char *data=*dataptr;
127   char *pos;
128 
129   if(!data || !*data || *data=='.') {
130     *dataptr=NULL;
131 
132     return NULL;
133   }
134 
135   for(pos=data;*pos;pos++) {
136     if(*pos=='\n') {
137       *pos='\0';
138 
139       Debug("[%s]\n",data);
140 
141       *dataptr=pos+1;
142 
143       return data;
144     }
145   }
146 
147   Debug("[%s]\n",data);
148 
149   *dataptr=NULL;
150 
151   return data;
152 }
153 
DiscDBMakeURI(DiscDBServer * server,DiscDBHello * hello,char * cmd)154 static GString *DiscDBMakeURI(DiscDBServer *server,DiscDBHello *hello,
155 			      char *cmd)
156 {
157   GString *uri;
158 
159   uri=g_string_new(NULL);
160 
161   g_string_printf(uri,"http://%s/%s?cmd=%s&hello=private+free.the.cddb+%s+%s"
162 		   "&proto=%d",
163 		   server->name,server->cgi_prog,cmd,
164 		   hello->hello_program,hello->hello_version,
165 		   hello->proto_version);
166 
167   return uri;
168 }
169 
DiscDBMakeRequest(DiscDBServer * server,DiscDBHello * hello,char * cmd)170 static char *DiscDBMakeRequest(DiscDBServer *server,DiscDBHello *hello,
171 			       char *cmd)
172 {
173   GString *uri;
174   GString *proxy=NULL;
175   GString *user=NULL;
176   char user_agent[256];
177   struct curl_slist *headers=NULL;
178   FILE *outfile;
179   char *data=NULL;
180   int success;
181   CURL *curl_handle;
182   long filesize;
183 
184   curl_global_init(CURL_GLOBAL_ALL);
185   curl_handle=curl_easy_init();
186 
187   if(curl_handle) {
188     if(server->use_proxy) {
189       proxy=g_string_new(NULL);
190 
191       g_string_printf(proxy,"%s:%d",server->proxy->name,
192                        server->proxy->port);
193 
194       curl_easy_setopt(curl_handle,CURLOPT_PROXY,proxy->str);
195 
196       if(*server->proxy->username) {
197 
198         user=g_string_new(NULL);
199 
200         g_string_printf(user,"%s:%s",server->proxy->username,
201                          server->proxy->pswd);
202 
203         curl_easy_setopt(curl_handle,CURLOPT_PROXYUSERPWD,user->str);
204       }
205     }
206 
207     uri=DiscDBMakeURI(server,hello,cmd);
208 
209     Debug(_("URI is %s\n"),uri->str);
210 
211     curl_easy_setopt(curl_handle,CURLOPT_URL,uri->str);
212 
213     g_snprintf(user_agent,256,"User-Agent: %s %s",
214                hello->hello_program,hello->hello_version);
215 
216     headers=curl_slist_append(headers,user_agent);
217 
218     curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER,headers);
219 
220     outfile=tmpfile();
221 
222     if(outfile) {
223       curl_easy_setopt(curl_handle,CURLOPT_FILE,outfile);
224 
225       success=curl_easy_perform(curl_handle);
226 
227       if(success==0) {
228         filesize=ftell(outfile);
229 
230         rewind(outfile);
231 
232         data=(char *)malloc(filesize+1);
233 
234         if(data) {
235           if (fread(data,filesize,1,outfile) > 0)
236             data[filesize]='\0';
237         }
238       }
239 
240       fclose(outfile);
241     }
242 
243     curl_slist_free_all(headers);
244 
245     curl_easy_cleanup(curl_handle);
246 
247     g_string_free(uri,TRUE);
248 
249     if(server->use_proxy) {
250       if (proxy)
251         g_string_free(proxy,TRUE);
252 
253       if(*server->proxy->username) {
254         if (user)
255           g_string_free(user,TRUE);
256       }
257     }
258   }
259 
260   curl_global_cleanup();
261 
262   return data;
263 }
264 
265 
266 /* Query the DiscDB for the CD currently in the CD-ROM */
267 
DiscDBDoQuery(DiscInfo * disc,DiscDBServer * server,DiscDBHello * hello,DiscDBQuery * query)268 gboolean DiscDBDoQuery(DiscInfo *disc,DiscDBServer *server,
269 		       DiscDBHello *hello,DiscDBQuery *query)
270 {
271   int index;
272   GString *cmd;
273   char *result,*inbuffer;
274   char *dataptr;
275 
276   query->query_matches=0;
277 
278   if(!disc->have_info) CDStat(disc,TRUE);
279 
280   cmd=g_string_new(NULL);
281 
282   g_string_printf(cmd,"cddb+query+%08x+%d",DiscDBDiscid(disc),
283 		    disc->num_tracks);
284 
285   for(index=0;index<disc->num_tracks;index++)
286     g_string_append_printf(cmd,"+%d",disc->track[index].start_frame);
287 
288   g_string_append_printf(cmd,"+%d",disc->length.mins*60 + disc->length.secs);
289 
290   Debug(_("Query is [%s]\n"),cmd->str);
291 
292   result=DiscDBMakeRequest(server,hello,cmd->str);
293 
294   g_string_free(cmd,TRUE);
295 
296   if(!result) {
297     return FALSE;
298   }
299 
300   dataptr=result;
301 
302   inbuffer=DiscDBReadLine(&dataptr);
303 
304   switch(strtol(strtok(inbuffer," "),NULL,10)) {
305     /* 200 - exact match */
306   case 200:
307     query->query_match=MATCH_EXACT;
308     query->query_matches=1;
309 
310     query->query_list[0].list_genre=
311       DiscDBGenreValue(g_strstrip(strtok(NULL," ")));
312 
313     sscanf(g_strstrip(strtok(NULL," ")),"%xd",
314 	   &query->query_list[0].list_id);
315 
316     DiscDBParseTitle(g_strstrip(strtok(NULL,"")),
317 		     query->query_list[0].list_title,
318 		     query->query_list[0].list_artist,"/");
319 
320     break;
321     /* 210 - multiple exact matches */
322   case 210:
323     query->query_match=MATCH_EXACT;
324     query->query_matches=0;
325 
326 
327     while(query->query_matches < MAX_INEXACT_MATCHES &&
328           (inbuffer=DiscDBReadLine(&dataptr))) {
329       query->query_list[query->query_matches].list_genre=
330 	DiscDBGenreValue(g_strstrip(strtok(inbuffer," ")));
331 
332       sscanf(g_strstrip(strtok(NULL," ")),"%xd",
333 	     &query->query_list[query->query_matches].list_id);
334 
335       DiscDBParseTitle(g_strstrip(strtok(NULL,"")),
336 		       query->query_list[query->query_matches].list_title,
337 		       query->query_list[query->query_matches].list_artist,
338 		       "/");
339 
340       query->query_matches++;
341     }
342    break;
343     /* 211 - inexact match */
344   case 211:
345     query->query_match=MATCH_INEXACT;
346     query->query_matches=0;
347 
348     while(query->query_matches < MAX_INEXACT_MATCHES &&
349           (inbuffer=DiscDBReadLine(&dataptr))) {
350       query->query_list[query->query_matches].list_genre=
351 	DiscDBGenreValue(g_strstrip(strtok(inbuffer," ")));
352 
353       sscanf(g_strstrip(strtok(NULL," ")),"%xd",
354 	     &query->query_list[query->query_matches].list_id);
355 
356       DiscDBParseTitle(g_strstrip(strtok(NULL,"")),
357 		       query->query_list[query->query_matches].list_title,
358 		       query->query_list[query->query_matches].list_artist,
359 		       "/");
360 
361       query->query_matches++;
362     }
363 
364     break;
365     /* No match */
366   default:
367     query->query_match=MATCH_NOMATCH;
368 
369     free(result);
370 
371     return FALSE;
372   }
373 
374   free(result);
375 
376   return TRUE;
377 }
378 
379 /* Split string into title/artist */
380 
DiscDBParseTitle(char * buf,char * title,char * artist,char * sep)381 void DiscDBParseTitle(char *buf,char *title,char *artist,char *sep)
382 {
383   char *tmp;
384 
385   tmp=strtok(buf,sep);
386 
387   if(!tmp) return;
388 
389   g_snprintf(artist,256,"%s",g_strstrip(tmp));
390 
391   tmp=strtok(NULL,"");
392 
393   if(tmp)
394     g_snprintf(title,256,"%s",g_strstrip(tmp));
395   else strcpy(title,artist);
396 }
397 
398 /* Process a line of input data */
399 
DiscDBProcessLine(char * inbuffer,DiscData * data,int numtracks)400 static void DiscDBProcessLine(char *inbuffer,DiscData *data,
401                               int numtracks)
402 {
403   int track;
404   int len=0;
405   char *st;
406 
407   strtok(inbuffer,"\n\r");
408 
409   if(!strncasecmp(inbuffer,"# Revision: ",12)) {
410     data->revision=atoi(inbuffer+12);
411   }
412   else if(!strncasecmp(inbuffer,"DTITLE",6)) {
413     len=strlen(data->data_title);
414 
415     g_snprintf(data->data_title+len,256-len,"%s",inbuffer+7);
416   }
417   else if(!strncasecmp(inbuffer,"DYEAR",5)) {
418     strtok(inbuffer,"=");
419 
420     st = strtok(NULL, "");
421     if(st == NULL)
422         return;
423 
424     data->data_year=atoi(g_strstrip(st));
425   }
426   else if(!strncasecmp(inbuffer,"DGENRE",6)) {
427     strtok(inbuffer,"=");
428 
429     st = strtok(NULL, "");
430     if(st == NULL)
431         return;
432 
433     st=g_strstrip(st);
434 
435     if(*st) {
436       data->data_genre=DiscDBGenreValue(st);
437       data->data_id3genre=ID3GenreValue(st);
438     }
439   }
440   else if(!strncasecmp(inbuffer,"DID3",4)) {
441     strtok(inbuffer,"=");
442 
443     st = strtok(NULL, "");
444     if(st == NULL)
445         return;
446 
447     data->data_id3genre=atoi(g_strstrip(st));
448   }
449   else if(!strncasecmp(inbuffer,"TTITLE",6)) {
450     track=atoi(strtok(inbuffer+6,"="));
451 
452     if(track<numtracks)
453       len=strlen(data->data_track[track].track_name);
454 
455     st = strtok(NULL, "");
456     if(st == NULL)
457         return;
458 
459     g_snprintf(data->data_track[track].track_name+len,256-len,"%s",
460 	    st);
461   }
462   else if(!strncasecmp(inbuffer,"TARTIST",7)) {
463     data->data_multi_artist=TRUE;
464 
465     track=atoi(strtok(inbuffer+7,"="));
466 
467     if(track<numtracks)
468       len=strlen(data->data_track[track].track_artist);
469 
470     st = strtok(NULL, "");
471     if(st == NULL)
472         return;
473 
474     g_snprintf(data->data_track[track].track_artist+len,256-len,"%s",
475 	    st);
476   }
477   else if(!strncasecmp(inbuffer,"EXTD",4)) {
478     len=strlen(data->data_extended);
479 
480     g_snprintf(data->data_extended+len,4096-len,"%s",inbuffer+5);
481   }
482   else if(!strncasecmp(inbuffer,"EXTT",4)) {
483     track=atoi(strtok(inbuffer+4,"="));
484 
485     if(track<numtracks)
486       len=strlen(data->data_track[track].track_extended);
487 
488     st = strtok(NULL, "");
489     if(st == NULL)
490         return;
491 
492     g_snprintf(data->data_track[track].track_extended+len,4096-len,"%s",
493 	    st);
494   }
495   else if(!strncasecmp(inbuffer,"PLAYORDER",5)) {
496     len=strlen(data->data_playlist);
497 
498     g_snprintf(data->data_playlist+len,256-len,"%s",inbuffer+10);
499   }
500 }
501 
StrConvertEncoding(char * str,const char * from,const char * to,int max_len)502 static char *StrConvertEncoding(char *str,const char *from,
503                                 const char *to,int max_len)
504 {
505   char *conv_str;
506   gsize rb,wb;
507 
508   if(!str) return NULL;
509 
510   conv_str=g_convert_with_fallback(str,strlen(str),to,from,NULL,&rb,&wb,NULL);
511 
512   if(!conv_str) return str;
513 
514   g_snprintf(str,max_len,"%s",conv_str);
515 
516   g_free(conv_str);
517 
518   return str;
519 }
520 
DiscDBUTF8Validate(const DiscInfo * disc,const DiscData * data)521 gboolean DiscDBUTF8Validate(const DiscInfo *disc,const DiscData *data)
522 {
523   int track;
524 
525   if(data->data_title && !g_utf8_validate(data->data_title,-1,NULL))
526     return FALSE;
527   if(data->data_artist && !g_utf8_validate(data->data_artist,-1,NULL))
528     return FALSE;
529   if(data->data_extended && !g_utf8_validate(data->data_extended,-1,NULL))
530     return FALSE;
531 
532   for(track=0;track<disc->num_tracks;track++) {
533   if(data->data_track[track].track_name
534      && !g_utf8_validate(data->data_track[track].track_name,-1,NULL))
535     return FALSE;
536   if(data->data_track[track].track_artist
537      && !g_utf8_validate(data->data_track[track].track_artist,-1,NULL))
538     return FALSE;
539   if(data->data_track[track].track_extended
540      && !g_utf8_validate(data->data_track[track].track_extended,-1,NULL))
541     return FALSE;
542   }
543   return TRUE;
544 }
545 
546 
DiscDBConvertEncoding(DiscInfo * disc,DiscData * data,const char * from,const char * to)547 static void DiscDBConvertEncoding(DiscInfo *disc,DiscData *data,
548                                   const char *from,const char *to)
549 {
550   int track;
551 
552   StrConvertEncoding(data->data_title,from,to,256);
553   StrConvertEncoding(data->data_artist,from,to,256);
554   StrConvertEncoding(data->data_extended,from,to,4096);
555 
556   for(track=0;track<disc->num_tracks;track++) {
557     StrConvertEncoding(data->data_track[track].track_name,from,to,256);
558     StrConvertEncoding(data->data_track[track].track_artist,from,to,256);
559     StrConvertEncoding(data->data_track[track].track_extended,from,to,4096);
560   }
561 }
562 
563 /* Read the actual DiscDB entry */
564 
DiscDBRead(DiscInfo * disc,DiscDBServer * server,DiscDBHello * hello,DiscDBEntry * entry,DiscData * data,char * encoding)565 gboolean DiscDBRead(DiscInfo *disc,DiscDBServer *server,
566 		    DiscDBHello *hello,DiscDBEntry *entry,
567 		    DiscData *data,char *encoding)
568 {
569   int index;
570   GString *cmd;
571   char *result,*inbuffer,*dataptr;
572 
573   if(!disc->have_info) CDStat(disc,TRUE);
574 
575   data->data_genre=entry->entry_genre;
576   data->data_id=DiscDBDiscid(disc);
577   *(data->data_extended)='\0';
578   *(data->data_title)='\0';
579   *(data->data_artist)='\0';
580   *(data->data_playlist)='\0';
581   data->data_multi_artist=FALSE;
582   data->data_year=0;
583   data->data_id3genre=-1;
584   data->revision=-1;
585 
586   for(index=0;index<MAX_TRACKS;index++) {
587     *(data->data_track[index].track_name)='\0';
588     *(data->data_track[index].track_artist)='\0';
589     *(data->data_track[index].track_extended)='\0';
590   }
591 
592   cmd=g_string_new(NULL);
593 
594   g_string_printf(cmd,"cddb+read+%s+%08x",DiscDBGenre(entry->entry_genre),
595 		   entry->entry_id);
596 
597   result=DiscDBMakeRequest(server,hello,cmd->str);
598 
599   g_string_free(cmd,TRUE);
600 
601   if(!result) {
602     return FALSE;
603   }
604 
605   dataptr=result;
606 
607   inbuffer=DiscDBReadLine(&dataptr);
608 
609   while((inbuffer=DiscDBReadLine(&dataptr)))
610     DiscDBProcessLine(inbuffer,data,disc->num_tracks);
611 
612   /* Both disc title and artist have been stuffed in the title field, so the
613      need to be separated */
614 
615   DiscDBParseTitle(data->data_title,data->data_title,data->data_artist,"/");
616 
617   free(result);
618 
619   /* Don't allow the genre to be overwritten */
620   data->data_genre=entry->entry_genre;
621 
622   if(strcasecmp(encoding,"utf-8")) {
623     DiscDBConvertEncoding(disc,data,encoding,"utf-8");
624   }
625 
626   return TRUE;
627 }
628 
629 /* See if a disc is in the local database */
630 
DiscDBStatDiscData(DiscInfo * disc)631 gboolean DiscDBStatDiscData(DiscInfo *disc)
632 {
633   int index,id;
634   struct stat st;
635   char root_dir[256],file[256];
636 
637   if(!disc->have_info) CDStat(disc,TRUE);
638 
639   id=DiscDBDiscid(disc);
640 
641   g_snprintf(root_dir, 256, "%s/.cddb", g_get_home_dir());
642 
643   if(stat(root_dir, &st) < 0)
644     return FALSE;
645   else {
646     if(!S_ISDIR(st.st_mode))
647       return FALSE;
648   }
649 
650   g_snprintf(file,256,"%s/%08x",root_dir,id);
651   if(stat(file,&st)==0) return TRUE;
652 
653   for(index=0;index<12;index++) {
654     g_snprintf(file,256,"%s/%s/%08x",root_dir,DiscDBGenre(index),id);
655 
656     if(stat(file,&st) == 0)
657       return TRUE;
658   }
659 
660   return FALSE;
661 }
662 
663 /* Read from the local database */
664 
DiscDBReadDiscData(DiscInfo * disc,DiscData * ddata,const char * encoding)665 int DiscDBReadDiscData(DiscInfo *disc,DiscData *ddata, const char *encoding)
666 {
667   FILE *discdb_data=NULL;
668   int index,genre;
669   char root_dir[256],file[256],inbuf[512];
670   struct stat st;
671 
672   g_snprintf(root_dir, 256, "%s/.cddb", g_get_home_dir());
673 
674   if(stat(root_dir, &st) < 0) {
675     return -1;
676   } else {
677     if(!S_ISDIR(st.st_mode)) {
678       errno = ENOTDIR;
679       return -1;
680     }
681   }
682 
683   if(!disc->have_info) CDStat(disc,TRUE);
684 
685   ddata->data_id=DiscDBDiscid(disc);
686   *(ddata->data_extended)='\0';
687   *(ddata->data_title)='\0';
688   *(ddata->data_artist)='\0';
689   *(ddata->data_playlist)='\0';
690   ddata->data_multi_artist=FALSE;
691   ddata->data_year=0;
692   ddata->data_genre=7;
693   ddata->data_id3genre=-1;
694   ddata->revision=-1;
695 
696   for(index=0;index<MAX_TRACKS;index++) {
697     *(ddata->data_track[index].track_name)='\0';
698     *(ddata->data_track[index].track_artist)='\0';
699     *(ddata->data_track[index].track_extended)='\0';
700   }
701 
702   g_snprintf(file,256,"%s/%08x",root_dir,ddata->data_id);
703   if(stat(file,&st)==0) {
704     discdb_data=fopen(file, "r");
705   }
706   else {
707     for(genre=0;genre<12;genre++) {
708       g_snprintf(file,256,"%s/%s/%08x",root_dir,DiscDBGenre(genre),
709 	       ddata->data_id);
710 
711       if(stat(file,&st)==0) {
712 	discdb_data=fopen(file, "r");
713 
714 	ddata->data_genre=genre;
715 	break;
716       }
717     }
718 
719     if(genre==12) return -1;
720   }
721 
722   while(fgets(inbuf,512,discdb_data))
723     DiscDBProcessLine(inbuf,ddata,disc->num_tracks);
724 
725   /* Both disc title and artist have been stuffed in the title field, so the
726      need to be separated */
727 
728   DiscDBParseTitle(ddata->data_title,ddata->data_title,ddata->data_artist,"/");
729 
730   if(!DiscDBUTF8Validate(disc,ddata)) {
731     DiscDBConvertEncoding(disc,ddata,strcasecmp(encoding,"UTF-8")?
732 				     encoding:"ISO-8859-1","UTF-8");
733   }
734 
735   fclose(discdb_data);
736 
737   return 0;
738 }
739 
DiscDBWriteLine(char * header,int num,char * data,FILE * outfile,char * encoding)740 static void DiscDBWriteLine(char *header,int num,char *data,FILE *outfile,
741                             char *encoding)
742 {
743   char *offset, *next, *chunk;
744 
745   if(strcasecmp(encoding,"utf-8")) {
746     StrConvertEncoding(data,"utf-8",encoding,512);
747   }
748 
749   offset=data;
750 
751   do {
752     for(next=offset; next-offset<65&&*next; ) {
753       if (*next=='\\'&&*(next+1)) {
754 	next+=2;
755       }
756       else if(!strcasecmp(encoding,"utf-8")) {
757 	next=g_utf8_find_next_char(next,NULL);
758       }
759       else {
760 	next++;
761       }
762     }
763     chunk=g_strndup(offset,(gsize)(next-offset));
764     if(num==-1)
765       fprintf(outfile,"%s=%s\n",header,chunk);
766     else
767       fprintf(outfile,"%s%d=%s\n",header,num,chunk);
768     g_free(chunk);
769     offset=next;
770   } while (*offset);
771 }
772 
773 
774 /* Write to the local cache */
775 
DiscDBWriteDiscData(DiscInfo * disc,DiscData * ddata,FILE * outfile,gboolean gripext,gboolean freedbext,char * encoding)776 int DiscDBWriteDiscData(DiscInfo *disc,DiscData *ddata,FILE *outfile,
777                         gboolean gripext,gboolean freedbext,char *encoding)
778 {
779   FILE *discdb_data;
780   int track;
781   char root_dir[256],file[256],tmp[512];
782   struct stat st;
783 
784   if(!disc->have_info) CDStat(disc,TRUE);
785 
786   if(!outfile) {
787     g_snprintf(root_dir, 256, "%s/.cddb", g_get_home_dir());
788     g_snprintf(file,256,"%s/%08x",root_dir,ddata->data_id);
789 
790     if(stat(root_dir,&st)<0) {
791       if(errno != ENOENT) {
792 	Debug(_("Stat error %d on %s\n"),errno,root_dir);
793 	return -1;
794       }
795       else {
796 	Debug(_("Creating directory %s\n"),root_dir);
797 	mkdir(root_dir,0777);
798       }
799     } else {
800       if(!S_ISDIR(st.st_mode)) {
801 	Debug(_("Error: %s exists, but is a file\n"),root_dir);
802 	errno=ENOTDIR;
803 	return -1;
804       }
805     }
806 
807     if((discdb_data=fopen(file,"w"))==NULL) {
808       Debug(_("Error: Unable to open %s for writing\n"),file);
809       return -1;
810     }
811   }
812   else discdb_data=outfile;
813 
814   fprintf(discdb_data,"# xmcd CD database file generated by Grip %s\n",
815 	  VERSION);
816   fputs("# \n",discdb_data);
817   fputs("# Track frame offsets:\n",discdb_data);
818 
819   for(track=0;track<disc->num_tracks;track++)
820     fprintf(discdb_data, "#       %d\n",disc->track[track].start_frame);
821 
822   fputs("# \n",discdb_data);
823   fprintf(discdb_data,"# Disc length: %d seconds\n",disc->length.mins *
824 	  60 + disc->length.secs);
825   fputs("# \n",discdb_data);
826 
827   if(gripext) fprintf(discdb_data,"# Revision: %d\n",ddata->revision);
828   else fprintf(discdb_data,"# Revision: %d\n",ddata->revision+1);
829 
830   fprintf(discdb_data,"# Submitted via: Grip %s\n",VERSION);
831   fputs("# \n",discdb_data);
832   fprintf(discdb_data,"DISCID=%08x\n",ddata->data_id);
833 
834   g_snprintf(tmp,512,"%s / %s",ddata->data_artist,ddata->data_title);
835 
836   DiscDBWriteLine("DTITLE",-1,tmp,discdb_data,encoding);
837 
838   if(gripext||freedbext) {
839     if(ddata->data_year)
840       fprintf(discdb_data,"DYEAR=%d\n",ddata->data_year);
841     else fprintf(discdb_data,"DYEAR=\n");
842   }
843 
844   if(gripext) {
845     fprintf(discdb_data,"DGENRE=%s\n",DiscDBGenre(ddata->data_genre));
846     fprintf(discdb_data,"DID3=%d\n",ddata->data_id3genre);
847   }
848   else if(freedbext) {
849     fprintf(discdb_data,"DGENRE=%s\n",ID3GenreString(ddata->data_id3genre));
850   }
851 
852   for(track=0;track<disc->num_tracks;track++) {
853     if(gripext||!*(ddata->data_track[track].track_artist)) {
854       DiscDBWriteLine("TTITLE",track,ddata->data_track[track].track_name,
855 		      discdb_data,encoding);
856     }
857     else {
858       g_snprintf(tmp,512,"%s / %s",ddata->data_track[track].track_artist,
859 		 ddata->data_track[track].track_name);
860       DiscDBWriteLine("TTITLE",track,tmp,discdb_data,encoding);
861     }
862 
863     if(gripext&&*(ddata->data_track[track].track_artist))
864       DiscDBWriteLine("TARTIST",track,ddata->data_track[track].track_artist,
865 		      discdb_data,encoding);
866   }
867 
868   DiscDBWriteLine("EXTD",-1,ddata->data_extended,discdb_data,encoding);
869 
870   for(track=0;track<disc->num_tracks;track++)
871     DiscDBWriteLine("EXTT",track,
872                     ddata->data_track[track].track_extended,discdb_data,
873                     encoding);
874 
875   if(outfile)
876     fprintf(discdb_data,"PLAYORDER=\n");
877   else {
878     fprintf(discdb_data,"PLAYORDER=%s\n",ddata->data_playlist);
879     fclose(discdb_data);
880   }
881 
882   return 0;
883 }
884