Get rid of nonsensical limits on -geometry x, y,w and h values, they only
[mplayer/glamo.git] / stream / stream_cddb.c
blob55620a8b2da7c825a1f557f6a1e4d6b57c46d56e
1 /*
2 * CDDB HTTP protocol
3 * by Bertrand Baudet <bertrand_baudet@yahoo.com>
4 * (C) 2002, MPlayer team.
6 * Implementation follow the freedb.howto1.06.txt specification
7 * from http://freedb.freedb.org
8 *
9 * discid computation by Jeremy D. Zawodny
10 * Copyright (c) 1998-2000 Jeremy D. Zawodny <Jeremy@Zawodny.com>
11 * Code release under GPL
15 #include "config.h"
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <fcntl.h>
20 #include <stdarg.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <limits.h>
25 #if defined(__MINGW32__) || defined(__CYGWIN__)
26 #ifdef __MINGW32__
27 #define mkdir(a,b) mkdir(a)
28 #endif
29 #include <windows.h>
30 #if HAVE_WINSOCK2_H
31 #include <winsock2.h>
32 #endif
33 #else
34 #include <netdb.h>
35 #include <sys/ioctl.h>
36 #endif
37 #include <sys/types.h>
38 #include <sys/stat.h>
40 #include "mp_msg.h"
41 #include "help_mp.h"
43 #if defined(__linux__)
44 #include <linux/cdrom.h>
45 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
46 #include <sys/cdio.h>
47 #elif defined(__MINGW32__) || defined(__CYGWIN__)
48 #include <ddk/ntddcdrm.h>
49 #elif (__bsdi__)
50 #include <dvd.h>
51 #elif defined(__APPLE__) || defined(__DARWIN__)
52 #include <IOKit/storage/IOCDTypes.h>
53 #include <IOKit/storage/IOCDMediaBSDClient.h>
54 #include "mpbswap.h"
55 #endif
57 #include "cdd.h"
58 #include "version.h"
59 #include "stream.h"
60 #include "network.h"
61 #include "libavutil/common.h"
63 #define DEFAULT_FREEDB_SERVER "freedb.freedb.org"
64 #define DEFAULT_CACHE_DIR "/.cddb/"
66 stream_t* open_cdda(char *dev, char *track);
68 static cd_toc_t cdtoc[100];
69 static int cdtoc_last_track;
71 int
72 read_toc(const char *dev) {
73 int first = 0, last = -1;
74 int i;
75 #if defined(__MINGW32__) || defined(__CYGWIN__)
76 HANDLE drive;
77 DWORD r;
78 CDROM_TOC toc;
79 char device[10];
81 sprintf(device, "\\\\.\\%s", dev);
82 drive = CreateFile(device, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
84 if(!DeviceIoControl(drive, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(CDROM_TOC), &r, 0)) {
85 mp_msg(MSGT_OPEN, MSGL_ERR, MSGTR_MPDEMUX_CDDB_FailedToReadTOC);
86 return 0;
89 first = toc.FirstTrack - 1; last = toc.LastTrack;
90 for (i = first; i <= last; i++) {
91 cdtoc[i].min = toc.TrackData[i].Address[1];
92 cdtoc[i].sec = toc.TrackData[i].Address[2];
93 cdtoc[i].frame = toc.TrackData[i].Address[3];
95 CloseHandle(drive);
97 #else
98 int drive;
99 drive = open(dev, O_RDONLY | O_NONBLOCK);
100 if( drive<0 ) {
101 return drive;
104 #if defined(__linux__) || defined(__bsdi__)
106 struct cdrom_tochdr tochdr;
107 ioctl(drive, CDROMREADTOCHDR, &tochdr);
108 first = tochdr.cdth_trk0 - 1; last = tochdr.cdth_trk1;
110 for (i = first; i <= last; i++) {
111 struct cdrom_tocentry tocentry;
112 tocentry.cdte_track = (i == last) ? 0xAA : i + 1;
113 tocentry.cdte_format = CDROM_MSF;
114 ioctl(drive, CDROMREADTOCENTRY, &tocentry);
115 cdtoc[i].min = tocentry.cdte_addr.msf.minute;
116 cdtoc[i].sec = tocentry.cdte_addr.msf.second;
117 cdtoc[i].frame = tocentry.cdte_addr.msf.frame;
119 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
121 struct ioc_toc_header tochdr;
122 ioctl(drive, CDIOREADTOCHEADER, &tochdr);
123 first = tochdr.starting_track - 1; last = tochdr.ending_track;
125 for (i = first; i <= last; i++) {
126 struct ioc_read_toc_single_entry tocentry;
127 tocentry.track = (i == last) ? 0xAA : i + 1;
128 tocentry.address_format = CD_MSF_FORMAT;
129 ioctl(drive, CDIOREADTOCENTRY, &tocentry);
130 cdtoc[i].min = tocentry.entry.addr.msf.minute;
131 cdtoc[i].sec = tocentry.entry.addr.msf.second;
132 cdtoc[i].frame = tocentry.entry.addr.msf.frame;
134 #elif defined(__NetBSD__) || defined(__OpenBSD__)
136 struct ioc_toc_header tochdr;
137 ioctl(drive, CDIOREADTOCHEADER, &tochdr);
138 first = tochdr.starting_track - 1; last = tochdr.ending_track;
140 for (i = first; i <= last; i++) {
141 struct ioc_read_toc_entry tocentry;
142 struct cd_toc_entry toc_buffer;
143 tocentry.starting_track = (i == last) ? 0xAA : i + 1;
144 tocentry.address_format = CD_MSF_FORMAT;
145 tocentry.data = &toc_buffer;
146 tocentry.data_len = sizeof(toc_buffer);
147 ioctl(drive, CDIOREADTOCENTRYS, &tocentry);
148 cdtoc[i].min = toc_buffer.addr.msf.minute;
149 cdtoc[i].sec = toc_buffer.addr.msf.second;
150 cdtoc[i].frame = toc_buffer.addr.msf.frame;
152 #elif defined(__APPLE__) || defined(__DARWIN__)
154 dk_cd_read_toc_t tochdr;
155 uint8_t buf[4];
156 uint8_t buf2[100 * sizeof(CDTOCDescriptor) + sizeof(CDTOC)];
157 memset(&tochdr, 0, sizeof(tochdr));
158 tochdr.bufferLength = sizeof(buf);
159 tochdr.buffer = &buf;
160 if (!ioctl(drive, DKIOCCDREADTOC, &tochdr)
161 && tochdr.bufferLength == sizeof(buf)) {
162 first = buf[2] - 1;
163 last = buf[3];
165 if (last >= 0) {
166 memset(&tochdr, 0, sizeof(tochdr));
167 tochdr.bufferLength = sizeof(buf2);
168 tochdr.buffer = &buf2;
169 tochdr.format = kCDTOCFormatTOC;
170 if (ioctl(drive, DKIOCCDREADTOC, &tochdr)
171 || tochdr.bufferLength < sizeof(CDTOC))
172 last = -1;
174 if (last >= 0) {
175 CDTOC *cdToc = (CDTOC *)buf2;
176 CDTrackInfo lastTrack;
177 dk_cd_read_track_info_t trackInfoParams;
178 for (i = first; i < last; ++i) {
179 CDMSF msf = CDConvertTrackNumberToMSF(i + 1, cdToc);
180 cdtoc[i].min = msf.minute;
181 cdtoc[i].sec = msf.second;
182 cdtoc[i].frame = msf.frame;
184 memset(&trackInfoParams, 0, sizeof(trackInfoParams));
185 trackInfoParams.addressType = kCDTrackInfoAddressTypeTrackNumber;
186 trackInfoParams.bufferLength = sizeof(lastTrack);
187 trackInfoParams.address = last;
188 trackInfoParams.buffer = &lastTrack;
189 if (!ioctl(drive, DKIOCCDREADTRACKINFO, &trackInfoParams)) {
190 CDMSF msf = CDConvertLBAToMSF(be2me_32(lastTrack.trackStartAddress)
191 + be2me_32(lastTrack.trackSize));
192 cdtoc[last].min = msf.minute;
193 cdtoc[last].sec = msf.second;
194 cdtoc[last].frame = msf.frame;
198 #endif
199 close(drive);
200 #endif
201 for (i = first; i <= last; i++)
202 cdtoc[i].frame += (cdtoc[i].min * 60 + cdtoc[i].sec) * 75;
203 return last;
206 /**
207 \brief Reads TOC from CD in the given device and prints the number of tracks
208 and the length of each track in minute:second:frame format.
209 \param *dev the device to analyse
210 \return if the command line -identify is given, returns the last track of
211 the TOC or -1 if the TOC can't be read,
212 otherwise just returns 0 and let cddb_resolve the TOC
214 int cdd_identify(const char *dev)
216 cdtoc_last_track = 0;
217 if (mp_msg_test(MSGT_IDENTIFY, MSGL_INFO))
219 int i, min, sec, frame;
220 cdtoc_last_track = read_toc(dev);
221 if (cdtoc_last_track < 0) {
222 mp_msg(MSGT_OPEN, MSGL_ERR, MSGTR_MPDEMUX_CDDB_FailedToOpenDevice, dev);
223 return -1;
225 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ID_CDDA_TRACKS=%d\n", cdtoc_last_track);
226 for (i = 1; i <= cdtoc_last_track; i++)
228 frame = cdtoc[i].frame - cdtoc[i-1].frame;
229 sec = frame / 75;
230 frame -= sec * 75;
231 min = sec / 60;
232 sec -= min * 60;
233 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CDDA_TRACK_%d_MSF=%02d:%02d:%02d\n", i, min, sec, frame);
236 return cdtoc_last_track;
239 unsigned int
240 cddb_sum(int n) {
241 unsigned int ret;
243 ret = 0;
244 while (n > 0) {
245 ret += (n % 10);
246 n /= 10;
248 return ret;
251 unsigned long
252 cddb_discid(int tot_trks) {
253 unsigned int i, t = 0, n = 0;
255 i = 0;
256 while (i < (unsigned int)tot_trks) {
257 n = n + cddb_sum((cdtoc[i].min * 60) + cdtoc[i].sec);
258 i++;
260 t = ((cdtoc[tot_trks].min * 60) + cdtoc[tot_trks].sec) -
261 ((cdtoc[0].min * 60) + cdtoc[0].sec);
262 return (n % 0xff) << 24 | t << 8 | tot_trks;
268 cddb_http_request(char *command, int (*reply_parser)(HTTP_header_t*,cddb_data_t*), cddb_data_t *cddb_data) {
269 char request[4096];
270 int fd, ret = 0;
271 URL_t *url;
272 HTTP_header_t *http_hdr;
274 if( reply_parser==NULL || command==NULL || cddb_data==NULL ) return -1;
276 sprintf( request, "http://%s/~cddb/cddb.cgi?cmd=%s%s&proto=%d", cddb_data->freedb_server, command, cddb_data->cddb_hello, cddb_data->freedb_proto_level );
277 mp_msg(MSGT_OPEN, MSGL_INFO,"Request[%s]\n", request );
279 url = url_new(request);
280 if( url==NULL ) {
281 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_NotAValidURL);
282 return -1;
285 fd = http_send_request(url,0);
286 if( fd<0 ) {
287 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_FailedToSendHTTPRequest);
288 return -1;
291 http_hdr = http_read_response( fd );
292 if( http_hdr==NULL ) {
293 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_FailedToReadHTTPResponse);
294 return -1;
297 http_debug_hdr(http_hdr);
298 mp_msg(MSGT_OPEN, MSGL_INFO,"body=[%s]\n", http_hdr->body );
300 switch(http_hdr->status_code) {
301 case 200:
302 ret = reply_parser(http_hdr, cddb_data);
303 break;
304 case 400:
305 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_HTTPErrorNOTFOUND);
306 break;
307 default:
308 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_HTTPErrorUnknown);
311 http_free( http_hdr );
312 url_free( url );
314 return ret;
318 cddb_read_cache(cddb_data_t *cddb_data) {
319 char file_name[100];
320 struct stat stats;
321 int file_fd, ret;
322 size_t file_size;
324 if( cddb_data==NULL || cddb_data->cache_dir==NULL ) return -1;
326 sprintf( file_name, "%s%08lx", cddb_data->cache_dir, cddb_data->disc_id);
328 file_fd = open(file_name, O_RDONLY
329 #if defined(__MINGW32__) || defined(__CYGWIN__)
330 | O_BINARY
331 #endif
333 if( file_fd<0 ) {
334 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_NoCacheFound);
335 return -1;
338 ret = fstat( file_fd, &stats );
339 if( ret<0 ) {
340 perror("fstat");
341 file_size = 4096;
342 } else {
343 file_size = stats.st_size < UINT_MAX ? stats.st_size : UINT_MAX - 1;
346 cddb_data->xmcd_file = malloc(file_size+1);
347 if( cddb_data->xmcd_file==NULL ) {
348 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MemAllocFailed);
349 close(file_fd);
350 return -1;
352 cddb_data->xmcd_file_size = read(file_fd, cddb_data->xmcd_file, file_size);
353 if( cddb_data->xmcd_file_size!=file_size ) {
354 mp_msg(MSGT_DEMUX, MSGL_WARN, MSGTR_MPDEMUX_CDDB_NotAllXMCDFileHasBeenRead);
355 close(file_fd);
356 return -1;
358 cddb_data->xmcd_file[cddb_data->xmcd_file_size] = 0;
360 close(file_fd);
362 return 0;
366 cddb_write_cache(cddb_data_t *cddb_data) {
367 // We have the file, save it for cache.
368 struct stat file_stat;
369 char file_name[100];
370 int file_fd, ret;
371 int wrote=0;
373 if( cddb_data==NULL || cddb_data->cache_dir==NULL ) return -1;
375 // Check if the CDDB cache dir exist
376 ret = stat( cddb_data->cache_dir, &file_stat );
377 if( ret<0 ) {
378 // Directory not present, create it.
379 ret = mkdir( cddb_data->cache_dir, 0755 );
380 #ifdef __MINGW32__
381 if( ret<0 && errno != EEXIST ) {
382 #else
383 if( ret<0 ) {
384 #endif
385 perror("mkdir");
386 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_FailedToCreateDirectory, cddb_data->cache_dir);
387 return -1;
391 sprintf( file_name, "%s%08lx", cddb_data->cache_dir, cddb_data->disc_id );
393 file_fd = creat(file_name, S_IRUSR|S_IWUSR);
394 if( file_fd<0 ) {
395 perror("create");
396 return -1;
399 wrote = write(file_fd, cddb_data->xmcd_file, cddb_data->xmcd_file_size);
400 if( wrote<0 ) {
401 perror("write");
402 close(file_fd);
403 return -1;
405 if( (unsigned int)wrote!=cddb_data->xmcd_file_size ) {
406 mp_msg(MSGT_DEMUX, MSGL_WARN, MSGTR_MPDEMUX_CDDB_NotAllXMCDFileHasBeenWritten);
407 close(file_fd);
408 return -1;
411 close(file_fd);
413 return 0;
417 cddb_read_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data) {
418 unsigned long disc_id;
419 char category[100];
420 char *ptr=NULL, *ptr2=NULL;
421 int ret, status;
423 if( http_hdr==NULL || cddb_data==NULL ) return -1;
425 ret = sscanf( http_hdr->body, "%d ", &status);
426 if( ret!=1 ) {
427 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_ParseError);
428 return -1;
431 switch(status) {
432 case 210:
433 ret = sscanf( http_hdr->body, "%d %99s %08lx", &status, category, &disc_id);
434 if( ret!=3 ) {
435 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_ParseError);
436 return -1;
438 // Check if it's a xmcd database file
439 ptr = strstr(http_hdr->body, "# xmcd");
440 if( ptr==NULL ) {
441 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_InvalidXMCDDatabaseReturned);
442 return -1;
444 ptr = strdup(ptr);
445 // Ok found the beginning of the file
446 // look for the end
447 ptr2 = strstr(ptr, "\n.\r\n");
448 if (!ptr2)
449 ptr2 = strstr(ptr, "\n.\n");
450 if (ptr2) ptr2++;
451 else {
452 mp_msg(MSGT_DEMUX, MSGL_FIXME, "Unable to find '.'\n");
453 ptr2=ptr+strlen(ptr); //return -1;
455 // Ok found the end
456 // do a sanity check
457 if( http_hdr->body_size<(unsigned int)(ptr2-ptr) ) {
458 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_UnexpectedFIXME);
459 return -1;
461 cddb_data->xmcd_file = ptr;
462 cddb_data->xmcd_file_size = ptr2-ptr;
463 cddb_data->xmcd_file[cddb_data->xmcd_file_size] = '\0';
464 return cddb_write_cache(cddb_data);
465 default:
466 mp_msg(MSGT_DEMUX, MSGL_FIXME, MSGTR_MPDEMUX_CDDB_UnhandledCode);
468 return 0;
472 cddb_request_titles(cddb_data_t *cddb_data) {
473 char command[1024];
474 sprintf( command, "cddb+read+%s+%08lx", cddb_data->category, cddb_data->disc_id);
475 return cddb_http_request(command, cddb_read_parse, cddb_data);
479 cddb_parse_matches_list(HTTP_header_t *http_hdr, cddb_data_t *cddb_data) {
480 char album_title[100];
481 char *ptr = NULL;
482 int ret;
484 ptr = strstr(http_hdr->body, "\n");
485 if( ptr==NULL ) {
486 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_UnableToFindEOL);
487 return -1;
489 ptr++;
490 // We have a list of exact/inexact matches, so which one do we use?
491 // So let's take the first one.
492 ret = sscanf(ptr, "%99s %08lx %99s", cddb_data->category, &(cddb_data->disc_id), album_title);
493 if( ret!=3 ) {
494 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_ParseError);
495 return -1;
497 ptr = strstr(http_hdr->body, album_title);
498 if( ptr!=NULL ) {
499 char *ptr2;
500 int len;
501 ptr2 = strstr(ptr, "\n");
502 if( ptr2==NULL ) {
503 len = (http_hdr->body_size)-(ptr-(http_hdr->body));
504 } else {
505 len = ptr2-ptr+1;
507 len = FFMIN(sizeof(album_title) - 1, len);
508 strncpy(album_title, ptr, len);
509 album_title[len]='\0';
511 mp_msg(MSGT_DEMUX, MSGL_STATUS, MSGTR_MPDEMUX_CDDB_ParseOKFoundAlbumTitle, album_title);
512 return 0;
516 cddb_query_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data) {
517 char album_title[100];
518 char *ptr = NULL;
519 int ret, status;
521 ret = sscanf( http_hdr->body, "%d ", &status);
522 if( ret!=1 ) {
523 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_ParseError);
524 return -1;
527 switch(status) {
528 case 200:
529 // Found exact match
530 ret = sscanf(http_hdr->body, "%d %99s %08lx %99s", &status, cddb_data->category, &(cddb_data->disc_id), album_title);
531 if( ret!=4 ) {
532 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_ParseError);
533 return -1;
535 ptr = strstr(http_hdr->body, album_title);
536 if( ptr!=NULL ) {
537 char *ptr2;
538 int len;
539 ptr2 = strstr(ptr, "\n");
540 if( ptr2==NULL ) {
541 len = (http_hdr->body_size)-(ptr-(http_hdr->body));
542 } else {
543 len = ptr2-ptr+1;
545 len = FFMIN(sizeof(album_title) - 1, len);
546 strncpy(album_title, ptr, len);
547 album_title[len]='\0';
549 mp_msg(MSGT_DEMUX, MSGL_STATUS, MSGTR_MPDEMUX_CDDB_ParseOKFoundAlbumTitle, album_title);
550 return cddb_request_titles(cddb_data);
551 case 202:
552 // No match found
553 mp_msg(MSGT_DEMUX, MSGL_WARN, MSGTR_MPDEMUX_CDDB_AlbumNotFound);
554 break;
555 case 210:
556 // Found exact matches, list follows
557 cddb_parse_matches_list(http_hdr, cddb_data);
558 return cddb_request_titles(cddb_data);
560 body=[210 Found exact matches, list follows (until terminating `.')
561 misc c711930d Santana / Supernatural
562 rock c711930d Santana / Supernatural
563 blues c711930d Santana / Supernatural
566 case 211:
567 // Found inexact matches, list follows
568 cddb_parse_matches_list(http_hdr, cddb_data);
569 return cddb_request_titles(cddb_data);
570 case 500:
571 mp_msg(MSGT_DEMUX, MSGL_FIXME, MSGTR_MPDEMUX_CDDB_ServerReturnsCommandSyntaxErr);
572 break;
573 default:
574 mp_msg(MSGT_DEMUX, MSGL_FIXME, MSGTR_MPDEMUX_CDDB_UnhandledCode);
576 return -1;
580 cddb_proto_level_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data) {
581 int max;
582 int ret, status;
583 char *ptr;
585 ret = sscanf( http_hdr->body, "%d ", &status);
586 if( ret!=1 ) {
587 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_ParseError);
588 return -1;
591 switch(status) {
592 case 210:
593 ptr = strstr(http_hdr->body, "max proto:");
594 if( ptr==NULL ) {
595 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_ParseError);
596 return -1;
598 ret = sscanf(ptr, "max proto: %d", &max);
599 if( ret!=1 ) {
600 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_ParseError);
601 return -1;
603 cddb_data->freedb_proto_level = max;
604 return 0;
605 default:
606 mp_msg(MSGT_DEMUX, MSGL_FIXME, MSGTR_MPDEMUX_CDDB_UnhandledCode);
608 return -1;
612 cddb_get_proto_level(cddb_data_t *cddb_data) {
613 return cddb_http_request("stat", cddb_proto_level_parse, cddb_data);
617 cddb_freedb_sites_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data) {
618 int ret, status;
620 ret = sscanf( http_hdr->body, "%d ", &status);
621 if( ret!=1 ) {
622 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_ParseError);
623 return -1;
626 switch(status) {
627 case 210:
628 // TODO: Parse the sites
629 ret = cddb_data->anonymous; // For gcc complaining about unused parameter.
630 return 0;
631 case 401:
632 mp_msg(MSGT_DEMUX, MSGL_FIXME, MSGTR_MPDEMUX_CDDB_NoSitesInfoAvailable);
633 break;
634 default:
635 mp_msg(MSGT_DEMUX, MSGL_FIXME, MSGTR_MPDEMUX_CDDB_UnhandledCode);
637 return -1;
641 cddb_get_freedb_sites(cddb_data_t *cddb_data) {
642 return cddb_http_request("sites", cddb_freedb_sites_parse, cddb_data);
645 void
646 cddb_create_hello(cddb_data_t *cddb_data) {
647 char host_name[51];
648 char *user_name;
650 if( cddb_data->anonymous ) { // Default is anonymous
651 /* Note from Eduardo Pérez Ureta <eperez@it.uc3m.es> :
652 * We don't send current user/host name in hello to prevent spam.
653 * Software that sends this is considered spyware
654 * that most people don't like.
656 user_name = "anonymous";
657 strcpy(host_name, "localhost");
658 } else {
659 if( gethostname(host_name, 50)<0 ) {
660 strcpy(host_name, "localhost");
662 user_name = getenv("LOGNAME");
664 sprintf( cddb_data->cddb_hello, "&hello=%s+%s+%s+%s", user_name, host_name, "MPlayer", VERSION );
667 int
668 cddb_retrieve(cddb_data_t *cddb_data) {
669 char offsets[1024], command[1024];
670 char *ptr;
671 unsigned int i, time_len;
672 int ret;
674 ptr = offsets;
675 for( i=0; i<cddb_data->tracks ; i++ ) {
676 ptr += sprintf(ptr, "%d+", cdtoc[i].frame );
677 if (ptr-offsets > sizeof offsets - 40) break;
679 ptr[0]=0;
680 time_len = (cdtoc[cddb_data->tracks].frame)/75;
682 cddb_data->freedb_server = DEFAULT_FREEDB_SERVER;
683 cddb_data->freedb_proto_level = 1;
684 cddb_data->xmcd_file = NULL;
686 cddb_create_hello(cddb_data);
687 if( cddb_get_proto_level(cddb_data)<0 ) {
688 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_FailedToGetProtocolLevel);
689 return -1;
692 //cddb_get_freedb_sites(&cddb_data);
694 sprintf(command, "cddb+query+%08lx+%d+%s%d", cddb_data->disc_id, cddb_data->tracks, offsets, time_len );
695 ret = cddb_http_request(command, cddb_query_parse, cddb_data);
696 if( ret<0 ) return -1;
698 if( cddb_data->cache_dir!=NULL ) {
699 free(cddb_data->cache_dir);
701 return 0;
705 cddb_resolve(const char *dev, char **xmcd_file) {
706 char cddb_cache_dir[] = DEFAULT_CACHE_DIR;
707 char *home_dir = NULL;
708 cddb_data_t cddb_data;
710 if (cdtoc_last_track <= 0)
712 cdtoc_last_track = read_toc(dev);
713 if (cdtoc_last_track < 0) {
714 mp_msg(MSGT_OPEN, MSGL_ERR, MSGTR_MPDEMUX_CDDB_FailedToOpenDevice, dev);
715 return -1;
718 cddb_data.tracks = cdtoc_last_track;
719 cddb_data.disc_id = cddb_discid(cddb_data.tracks);
720 cddb_data.anonymous = 1; // Don't send user info by default
722 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CDDB_DISCID=%08lx\n", cddb_data.disc_id);
724 // Check if there is a CD in the drive
725 // FIXME: That's not really a good way to check
726 if( cddb_data.disc_id==0 ) {
727 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MPDEMUX_CDDB_NoCDInDrive);
728 return -1;
731 home_dir = getenv("HOME");
732 #ifdef __MINGW32__
733 if( home_dir==NULL ) home_dir = getenv("USERPROFILE");
734 if( home_dir==NULL ) home_dir = getenv("HOMEPATH");
735 // Last resort, store the cddb cache in the mplayer directory
736 if( home_dir==NULL ) home_dir = (char *)get_path("");
737 #endif
738 if( home_dir==NULL ) {
739 cddb_data.cache_dir = NULL;
740 } else {
741 cddb_data.cache_dir = malloc(strlen(home_dir)+strlen(cddb_cache_dir)+1);
742 if( cddb_data.cache_dir==NULL ) {
743 mp_msg(MSGT_DEMUX, MSGL_ERR, MSGTR_MemAllocFailed);
744 return -1;
746 sprintf(cddb_data.cache_dir, "%s%s", home_dir, cddb_cache_dir );
749 // Check for a cached file
750 if( cddb_read_cache(&cddb_data)<0 ) {
751 // No Cache found
752 if( cddb_retrieve(&cddb_data)<0 ) {
753 return -1;
757 if( cddb_data.xmcd_file!=NULL ) {
758 // printf("%s\n", cddb_data.xmcd_file );
759 *xmcd_file = cddb_data.xmcd_file;
760 return 0;
763 return -1;
766 /*******************************************************************************************************************
768 * xmcd parser
770 *******************************************************************************************************************/
771 char*
772 xmcd_parse_dtitle(cd_info_t *cd_info, char *line) {
773 char *ptr, *album;
774 ptr = strstr(line, "DTITLE=");
775 if( ptr!=NULL ) {
776 ptr += 7;
777 album = strstr(ptr, "/");
778 if( album==NULL ) return NULL;
779 cd_info->album = malloc(strlen(album+2)+1);
780 if( cd_info->album==NULL ) {
781 return NULL;
783 strcpy( cd_info->album, album+2 );
784 album--;
785 album[0] = '\0';
786 cd_info->artist = malloc(strlen(ptr)+1);
787 if( cd_info->artist==NULL ) {
788 return NULL;
790 strcpy( cd_info->artist, ptr );
792 return ptr;
795 char*
796 xmcd_parse_dgenre(cd_info_t *cd_info, char *line) {
797 char *ptr;
798 ptr = strstr(line, "DGENRE=");
799 if( ptr!=NULL ) {
800 ptr += 7;
801 cd_info->genre = malloc(strlen(ptr)+1);
802 if( cd_info->genre==NULL ) {
803 return NULL;
805 strcpy( cd_info->genre, ptr );
807 return ptr;
810 char*
811 xmcd_parse_ttitle(cd_info_t *cd_info, char *line) {
812 unsigned int track_nb;
813 unsigned long sec, off;
814 char *ptr;
815 ptr = strstr(line, "TTITLE");
816 if( ptr!=NULL ) {
817 ptr += 6;
818 // Here we point to the track number
819 track_nb = atoi(ptr);
820 ptr = strstr(ptr, "=");
821 if( ptr==NULL ) return NULL;
822 ptr++;
824 sec = cdtoc[track_nb].frame;
825 off = cdtoc[track_nb+1].frame-sec+1;
827 cd_info_add_track( cd_info, ptr, track_nb+1, (unsigned int)(off/(60*75)), (unsigned int)((off/75)%60), (unsigned int)(off%75), sec, off );
829 return ptr;
832 cd_info_t*
833 cddb_parse_xmcd(char *xmcd_file) {
834 cd_info_t *cd_info = NULL;
835 int length, pos = 0;
836 char *ptr, *ptr2;
837 unsigned int audiolen;
838 if( xmcd_file==NULL ) return NULL;
840 cd_info = cd_info_new();
841 if( cd_info==NULL ) {
842 return NULL;
845 length = strlen(xmcd_file);
846 ptr = xmcd_file;
847 while( ptr!=NULL && pos<length ) {
848 // Read a line
849 ptr2 = ptr;
850 while( ptr2[0]!='\0' && ptr2[0]!='\r' && ptr2[0]!='\n' ) ptr2++;
851 if( ptr2[0]=='\0' ) {
852 break;
854 ptr2[0] = '\0';
855 // Ignore comments
856 if( ptr[0]!='#' ) {
857 // Search for the album title
858 if( xmcd_parse_dtitle(cd_info, ptr) );
859 // Search for the genre
860 else if( xmcd_parse_dgenre(cd_info, ptr) );
861 // Search for a track title
862 else if( xmcd_parse_ttitle(cd_info, ptr) ) audiolen++; // <-- audiolen++ to shut up gcc warning
864 if( ptr2[1]=='\n' ) ptr2++;
865 pos = (ptr2+1)-ptr;
866 ptr = ptr2+1;
869 audiolen = cdtoc[cd_info->nb_tracks].frame-cdtoc[0].frame;
870 cd_info->min = (unsigned int)(audiolen/(60*75));
871 cd_info->sec = (unsigned int)((audiolen/75)%60);
872 cd_info->msec = (unsigned int)(audiolen%75);
874 return cd_info;