x11: allow unicode input
[mplayer.git] / stream / stream_cddb.c
blobaab557ab868430a1d263f8490ca71bde87eba2d2
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 #include "osdep/io.h"
40 #if defined(__MINGW32__) || defined(__CYGWIN__)
41 #include <windows.h>
42 #if HAVE_WINSOCK2_H
43 #include <winsock2.h>
44 #endif
45 #else
46 #include <netdb.h>
47 #include <sys/ioctl.h>
48 #endif
49 #include <sys/types.h>
50 #include <sys/stat.h>
52 #include "mp_msg.h"
53 #include "path.h"
55 #if defined(__linux__)
56 #include <linux/cdrom.h>
57 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
58 #include <sys/cdio.h>
59 #elif defined(__MINGW32__) || defined(__CYGWIN__)
60 #include <ddk/ntddcdrm.h>
61 #elif defined(__bsdi__)
62 #include <dvd.h>
63 #elif defined(__APPLE__) || defined(__DARWIN__)
64 #include <IOKit/storage/IOCDTypes.h>
65 #include <IOKit/storage/IOCDMediaBSDClient.h>
66 #include "mpbswap.h"
67 #endif
69 #include "osdep/osdep.h"
71 #include "cdd.h"
72 #include "mpcommon.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 typedef struct {
81 char cddb_hello[1024];
82 unsigned long disc_id;
83 unsigned int tracks;
84 char *cache_dir;
85 char *freedb_server;
86 int freedb_proto_level;
87 int anonymous;
88 char category[100];
89 char *xmcd_file;
90 size_t xmcd_file_size;
91 void *user_data;
92 } cddb_data_t;
94 typedef struct {
95 unsigned int min, sec, frame;
96 } cd_toc_t;
98 static cd_toc_t cdtoc[100];
99 static int cdtoc_last_track;
101 static int read_toc(const char *dev)
103 int first = 0, last = -1;
104 int i;
105 #if defined(__MINGW32__) || defined(__CYGWIN__)
106 HANDLE drive;
107 DWORD r;
108 CDROM_TOC toc;
109 char device[10];
111 sprintf(device, "\\\\.\\%s", dev);
112 drive = CreateFile(device, GENERIC_READ, FILE_SHARE_READ, NULL,
113 OPEN_EXISTING, 0, 0);
115 if (!DeviceIoControl(drive, IOCTL_CDROM_READ_TOC, NULL, 0, &toc,
116 sizeof(CDROM_TOC), &r, 0)) {
117 mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to read TOC.\n");
118 return 0;
121 first = toc.FirstTrack - 1; last = toc.LastTrack;
122 for (i = first; i <= last; i++) {
123 cdtoc[i].min = toc.TrackData[i].Address[1];
124 cdtoc[i].sec = toc.TrackData[i].Address[2];
125 cdtoc[i].frame = toc.TrackData[i].Address[3];
127 CloseHandle(drive);
129 #elif defined(__OS2__)
130 UCHAR auchParamDisk[4] = {'C', 'D', '0', '1'};
132 struct {
133 BYTE bFirstTrack;
134 BYTE bLastTrack;
135 BYTE bLeadOutF;
136 BYTE bLeadOutS;
137 BYTE bLeadOutM;
138 BYTE bLeadOutReserved;
139 } __attribute__((packed)) sDataDisk;
141 struct {
142 UCHAR auchSign[4];
143 BYTE bTrack;
144 } __attribute__((packed)) sParamTrack = {{'C', 'D', '0', '1'},};
146 struct {
147 BYTE bStartF;
148 BYTE bStartS;
149 BYTE bStartM;
150 BYTE bStartReserved;
151 BYTE bControlInfo;
152 } __attribute__((packed)) sDataTrack;
154 HFILE hcd;
155 ULONG ulAction;
156 ULONG ulParamLen;
157 ULONG ulDataLen;
158 ULONG rc;
160 rc = DosOpen(dev, &hcd, &ulAction, 0, FILE_NORMAL,
161 OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
162 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_DASD,
163 NULL);
164 if (rc) {
165 mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to read TOC.\n");
166 return -1;
169 rc = DosDevIOCtl(hcd, IOCTL_CDROMAUDIO, CDROMAUDIO_GETAUDIODISK,
170 auchParamDisk, sizeof(auchParamDisk), &ulParamLen,
171 &sDataDisk, sizeof(sDataDisk), &ulDataLen);
172 if (!rc) {
173 first = sDataDisk.bFirstTrack - 1;
174 last = sDataDisk.bLastTrack;
175 for (i = first; i <= last; i++) {
176 if (i == last) {
177 sDataTrack.bStartM = sDataDisk.bLeadOutM;
178 sDataTrack.bStartS = sDataDisk.bLeadOutS;
179 sDataTrack.bStartF = sDataDisk.bLeadOutF;
180 } else {
181 sParamTrack.bTrack = i + 1;
182 rc = DosDevIOCtl(hcd, IOCTL_CDROMAUDIO, CDROMAUDIO_GETAUDIOTRACK,
183 &sParamTrack, sizeof(sParamTrack), &ulParamLen,
184 &sDataTrack, sizeof(sDataTrack), &ulDataLen);
185 if (rc)
186 break;
189 cdtoc[i].min = sDataTrack.bStartM;
190 cdtoc[i].sec = sDataTrack.bStartS;
191 cdtoc[i].frame = sDataTrack.bStartF;
195 DosClose(hcd);
197 if (rc) {
198 mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to read TOC.\n");
199 return -1;
201 #else
202 int drive;
203 drive = open(dev, O_RDONLY | O_NONBLOCK);
204 if (drive < 0) {
205 return drive;
208 #if defined(__linux__) || defined(__bsdi__)
210 struct cdrom_tochdr tochdr;
211 ioctl(drive, CDROMREADTOCHDR, &tochdr);
212 first = tochdr.cdth_trk0 - 1; last = tochdr.cdth_trk1;
214 for (i = first; i <= last; i++) {
215 struct cdrom_tocentry tocentry;
216 tocentry.cdte_track = (i == last) ? 0xAA : i + 1;
217 tocentry.cdte_format = CDROM_MSF;
218 ioctl(drive, CDROMREADTOCENTRY, &tocentry);
219 cdtoc[i].min = tocentry.cdte_addr.msf.minute;
220 cdtoc[i].sec = tocentry.cdte_addr.msf.second;
221 cdtoc[i].frame = tocentry.cdte_addr.msf.frame;
223 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
225 struct ioc_toc_header tochdr;
226 ioctl(drive, CDIOREADTOCHEADER, &tochdr);
227 first = tochdr.starting_track - 1; last = tochdr.ending_track;
229 for (i = first; i <= last; i++) {
230 struct ioc_read_toc_single_entry tocentry;
231 tocentry.track = (i == last) ? 0xAA : i + 1;
232 tocentry.address_format = CD_MSF_FORMAT;
233 ioctl(drive, CDIOREADTOCENTRY, &tocentry);
234 cdtoc[i].min = tocentry.entry.addr.msf.minute;
235 cdtoc[i].sec = tocentry.entry.addr.msf.second;
236 cdtoc[i].frame = tocentry.entry.addr.msf.frame;
238 #elif defined(__NetBSD__) || defined(__OpenBSD__)
240 struct ioc_toc_header tochdr;
241 ioctl(drive, CDIOREADTOCHEADER, &tochdr);
242 first = tochdr.starting_track - 1; last = tochdr.ending_track;
244 for (i = first; i <= last; i++) {
245 struct ioc_read_toc_entry tocentry;
246 struct cd_toc_entry toc_buffer;
247 tocentry.starting_track = (i == last) ? 0xAA : i + 1;
248 tocentry.address_format = CD_MSF_FORMAT;
249 tocentry.data = &toc_buffer;
250 tocentry.data_len = sizeof(toc_buffer);
251 ioctl(drive, CDIOREADTOCENTRYS, &tocentry);
252 cdtoc[i].min = toc_buffer.addr.msf.minute;
253 cdtoc[i].sec = toc_buffer.addr.msf.second;
254 cdtoc[i].frame = toc_buffer.addr.msf.frame;
256 #elif defined(__APPLE__) || defined(__DARWIN__)
258 dk_cd_read_toc_t tochdr;
259 uint8_t buf[4];
260 uint8_t buf2[100 * sizeof(CDTOCDescriptor) + sizeof(CDTOC)];
261 memset(&tochdr, 0, sizeof(tochdr));
262 tochdr.bufferLength = sizeof(buf);
263 tochdr.buffer = &buf;
264 if (!ioctl(drive, DKIOCCDREADTOC, &tochdr)
265 && tochdr.bufferLength == sizeof(buf)) {
266 first = buf[2] - 1;
267 last = buf[3];
269 if (last >= 0) {
270 memset(&tochdr, 0, sizeof(tochdr));
271 tochdr.bufferLength = sizeof(buf2);
272 tochdr.buffer = &buf2;
273 tochdr.format = kCDTOCFormatTOC;
274 if (ioctl(drive, DKIOCCDREADTOC, &tochdr)
275 || tochdr.bufferLength < sizeof(CDTOC))
276 last = -1;
278 if (last >= 0) {
279 CDTOC *cdToc = (CDTOC *)buf2;
280 CDTrackInfo lastTrack;
281 dk_cd_read_track_info_t trackInfoParams;
282 for (i = first; i < last; ++i) {
283 CDMSF msf = CDConvertTrackNumberToMSF(i + 1, cdToc);
284 cdtoc[i].min = msf.minute;
285 cdtoc[i].sec = msf.second;
286 cdtoc[i].frame = msf.frame;
288 memset(&trackInfoParams, 0, sizeof(trackInfoParams));
289 trackInfoParams.addressType = kCDTrackInfoAddressTypeTrackNumber;
290 trackInfoParams.bufferLength = sizeof(lastTrack);
291 trackInfoParams.address = last;
292 trackInfoParams.buffer = &lastTrack;
293 if (!ioctl(drive, DKIOCCDREADTRACKINFO, &trackInfoParams)) {
294 CDMSF msf = CDConvertLBAToMSF(be2me_32(lastTrack.trackStartAddress)
295 + be2me_32(lastTrack.trackSize));
296 cdtoc[last].min = msf.minute;
297 cdtoc[last].sec = msf.second;
298 cdtoc[last].frame = msf.frame;
302 #endif
303 close(drive);
304 #endif
305 for (i = first; i <= last; i++)
306 cdtoc[i].frame += (cdtoc[i].min * 60 + cdtoc[i].sec) * 75;
307 return last;
311 \brief Reads TOC from CD in the given device and prints the number of tracks
312 and the length of each track in minute:second:frame format.
313 \param *dev the device to analyse
314 \return if the command line -identify is given, returns the last track of
315 the TOC or -1 if the TOC can't be read,
316 otherwise just returns 0 and let cddb_resolve the TOC
318 int cdd_identify(const char *dev)
320 cdtoc_last_track = 0;
321 if (mp_msg_test(MSGT_IDENTIFY, MSGL_INFO)) {
322 int i, min, sec, frame;
323 cdtoc_last_track = read_toc(dev);
324 if (cdtoc_last_track < 0) {
325 mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to open %s device.\n", dev);
326 return -1;
328 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ID_CDDA_TRACKS=%d\n", cdtoc_last_track);
329 for (i = 1; i <= cdtoc_last_track; i++) {
330 frame = cdtoc[i].frame - cdtoc[i-1].frame;
331 sec = frame / 75;
332 frame -= sec * 75;
333 min = sec / 60;
334 sec -= min * 60;
335 mp_msg(MSGT_IDENTIFY, MSGL_INFO,
336 "ID_CDDA_TRACK_%d_MSF=%02d:%02d:%02d\n", i, min, sec, frame);
339 return cdtoc_last_track;
342 static unsigned int cddb_sum(int n)
344 unsigned int ret;
346 ret = 0;
347 while (n > 0) {
348 ret += (n % 10);
349 n /= 10;
351 return ret;
354 static unsigned long cddb_discid(int tot_trks)
356 unsigned int i, t = 0, n = 0;
358 i = 0;
359 while (i < (unsigned int)tot_trks) {
360 n = n + cddb_sum((cdtoc[i].min * 60) + cdtoc[i].sec);
361 i++;
363 t = ((cdtoc[tot_trks].min * 60) + cdtoc[tot_trks].sec) -
364 ((cdtoc[0].min * 60) + cdtoc[0].sec);
365 return (n % 0xff) << 24 | t << 8 | tot_trks;
370 static int cddb_http_request(char *command,
371 int (*reply_parser)(HTTP_header_t*, cddb_data_t*),
372 cddb_data_t *cddb_data)
374 char request[4096];
375 int fd, ret = 0;
376 URL_t *url;
377 HTTP_header_t *http_hdr;
379 if (reply_parser == NULL || command == NULL || cddb_data == NULL)
380 return -1;
382 sprintf(request, "http://%s/~cddb/cddb.cgi?cmd=%s%s&proto=%d",
383 cddb_data->freedb_server, command, cddb_data->cddb_hello,
384 cddb_data->freedb_proto_level);
385 mp_msg(MSGT_OPEN, MSGL_INFO,"Request[%s]\n", request);
387 url = url_new(request);
388 if (url == NULL) {
389 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "not a valid URL\n");
390 return -1;
393 fd = http_send_request(url,0);
394 if (fd < 0) {
395 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to send the HTTP request.\n");
396 return -1;
399 http_hdr = http_read_response(fd);
400 if (http_hdr == NULL) {
401 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to read the HTTP response.\n");
402 return -1;
405 http_debug_hdr(http_hdr);
406 mp_msg(MSGT_OPEN, MSGL_INFO,"body=[%s]\n", http_hdr->body);
408 switch (http_hdr->status_code) {
409 case 200:
410 ret = reply_parser(http_hdr, cddb_data);
411 break;
412 case 400:
413 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Not Found.\n");
414 break;
415 default:
416 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "unknown error code\n");
419 http_free(http_hdr);
420 url_free(url);
422 return ret;
425 static int cddb_read_cache(cddb_data_t *cddb_data)
427 char file_name[100];
428 struct stat stats;
429 int file_fd, ret;
430 size_t file_size;
432 if (cddb_data == NULL || cddb_data->cache_dir == NULL)
433 return -1;
435 sprintf(file_name, "%s%08lx", cddb_data->cache_dir, cddb_data->disc_id);
437 file_fd = open(file_name, O_RDONLY | O_BINARY);
438 if (file_fd < 0) {
439 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "No cache found.\n");
440 return -1;
443 ret = fstat(file_fd, &stats);
444 if (ret < 0) {
445 perror("fstat");
446 file_size = 4096;
447 } else {
448 file_size = stats.st_size < UINT_MAX ? stats.st_size : UINT_MAX - 1;
451 cddb_data->xmcd_file = malloc(file_size + 1);
452 if (cddb_data->xmcd_file == NULL) {
453 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Memory allocation failed.\n");
454 close(file_fd);
455 return -1;
457 cddb_data->xmcd_file_size = read(file_fd, cddb_data->xmcd_file, file_size);
458 if (cddb_data->xmcd_file_size != file_size) {
459 mp_tmsg(MSGT_DEMUX, MSGL_WARN, "Not all the xmcd file has been read.\n");
460 close(file_fd);
461 return -1;
463 cddb_data->xmcd_file[cddb_data->xmcd_file_size] = 0;
465 close(file_fd);
467 return 0;
470 static int cddb_write_cache(cddb_data_t *cddb_data)
472 // We have the file, save it for cache.
473 char file_name[100];
474 int file_fd, ret;
475 int wrote = 0;
477 if (cddb_data == NULL || cddb_data->cache_dir == NULL)
478 return -1;
480 // Check if the CDDB cache dir exist
481 if (!mp_path_exists(cddb_data->cache_dir)) {
482 // Directory not present, create it.
483 ret = mkdir(cddb_data->cache_dir, 0755);
484 #ifdef __MINGW32__
485 if (ret < 0 && errno != EEXIST) {
486 #else
487 if (ret < 0) {
488 #endif
489 perror("mkdir");
490 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to create directory %s.\n",
491 cddb_data->cache_dir);
492 return -1;
496 sprintf(file_name, "%s%08lx", cddb_data->cache_dir, cddb_data->disc_id);
498 file_fd = creat(file_name, S_IRUSR | S_IWUSR);
499 if (file_fd < 0) {
500 perror("create");
501 return -1;
504 wrote = write(file_fd, cddb_data->xmcd_file, cddb_data->xmcd_file_size);
505 if (wrote < 0) {
506 perror("write");
507 close(file_fd);
508 return -1;
510 if ((unsigned int) wrote != cddb_data->xmcd_file_size) {
511 mp_tmsg(MSGT_DEMUX, MSGL_WARN, "Not all of the xmcd file has been written.\n");
512 close(file_fd);
513 return -1;
516 close(file_fd);
518 return 0;
521 static int cddb_read_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data)
523 unsigned long disc_id;
524 char category[100];
525 char *ptr = NULL, *ptr2 = NULL;
526 int ret, status;
528 if (http_hdr == NULL || cddb_data == NULL)
529 return -1;
531 ret = sscanf(http_hdr->body, "%d ", &status);
532 if (ret != 1) {
533 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
534 return -1;
537 switch (status) {
538 case 210:
539 ret = sscanf(http_hdr->body, "%d %99s %08lx", &status,
540 category, &disc_id);
541 if (ret != 3) {
542 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
543 return -1;
545 // Check if it's a xmcd database file
546 ptr = strstr(http_hdr->body, "# xmcd");
547 if (ptr == NULL) {
548 mp_tmsg(MSGT_DEMUX, MSGL_ERR,
549 "Invalid xmcd database file returned.\n");
550 return -1;
552 ptr = strdup(ptr);
553 // Ok found the beginning of the file
554 // look for the end
555 ptr2 = strstr(ptr, "\n.\r\n");
556 if (!ptr2)
557 ptr2 = strstr(ptr, "\n.\n");
558 if (ptr2) {
559 ptr2++;
560 } else {
561 mp_msg(MSGT_DEMUX, MSGL_FIXME, "Unable to find '.'\n");
562 ptr2 = ptr + strlen(ptr); //return -1;
564 // Ok found the end
565 // do a sanity check
566 if (http_hdr->body_size < (unsigned int)(ptr2 - ptr)) {
567 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "unexpected FIXME\n");
568 return -1;
570 cddb_data->xmcd_file = ptr;
571 cddb_data->xmcd_file_size = ptr2 - ptr;
572 cddb_data->xmcd_file[cddb_data->xmcd_file_size] = '\0';
573 return cddb_write_cache(cddb_data);
574 default:
575 mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "unhandled code\n");
577 return 0;
580 static int cddb_request_titles(cddb_data_t *cddb_data)
582 char command[1024];
583 sprintf(command, "cddb+read+%s+%08lx",
584 cddb_data->category, cddb_data->disc_id);
585 return cddb_http_request(command, cddb_read_parse, cddb_data);
588 static int cddb_parse_matches_list(HTTP_header_t *http_hdr, cddb_data_t *cddb_data)
590 char album_title[100];
591 char *ptr = NULL;
592 int ret;
594 ptr = strstr(http_hdr->body, "\n");
595 if (ptr == NULL) {
596 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Unable to find end of line.\n");
597 return -1;
599 ptr++;
600 // We have a list of exact/inexact matches, so which one do we use?
601 // So let's take the first one.
602 ret = sscanf(ptr, "%99s %08lx %99s", cddb_data->category,
603 &(cddb_data->disc_id), album_title);
604 if (ret != 3) {
605 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
606 return -1;
608 ptr = strstr(http_hdr->body, album_title);
609 if (ptr != NULL) {
610 char *ptr2;
611 int len;
612 ptr2 = strstr(ptr, "\n");
613 if (ptr2 == NULL) {
614 len = (http_hdr->body_size)-(ptr-(http_hdr->body));
615 } else {
616 len = ptr2-ptr+1;
618 len = FFMIN(sizeof(album_title) - 1, len);
619 strncpy(album_title, ptr, len);
620 album_title[len]='\0';
622 mp_tmsg(MSGT_DEMUX, MSGL_STATUS, "Parse OK, found: %s\n", album_title);
623 return 0;
626 static int cddb_query_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data)
628 char album_title[100];
629 char *ptr = NULL;
630 int ret, status;
632 ret = sscanf(http_hdr->body, "%d ", &status);
633 if (ret != 1) {
634 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
635 return -1;
638 switch (status) {
639 case 200:
640 // Found exact match
641 ret = sscanf(http_hdr->body, "%d %99s %08lx %99s", &status,
642 cddb_data->category, &(cddb_data->disc_id), album_title);
643 if (ret != 4) {
644 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
645 return -1;
647 ptr = strstr(http_hdr->body, album_title);
648 if (ptr != NULL) {
649 char *ptr2;
650 int len;
651 ptr2 = strstr(ptr, "\n");
652 if (ptr2 == NULL) {
653 len = (http_hdr->body_size)-(ptr-(http_hdr->body));
654 } else {
655 len = ptr2-ptr+1;
657 len = FFMIN(sizeof(album_title) - 1, len);
658 strncpy(album_title, ptr, len);
659 album_title[len]='\0';
661 mp_tmsg(MSGT_DEMUX, MSGL_STATUS, "Parse OK, found: %s\n", album_title);
662 return cddb_request_titles(cddb_data);
663 case 202:
664 // No match found
665 mp_tmsg(MSGT_DEMUX, MSGL_WARN, "Album not found.\n");
666 break;
667 case 210:
668 // Found exact matches, list follows
669 cddb_parse_matches_list(http_hdr, cddb_data);
670 return cddb_request_titles(cddb_data);
672 body=[210 Found exact matches, list follows (until terminating `.')
673 misc c711930d Santana / Supernatural
674 rock c711930d Santana / Supernatural
675 blues c711930d Santana / Supernatural
678 case 211:
679 // Found inexact matches, list follows
680 cddb_parse_matches_list(http_hdr, cddb_data);
681 return cddb_request_titles(cddb_data);
682 case 500:
683 mp_tmsg(MSGT_DEMUX, MSGL_FIXME,
684 "Server returns: Command syntax error\n");
685 break;
686 default:
687 mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "unhandled code\n");
689 return -1;
692 static int cddb_proto_level_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data)
694 int max;
695 int ret, status;
696 char *ptr;
698 ret = sscanf(http_hdr->body, "%d ", &status);
699 if (ret != 1) {
700 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
701 return -1;
704 switch (status) {
705 case 210:
706 ptr = strstr(http_hdr->body, "max proto:");
707 if (ptr == NULL) {
708 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
709 return -1;
711 ret = sscanf(ptr, "max proto: %d", &max);
712 if (ret != 1) {
713 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
714 return -1;
716 cddb_data->freedb_proto_level = max;
717 return 0;
718 default:
719 mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "unhandled code\n");
721 return -1;
724 static int cddb_get_proto_level(cddb_data_t *cddb_data)
726 return cddb_http_request("stat", cddb_proto_level_parse, cddb_data);
729 static void cddb_create_hello(cddb_data_t *cddb_data)
731 char host_name[51];
732 char *user_name;
734 if (cddb_data->anonymous) { // Default is anonymous
735 /* Note from Eduardo Pérez Ureta <eperez@it.uc3m.es> :
736 * We don't send current user/host name in hello to prevent spam.
737 * Software that sends this is considered spyware
738 * that most people don't like.
740 user_name = "anonymous";
741 strcpy(host_name, "localhost");
742 } else {
743 if (gethostname(host_name, 50) < 0) {
744 strcpy(host_name, "localhost");
746 user_name = getenv("LOGNAME");
748 sprintf(cddb_data->cddb_hello, "&hello=%s+%s+%s",
749 user_name, host_name, mplayer_version);
752 static int cddb_retrieve(cddb_data_t *cddb_data)
754 char offsets[1024], command[1024];
755 char *ptr;
756 unsigned int i, time_len;
757 int ret;
759 ptr = offsets;
760 for (i = 0; i < cddb_data->tracks ; i++) {
761 ptr += sprintf(ptr, "%d+", cdtoc[i].frame);
762 if (ptr-offsets > sizeof offsets - 40) break;
764 ptr[0] = 0;
765 time_len = (cdtoc[cddb_data->tracks].frame)/75;
767 cddb_data->freedb_server = DEFAULT_FREEDB_SERVER;
768 cddb_data->freedb_proto_level = 1;
769 cddb_data->xmcd_file = NULL;
771 cddb_create_hello(cddb_data);
772 if (cddb_get_proto_level(cddb_data) < 0) {
773 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to get the protocol level.\n");
774 return -1;
777 sprintf(command, "cddb+query+%08lx+%d+%s%d", cddb_data->disc_id,
778 cddb_data->tracks, offsets, time_len);
779 ret = cddb_http_request(command, cddb_query_parse, cddb_data);
780 if (ret < 0)
781 return -1;
783 free(cddb_data->cache_dir);
784 return 0;
787 int cddb_resolve(const char *dev, char **xmcd_file)
789 char cddb_cache_dir[] = DEFAULT_CACHE_DIR;
790 char *home_dir = NULL;
791 cddb_data_t cddb_data;
793 if (cdtoc_last_track <= 0) {
794 cdtoc_last_track = read_toc(dev);
795 if (cdtoc_last_track < 0) {
796 mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to open %s device.\n", dev);
797 return -1;
800 cddb_data.tracks = cdtoc_last_track;
801 cddb_data.disc_id = cddb_discid(cddb_data.tracks);
802 cddb_data.anonymous = 1; // Don't send user info by default
804 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CDDB_DISCID=%08lx\n",
805 cddb_data.disc_id);
807 // Check if there is a CD in the drive
808 // FIXME: That's not really a good way to check
809 if (cddb_data.disc_id == 0) {
810 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "No CD in the drive.\n");
811 return -1;
814 home_dir = getenv("HOME");
815 #ifdef __MINGW32__
816 if (home_dir == NULL)
817 home_dir = getenv("USERPROFILE");
818 if (home_dir == NULL)
819 home_dir = getenv("HOMEPATH");
820 // Last resort, store the cddb cache in the mplayer directory
821 if (home_dir == NULL)
822 home_dir = (char *)get_path("");
823 #endif
824 if (home_dir == NULL) {
825 cddb_data.cache_dir = NULL;
826 } else {
827 cddb_data.cache_dir = malloc(strlen(home_dir)
828 + strlen(cddb_cache_dir) + 1);
829 if (cddb_data.cache_dir == NULL) {
830 mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Memory allocation failed.\n");
831 return -1;
833 sprintf(cddb_data.cache_dir, "%s%s", home_dir, cddb_cache_dir);
836 // Check for a cached file
837 if (cddb_read_cache(&cddb_data) < 0) {
838 // No Cache found
839 if (cddb_retrieve(&cddb_data) < 0) {
840 return -1;
844 if (cddb_data.xmcd_file != NULL) {
845 // printf("%s\n", cddb_data.xmcd_file);
846 *xmcd_file = cddb_data.xmcd_file;
847 return 0;
850 return -1;
853 /***************
854 * xmcd parser *
855 ***************/
856 static char *xmcd_parse_dtitle(cd_info_t *cd_info, char *line)
858 char *ptr, *album;
859 ptr = strstr(line, "DTITLE=");
860 if (ptr != NULL) {
861 ptr += 7;
862 album = strstr(ptr, "/");
863 if (album == NULL)
864 return NULL;
865 cd_info->album = malloc(strlen(album + 2) + 1);
866 if (cd_info->album == NULL) {
867 return NULL;
869 strcpy(cd_info->album, album + 2);
870 album--;
871 album[0] = '\0';
872 cd_info->artist = malloc(strlen(ptr) + 1);
873 if (cd_info->artist == NULL) {
874 return NULL;
876 strcpy(cd_info->artist, ptr);
878 return ptr;
881 static char *xmcd_parse_dgenre(cd_info_t *cd_info, char *line)
883 char *ptr;
884 ptr = strstr(line, "DGENRE=");
885 if (ptr != NULL) {
886 ptr += 7;
887 cd_info->genre = malloc(strlen(ptr)+1);
888 if (cd_info->genre == NULL) {
889 return NULL;
891 strcpy(cd_info->genre, ptr);
893 return ptr;
896 static char *xmcd_parse_ttitle(cd_info_t *cd_info, char *line)
898 unsigned int track_nb;
899 unsigned long sec, off;
900 char *ptr;
901 ptr = strstr(line, "TTITLE");
902 if (ptr != NULL) {
903 ptr += 6;
904 // Here we point to the track number
905 track_nb = atoi(ptr);
906 ptr = strstr(ptr, "=");
907 if (ptr == NULL)
908 return NULL;
909 ptr++;
911 sec = cdtoc[track_nb].frame;
912 off = cdtoc[track_nb + 1].frame - sec + 1;
914 cd_info_add_track(cd_info, ptr, track_nb + 1,
915 (unsigned int) (off / (60 * 75)),
916 (unsigned int) ((off / 75) % 60),
917 (unsigned int) (off % 75),
918 sec, off);
920 return ptr;
923 cd_info_t *cddb_parse_xmcd(char *xmcd_file)
925 cd_info_t *cd_info = NULL;
926 int length, pos = 0;
927 char *ptr, *ptr2;
928 unsigned int audiolen;
929 if (xmcd_file == NULL)
930 return NULL;
932 cd_info = cd_info_new();
933 if (cd_info == NULL) {
934 return NULL;
937 length = strlen(xmcd_file);
938 ptr = xmcd_file;
939 while (ptr != NULL && pos < length) {
940 // Read a line
941 ptr2 = ptr;
942 while(ptr2[0] != '\0' && ptr2[0] != '\r' && ptr2[0] != '\n')
943 ptr2++;
944 if (ptr2[0] == '\0') {
945 break;
947 ptr2[0] = '\0';
948 // Ignore comments
949 if (ptr[0] != '#') {
950 // Search for the album title
951 if (xmcd_parse_dtitle(cd_info, ptr))
953 // Search for the genre
954 else if (xmcd_parse_dgenre(cd_info, ptr))
956 // Search for a track title
957 else if (xmcd_parse_ttitle(cd_info, ptr))
958 audiolen++; // <-- audiolen++ to shut up gcc warning
960 if (ptr2[1] == '\n')
961 ptr2++;
962 pos = (ptr2 + 1) - ptr;
963 ptr = ptr2 + 1;
966 audiolen = cdtoc[cd_info->nb_tracks].frame-cdtoc[0].frame;
967 cd_info->min = (unsigned int) (audiolen / (60 * 75));
968 cd_info->sec = (unsigned int) ((audiolen / 75) % 60);
969 cd_info->msec = (unsigned int) (audiolen % 75);
971 return cd_info;