Merge svn changes up to r30475
[mplayer/glamo.git] / stream / stream_cddb.c
blob0c1bc0ccc6f193f5f118c06008996b2f96198a98
1 /*
2 * CDDB HTTP protocol
4 * Copyright (C) 2002 Bertrand Baudet <bertrand_baudet@yahoo.com>
6 * Implementation follow the freedb.howto1.06.txt specification
7 * from http://freedb.freedb.org
9 * discid computation by Jeremy D. Zawodny
10 * Copyright (c) 1998-2000 Jeremy D. Zawodny <Jeremy@Zawodny.com>
12 * This file is part of MPlayer.
14 * MPlayer is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * MPlayer is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include "config.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <fcntl.h>
34 #include <stdarg.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <limits.h>
39 #if defined(__MINGW32__) || defined(__CYGWIN__)
40 #ifdef __MINGW32__
41 #define mkdir(a,b) mkdir(a)
42 #endif
43 #include <windows.h>
44 #if HAVE_WINSOCK2_H
45 #include <winsock2.h>
46 #endif
47 #else
48 #include <netdb.h>
49 #include <sys/ioctl.h>
50 #endif
51 #include <sys/types.h>
52 #include <sys/stat.h>
54 #include "mp_msg.h"
55 #include "help_mp.h"
57 #if defined(__linux__)
58 #include <linux/cdrom.h>
59 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
60 #include <sys/cdio.h>
61 #elif defined(__MINGW32__) || defined(__CYGWIN__)
62 #include <ddk/ntddcdrm.h>
63 #elif (__bsdi__)
64 #include <dvd.h>
65 #elif defined(__APPLE__) || defined(__DARWIN__)
66 #include <IOKit/storage/IOCDTypes.h>
67 #include <IOKit/storage/IOCDMediaBSDClient.h>
68 #include "mpbswap.h"
69 #endif
71 #include "cdd.h"
72 #include "version.h"
73 #include "stream.h"
74 #include "network.h"
75 #include "libavutil/common.h"
77 #define DEFAULT_FREEDB_SERVER "freedb.freedb.org"
78 #define DEFAULT_CACHE_DIR "/.cddb/"
80 stream_t* open_cdda(char *dev, char *track);
82 static cd_toc_t cdtoc[100];
83 static int cdtoc_last_track;
85 int
86 read_toc(const char *dev) {
87 int first = 0, last = -1;
88 int i;
89 #if defined(__MINGW32__) || defined(__CYGWIN__)
90 HANDLE drive;
91 DWORD r;
92 CDROM_TOC toc;
93 char device[10];
95 sprintf(device, "\\\\.\\%s", dev);
96 drive = CreateFile(device, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
98 if(!DeviceIoControl(drive, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(CDROM_TOC), &r, 0)) {
99 mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to read TOC.\n");
100 return 0;
103 first = toc.FirstTrack - 1; last = toc.LastTrack;
104 for (i = first; i <= last; i++) {
105 cdtoc[i].min = toc.TrackData[i].Address[1];
106 cdtoc[i].sec = toc.TrackData[i].Address[2];
107 cdtoc[i].frame = toc.TrackData[i].Address[3];
109 CloseHandle(drive);
111 #else
112 int drive;
113 drive = open(dev, O_RDONLY | O_NONBLOCK);
114 if( drive<0 ) {
115 return drive;
118 #if defined(__linux__) || defined(__bsdi__)
120 struct cdrom_tochdr tochdr;
121 ioctl(drive, CDROMREADTOCHDR, &tochdr);
122 first = tochdr.cdth_trk0 - 1; last = tochdr.cdth_trk1;
124 for (i = first; i <= last; i++) {
125 struct cdrom_tocentry tocentry;
126 tocentry.cdte_track = (i == last) ? 0xAA : i + 1;
127 tocentry.cdte_format = CDROM_MSF;
128 ioctl(drive, CDROMREADTOCENTRY, &tocentry);
129 cdtoc[i].min = tocentry.cdte_addr.msf.minute;
130 cdtoc[i].sec = tocentry.cdte_addr.msf.second;
131 cdtoc[i].frame = tocentry.cdte_addr.msf.frame;
133 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
135 struct ioc_toc_header tochdr;
136 ioctl(drive, CDIOREADTOCHEADER, &tochdr);
137 first = tochdr.starting_track - 1; last = tochdr.ending_track;
139 for (i = first; i <= last; i++) {
140 struct ioc_read_toc_single_entry tocentry;
141 tocentry.track = (i == last) ? 0xAA : i + 1;
142 tocentry.address_format = CD_MSF_FORMAT;
143 ioctl(drive, CDIOREADTOCENTRY, &tocentry);
144 cdtoc[i].min = tocentry.entry.addr.msf.minute;
145 cdtoc[i].sec = tocentry.entry.addr.msf.second;
146 cdtoc[i].frame = tocentry.entry.addr.msf.frame;
148 #elif defined(__NetBSD__) || defined(__OpenBSD__)
150 struct ioc_toc_header tochdr;
151 ioctl(drive, CDIOREADTOCHEADER, &tochdr);
152 first = tochdr.starting_track - 1; last = tochdr.ending_track;
154 for (i = first; i <= last; i++) {
155 struct ioc_read_toc_entry tocentry;
156 struct cd_toc_entry toc_buffer;
157 tocentry.starting_track = (i == last) ? 0xAA : i + 1;
158 tocentry.address_format = CD_MSF_FORMAT;
159 tocentry.data = &toc_buffer;
160 tocentry.data_len = sizeof(toc_buffer);
161 ioctl(drive, CDIOREADTOCENTRYS, &tocentry);
162 cdtoc[i].min = toc_buffer.addr.msf.minute;
163 cdtoc[i].sec = toc_buffer.addr.msf.second;
164 cdtoc[i].frame = toc_buffer.addr.msf.frame;
166 #elif defined(__APPLE__) || defined(__DARWIN__)
168 dk_cd_read_toc_t tochdr;
169 uint8_t buf[4];
170 uint8_t buf2[100 * sizeof(CDTOCDescriptor) + sizeof(CDTOC)];
171 memset(&tochdr, 0, sizeof(tochdr));
172 tochdr.bufferLength = sizeof(buf);
173 tochdr.buffer = &buf;
174 if (!ioctl(drive, DKIOCCDREADTOC, &tochdr)
175 && tochdr.bufferLength == sizeof(buf)) {
176 first = buf[2] - 1;
177 last = buf[3];
179 if (last >= 0) {
180 memset(&tochdr, 0, sizeof(tochdr));
181 tochdr.bufferLength = sizeof(buf2);
182 tochdr.buffer = &buf2;
183 tochdr.format = kCDTOCFormatTOC;
184 if (ioctl(drive, DKIOCCDREADTOC, &tochdr)
185 || tochdr.bufferLength < sizeof(CDTOC))
186 last = -1;
188 if (last >= 0) {
189 CDTOC *cdToc = (CDTOC *)buf2;
190 CDTrackInfo lastTrack;
191 dk_cd_read_track_info_t trackInfoParams;
192 for (i = first; i < last; ++i) {
193 CDMSF msf = CDConvertTrackNumberToMSF(i + 1, cdToc);
194 cdtoc[i].min = msf.minute;
195 cdtoc[i].sec = msf.second;
196 cdtoc[i].frame = msf.frame;
198 memset(&trackInfoParams, 0, sizeof(trackInfoParams));
199 trackInfoParams.addressType = kCDTrackInfoAddressTypeTrackNumber;
200 trackInfoParams.bufferLength = sizeof(lastTrack);
201 trackInfoParams.address = last;
202 trackInfoParams.buffer = &lastTrack;
203 if (!ioctl(drive, DKIOCCDREADTRACKINFO, &trackInfoParams)) {
204 CDMSF msf = CDConvertLBAToMSF(be2me_32(lastTrack.trackStartAddress)
205 + be2me_32(lastTrack.trackSize));
206 cdtoc[last].min = msf.minute;
207 cdtoc[last].sec = msf.second;
208 cdtoc[last].frame = msf.frame;
212 #endif
213 close(drive);
214 #endif
215 for (i = first; i <= last; i++)
216 cdtoc[i].frame += (cdtoc[i].min * 60 + cdtoc[i].sec) * 75;
217 return last;
221 \brief Reads TOC from CD in the given device and prints the number of tracks
222 and the length of each track in minute:second:frame format.
223 \param *dev the device to analyse
224 \return if the command line -identify is given, returns the last track of
225 the TOC or -1 if the TOC can't be read,
226 otherwise just returns 0 and let cddb_resolve the TOC
228 int cdd_identify(const char *dev)
230 cdtoc_last_track = 0;
231 if (mp_msg_test(MSGT_IDENTIFY, MSGL_INFO))
233 int i, min, sec, frame;
234 cdtoc_last_track = read_toc(dev);
235 if (cdtoc_last_track < 0) {
236 mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to open %s device.\n", dev);
237 return -1;
239 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ID_CDDA_TRACKS=%d\n", cdtoc_last_track);
240 for (i = 1; i <= cdtoc_last_track; i++)
242 frame = cdtoc[i].frame - cdtoc[i-1].frame;
243 sec = frame / 75;
244 frame -= sec * 75;
245 min = sec / 60;
246 sec -= min * 60;
247 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CDDA_TRACK_%d_MSF=%02d:%02d:%02d\n", i, min, sec, frame);
250 return cdtoc_last_track;
253 unsigned int
254 cddb_sum(int n) {
255 unsigned int ret;
257 ret = 0;
258 while (n > 0) {
259 ret += (n % 10);
260 n /= 10;
262 return ret;
265 unsigned long
266 cddb_discid(int tot_trks) {
267 unsigned int i, t = 0, n = 0;
269 i = 0;
270 while (i < (unsigned int)tot_trks) {
271 n = n + cddb_sum((cdtoc[i].min * 60) + cdtoc[i].sec);
272 i++;
274 t = ((cdtoc[tot_trks].min * 60) + cdtoc[tot_trks].sec) -
275 ((cdtoc[0].min * 60) + cdtoc[0].sec);
276 return (n % 0xff) << 24 | t << 8 | tot_trks;
282 cddb_http_request(char *command, int (*reply_parser)(HTTP_header_t*,cddb_data_t*), cddb_data_t *cddb_data) {
283 char request[4096];
284 int fd, ret = 0;
285 URL_t *url;
286 HTTP_header_t *http_hdr;
288 if( reply_parser==NULL || command==NULL || cddb_data==NULL ) return -1;
290 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 );
291 mp_msg(MSGT_OPEN, MSGL_INFO,"Request[%s]\n", request );
293 url = url_new(request);
294 if( url==NULL ) {
295 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "not a valid URL\n");
296 return -1;
299 fd = http_send_request(url,0);
300 if( fd<0 ) {
301 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to send the HTTP request.\n");
302 return -1;
305 http_hdr = http_read_response( fd );
306 if( http_hdr==NULL ) {
307 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to read the HTTP response.\n");
308 return -1;
311 http_debug_hdr(http_hdr);
312 mp_msg(MSGT_OPEN, MSGL_INFO,"body=[%s]\n", http_hdr->body );
314 switch(http_hdr->status_code) {
315 case 200:
316 ret = reply_parser(http_hdr, cddb_data);
317 break;
318 case 400:
319 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Not Found.\n");
320 break;
321 default:
322 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "unknown error code\n");
325 http_free( http_hdr );
326 url_free( url );
328 return ret;
332 cddb_read_cache(cddb_data_t *cddb_data) {
333 char file_name[100];
334 struct stat stats;
335 int file_fd, ret;
336 size_t file_size;
338 if( cddb_data==NULL || cddb_data->cache_dir==NULL ) return -1;
340 sprintf( file_name, "%s%08lx", cddb_data->cache_dir, cddb_data->disc_id);
342 file_fd = open(file_name, O_RDONLY
343 #if defined(__MINGW32__) || defined(__CYGWIN__)
344 | O_BINARY
345 #endif
347 if( file_fd<0 ) {
348 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "No cache found.\n");
349 return -1;
352 ret = fstat( file_fd, &stats );
353 if( ret<0 ) {
354 perror("fstat");
355 file_size = 4096;
356 } else {
357 file_size = stats.st_size < UINT_MAX ? stats.st_size : UINT_MAX - 1;
360 cddb_data->xmcd_file = malloc(file_size+1);
361 if( cddb_data->xmcd_file==NULL ) {
362 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Memory allocation failed.\n");
363 close(file_fd);
364 return -1;
366 cddb_data->xmcd_file_size = read(file_fd, cddb_data->xmcd_file, file_size);
367 if( cddb_data->xmcd_file_size!=file_size ) {
368 mp_tmsg(MSGT_DEMUX, MSGL_WARN, "Not all the xmcd file has been read.\n");
369 close(file_fd);
370 return -1;
372 cddb_data->xmcd_file[cddb_data->xmcd_file_size] = 0;
374 close(file_fd);
376 return 0;
380 cddb_write_cache(cddb_data_t *cddb_data) {
381 // We have the file, save it for cache.
382 struct stat file_stat;
383 char file_name[100];
384 int file_fd, ret;
385 int wrote=0;
387 if( cddb_data==NULL || cddb_data->cache_dir==NULL ) return -1;
389 // Check if the CDDB cache dir exist
390 ret = stat( cddb_data->cache_dir, &file_stat );
391 if( ret<0 ) {
392 // Directory not present, create it.
393 ret = mkdir( cddb_data->cache_dir, 0755 );
394 #ifdef __MINGW32__
395 if( ret<0 && errno != EEXIST ) {
396 #else
397 if( ret<0 ) {
398 #endif
399 perror("mkdir");
400 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to create directory %s.\n", cddb_data->cache_dir);
401 return -1;
405 sprintf( file_name, "%s%08lx", cddb_data->cache_dir, cddb_data->disc_id );
407 file_fd = creat(file_name, S_IRUSR|S_IWUSR);
408 if( file_fd<0 ) {
409 perror("create");
410 return -1;
413 wrote = write(file_fd, cddb_data->xmcd_file, cddb_data->xmcd_file_size);
414 if( wrote<0 ) {
415 perror("write");
416 close(file_fd);
417 return -1;
419 if( (unsigned int)wrote!=cddb_data->xmcd_file_size ) {
420 mp_tmsg(MSGT_DEMUX, MSGL_WARN, "Not all of the xmcd file has been written.\n");
421 close(file_fd);
422 return -1;
425 close(file_fd);
427 return 0;
431 cddb_read_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data) {
432 unsigned long disc_id;
433 char category[100];
434 char *ptr=NULL, *ptr2=NULL;
435 int ret, status;
437 if( http_hdr==NULL || cddb_data==NULL ) return -1;
439 ret = sscanf( http_hdr->body, "%d ", &status);
440 if( ret!=1 ) {
441 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
442 return -1;
445 switch(status) {
446 case 210:
447 ret = sscanf( http_hdr->body, "%d %99s %08lx", &status, category, &disc_id);
448 if( ret!=3 ) {
449 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
450 return -1;
452 // Check if it's a xmcd database file
453 ptr = strstr(http_hdr->body, "# xmcd");
454 if( ptr==NULL ) {
455 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Invalid xmcd database file returned.\n");
456 return -1;
458 ptr = strdup(ptr);
459 // Ok found the beginning of the file
460 // look for the end
461 ptr2 = strstr(ptr, "\n.\r\n");
462 if (!ptr2)
463 ptr2 = strstr(ptr, "\n.\n");
464 if (ptr2) ptr2++;
465 else {
466 mp_msg(MSGT_DEMUX, MSGL_FIXME, "Unable to find '.'\n");
467 ptr2=ptr+strlen(ptr); //return -1;
469 // Ok found the end
470 // do a sanity check
471 if( http_hdr->body_size<(unsigned int)(ptr2-ptr) ) {
472 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "unexpected FIXME\n");
473 return -1;
475 cddb_data->xmcd_file = ptr;
476 cddb_data->xmcd_file_size = ptr2-ptr;
477 cddb_data->xmcd_file[cddb_data->xmcd_file_size] = '\0';
478 return cddb_write_cache(cddb_data);
479 default:
480 mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "unhandled code\n");
482 return 0;
486 cddb_request_titles(cddb_data_t *cddb_data) {
487 char command[1024];
488 sprintf( command, "cddb+read+%s+%08lx", cddb_data->category, cddb_data->disc_id);
489 return cddb_http_request(command, cddb_read_parse, cddb_data);
493 cddb_parse_matches_list(HTTP_header_t *http_hdr, cddb_data_t *cddb_data) {
494 char album_title[100];
495 char *ptr = NULL;
496 int ret;
498 ptr = strstr(http_hdr->body, "\n");
499 if( ptr==NULL ) {
500 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Unable to find end of line.\n");
501 return -1;
503 ptr++;
504 // We have a list of exact/inexact matches, so which one do we use?
505 // So let's take the first one.
506 ret = sscanf(ptr, "%99s %08lx %99s", cddb_data->category, &(cddb_data->disc_id), album_title);
507 if( ret!=3 ) {
508 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
509 return -1;
511 ptr = strstr(http_hdr->body, album_title);
512 if( ptr!=NULL ) {
513 char *ptr2;
514 int len;
515 ptr2 = strstr(ptr, "\n");
516 if( ptr2==NULL ) {
517 len = (http_hdr->body_size)-(ptr-(http_hdr->body));
518 } else {
519 len = ptr2-ptr+1;
521 len = FFMIN(sizeof(album_title) - 1, len);
522 strncpy(album_title, ptr, len);
523 album_title[len]='\0';
525 mp_tmsg(MSGT_DEMUX, MSGL_STATUS, "Parse OK, found: %s\n", album_title);
526 return 0;
530 cddb_query_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data) {
531 char album_title[100];
532 char *ptr = NULL;
533 int ret, status;
535 ret = sscanf( http_hdr->body, "%d ", &status);
536 if( ret!=1 ) {
537 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
538 return -1;
541 switch(status) {
542 case 200:
543 // Found exact match
544 ret = sscanf(http_hdr->body, "%d %99s %08lx %99s", &status, cddb_data->category, &(cddb_data->disc_id), album_title);
545 if( ret!=4 ) {
546 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
547 return -1;
549 ptr = strstr(http_hdr->body, album_title);
550 if( ptr!=NULL ) {
551 char *ptr2;
552 int len;
553 ptr2 = strstr(ptr, "\n");
554 if( ptr2==NULL ) {
555 len = (http_hdr->body_size)-(ptr-(http_hdr->body));
556 } else {
557 len = ptr2-ptr+1;
559 len = FFMIN(sizeof(album_title) - 1, len);
560 strncpy(album_title, ptr, len);
561 album_title[len]='\0';
563 mp_tmsg(MSGT_DEMUX, MSGL_STATUS, "Parse OK, found: %s\n", album_title);
564 return cddb_request_titles(cddb_data);
565 case 202:
566 // No match found
567 mp_tmsg(MSGT_DEMUX, MSGL_WARN, "Album not found.\n");
568 break;
569 case 210:
570 // Found exact matches, list follows
571 cddb_parse_matches_list(http_hdr, cddb_data);
572 return cddb_request_titles(cddb_data);
574 body=[210 Found exact matches, list follows (until terminating `.')
575 misc c711930d Santana / Supernatural
576 rock c711930d Santana / Supernatural
577 blues c711930d Santana / Supernatural
580 case 211:
581 // Found inexact matches, list follows
582 cddb_parse_matches_list(http_hdr, cddb_data);
583 return cddb_request_titles(cddb_data);
584 case 500:
585 mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "Server returns: Command syntax error\n");
586 break;
587 default:
588 mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "unhandled code\n");
590 return -1;
594 cddb_proto_level_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data) {
595 int max;
596 int ret, status;
597 char *ptr;
599 ret = sscanf( http_hdr->body, "%d ", &status);
600 if( ret!=1 ) {
601 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
602 return -1;
605 switch(status) {
606 case 210:
607 ptr = strstr(http_hdr->body, "max proto:");
608 if( ptr==NULL ) {
609 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
610 return -1;
612 ret = sscanf(ptr, "max proto: %d", &max);
613 if( ret!=1 ) {
614 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
615 return -1;
617 cddb_data->freedb_proto_level = max;
618 return 0;
619 default:
620 mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "unhandled code\n");
622 return -1;
626 cddb_get_proto_level(cddb_data_t *cddb_data) {
627 return cddb_http_request("stat", cddb_proto_level_parse, cddb_data);
631 cddb_freedb_sites_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data) {
632 int ret, status;
634 ret = sscanf( http_hdr->body, "%d ", &status);
635 if( ret!=1 ) {
636 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
637 return -1;
640 switch(status) {
641 case 210:
642 // TODO: Parse the sites
643 ret = cddb_data->anonymous; // For gcc complaining about unused parameter.
644 return 0;
645 case 401:
646 mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "No sites information available.\n");
647 break;
648 default:
649 mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "unhandled code\n");
651 return -1;
655 cddb_get_freedb_sites(cddb_data_t *cddb_data) {
656 return cddb_http_request("sites", cddb_freedb_sites_parse, cddb_data);
659 void
660 cddb_create_hello(cddb_data_t *cddb_data) {
661 char host_name[51];
662 char *user_name;
664 if( cddb_data->anonymous ) { // Default is anonymous
665 /* Note from Eduardo Pérez Ureta <eperez@it.uc3m.es> :
666 * We don't send current user/host name in hello to prevent spam.
667 * Software that sends this is considered spyware
668 * that most people don't like.
670 user_name = "anonymous";
671 strcpy(host_name, "localhost");
672 } else {
673 if( gethostname(host_name, 50)<0 ) {
674 strcpy(host_name, "localhost");
676 user_name = getenv("LOGNAME");
678 sprintf( cddb_data->cddb_hello, "&hello=%s+%s+%s+%s", user_name, host_name, "MPlayer", VERSION );
682 cddb_retrieve(cddb_data_t *cddb_data) {
683 char offsets[1024], command[1024];
684 char *ptr;
685 unsigned int i, time_len;
686 int ret;
688 ptr = offsets;
689 for( i=0; i<cddb_data->tracks ; i++ ) {
690 ptr += sprintf(ptr, "%d+", cdtoc[i].frame );
691 if (ptr-offsets > sizeof offsets - 40) break;
693 ptr[0]=0;
694 time_len = (cdtoc[cddb_data->tracks].frame)/75;
696 cddb_data->freedb_server = DEFAULT_FREEDB_SERVER;
697 cddb_data->freedb_proto_level = 1;
698 cddb_data->xmcd_file = NULL;
700 cddb_create_hello(cddb_data);
701 if( cddb_get_proto_level(cddb_data)<0 ) {
702 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to get the protocol level.\n");
703 return -1;
706 //cddb_get_freedb_sites(&cddb_data);
708 sprintf(command, "cddb+query+%08lx+%d+%s%d", cddb_data->disc_id, cddb_data->tracks, offsets, time_len );
709 ret = cddb_http_request(command, cddb_query_parse, cddb_data);
710 if( ret<0 ) return -1;
712 if( cddb_data->cache_dir!=NULL ) {
713 free(cddb_data->cache_dir);
715 return 0;
719 cddb_resolve(const char *dev, char **xmcd_file) {
720 char cddb_cache_dir[] = DEFAULT_CACHE_DIR;
721 char *home_dir = NULL;
722 cddb_data_t cddb_data;
724 if (cdtoc_last_track <= 0)
726 cdtoc_last_track = read_toc(dev);
727 if (cdtoc_last_track < 0) {
728 mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to open %s device.\n", dev);
729 return -1;
732 cddb_data.tracks = cdtoc_last_track;
733 cddb_data.disc_id = cddb_discid(cddb_data.tracks);
734 cddb_data.anonymous = 1; // Don't send user info by default
736 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CDDB_DISCID=%08lx\n", cddb_data.disc_id);
738 // Check if there is a CD in the drive
739 // FIXME: That's not really a good way to check
740 if( cddb_data.disc_id==0 ) {
741 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "No CD in the drive.\n");
742 return -1;
745 home_dir = getenv("HOME");
746 #ifdef __MINGW32__
747 if( home_dir==NULL ) home_dir = getenv("USERPROFILE");
748 if( home_dir==NULL ) home_dir = getenv("HOMEPATH");
749 // Last resort, store the cddb cache in the mplayer directory
750 if( home_dir==NULL ) home_dir = (char *)get_path("");
751 #endif
752 if( home_dir==NULL ) {
753 cddb_data.cache_dir = NULL;
754 } else {
755 cddb_data.cache_dir = malloc(strlen(home_dir)+strlen(cddb_cache_dir)+1);
756 if( cddb_data.cache_dir==NULL ) {
757 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Memory allocation failed.\n");
758 return -1;
760 sprintf(cddb_data.cache_dir, "%s%s", home_dir, cddb_cache_dir );
763 // Check for a cached file
764 if( cddb_read_cache(&cddb_data)<0 ) {
765 // No Cache found
766 if( cddb_retrieve(&cddb_data)<0 ) {
767 return -1;
771 if( cddb_data.xmcd_file!=NULL ) {
772 // printf("%s\n", cddb_data.xmcd_file );
773 *xmcd_file = cddb_data.xmcd_file;
774 return 0;
777 return -1;
780 /*******************************************************************************************************************
782 * xmcd parser
784 *******************************************************************************************************************/
785 char*
786 xmcd_parse_dtitle(cd_info_t *cd_info, char *line) {
787 char *ptr, *album;
788 ptr = strstr(line, "DTITLE=");
789 if( ptr!=NULL ) {
790 ptr += 7;
791 album = strstr(ptr, "/");
792 if( album==NULL ) return NULL;
793 cd_info->album = malloc(strlen(album+2)+1);
794 if( cd_info->album==NULL ) {
795 return NULL;
797 strcpy( cd_info->album, album+2 );
798 album--;
799 album[0] = '\0';
800 cd_info->artist = malloc(strlen(ptr)+1);
801 if( cd_info->artist==NULL ) {
802 return NULL;
804 strcpy( cd_info->artist, ptr );
806 return ptr;
809 char*
810 xmcd_parse_dgenre(cd_info_t *cd_info, char *line) {
811 char *ptr;
812 ptr = strstr(line, "DGENRE=");
813 if( ptr!=NULL ) {
814 ptr += 7;
815 cd_info->genre = malloc(strlen(ptr)+1);
816 if( cd_info->genre==NULL ) {
817 return NULL;
819 strcpy( cd_info->genre, ptr );
821 return ptr;
824 char*
825 xmcd_parse_ttitle(cd_info_t *cd_info, char *line) {
826 unsigned int track_nb;
827 unsigned long sec, off;
828 char *ptr;
829 ptr = strstr(line, "TTITLE");
830 if( ptr!=NULL ) {
831 ptr += 6;
832 // Here we point to the track number
833 track_nb = atoi(ptr);
834 ptr = strstr(ptr, "=");
835 if( ptr==NULL ) return NULL;
836 ptr++;
838 sec = cdtoc[track_nb].frame;
839 off = cdtoc[track_nb+1].frame-sec+1;
841 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 );
843 return ptr;
846 cd_info_t*
847 cddb_parse_xmcd(char *xmcd_file) {
848 cd_info_t *cd_info = NULL;
849 int length, pos = 0;
850 char *ptr, *ptr2;
851 unsigned int audiolen;
852 if( xmcd_file==NULL ) return NULL;
854 cd_info = cd_info_new();
855 if( cd_info==NULL ) {
856 return NULL;
859 length = strlen(xmcd_file);
860 ptr = xmcd_file;
861 while( ptr!=NULL && pos<length ) {
862 // Read a line
863 ptr2 = ptr;
864 while( ptr2[0]!='\0' && ptr2[0]!='\r' && ptr2[0]!='\n' ) ptr2++;
865 if( ptr2[0]=='\0' ) {
866 break;
868 ptr2[0] = '\0';
869 // Ignore comments
870 if( ptr[0]!='#' ) {
871 // Search for the album title
872 if( xmcd_parse_dtitle(cd_info, ptr) );
873 // Search for the genre
874 else if( xmcd_parse_dgenre(cd_info, ptr) );
875 // Search for a track title
876 else if( xmcd_parse_ttitle(cd_info, ptr) ) audiolen++; // <-- audiolen++ to shut up gcc warning
878 if( ptr2[1]=='\n' ) ptr2++;
879 pos = (ptr2+1)-ptr;
880 ptr = ptr2+1;
883 audiolen = cdtoc[cd_info->nb_tracks].frame-cdtoc[0].frame;
884 cd_info->min = (unsigned int)(audiolen/(60*75));
885 cd_info->sec = (unsigned int)((audiolen/75)%60);
886 cd_info->msec = (unsigned int)(audiolen%75);
888 return cd_info;