1 /* 2 * libcddb - CDDB Interface Library for xmcd/cda 3 * 4 * This library implements an interface to access the "classic" 5 * CDDB1 services. 6 * 7 * Copyright (C) 1993-2004 Ti Kan 8 * E-mail: xmcd@amb.org 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 * 24 */ 25 #ifndef lint 26 static char *_fcddb_c_ident_ = "@(#)fcddb.c 1.110 04/04/20"; 27 #endif 28 29 #include "fcddb.h" 30 #include "common_d/version.h" 31 #include "cddbp.h" 32 #include "genretbl.h" 33 #include "regiontbl.h" 34 #include "langtbl.h" 35 #include "roletbl.h" 36 37 #ifndef __VMS 38 /* UNIX */ 39 40 #ifndef NOREMOTE 41 #include <sys/socket.h> 42 #include <sys/time.h> 43 #include <netinet/in.h> 44 #include <arpa/inet.h> 45 #include <netdb.h> 46 #if defined(_AIX) || defined(__QNX__) 47 #include <sys/select.h> 48 #endif 49 #endif /* NOREMOTE */ 50 51 /* Directory path name convention */ 52 #define DIR_BEG '/' /* Directory start */ 53 #define DIR_SEP '/' /* Directory separator */ 54 #define DIR_END '/' /* Directory end */ 55 #define CUR_DIR "." /* Current directory */ 56 57 /* Defines used by fcddb_runcmd */ 58 #define STR_SHPATH "/bin/sh" /* Path to shell */ 59 #define STR_SHNAME "sh" /* Name of shell */ 60 #define STR_SHARG "-c" /* Shell arg */ 61 62 #else 63 /* OpenVMS */ 64 65 #ifndef NOREMOTE 66 #include <socket.h> 67 #include <time.h> 68 #include <in.h> 69 #include <inet.h> 70 #include <netdb.h> 71 #endif /* NOREMOTE */ 72 73 /* Directory path name convention */ 74 #define DIR_BEG '[' /* Directory start */ 75 #define DIR_SEP '.' /* Directory separator */ 76 #define DIR_END ']' /* Directory end */ 77 #define CUR_DIR "[]" /* Current directory */ 78 79 #endif /* __VMS */ 80 81 /* Max host name length */ 82 #ifndef HOST_NAM_SZ 83 #define HOST_NAM_SZ 64 84 #endif 85 86 /* Minimum/maximum macros */ 87 #define FCDDB2_MIN(a,b) (((a) > (b)) ? (b) : (a)) 88 #define FCDDB2_MAX(a,b) (((a) > (b)) ? (a) : (b)) 89 90 /* Skip white space macro */ 91 #define SKIP_SPC(p) while (*(p) == ' ' || *(p) == '\t') (p)++; 92 93 /* CDDBD status code macros */ 94 #define STATCODE_1ST(p) ((p)[0]) 95 #define STATCODE_2ND(p) ((p)[1]) 96 #define STATCODE_3RD(p) ((p)[2]) 97 #define STATCODE_CHECK(p) \ 98 (STATCODE_1ST(p) != '\0' && isdigit((int) STATCODE_1ST(p)) && \ 99 STATCODE_2ND(p) != '\0' && isdigit((int) STATCODE_2ND(p)) && \ 100 STATCODE_3RD(p) != '\0' && isdigit((int) STATCODE_3RD(p))) 101 102 /* HTTP status codes */ 103 #define HTTP_PROXYAUTH_FAIL 407 104 105 /* Number of seconds per day */ 106 #define SECS_PER_DAY (24 * 60 * 60) 107 108 /* Number of seconds to wait for an external command to complete */ 109 #define CMD_TIMEOUT_SECS 60 110 111 112 bool_t fcddb_debug; /* Debug flag */ 113 FILE *fcddb_errfp; /* Debug message file stream */ 114 fcddb_gmap_t *fcddb_gmap_head = NULL; /* Genre map list head */ 115 116 STATIC char fcddb_hellostr[HOST_NAM_SZ + (STR_BUF_SZ * 2)], 117 fcddb_extinfo[HOST_NAM_SZ + (STR_BUF_SZ * 2)], 118 *fcddb_auth_buf = NULL; 119 120 /* 121 * fcddb_sum 122 * Compute a checksum number for use by fcddb_discid 123 * 124 * Args: 125 * n - A numeric value 126 * 127 * Return: 128 * The checksum of n 129 */ 130 STATIC int 131 fcddb_sum(int n) 132 { 133 int ret; 134 135 /* For backward compatibility this algorithm must not change */ 136 for (ret = 0; n > 0; n /= 10) 137 ret += n % 10; 138 139 return (ret); 140 } 141 142 143 /* 144 * fcddb_line_filter 145 * Line filter to prevent multi-line strings from being stored in 146 * single-line fields. 147 * 148 * Args: 149 * str - The field string 150 * 151 * Return: 152 * Nothing 153 */ 154 STATIC void 155 fcddb_line_filter(char *str) 156 { 157 if (str == NULL) 158 return; 159 160 for (; *str != '\0'; str++) { 161 if (*str == '\\' && *(str+1) == 'n') { 162 *str = '\0'; 163 break; 164 } 165 } 166 } 167 168 169 /* 170 * fcddb_iso8859_to_utf8 171 * Convert the input ISO-8859-1 string to UTF-8 and return the 172 * resultant string. The output string buffer is dynamically allocated 173 * and should be freed by the caller via MEM_FREE when done. 174 * 175 * Args: 176 * str - source string (ISO-8859-1) 177 * 178 * Return: 179 * Return string (UTF-8) 180 */ 181 STATIC char * 182 fcddb_iso8859_to_utf8(char *str) 183 { 184 unsigned char *p, 185 *q, 186 *tmpbuf; 187 char *retbuf; 188 189 tmpbuf = (unsigned char *) MEM_ALLOC( 190 "utf8_to_buf", strlen(str) * 6 + 1 191 ); 192 if (tmpbuf == NULL) 193 return NULL; 194 195 p = (unsigned char *) str; 196 q = (unsigned char *) tmpbuf; 197 while (*p != '\0') { 198 if (*p <= 0x7f) { 199 /* Simple US-ASCII character: just copy it */ 200 *q++ = *p++; 201 } 202 else { 203 /* ISO8859 is byte-oriented, so this needs to 204 * only handle the "extended ASCII" characters 205 * 0xc0 to 0xfd. 206 */ 207 *q++ = (0xc0 | ((int) (0xc0 & *p) >> 6)); 208 *q++ = 0x80 | (*p & 0x3f); 209 p++; 210 } 211 } 212 *q = '\0'; 213 214 retbuf = (char *) MEM_ALLOC( 215 "utf8_retbuf", strlen((char *) tmpbuf) + 1 216 ); 217 if (retbuf != NULL) 218 (void) strcpy(retbuf, (char *) tmpbuf); 219 220 MEM_FREE(tmpbuf); 221 return (retbuf); 222 } 223 224 225 /* 226 * fcddb_utf8_to_iso8859 227 * Convert the input UTF-8 string to ISO8859-1 and return the 228 * resultant string. The output string buffer is dynamically allocated 229 * and should be freed by the caller via MEM_FREE when done. 230 * 231 * Args: 232 * str - source string (UTF-8) 233 * Return: 234 * 235 * Return string (ISO-8859-1) 236 */ 237 STATIC char * 238 fcddb_utf8_to_iso8859(char *str) 239 { 240 unsigned char *p, 241 *q, 242 *tmpbuf; 243 char *retbuf; 244 245 tmpbuf = (unsigned char *) MEM_ALLOC( 246 "iso8859_to_buf", strlen(str) + 1 247 ); 248 if (tmpbuf == NULL) 249 return NULL; 250 251 p = (unsigned char *) str; 252 q = (unsigned char *) tmpbuf; 253 while (*p != '\0') { 254 if (*p <= 0x7f) { 255 /* Simple US-ASCII character: just copy it */ 256 *q++ = *p++; 257 } 258 else if (*p >= 0xc0 && *p <= 0xfd) { 259 int n = 0; 260 261 if ((*p & 0xe0) == 0xc0) { 262 n = 2; 263 if (*(p+1) >= 0x80 && *(p+1) <= 0xfd) { 264 /* OK */ 265 *q = (((*p & 0x03) << 6) | 266 (*(p+1) & 0x3f)); 267 268 if ((*p & 0x1c) != 0) { 269 /* Decodes to more than 8 bits */ 270 *q = '_'; 271 } 272 else if (*q <= 0x7f) { 273 /* Not the shortest encoding: 274 * illegal. 275 */ 276 *q = '_'; 277 } 278 } 279 else { 280 /* Malformed UTF-8 character */ 281 *q = '_'; 282 } 283 q++; 284 } 285 else if ((*p & 0xf0) == 0xe0) { 286 /* Three-byte sequence */ 287 n = 3; 288 } 289 else if ((*p & 0xf8) == 0xf0) { 290 /* Four-byte sequence */ 291 n = 4; 292 } 293 else if ((*p & 0xfc) == 0xf8) { 294 /* Five-byte sequence */ 295 n = 5; 296 } 297 else if ((*p & 0xfe) == 0xfc) { 298 /* Six-byte sequence */ 299 n = 6; 300 } 301 302 if (n > 2) 303 *q++ = '_'; 304 305 while (n > 0) { 306 if (*(++p) == '\0') 307 break; 308 n--; 309 } 310 } 311 else { 312 /* Malformed UTF-8 sequence: skip */ 313 p++; 314 } 315 } 316 *q = '\0'; 317 318 retbuf = (char *) MEM_ALLOC( 319 "iso8859_retbuf", strlen((char *) tmpbuf) + 1 320 ); 321 if (retbuf != NULL) 322 (void) strcpy(retbuf, (char *) tmpbuf); 323 324 MEM_FREE(tmpbuf); 325 return (retbuf); 326 } 327 328 329 /* 330 * fcddb_strcasecmp 331 * Compare two strings a la strcmp(), except it is case-insensitive. 332 * 333 * Args: 334 * s1 - The first text string. 335 * s2 - The second text string. 336 * 337 * Return: 338 * Compare value. See strcmp(3). 339 */ 340 STATIC int 341 fcddb_strcasecmp(char *s1, char *s2) 342 { 343 char *buf1, 344 *buf2, 345 *p; 346 int ret; 347 348 if (s1 == NULL || s2 == NULL) 349 return 0; 350 351 /* Allocate tmp buffers */ 352 buf1 = (char *) MEM_ALLOC("strcasecmp_buf1", strlen(s1)+1); 353 buf2 = (char *) MEM_ALLOC("strcasecmp_buf2", strlen(s2)+1); 354 if (buf1 == NULL || buf2 == NULL) 355 return -1; /* Shrug */ 356 357 /* Convert both strings to lower case and store in tmp buffer */ 358 for (p = buf1; *s1 != '\0'; s1++, p++) 359 *p = (char) ((isupper((int) *s1)) ? tolower((int) *s1) : *s1); 360 *p = '\0'; 361 for (p = buf2; *s2 != '\0'; s2++, p++) 362 *p = (char) ((isupper((int) *s2)) ? tolower((int) *s2) : *s2); 363 *p = '\0'; 364 365 ret = strcmp(buf1, buf2); 366 367 MEM_FREE(buf1); 368 MEM_FREE(buf2); 369 370 return (ret); 371 } 372 373 374 #ifdef __VMS 375 376 /* 377 * fcddb_vms_dirconv 378 * Convert VMS directory notation from disk:[a.b.c] to disk:[a.b]c.dir 379 * syntax. 380 * 381 * Args: 382 * path - Input directory path string 383 * 384 * Return: 385 * The output path string. The string buffer should be deallocated 386 * by the caller when done. If an error is encountered, NULL is 387 * returned. 388 */ 389 STATIC char * 390 fcddb_vms_dirconv(char *path) 391 { 392 char *cp, 393 *cp2, 394 *cp3, 395 *buf, 396 tmp[STR_BUF_SZ]; 397 398 buf = (char *) MEM_ALLOC("vms_dirconv", strlen(path) + 16); 399 if (buf == NULL) 400 return NULL; 401 402 (void) strcpy(buf, path); 403 404 if ((cp = strchr(buf, DIR_BEG)) != NULL) { 405 if ((cp2 = strrchr(buf, DIR_END)) != NULL) { 406 if ((cp3 = strrchr(buf, DIR_SEP)) != NULL) { 407 if (fcddb_strcasecmp(cp3, ".dir") == 0) { 408 /* Already in the desired form */ 409 return (buf); 410 } 411 *cp2 = '\0'; 412 *cp3 = DIR_END; 413 (void) strcat(cp3, ".dir"); 414 return (buf); 415 } 416 else { 417 if ((cp = strchr(buf, ':')) == NULL) 418 cp = buf; 419 else 420 cp++; 421 422 if (strcmp(cp, "[000000]") == 0) 423 return (buf); 424 425 *cp2 = '\0'; 426 (void) strcpy(tmp, cp+1); 427 (void) sprintf(cp, "[000000]%s.dir", tmp); 428 return (buf); 429 } 430 } 431 else { 432 /* Invalid path */ 433 return NULL; 434 } 435 } 436 else { 437 if ((cp2 = strrchr(buf, DIR_SEP)) != NULL) { 438 if ((cp3 = strchr(buf, ':')) == NULL) 439 cp3 = buf; 440 else 441 cp3++; 442 443 if (fcddb_strcasecmp(cp2, ".dir") == 0) { 444 (void) sprintf(tmp, "[000000]%s", cp3); 445 strcpy(cp3, tmp); 446 return (buf); 447 } 448 else { 449 /* Invalid path */ 450 return NULL; 451 } 452 } 453 else { 454 if ((cp3 = strchr(buf, ':')) != NULL) { 455 (void) strcpy(cp3+1, "[000000]"); 456 return (buf); 457 } 458 459 /* Invalid path */ 460 return NULL; 461 } 462 } 463 /*NOTREACHED*/ 464 } 465 466 #endif /* __VMS */ 467 468 469 /* 470 * fcddb_ckmkdir 471 * Check the specified directory path, if it doesn't exist, 472 * attempt to create it. 473 * 474 * Args: 475 * path - Directory path to check or create 476 * mode - Directory permissions 477 * 478 * Return: 479 * CddbResult status code 480 */ 481 STATIC CddbResult 482 fcddb_ckmkdir(char *path, mode_t mode) 483 { 484 char *dpath = NULL; 485 struct stat stbuf; 486 487 #ifdef __VMS 488 if ((dpath = fcddb_vms_dirconv(path)) == NULL) 489 return CDDBTRNCannotCreateFile; 490 #else 491 if ((dpath = fcddb_strdup(path)) == NULL) 492 return CDDBTRNOutOfMemory; 493 #endif 494 495 if (stat(dpath, &stbuf) < 0) { 496 if (errno == ENOENT) { 497 if (mkdir(path, mode) < 0) { 498 FCDDBDBG(fcddb_errfp, "fcddb_ckmkdir: %s " 499 "mkdir failed, errno=%d\n", 500 path, errno 501 ); 502 MEM_FREE(dpath); 503 return CDDBTRNCannotCreateFile; 504 } 505 (void) chmod(path, mode); 506 } 507 else { 508 FCDDBDBG(fcddb_errfp, 509 "fcddb_ckmkdir: %s stat failed, errno=%d\n", 510 path, errno 511 ); 512 MEM_FREE(dpath); 513 return CDDBTRNCannotCreateFile; 514 } 515 } 516 else if (!S_ISDIR(stbuf.st_mode)) { 517 FCDDBDBG(fcddb_errfp, "fcddb_ckmkdir: " 518 "%s is not a directory! (mode=0x%x)\n", 519 path, (int) stbuf.st_mode 520 ); 521 MEM_FREE(dpath); 522 return CDDBTRNCannotCreateFile; 523 } 524 525 MEM_FREE(dpath); 526 return Cddb_OK; 527 } 528 529 530 /* 531 * fcddb_strcat 532 * Similar to strcat() except this handles special meta characters 533 * in CDDB data. 534 * 535 * Args: 536 * s1 - target string 537 * s2 - source string 538 * 539 * Return: 540 * Pointer to target string if successful, or NULL on failure 541 */ 542 STATIC char * 543 fcddb_strcat(char *s1, char *s2) 544 { 545 int n; 546 char *cp = s1; 547 bool_t proc_slash; 548 549 if (s1 == NULL || s2 == NULL) 550 return NULL; 551 552 /* Concatenate two strings, with special handling for newline 553 * and tab characters. 554 */ 555 proc_slash = FALSE; 556 n = strlen(s1); 557 s1 += n; 558 559 if (n > 0 && *(s1 - 1) == '\\') { 560 proc_slash = TRUE; /* Handle broken escape sequences */ 561 s1--; 562 } 563 564 for (; *s2 != '\0'; s1++, s2++) { 565 if (*s2 == '\\') { 566 if (proc_slash) { 567 proc_slash = FALSE; 568 continue; 569 } 570 proc_slash = TRUE; 571 s2++; 572 } 573 574 if (proc_slash) { 575 proc_slash = FALSE; 576 577 switch (*s2) { 578 case 'n': 579 *s1 = '\n'; 580 break; 581 case 't': 582 *s1 = '\t'; 583 break; 584 case '\\': 585 *s1 = '\\'; 586 break; 587 case '\0': 588 *s1 = '\\'; 589 s2--; 590 break; 591 default: 592 *s1++ = '\\'; 593 *s1 = *s2; 594 break; 595 } 596 } 597 else 598 *s1 = *s2; 599 } 600 *s1 = '\0'; 601 602 return (cp); 603 } 604 605 606 /* 607 * fcddb_http_xlat 608 * String translator that handles HTTP character escape sequences 609 * 610 * Args: 611 * s1 - source string 612 * s2 - target string 613 * 614 * Return: 615 * Nothing 616 */ 617 STATIC void 618 fcddb_http_xlat(char *s1, char *s2) 619 { 620 char *p, 621 *q; 622 623 for (p = s1, q = s2; *p != '\0'; p++) { 624 switch (*p) { 625 case '?': 626 case '=': 627 case '+': 628 case '&': 629 case ' ': 630 case '%': 631 (void) sprintf(q, "%%%02X", *p); 632 q += 3; 633 break; 634 default: 635 *q = *p; 636 q++; 637 break; 638 } 639 } 640 *q = '\0'; 641 } 642 643 644 /* 645 * Data used by fcddb_b64encode 646 */ 647 STATIC char b64map[] = { 648 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 649 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 650 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 651 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 652 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 653 '8', '9', '+', '/' 654 }; 655 656 #define B64_PAD '=' 657 658 659 /* 660 * fcddb_b64encode 661 * Base64 encoding function 662 * 663 * Args: 664 * ibuf - Input string buffer 665 * len - Number of characters 666 * obuf - Output string buffer 667 * brklines - Whether to break output into multiple lines 668 * 669 * Return: 670 * Nothing 671 */ 672 STATIC void 673 fcddb_b64encode(byte_t *ibuf, int len, byte_t *obuf, bool_t brklines) 674 { 675 int i, j, k, n, 676 c[4]; 677 byte_t sbuf[4]; 678 679 for (i = k = 0; (i + 3) <= len; i += 3, ibuf += 3) { 680 c[0] = ((int) ibuf[0] >> 2); 681 c[1] = ((((int) ibuf[0] & 0x03) << 4) | 682 (((int) ibuf[1] & 0xf0) >> 4)); 683 c[2] = ((((int) ibuf[1] & 0x0f) << 2) | 684 (((int) ibuf[2] & 0xc0) >> 6)); 685 c[3] = ((int) ibuf[2] & 0x3f); 686 687 for (j = 0; j < 4; j++) 688 *obuf++ = b64map[c[j]]; 689 690 if (brklines && ++k == 16) { 691 k = 0; 692 *obuf++ = '\n'; 693 } 694 } 695 696 if (i < len) { 697 n = len - i; 698 (void) strncpy((char *) sbuf, (char *) ibuf, n); 699 for (j = n; j < 3; j++) 700 sbuf[j] = (unsigned char) 0; 701 702 n++; 703 ibuf = sbuf; 704 c[0] = ((int) ibuf[0] >> 2); 705 c[1] = ((((int) ibuf[0] & 0x03) << 4) | 706 (((int) ibuf[1] & 0xf0) >> 4)); 707 c[2] = ((((int) ibuf[1] & 0x0f) << 2) | 708 (((int) ibuf[2] & 0xc0) >> 6)); 709 c[3] = ((int) ibuf[2] & 0x3f); 710 711 for (j = 0; j < 4; j++) 712 *obuf++ = (j < n) ? b64map[c[j]] : B64_PAD; 713 714 if (brklines && ++k == 16) 715 *obuf++ = '\n'; 716 } 717 718 if (brklines) 719 *obuf++ = '\n'; 720 721 *obuf = '\0'; 722 } 723 724 725 /* 726 * fcddb_genre_addcateg 727 * Add a new category to the genre map table. 728 * 729 * Args: 730 * categ - The category name string 731 * 732 * Return: 733 * Nothing. 734 */ 735 STATIC void 736 fcddb_genre_addcateg(char *categ) 737 { 738 fcddb_gmap_t *gmp, 739 *gmp2; 740 741 gmp2 = (fcddb_gmap_t *)(void *) MEM_ALLOC( 742 "CddbGenremap", sizeof(fcddb_gmap_t) 743 ); 744 if (gmp2 == NULL) 745 return; /* Can't add: out of memory */ 746 747 (void) memset(gmp2, 0, sizeof(fcddb_gmap_t)); 748 749 /* Map to unclassifiable */ 750 gmp2->cddb1name = fcddb_strdup(categ); 751 gmp2->meta.id = "220"; 752 gmp2->meta.name = "Unclassifiable"; 753 gmp2->sub.id = "221"; 754 gmp2->sub.name = "General Unclassifiable"; 755 gmp2->flags = CDDB_GMAP_DUP | CDDB_GMAP_DYN; 756 gmp2->next = NULL; 757 758 if (fcddb_gmap_head == NULL) { 759 fcddb_gmap_head = gmp2; 760 } 761 else for (gmp = fcddb_gmap_head; gmp != NULL; gmp = gmp->next) { 762 if (gmp->next == NULL) { 763 gmp->next = gmp2; 764 break; 765 } 766 } 767 } 768 769 770 /* 771 * fcddb_clear_url 772 * Clear a URL structure 773 * 774 * Args: 775 * up - Pointer to the URL structure 776 * 777 * Return: 778 * Nothing 779 */ 780 STATIC void 781 fcddb_clear_url(cddb_url_t *up) 782 { 783 if (up->type != NULL) { 784 MEM_FREE(up->type); 785 up->type = NULL; 786 } 787 if (up->href != NULL) { 788 MEM_FREE(up->href); 789 up->href = NULL; 790 } 791 if (up->displaylink != NULL) { 792 MEM_FREE(up->displaylink); 793 up->displaylink = NULL; 794 } 795 if (up->displaytext != NULL) { 796 MEM_FREE(up->displaytext); 797 up->displaytext = NULL; 798 } 799 if (up->category != NULL) { 800 MEM_FREE(up->category); 801 up->category = NULL; 802 } 803 if (up->description != NULL) { 804 MEM_FREE(up->description); 805 up->description = NULL; 806 } 807 } 808 809 810 /* 811 * fcddb_clear_urllist 812 * Clear a URL list structure 813 * 814 * Args: 815 * up - Pointer to the URL list structure 816 * 817 * Return: 818 * Nothing 819 */ 820 STATIC void 821 fcddb_clear_urllist(cddb_urllist_t *ulp) 822 { 823 cddb_url_t *up, 824 *nextp; 825 826 827 for (up = ulp->urls; up != NULL; up = nextp) { 828 nextp = up->next; 829 fcddb_clear_url(up); 830 MEM_FREE(up); 831 } 832 ulp->urls = NULL; 833 ulp->count = 0; 834 } 835 836 837 /* 838 * fcddb_clear_urlmanager 839 * Clear a URL manager structure 840 * 841 * Args: 842 * up - Pointer to the URL manager structure 843 * 844 * Return: 845 * Nothing 846 */ 847 STATIC void 848 fcddb_clear_urlmanager(cddb_urlmanager_t *mp) 849 { 850 cddb_urllist_t *ulp = &mp->urllist; 851 852 fcddb_clear_urllist(ulp); 853 mp->control = NULL; 854 } 855 856 857 /* 858 * fcddb_clear_fullname 859 * Clear a full name structure 860 * 861 * Args: 862 * up - Pointer to the full name structure 863 * 864 * Return: 865 * Nothing 866 */ 867 STATIC void 868 fcddb_clear_fullname(cddb_fullname_t *fnp) 869 { 870 if (fnp->name != NULL) { 871 MEM_FREE(fnp->name); 872 fnp->name = NULL; 873 } 874 if (fnp->lastname != NULL) { 875 MEM_FREE(fnp->lastname); 876 fnp->lastname = NULL; 877 } 878 if (fnp->firstname != NULL) { 879 MEM_FREE(fnp->firstname); 880 fnp->firstname = NULL; 881 } 882 if (fnp->the != NULL) { 883 MEM_FREE(fnp->the); 884 fnp->the = NULL; 885 } 886 } 887 888 889 /* 890 * fcddb_clear_credit 891 * Clear a credit structure 892 * 893 * Args: 894 * up - Pointer to the credit structure 895 * 896 * Return: 897 * Nothing 898 */ 899 STATIC void 900 fcddb_clear_credit(cddb_credit_t *cp) 901 { 902 fcddb_clear_fullname(&cp->fullname); 903 if (cp->notes != NULL) { 904 MEM_FREE(cp->notes); 905 cp->notes = NULL; 906 } 907 cp->role = NULL; 908 cp->next = NULL; 909 } 910 911 912 /* 913 * fcddb_clear_credits 914 * Clear a credits structure 915 * 916 * Args: 917 * up - Pointer to the credits structure 918 * 919 * Return: 920 * Nothing 921 */ 922 STATIC void 923 fcddb_clear_credits(cddb_credits_t *csp) 924 { 925 cddb_credit_t *cp, 926 *nextp; 927 928 for (cp = csp->credits; cp != NULL; cp = nextp) { 929 nextp = cp->next; 930 fcddb_clear_credit(cp); 931 MEM_FREE(cp); 932 } 933 csp->credits = NULL; 934 csp->count = 0; 935 } 936 937 938 /* 939 * fcddb_clear_segment 940 * Clear a segment structure 941 * 942 * Args: 943 * up - Pointer to the segment structure 944 * 945 * Return: 946 * Nothing 947 */ 948 STATIC void 949 fcddb_clear_segment(cddb_segment_t *sp) 950 { 951 fcddb_clear_credits(&sp->credits); 952 953 if (sp->name != NULL) { 954 MEM_FREE(sp->name); 955 sp->name = NULL; 956 } 957 if (sp->notes != NULL) { 958 MEM_FREE(sp->notes); 959 sp->notes = NULL; 960 } 961 if (sp->starttrack != NULL) { 962 MEM_FREE(sp->starttrack); 963 sp->starttrack = NULL; 964 } 965 if (sp->startframe != NULL) { 966 MEM_FREE(sp->startframe); 967 sp->startframe = NULL; 968 } 969 if (sp->endtrack != NULL) { 970 MEM_FREE(sp->endtrack); 971 sp->endtrack = NULL; 972 } 973 if (sp->endframe != NULL) { 974 MEM_FREE(sp->endframe); 975 sp->endframe = NULL; 976 } 977 sp->control = NULL; 978 sp->next = NULL; 979 } 980 981 982 /* 983 * fcddb_clear_segments 984 * Clear a segments structure 985 * 986 * Args: 987 * up - Pointer to the segments structure 988 * 989 * Return: 990 * Nothing 991 */ 992 STATIC void 993 fcddb_clear_segments(cddb_segments_t *ssp) 994 { 995 cddb_segment_t *sp, 996 *nextp; 997 998 for (sp = ssp->segments; sp != NULL; sp = nextp) { 999 nextp = sp->next; 1000 fcddb_clear_segment(sp); 1001 MEM_FREE(sp); 1002 } 1003 ssp->segments = NULL; 1004 ssp->count = 0; 1005 } 1006 1007 1008 /* 1009 * fcddb_clear_genretree 1010 * Clear a genre tree structure 1011 * 1012 * Args: 1013 * up - Pointer to the genre tree structure 1014 * 1015 * Return: 1016 * Nothing 1017 */ 1018 STATIC void 1019 fcddb_clear_genretree(cddb_genretree_t *gp) 1020 { 1021 cddb_genre_t *p, 1022 *q, 1023 *nextp, 1024 *nextq; 1025 fcddb_gmap_t *gmp, 1026 *gmp2; 1027 1028 /* Free genre tree */ 1029 nextp = NULL; 1030 for (p = gp->genres; p != NULL; p = nextp) { 1031 nextp = p->nextmeta; 1032 1033 nextq = NULL; 1034 for (q = p->next; q != NULL; q = nextq) { 1035 nextq = q->next; 1036 1037 if (q->id != NULL) 1038 MEM_FREE(q->id); 1039 if (q->name != NULL) 1040 MEM_FREE(q->name); 1041 1042 MEM_FREE(q); 1043 } 1044 1045 if (p->id != NULL) 1046 MEM_FREE(p->id); 1047 if (p->name != NULL) 1048 MEM_FREE(p->name); 1049 1050 MEM_FREE(p); 1051 } 1052 gp->genres = NULL; 1053 gp->count = 0; 1054 1055 /* Deallocate dynamic genre mapping if applicable */ 1056 gmp2 = NULL; 1057 for (gmp = fcddb_gmap_head; gmp != NULL; gmp = gmp2) { 1058 gmp2 = gmp->next; 1059 1060 if ((gmp->flags & CDDB_GMAP_DYN) != 0) { 1061 if (gmp->cddb1name != NULL) 1062 MEM_FREE(gmp->cddb1name); 1063 if (gmp->meta.id != NULL) 1064 MEM_FREE(gmp->meta.id); 1065 if (gmp->meta.name != NULL) 1066 MEM_FREE(gmp->meta.name); 1067 if (gmp->sub.id != NULL) 1068 MEM_FREE(gmp->sub.id); 1069 if (gmp->sub.name != NULL) 1070 MEM_FREE(gmp->sub.name); 1071 MEM_FREE(gmp); 1072 } 1073 } 1074 fcddb_gmap_head = NULL; 1075 } 1076 1077 1078 /* 1079 * fcddb_clear_roletree 1080 * Clear a role tree structure 1081 * 1082 * Args: 1083 * up - Pointer to the role tree structure 1084 * 1085 * Return: 1086 * Nothing 1087 */ 1088 STATIC void 1089 fcddb_clear_roletree(cddb_roletree_t *rtp) 1090 { 1091 cddb_rolelist_t *rlp, 1092 *nextp; 1093 1094 nextp = NULL; 1095 for (rlp = rtp->rolelists; rlp != NULL; rlp = nextp) { 1096 nextp = rlp->next; 1097 MEM_FREE(rlp); 1098 } 1099 rtp->rolelists = NULL; 1100 rtp->count = 0; 1101 } 1102 1103 1104 /* 1105 * fcddb_clear_userinfo 1106 * Clear a user info structure 1107 * 1108 * Args: 1109 * up - Pointer to the user info structure 1110 * 1111 * Return: 1112 * Nothing 1113 */ 1114 STATIC void 1115 fcddb_clear_userinfo(cddb_userinfo_t *up) 1116 { 1117 if (up->userhandle != NULL) { 1118 MEM_FREE(up->userhandle); 1119 up->userhandle = NULL; 1120 } 1121 if (up->password != NULL) { 1122 MEM_FREE(up->password); 1123 up->password = NULL; 1124 } 1125 if (up->passwordhint != NULL) { 1126 MEM_FREE(up->passwordhint); 1127 up->passwordhint = NULL; 1128 } 1129 if (up->emailaddress != NULL) { 1130 MEM_FREE(up->emailaddress); 1131 up->emailaddress = NULL; 1132 } 1133 if (up->regionid != NULL) { 1134 MEM_FREE(up->regionid); 1135 up->regionid = NULL; 1136 } 1137 if (up->postalcode != NULL) { 1138 MEM_FREE(up->postalcode); 1139 up->postalcode = NULL; 1140 } 1141 if (up->age != NULL) { 1142 MEM_FREE(up->age); 1143 up->age = NULL; 1144 } 1145 if (up->sex != NULL) { 1146 MEM_FREE(up->sex); 1147 up->sex = NULL; 1148 } 1149 up->allowemail = 0; 1150 up->allowstats = 0; 1151 } 1152 1153 1154 /* 1155 * fcddb_clear_options 1156 * Clear a options structure 1157 * 1158 * Args: 1159 * up - Pointer to the options structure 1160 * 1161 * Return: 1162 * Nothing 1163 */ 1164 STATIC void 1165 fcddb_clear_options(cddb_options_t *op) 1166 { 1167 if (op->proxyserver != NULL) { 1168 MEM_FREE(op->proxyserver); 1169 op->proxyserver = NULL; 1170 } 1171 if (op->proxyusername != NULL) { 1172 MEM_FREE(op->proxyusername); 1173 op->proxyusername = NULL; 1174 } 1175 if (op->proxypassword != NULL) { 1176 MEM_FREE(op->proxypassword); 1177 op->proxypassword = NULL; 1178 } 1179 if (op->localcachepath != NULL) { 1180 MEM_FREE(op->localcachepath); 1181 op->localcachepath = NULL; 1182 } 1183 op->proxyport = 0; 1184 op->servertimeout = 0; 1185 op->testsubmitmode = 0; 1186 op->localcachetimeout = 0; 1187 } 1188 1189 1190 /* 1191 * fcddb_clear_track 1192 * Clear a track structure 1193 * 1194 * Args: 1195 * up - Pointer to the track structure 1196 * 1197 * Return: 1198 * Nothing 1199 */ 1200 STATIC void 1201 fcddb_clear_track(cddb_track_t *tp) 1202 { 1203 fcddb_clear_credits(&tp->credits); 1204 1205 if (tp->title != NULL) { 1206 MEM_FREE(tp->title); 1207 tp->title = NULL; 1208 } 1209 if (tp->notes != NULL) { 1210 MEM_FREE(tp->notes); 1211 tp->notes = NULL; 1212 } 1213 tp->control = NULL; 1214 } 1215 1216 1217 /* 1218 * fcddb_clear_tracks 1219 * Clear a tracks structure 1220 * 1221 * Args: 1222 * up - Pointer to the tracks structure 1223 * 1224 * Return: 1225 * Nothing 1226 */ 1227 STATIC void 1228 fcddb_clear_tracks(cddb_tracks_t *tsp) 1229 { 1230 int i; 1231 1232 for (i = 0; i < MAXTRACK; i++) 1233 fcddb_clear_track(&tsp->track[i]); 1234 1235 tsp->count = 0; 1236 } 1237 1238 1239 /* 1240 * fcddb_clear_disc 1241 * Clear a disc structure 1242 * 1243 * Args: 1244 * up - Pointer to the disc structure 1245 * 1246 * Return: 1247 * Nothing 1248 */ 1249 STATIC void 1250 fcddb_clear_disc(cddb_disc_t *dp) 1251 { 1252 fcddb_idlist_t *ip, 1253 *nextp; 1254 1255 fcddb_clear_credits(&dp->credits); 1256 fcddb_clear_segments(&dp->segments); 1257 fcddb_clear_fullname(&dp->fullname); 1258 fcddb_clear_tracks(&dp->tracks); 1259 1260 if (dp->category != NULL) { 1261 MEM_FREE(dp->category); 1262 dp->category = NULL; 1263 } 1264 if (dp->discid != NULL) { 1265 MEM_FREE(dp->discid); 1266 dp->discid = NULL; 1267 } 1268 if (dp->toc != NULL) { 1269 MEM_FREE(dp->toc); 1270 dp->toc = NULL; 1271 } 1272 if (dp->title != NULL) { 1273 MEM_FREE(dp->title); 1274 dp->title = NULL; 1275 } 1276 if (dp->notes != NULL) { 1277 MEM_FREE(dp->notes); 1278 dp->notes = NULL; 1279 } 1280 if (dp->revision != NULL) { 1281 MEM_FREE(dp->revision); 1282 dp->revision = NULL; 1283 } 1284 if (dp->submitter != NULL) { 1285 MEM_FREE(dp->submitter); 1286 dp->submitter = NULL; 1287 } 1288 1289 dp->next = NULL; 1290 dp->genre = NULL; 1291 dp->control = NULL; 1292 1293 nextp = NULL; 1294 for (ip = dp->idlist; ip != NULL; ip = nextp) { 1295 nextp = ip->next; 1296 MEM_FREE(ip); 1297 } 1298 dp->idlist = NULL; 1299 } 1300 1301 1302 /* 1303 * fcddb_clear_discs 1304 * Clear a discs structure 1305 * 1306 * Args: 1307 * up - Pointer to the discs structure 1308 * 1309 * Return: 1310 * Nothing 1311 */ 1312 STATIC void 1313 fcddb_clear_discs(cddb_discs_t *dsp) 1314 { 1315 cddb_disc_t *dp, 1316 *nextp; 1317 1318 nextp = NULL; 1319 for (dp = dsp->discs; dp != NULL; dp = nextp) { 1320 nextp = dp->next; 1321 fcddb_clear_disc(dp); 1322 MEM_FREE(dp); 1323 } 1324 1325 dsp->discs = NULL; 1326 dsp->count = 0; 1327 } 1328 1329 1330 /* 1331 * fcddb_clear_control 1332 * Clear a control structure 1333 * 1334 * Args: 1335 * up - Pointer to the control structure 1336 * 1337 * Return: 1338 * Nothing 1339 */ 1340 STATIC void 1341 fcddb_clear_control(cddb_control_t *cp) 1342 { 1343 if (cp->hostname != NULL) { 1344 MEM_FREE(cp->hostname); 1345 cp->hostname = NULL; 1346 } 1347 if (cp->clientname != NULL) { 1348 MEM_FREE(cp->clientname); 1349 cp->clientname = NULL; 1350 } 1351 if (cp->clientid != NULL) { 1352 MEM_FREE(cp->clientid); 1353 cp->clientid = NULL; 1354 } 1355 if (cp->clientver != NULL) { 1356 MEM_FREE(cp->clientver); 1357 cp->clientver = NULL; 1358 } 1359 fcddb_clear_userinfo(&cp->userinfo); 1360 fcddb_clear_options(&cp->options); 1361 fcddb_clear_disc(&cp->disc); 1362 fcddb_clear_discs(&cp->discs); 1363 fcddb_clear_genretree(&cp->genretree); 1364 fcddb_clear_roletree(&cp->roletree); 1365 fcddb_clear_urllist(&cp->urllist); 1366 } 1367 1368 1369 /* 1370 * fcddb_putentry 1371 * Write an entry into a CDDB file, used by fcddb_write_cddb() 1372 * 1373 * Args: 1374 * fp - FILE stream pointer 1375 * idstr - The entry identifier 1376 * entry - The entry string 1377 * 1378 * Return: 1379 * Nothing 1380 */ 1381 STATIC void 1382 fcddb_putentry(FILE *fp, char *idstr, char *entry) 1383 { 1384 int i, 1385 n; 1386 char *cp, 1387 tmpbuf[STR_BUF_SZ]; 1388 1389 if (entry == NULL) { 1390 /* Null entry */ 1391 (void) sprintf(tmpbuf, "%s=\r\n", idstr); 1392 (void) fputs(tmpbuf, fp); 1393 } 1394 else { 1395 /* Write entry to file, splitting into multiple lines 1396 * if necessary. Special handling for newline and tab 1397 * characters. 1398 */ 1399 cp = entry; 1400 1401 do { 1402 (void) sprintf(tmpbuf, "%s=", idstr); 1403 (void) fputs(tmpbuf, fp); 1404 1405 n = FCDDB2_MIN((int) strlen(cp), STR_BUF_SZ); 1406 1407 for (i = 0; i < n; i++, cp++) { 1408 switch (*cp) { 1409 case '\n': 1410 (void) fputs("\\n", fp); 1411 break; 1412 case '\t': 1413 (void) fputs("\\t", fp); 1414 break; 1415 case '\\': 1416 (void) fputs("\\\\", fp); 1417 break; 1418 default: 1419 (void) fputc(*cp, fp); 1420 break; 1421 } 1422 } 1423 1424 (void) fputs("\r\n", fp); 1425 if (*cp == '\0') 1426 break; 1427 1428 } while (n == STR_BUF_SZ); 1429 } 1430 } 1431 1432 1433 /* 1434 * fcddb_write_cddb 1435 * Write a CDDB file 1436 * 1437 * Args: 1438 * path - file path 1439 * dp - Pointer to disc structure 1440 * issubmit - Whether this function is invoked during a submit 1441 * 1442 * Return: 1443 * CddbResult status code 1444 */ 1445 STATIC CddbResult 1446 fcddb_write_cddb(char *path, cddb_disc_t *dp, bool_t issubmit) 1447 { 1448 cddb_control_t *cp = (cddb_control_t *) dp->control; 1449 FILE *fp; 1450 CddbResult ret; 1451 int i, 1452 revision, 1453 ntrks; 1454 unsigned int discid_val = 0; 1455 fcddb_idlist_t *ip; 1456 char *dir, 1457 *up, 1458 idstr[12], 1459 tmpbuf[STR_BUF_SZ * 2]; 1460 unsigned int addr[MAXTRACK]; 1461 1462 if (cp == NULL) 1463 return Cddb_E_INVALIDARG; 1464 1465 (void) sscanf(dp->discid, "%x", (unsigned int *) &discid_val); 1466 1467 dir = fcddb_dirname(path); 1468 FCDDBDBG(fcddb_errfp, "fcddb_write_cddb: making %s\n", dir); 1469 if ((ret = fcddb_mkdir(dir, 0755)) != Cddb_OK) 1470 return (ret); 1471 1472 (void) unlink(path); 1473 1474 FCDDBDBG(fcddb_errfp, "fcddb_write_cddb: writing %s\n", path); 1475 if ((fp = fopen(path, "w")) == NULL) { 1476 FCDDBDBG(fcddb_errfp, 1477 "fcddb_write_cddb: cannot open file for writing: " 1478 "errno=%d\n", errno); 1479 return CDDBTRNCannotCreateFile; 1480 } 1481 1482 /* File header */ 1483 (void) fputs( 1484 "# xmcd CD database file\r\n" 1485 "# Copyright (C) 2001 CDDB, Inc.\r\n#\r\n", 1486 fp 1487 ); 1488 1489 ntrks = fcddb_parse_toc((char *) dp->toc, addr); 1490 if (ntrks == 0 || ntrks != (int) (discid_val & 0xff)) { 1491 (void) fclose(fp); 1492 (void) unlink(path); 1493 return Cddb_E_INVALIDARG; 1494 } 1495 1496 /* Track frame offsets */ 1497 (void) fputs("# Track frame offsets:\r\n", fp); 1498 for (i = 0; i < ntrks; i++) { 1499 (void) sprintf(tmpbuf, "#\t%u\r\n", addr[i]); 1500 (void) fputs(tmpbuf, fp); 1501 } 1502 1503 /* Disc length */ 1504 (void) sprintf(tmpbuf, "#\r\n# Disc length: %u seconds\r\n#\r\n", 1505 addr[ntrks] / FRAME_PER_SEC); 1506 (void) fputs(tmpbuf, fp); 1507 1508 /* Revision */ 1509 if (dp->revision == NULL) 1510 revision = 1; 1511 else { 1512 revision = atoi((char *) dp->revision); 1513 if (revision >= 0 && issubmit) 1514 revision++; 1515 } 1516 (void) sprintf(tmpbuf, "# Revision: %d\r\n", revision); 1517 (void) fputs(tmpbuf, fp); 1518 1519 /* Submitter */ 1520 if (issubmit || dp->submitter == NULL) 1521 (void) sprintf(tmpbuf, "# Submitted via: %s %s\r\n#\r\n", 1522 cp->clientname, cp->clientver); 1523 else 1524 (void) sprintf(tmpbuf, "# Submitted via: %s\r\n#\r\n", 1525 dp->submitter); 1526 (void) fputs(tmpbuf, fp); 1527 1528 /* Disc IDs */ 1529 (void) sprintf(tmpbuf, "DISCID=%s", dp->discid); 1530 i = 1; 1531 for (ip = dp->idlist; ip != NULL; ip = ip->next) { 1532 if (ip->discid == discid_val) 1533 /* This is our own ID, which we already processed */ 1534 continue; 1535 1536 if (i == 0) 1537 (void) sprintf(tmpbuf, "DISCID=%08x", ip->discid); 1538 else 1539 (void) sprintf(tmpbuf, "%s,%08x", tmpbuf, ip->discid); 1540 1541 i++; 1542 if (i == 8) { 1543 (void) strcat(tmpbuf, "\r\n"); 1544 (void) fputs(tmpbuf, fp); 1545 i = 0; 1546 } 1547 } 1548 if (i != 0) { 1549 (void) strcat(tmpbuf, "\r\n"); 1550 (void) fputs(tmpbuf, fp); 1551 } 1552 1553 /* Disc artist/title */ 1554 (void) sprintf(tmpbuf, "%s%s%s", 1555 (dp->fullname.name == NULL) ? 1556 "" : dp->fullname.name, 1557 (dp->fullname.name != NULL && dp->title != NULL) ? 1558 " / " : "", 1559 (dp->title == NULL) ? 1560 "" : dp->title); 1561 if ((up = fcddb_utf8_to_iso8859(tmpbuf)) == NULL) { 1562 (void) fclose(fp); 1563 (void) unlink(path); 1564 return CDDBTRNOutOfMemory; 1565 } 1566 fcddb_putentry(fp, "DTITLE", up); 1567 MEM_FREE(up); 1568 1569 /* Track titles */ 1570 for (i = 0; i < ntrks; i++) { 1571 (void) sprintf(idstr, "TTITLE%u", i); 1572 up = fcddb_utf8_to_iso8859(dp->tracks.track[i].title); 1573 if (up == NULL) { 1574 (void) fclose(fp); 1575 (void) unlink(path); 1576 return CDDBTRNOutOfMemory; 1577 } 1578 fcddb_putentry(fp, idstr, up); 1579 MEM_FREE(up); 1580 } 1581 1582 /* Disc notes */ 1583 if ((up = fcddb_utf8_to_iso8859(dp->notes)) == NULL) { 1584 (void) fclose(fp); 1585 (void) unlink(path); 1586 return CDDBTRNOutOfMemory; 1587 } 1588 fcddb_putentry(fp, "EXTD", up); 1589 MEM_FREE(up); 1590 1591 /* Track notes */ 1592 for (i = 0; i < ntrks; i++) { 1593 up = fcddb_utf8_to_iso8859(dp->tracks.track[i].notes); 1594 if (up == NULL) { 1595 (void) fclose(fp); 1596 (void) unlink(path); 1597 return CDDBTRNOutOfMemory; 1598 } 1599 (void) sprintf(idstr, "EXTT%u", i); 1600 fcddb_putentry(fp, idstr, up); 1601 MEM_FREE(up); 1602 } 1603 1604 /* Track program sequence */ 1605 fcddb_putentry(fp, "PLAYORDER", NULL); 1606 1607 (void) fclose(fp); 1608 1609 return Cddb_OK; 1610 } 1611 1612 1613 /* 1614 * fcddb_parse_cddb_data 1615 * Parse CDDB data and fill the disc structure 1616 * 1617 * Args: 1618 * cp - Pointer to the control structure 1619 * buf - CDDB data 1620 * discid - The disc ID 1621 * category - The category 1622 * toc - The TOC 1623 * wrcache - Whether to write to cache 1624 * dp - Pointer to disc structure 1625 * 1626 * Return: 1627 * CddbResult status code 1628 */ 1629 STATIC CddbResult 1630 fcddb_parse_cddb_data( 1631 cddb_control_t *cp, 1632 char *buf, 1633 char *discid, 1634 char *category, 1635 char *toc, 1636 bool_t wrcache, 1637 cddb_disc_t *dp 1638 ) 1639 { 1640 char *p, 1641 *q, 1642 *r, 1643 *up, 1644 filepath[FILE_PATH_SZ]; 1645 fcddb_idlist_t *ip; 1646 int n; 1647 unsigned int discid_val = 0; 1648 CddbResult ret; 1649 1650 FCDDBDBG(fcddb_errfp, "fcddb_parse_cddb_data: %s/%s\n", 1651 category, discid); 1652 1653 (void) sscanf(discid, "%x", (unsigned int *) &discid_val); 1654 1655 ret = Cddb_OK; 1656 p = buf; 1657 for (;;) { 1658 SKIP_SPC(p); 1659 1660 if (*p == '.' || *p == '\0') 1661 break; /* Done */ 1662 1663 if ((q = strchr(p, '\n')) != NULL) 1664 *q = '\0'; 1665 1666 n = strlen(p) - 1; 1667 if (*(p + n) == '\r') 1668 *(p + n) = '\0'; /* Zap carriage return */ 1669 1670 if (*p == '#') { /* Comment */ 1671 if (strncmp(p, "# Revision: ", 12) == 0) 1672 dp->revision = fcddb_strdup(p + 12); 1673 else if (strncmp(p, "# Submitted via: ", 17) == 0) 1674 dp->submitter = fcddb_strdup(p + 17); 1675 } 1676 else if (strncmp(p, "DISCID=", 7) == 0) { 1677 p += 7; 1678 while ((r = strchr(p, ',')) != NULL) { 1679 *r = '\0'; 1680 ip = (fcddb_idlist_t *) MEM_ALLOC( 1681 "idlist", 1682 sizeof(fcddb_idlist_t) 1683 ); 1684 if (ip == NULL) { 1685 ret = CDDBTRNOutOfMemory; 1686 break; 1687 } 1688 (void) sscanf(p, "%x", 1689 (unsigned int *) &ip->discid); 1690 ip->next = dp->idlist; 1691 dp->idlist = ip; 1692 p = r+1; 1693 } 1694 if (ret != Cddb_OK) 1695 break; 1696 1697 ip = (fcddb_idlist_t *) MEM_ALLOC( 1698 "idlist", 1699 sizeof(fcddb_idlist_t) 1700 ); 1701 if (ip == NULL) { 1702 ret = CDDBTRNOutOfMemory; 1703 break; 1704 } 1705 (void) sscanf(p, "%x", (unsigned int *) &ip->discid); 1706 ip->next = dp->idlist; 1707 dp->idlist = ip; 1708 } 1709 else if (strncmp(p, "DTITLE=", 7) == 0) { 1710 /* Disc artist / title */ 1711 p += 7; 1712 1713 fcddb_line_filter(p); 1714 if ((up = fcddb_iso8859_to_utf8(p)) == NULL) { 1715 ret = CDDBTRNOutOfMemory; 1716 break; 1717 } 1718 1719 /* Put it all in artist field first, 1720 * we'll separate artist and title later. 1721 */ 1722 if (dp->fullname.name == NULL) { 1723 dp->fullname.name = (char *) MEM_ALLOC( 1724 "dp->fullname.name", 1725 strlen(up) + 1 1726 ); 1727 if (dp->fullname.name != NULL) 1728 dp->fullname.name[0] = '\0'; 1729 } 1730 else { 1731 dp->fullname.name = (char *) MEM_REALLOC( 1732 "dp->fullname.name", 1733 dp->fullname.name, 1734 strlen(dp->fullname.name) + 1735 strlen(up) + 1 1736 ); 1737 } 1738 1739 if (dp->fullname.name == NULL) { 1740 MEM_FREE(up); 1741 ret = CDDBTRNOutOfMemory; 1742 break; 1743 } 1744 1745 (void) fcddb_strcat(dp->fullname.name, up); 1746 MEM_FREE(up); 1747 } 1748 else if (sscanf(p, "TTITLE%d=", &n) > 0) { 1749 /* Track title */ 1750 p = strchr(p, '='); 1751 p++; 1752 1753 if (n >= (int) (discid_val & 0xff)) 1754 continue; 1755 1756 fcddb_line_filter(p); 1757 if ((up = fcddb_iso8859_to_utf8(p)) == NULL) { 1758 ret = CDDBTRNOutOfMemory; 1759 break; 1760 } 1761 1762 if (dp->tracks.track[n].title == NULL) { 1763 dp->tracks.track[n].title = (char *) MEM_ALLOC( 1764 "track[n].title", 1765 strlen(up) + 1 1766 ); 1767 if (dp->tracks.track[n].title != NULL) 1768 dp->tracks.track[n].title[0] = '\0'; 1769 } 1770 else { 1771 dp->tracks.track[n].title = (char *) 1772 MEM_REALLOC( 1773 "track[n].title", 1774 dp->tracks.track[n].title, 1775 strlen(dp->tracks.track[n].title) + 1776 strlen(up) + 1 1777 ); 1778 } 1779 1780 if (dp->tracks.track[n].title == NULL) { 1781 MEM_FREE(up); 1782 ret = CDDBTRNOutOfMemory; 1783 break; 1784 } 1785 1786 (void) fcddb_strcat(dp->tracks.track[n].title, up); 1787 MEM_FREE(up); 1788 } 1789 else if (strncmp(p, "EXTD=", 5) == 0) { 1790 /* Disc notes */ 1791 p += 5; 1792 1793 if ((up = fcddb_iso8859_to_utf8(p)) == NULL) { 1794 ret = CDDBTRNOutOfMemory; 1795 break; 1796 } 1797 1798 if (dp->notes == NULL) { 1799 dp->notes = (char *) MEM_ALLOC( 1800 "dp->notes", 1801 strlen(up) + 1 1802 ); 1803 if (dp->notes != NULL) 1804 dp->notes[0] = '\0'; 1805 } 1806 else { 1807 dp->notes = (char *) MEM_REALLOC( 1808 "dp->notes", 1809 dp->notes, 1810 strlen(dp->notes) + 1811 strlen(up) + 1 1812 ); 1813 } 1814 1815 if (dp->notes == NULL) { 1816 MEM_FREE(up); 1817 ret = CDDBTRNOutOfMemory; 1818 break; 1819 } 1820 1821 (void) fcddb_strcat(dp->notes, up); 1822 MEM_FREE(up); 1823 } 1824 else if (sscanf(p, "EXTT%d=", &n) > 0) { 1825 /* Track notes */ 1826 p = strchr(p, '='); 1827 p++; 1828 1829 if (n >= (int) (discid_val & 0xff)) 1830 continue; 1831 1832 if ((up = fcddb_iso8859_to_utf8(p)) == NULL) { 1833 ret = CDDBTRNOutOfMemory; 1834 break; 1835 } 1836 1837 if (dp->tracks.track[n].notes == NULL) { 1838 dp->tracks.track[n].notes = (char *) 1839 MEM_ALLOC( 1840 "track[n].notes", 1841 strlen(up) + 1 1842 ); 1843 1844 if (dp->tracks.track[n].notes != NULL) 1845 dp->tracks.track[n].notes[0] = '\0'; 1846 } 1847 else { 1848 dp->tracks.track[n].notes = (char *) 1849 MEM_REALLOC( 1850 "track[n].notes", 1851 dp->tracks.track[n].notes, 1852 strlen(dp->tracks.track[n].notes) + 1853 strlen(up) + 1 1854 ); 1855 } 1856 1857 if (dp->tracks.track[n].notes == NULL) { 1858 MEM_FREE(up); 1859 ret = CDDBTRNOutOfMemory; 1860 break; 1861 } 1862 1863 (void) fcddb_strcat(dp->tracks.track[n].notes, up); 1864 MEM_FREE(up); 1865 } 1866 1867 p = q + 1; 1868 } 1869 1870 if (ret != Cddb_OK) 1871 return (ret); 1872 1873 /* Separate into disc artist & title fields */ 1874 if ((p = strchr(dp->fullname.name, '/')) != NULL && 1875 p > (dp->fullname.name + 1) && 1876 *(p-1) == ' ' && *(p+1) == ' ' && *(p+2) != '\0') { 1877 q = dp->fullname.name; 1878 1879 /* Artist */ 1880 *(p-1) = '\0'; 1881 dp->fullname.name = (CddbStr) fcddb_strdup(q); 1882 1883 /* Title */ 1884 dp->title = (CddbStr) fcddb_strdup(p+2); 1885 1886 MEM_FREE(q); 1887 } 1888 else { 1889 /* Move the whole string to title */ 1890 dp->title = dp->fullname.name; 1891 dp->fullname.name = NULL; 1892 } 1893 1894 /* Set up other fields */ 1895 dp->control = (void *) cp; 1896 dp->discid = fcddb_strdup(discid); 1897 dp->toc = (CddbStr) fcddb_strdup(toc); 1898 dp->tracks.count = (long) (discid_val & 0xff); 1899 dp->category = fcddb_strdup(category); 1900 dp->genre = fcddb_genre_categ2gp(cp, category); 1901 1902 if (wrcache) { 1903 #ifdef __VMS 1904 (void) sprintf(filepath, "%s.%s]%s.", 1905 cp->options.localcachepath, category, discid); 1906 #else 1907 (void) sprintf(filepath, "%s/%s/%s", 1908 cp->options.localcachepath, category, discid); 1909 #endif 1910 1911 (void) fcddb_write_cddb(filepath, dp, FALSE); 1912 } 1913 1914 return Cddb_OK; 1915 } 1916 1917 1918 /* 1919 * fcddb_connect 1920 * Make a network connection to the server 1921 * 1922 * Args: 1923 * host - Server host name 1924 * port - Server port 1925 * fd - Return file descriptor 1926 * 1927 * Return: 1928 * CddbResult status code 1929 */ 1930 #ifdef NOREMOTE 1931 /*ARGSUSED*/ 1932 #endif 1933 STATIC CddbResult 1934 fcddb_connect(char *host, unsigned short port, int *fd) 1935 { 1936 #ifndef NOREMOTE 1937 struct hostent *hp; 1938 struct in_addr ad; 1939 struct sockaddr_in sin; 1940 1941 sin.sin_port = htons(port); 1942 sin.sin_family = AF_INET; 1943 1944 FCDDBDBG(fcddb_errfp, "fcddb_connect: %s:%d\n", host, (int) port); 1945 /* Find server host address */ 1946 if ((hp = gethostbyname(host)) != NULL) { 1947 (void) memcpy((char *) &sin.sin_addr, hp->h_addr, 1948 hp->h_length); 1949 } 1950 else { 1951 if ((ad.s_addr = inet_addr(host)) != (unsigned long) -1) 1952 (void) memcpy((char *) &sin.sin_addr, 1953 (char *) &ad.s_addr, sizeof(ad.s_addr)); 1954 else 1955 return CDDBTRNHostNotFound; 1956 } 1957 1958 /* Open socket */ 1959 if ((*fd = SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { 1960 *fd = -1; 1961 return ((CddbResult) (errno == EINTR ? 1962 CDDBTRNServerTimeout : CDDBTRNSockCreateErr)); 1963 } 1964 1965 /* Connect to server */ 1966 if (CONNECT(*fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) { 1967 (void) close(*fd); 1968 *fd = -1; 1969 return ((CddbResult) (errno == EINTR ? 1970 CDDBTRNServerTimeout : CDDBTRNSockOpenErr)); 1971 } 1972 1973 return Cddb_OK; 1974 #else 1975 return CDDBCTLDisabled; 1976 #endif /* NOREMOTE */ 1977 } 1978 1979 1980 /* 1981 * fcddb_sendcmd 1982 * Send a command to the server, and get server response 1983 * 1984 * Args: 1985 * fd - file descriptor returned by fcddb_connect() 1986 * buf - command and response buffer (this function may reallocate 1987 * the buffer depending on response data size) 1988 * buflen - The buffer size 1989 * isproxy - Whether we're using a proxy server 1990 * 1991 * Return: 1992 * CddbResult status code 1993 */ 1994 #ifdef NOREMOTE 1995 /*ARGSUSED*/ 1996 #endif 1997 STATIC CddbResult 1998 fcddb_sendcmd(int fd, char **buf, size_t *buflen, bool_t isproxy) 1999 { 2000 #ifndef NOREMOTE 2001 int len, 2002 tot, 2003 ret, 2004 rbuflen; 2005 char *p; 2006 2007 p = *buf; 2008 len = strlen(p); 2009 tot = 0; 2010 2011 /* Send command to server */ 2012 FCDDBDBG(fcddb_errfp, 2013 "fcddb_sendcmd: Sending command:\n------\n%s\n------\n", 2014 *buf); 2015 2016 while (len > 0) { 2017 if ((ret = send(fd, p, len, 0)) < 0) { 2018 return ((CddbResult) (errno == EINTR ? 2019 CDDBTRNServerTimeout : CDDBTRNSendFailed)); 2020 } 2021 2022 p += ret; 2023 tot += ret; 2024 len -= ret; 2025 } 2026 2027 if (tot == 0) 2028 return CDDBTRNSendFailed; 2029 2030 /* Read server response to command */ 2031 p = *buf; 2032 rbuflen = *buflen; 2033 2034 for (;;) { 2035 /* Check remaining buffer size, enlarge if needed */ 2036 if (rbuflen <= 64) { 2037 int offset = p - *buf; 2038 2039 *buf = (char *) MEM_REALLOC( 2040 "http_buf", 2041 *buf, 2042 *buflen + CDDBP_CMDLEN 2043 ); 2044 if (*buf == NULL) 2045 return CDDBTRNOutOfMemory; 2046 2047 rbuflen += CDDBP_CMDLEN; 2048 *buflen += CDDBP_CMDLEN; 2049 p = *buf + offset; 2050 } 2051 2052 /* Receive response from server */ 2053 if ((len = recv(fd, p, rbuflen-1, 0)) < 0) { 2054 return ((CddbResult) (errno == EINTR ? 2055 CDDBTRNServerTimeout : CDDBTRNRecvFailed)); 2056 } 2057 2058 *(p + len) = '\0'; /* Mark end of data */ 2059 if (len == 0) 2060 break; 2061 2062 rbuflen -= len; 2063 p += len; 2064 } 2065 2066 FCDDBDBG(fcddb_errfp, 2067 "fcddb_sendcmd: Server response:\n------\n%s\n------\n", 2068 *buf); 2069 2070 /* Check for proxy authorization failure */ 2071 if (isproxy && strncmp(*buf, "HTTP/", 5) == 0) { 2072 p = strchr((*buf)+5, ' '); 2073 if (p != NULL && isdigit((int) *(p+1)) && 2074 (atoi(p+1) == HTTP_PROXYAUTH_FAIL)) { 2075 /* Need proxy authorization */ 2076 return CDDBTRNHTTPProxyError; 2077 } 2078 } 2079 2080 return Cddb_OK; 2081 #else 2082 return CDDBCTLDisabled; 2083 #endif /* NOREMOTE */ 2084 } 2085 2086 2087 /* 2088 * fcddb_disconnect 2089 * Disconnect from a server 2090 * 2091 * Args: 2092 * fd - File descriptor returned from fcddb_connect() 2093 * 2094 * Return: 2095 * Nothing 2096 */ 2097 STATIC void 2098 fcddb_disconnect(int fd) 2099 { 2100 (void) close(fd); 2101 } 2102 2103 2104 /* 2105 * fcddb_query_cddb 2106 * Send a query command to the server 2107 * 2108 * Args: 2109 * cp - Pointer to the control structure 2110 * discid - The disc ID 2111 * toc - The TOC 2112 * addr - Array of track offset frames 2113 * conhost - Server host 2114 * conport - Server port 2115 * isproxy - Whether we're using a proxy server 2116 * category - Return category string 2117 * matchcode - Return match code 2118 * 2119 * Return: 2120 * CddbResult status code 2121 */ 2122 #ifdef NOREMOTE 2123 /*ARGSUSED*/ 2124 #endif 2125 STATIC CddbResult 2126 fcddb_query_cddb( 2127 cddb_control_t *cp, 2128 char *discid, 2129 char *toc, 2130 unsigned int *addr, 2131 char *conhost, 2132 unsigned short conport, 2133 bool_t isproxy, 2134 char *category, 2135 int *matchcode 2136 ) 2137 { 2138 int i; 2139 unsigned int discid_val = 0; 2140 size_t buflen; 2141 FILE *fp; 2142 fcddb_gmap_t *gmp; 2143 cddb_disc_t *dp; 2144 char *buf, 2145 *p, 2146 *q, 2147 *r, 2148 *s, 2149 *categp = NULL, 2150 filepath[FILE_PATH_SZ]; 2151 time_t t; 2152 struct stat stbuf; 2153 #ifndef NOREMOTE 2154 CddbResult ret; 2155 int fd, 2156 ntrks; 2157 char urlstr[HOST_NAM_SZ + FILE_PATH_SZ + 12]; 2158 bool_t valid_resp; 2159 #endif 2160 2161 fcddb_clear_discs(&cp->discs); 2162 2163 (void) sscanf(discid, "%x", (unsigned int *) &discid_val); 2164 2165 buflen = CDDBP_CMDLEN; 2166 if ((buf = (char *) MEM_ALLOC("http_buf", buflen)) == NULL) { 2167 *matchcode = MATCH_NONE; 2168 return CDDBTRNOutOfMemory; 2169 } 2170 2171 t = time(NULL); 2172 2173 /* Try to locate the entry in the local cache */ 2174 for (gmp = fcddb_gmap_head; gmp != NULL; gmp = gmp->next) { 2175 #ifdef __VMS 2176 (void) sprintf(filepath, "%s.%s]%s.", 2177 cp->options.localcachepath, 2178 gmp->cddb1name, discid); 2179 #else 2180 (void) sprintf(filepath, "%s/%s/%s", 2181 cp->options.localcachepath, 2182 gmp->cddb1name, discid); 2183 #endif 2184 2185 if (stat(filepath, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 2186 continue; 2187 2188 #ifndef NOREMOTE 2189 if ((cp->options.localcacheflags & CACHE_DONT_CONNECT) == 0 && 2190 (t - stbuf.st_mtime) > 2191 (int) (cp->options.localcachetimeout * SECS_PER_DAY)) { 2192 2193 /* If any potential cached match is expired, 2194 * force a server query. 2195 */ 2196 fcddb_clear_discs(&cp->discs); 2197 break; 2198 } 2199 #endif 2200 2201 fp = NULL; 2202 if ((fp = fopen(filepath, "r")) != NULL && 2203 fgets(buf, buflen, fp) != NULL && 2204 strncmp(buf, "# xmcd", 6) == 0) { 2205 2206 /* Don't use fcddb_obj_alloc here because 2207 * we don't want CddbReleaseObject to 2208 * free it up. 2209 */ 2210 dp = (cddb_disc_t *) MEM_ALLOC( 2211 "CddbDisc", sizeof(cddb_disc_t) 2212 ); 2213 if (dp == NULL) { 2214 MEM_FREE(buf); 2215 *matchcode = MATCH_NONE; 2216 return CDDBTRNOutOfMemory; 2217 } 2218 (void) memset(dp, 0, sizeof(cddb_disc_t)); 2219 (void) strcpy(dp->objtype, "CddbDisc"); 2220 2221 /* Category: save and convert to genre */ 2222 dp->category = categp = fcddb_strdup(gmp->cddb1name); 2223 dp->genre = fcddb_genre_categ2gp(cp, gmp->cddb1name); 2224 2225 /* Disc ID */ 2226 dp->discid = fcddb_strdup(discid); 2227 2228 /* Artist/title */ 2229 while (fgets(buf, buflen, fp) != NULL) { 2230 if (buf[0] == '#' || 2231 strncmp(buf, "DTITLE=", 7) != 0) 2232 continue; 2233 2234 p = buf + 7; 2235 fcddb_line_filter(p); 2236 2237 if ((q = strchr(p, '\r')) != NULL || 2238 (q = strchr(p, '\n')) != NULL) 2239 *q = '\0'; 2240 2241 /* Put it all in artist first */ 2242 if (dp->fullname.name == NULL) { 2243 dp->fullname.name = (char *) 2244 MEM_ALLOC( 2245 "dp->fullname.name", 2246 strlen(p) + 1 2247 ); 2248 if (dp->fullname.name != NULL) 2249 dp->fullname.name[0] = '\0'; 2250 } 2251 else { 2252 dp->fullname.name = (char *) 2253 MEM_REALLOC( 2254 "dp->fullname.name", 2255 dp->fullname.name, 2256 strlen(dp->fullname.name) + 2257 strlen(p) + 1 2258 ); 2259 } 2260 2261 if (dp->fullname.name == NULL) { 2262 MEM_FREE(buf); 2263 *matchcode = MATCH_NONE; 2264 return CDDBTRNOutOfMemory; 2265 } 2266 2267 (void) fcddb_strcat(dp->fullname.name, p); 2268 } 2269 2270 /* Separate into disc artist & title fields */ 2271 r = strchr(dp->fullname.name, '/'); 2272 if (r != NULL && r > (dp->fullname.name + 1) && 2273 *(r-1) == ' ' && *(r+1) == ' ' 2274 && *(r+2) != '\0') { 2275 s = dp->fullname.name; 2276 2277 /* Artist */ 2278 *(r-1) = '\0'; 2279 dp->fullname.name = (CddbStr) fcddb_strdup(s); 2280 2281 /* Title */ 2282 dp->title = (CddbStr) fcddb_strdup(r+2); 2283 2284 MEM_FREE(s); 2285 } 2286 else { 2287 /* Move the whole string to title */ 2288 dp->title = dp->fullname.name; 2289 dp->fullname.name = NULL; 2290 } 2291 2292 /* TOC */ 2293 dp->toc = (CddbStr) fcddb_strdup(toc); 2294 2295 /* Add to discs list */ 2296 dp->next = cp->discs.discs; 2297 cp->discs.discs = dp; 2298 cp->discs.count++; 2299 } 2300 2301 if (fp != NULL) 2302 (void) fclose(fp); 2303 } 2304 2305 if (cp->discs.count == 1) { 2306 /* Matched one local cache entry */ 2307 *matchcode = MATCH_EXACT; 2308 (void) strcpy(category, categp); 2309 fcddb_clear_discs(&cp->discs); 2310 MEM_FREE(buf); 2311 2312 FCDDBDBG(fcddb_errfp, 2313 "\nfcddb_query_cddb: Exact match in cache %s/%s\n", 2314 category, discid); 2315 return Cddb_OK; 2316 } 2317 else if (cp->discs.count > 1) { 2318 /* Matched multiple local cache entries */ 2319 *matchcode = MATCH_MULTIPLE; 2320 MEM_FREE(buf); 2321 2322 FCDDBDBG(fcddb_errfp, 2323 "\nfcddb_query_cddb: Multiple matches in cache\n"); 2324 return Cddb_OK; 2325 } 2326 2327 #ifdef NOREMOTE 2328 *matchcode = MATCH_NONE; 2329 MEM_FREE(buf); 2330 2331 FCDDBDBG(fcddb_errfp, "\nfcddb_query_cddb: No match in cache\n"); 2332 return Cddb_OK; 2333 #else 2334 if ((cp->options.localcacheflags & CACHE_DONT_CONNECT) != 0) { 2335 *matchcode = MATCH_NONE; 2336 MEM_FREE(buf); 2337 2338 FCDDBDBG(fcddb_errfp, 2339 "\nfcddb_query_cddb: No match in cache\n"); 2340 return Cddb_OK; 2341 } 2342 2343 /* 2344 * Set up CDDB query command string 2345 */ 2346 2347 FCDDBDBG(fcddb_errfp, "\nfcddb_query_cddb: server query\n"); 2348 2349 ntrks = (int) (discid_val & 0xff); 2350 urlstr[0] = '\0'; 2351 2352 if (isproxy) { 2353 (void) sprintf(urlstr, "http://%s:%d", 2354 CDDB_SERVER_HOST, HTTP_PORT); 2355 } 2356 2357 (void) strcat(urlstr, CDDB_CGI_PATH); 2358 2359 (void) sprintf(buf, "GET %s?cmd=cddb+query+%s+%d", 2360 urlstr, discid, ntrks); 2361 2362 for (i = 0; i < ntrks; i++) 2363 (void) sprintf(buf, "%s+%u", buf, addr[i]); 2364 2365 (void) sprintf(buf, "%s+%u&%s&proto=4 HTTP/1.0\r\n", buf, 2366 addr[ntrks] / FRAME_PER_SEC, 2367 fcddb_hellostr); 2368 2369 if (isproxy && fcddb_auth_buf != NULL) { 2370 (void) sprintf(buf, "%sProxy-Authorization: Basic %s\r\n", buf, 2371 fcddb_auth_buf); 2372 } 2373 2374 (void) sprintf(buf, "%s%s%s\r\n%s\r\n", buf, 2375 "Host: ", CDDB_SERVER_HOST, 2376 fcddb_extinfo); 2377 2378 /* Open network connection to server */ 2379 if ((ret = fcddb_connect(conhost, conport, &fd)) != Cddb_OK) { 2380 if (buf != NULL) 2381 MEM_FREE(buf); 2382 *matchcode = MATCH_NONE; 2383 return (ret); 2384 } 2385 2386 /* 2387 * Send the command to the CDDB server and get response code 2388 */ 2389 if ((ret = fcddb_sendcmd(fd, &buf, &buflen, isproxy)) != Cddb_OK) { 2390 if (buf != NULL) 2391 MEM_FREE(buf); 2392 *matchcode = MATCH_NONE; 2393 return (ret); 2394 } 2395 2396 /* Close server connection */ 2397 fcddb_disconnect(fd); 2398 2399 /* Check for CDDB command response */ 2400 valid_resp = FALSE; 2401 p = buf; 2402 while ((q = strchr(p, '\n')) != NULL) { 2403 *q = '\0'; 2404 2405 if (STATCODE_CHECK(p)) { 2406 valid_resp = TRUE; 2407 *q = '\n'; 2408 break; 2409 } 2410 2411 *q = '\n'; 2412 p = q + 1; 2413 SKIP_SPC(p); 2414 } 2415 2416 if (!valid_resp) { 2417 /* Server error */ 2418 return CDDBTRNBadResponseSyntax; 2419 } 2420 2421 /* 2422 * Check command status 2423 */ 2424 2425 switch (STATCODE_1ST(p)) { 2426 case STAT_GEN_OK: 2427 case STAT_GEN_OKCONT: 2428 switch (STATCODE_3RD(p)) { 2429 case STAT_QURY_EXACT: 2430 if (STATCODE_2ND(p) == STAT_SUB_READY) { 2431 /* Single exact match */ 2432 p += 4; 2433 SKIP_SPC(p); 2434 2435 if (*p == '\0' || 2436 (q = strchr(p, ' ')) == NULL) { 2437 /* Invalid server response */ 2438 ret = CDDBTRNBadResponseSyntax; 2439 break; 2440 } 2441 *q = '\0'; 2442 2443 /* Get category */ 2444 (void) strncpy(category, p, 2445 FILE_BASE_SZ - 1); 2446 category[FILE_BASE_SZ - 1] = '\0'; 2447 2448 *q = ' '; 2449 2450 *matchcode = MATCH_EXACT; 2451 ret = Cddb_OK; 2452 break; 2453 } 2454 /*FALLTHROUGH*/ 2455 2456 case STAT_QURY_INEXACT: 2457 if (STATCODE_2ND(p) != STAT_SUB_OUTPUT) { 2458 /* Invalid server response */ 2459 ret = CDDBTRNBadResponseSyntax; 2460 break; 2461 } 2462 2463 ret = Cddb_OK; 2464 2465 p += 4; 2466 SKIP_SPC(p); 2467 if (*p == '\0' || (q = strchr(p, '\n')) == NULL) { 2468 /* Invalid server response */ 2469 ret = CDDBTRNBadResponseSyntax; 2470 break; 2471 } 2472 2473 for (;;) { 2474 p = q + 1; 2475 SKIP_SPC(p); 2476 if ((q = strchr(p, '\n')) != NULL) 2477 *q = '\0'; 2478 2479 if (*p == '.' || *p == '\0') 2480 /* Done */ 2481 break; 2482 2483 /* Zap carriage return if needed */ 2484 i = strlen(p) - 1; 2485 if (*(p+i) == '\r') 2486 *(p+i) = '\0'; 2487 2488 /* Don't use fcddb_obj_alloc here because 2489 * we don't want CddbReleaseObject to 2490 * free it up. 2491 */ 2492 dp = (cddb_disc_t *) MEM_ALLOC( 2493 "CddbDisc", sizeof(cddb_disc_t) 2494 ); 2495 if (dp == NULL) { 2496 *matchcode = MATCH_NONE; 2497 return CDDBTRNOutOfMemory; 2498 } 2499 (void) memset(dp, 0, sizeof(cddb_disc_t)); 2500 (void) strcpy(dp->objtype, "CddbDisc"); 2501 2502 r = p; 2503 if ((s = strchr(r, ' ')) == NULL) { 2504 /* Invalid server response */ 2505 MEM_FREE(dp); 2506 *matchcode = MATCH_NONE; 2507 return CDDBTRNBadResponseSyntax; 2508 } 2509 *s = '\0'; 2510 2511 /* If the server returned an unrecognized 2512 * category, add it to the table. 2513 */ 2514 if (fcddb_genre_categ2id(r) == NULL) 2515 fcddb_genre_addcateg(r); 2516 2517 /* Category: save and convert to genre */ 2518 dp->category = fcddb_strdup(r); 2519 dp->genre = fcddb_genre_categ2gp(cp, r); 2520 2521 *s = ' '; 2522 r = s + 1; 2523 SKIP_SPC(r); 2524 if ((s = strchr(r, ' ')) == NULL) { 2525 /* Invalid server response */ 2526 MEM_FREE(dp); 2527 *matchcode = MATCH_NONE; 2528 return CDDBTRNBadResponseSyntax; 2529 } 2530 *s = '\0'; 2531 2532 /* Disc ID */ 2533 dp->discid = fcddb_strdup(r); 2534 2535 *s = ' '; 2536 r = s + 1; 2537 SKIP_SPC(r); 2538 if (*r == '\0') { 2539 /* Invalid server response */ 2540 MEM_FREE(dp); 2541 *matchcode = MATCH_NONE; 2542 return CDDBTRNBadResponseSyntax; 2543 } 2544 2545 /* Artist / Title */ 2546 2547 /* Put it all in artist first */ 2548 dp->fullname.name = (CddbStr) fcddb_strdup(r); 2549 2550 /* Separate into disc artist & title fields */ 2551 r = strchr(dp->fullname.name, '/'); 2552 if (r != NULL && r > (dp->fullname.name + 1) && 2553 *(r-1) == ' ' && *(r+1) == ' ' 2554 && *(r+2) != '\0') { 2555 s = dp->fullname.name; 2556 2557 /* Artist */ 2558 *(r-1) = '\0'; 2559 dp->fullname.name = 2560 (CddbStr) fcddb_strdup(s); 2561 2562 /* Title */ 2563 dp->title = 2564 (CddbStr) fcddb_strdup(r+2); 2565 2566 MEM_FREE(s); 2567 } 2568 else { 2569 /* Move the whole string to title */ 2570 dp->title = dp->fullname.name; 2571 dp->fullname.name = NULL; 2572 } 2573 2574 /* TOC */ 2575 dp->toc = (CddbStr) fcddb_strdup(toc); 2576 2577 if (dp != NULL) { 2578 /* Add to discs list */ 2579 dp->next = cp->discs.discs; 2580 cp->discs.discs = dp; 2581 cp->discs.count++; 2582 } 2583 } 2584 2585 *matchcode = MATCH_MULTIPLE; 2586 break; 2587 2588 case STAT_QURY_NONE: 2589 ret = Cddb_OK; 2590 break; 2591 } 2592 break; 2593 2594 case STAT_GEN_CLIENT: 2595 switch (STATCODE_3RD(p)) { 2596 case STAT_QURY_AUTHFAIL: 2597 if (STATCODE_2ND(p) != STAT_SUB_CLOSE) { 2598 /* Invalid server response */ 2599 ret = CDDBTRNBadResponseSyntax; 2600 break; 2601 } 2602 2603 if (isproxy) { 2604 ret = CDDBTRNHTTPProxyError; 2605 break; 2606 } 2607 break; 2608 2609 default: 2610 break; 2611 } 2612 /*FALLTHROUGH*/ 2613 2614 case STAT_GEN_OKFAIL: 2615 case STAT_GEN_INFO: 2616 case STAT_GEN_ERROR: 2617 default: 2618 /* Error */ 2619 ret = CDDBTRNRecordNotFound; 2620 break; 2621 } 2622 2623 if (buf != NULL) 2624 MEM_FREE(buf); 2625 2626 if (ret != Cddb_OK) 2627 *matchcode = MATCH_NONE; 2628 2629 return (ret); 2630 #endif /* NOREMOTE */ 2631 } 2632 2633 2634 /* 2635 * fcddb_region_init 2636 * Initialize the region list 2637 * 2638 * Args: 2639 * cp - Pointer to the control structure 2640 * 2641 * Return: 2642 * CddbResult status code 2643 */ 2644 STATIC CddbResult 2645 fcddb_region_init(cddb_control_t *cp) 2646 { 2647 cddb_region_t *rp, 2648 *prevp; 2649 int i; 2650 2651 prevp = NULL; 2652 for (i = 0; fcddb_region_tbl[i].id != NULL; i++) { 2653 rp = &fcddb_region_tbl[i]; 2654 (void) strcpy(rp->objtype, "CddbRegion"); 2655 2656 if (i == 0) 2657 cp->regionlist.regions = rp; 2658 else 2659 prevp->next = rp; 2660 2661 cp->regionlist.count++; 2662 prevp = rp; 2663 } 2664 2665 return Cddb_OK; 2666 } 2667 2668 2669 /* 2670 * fcddb_language_init 2671 * Initialize the language list 2672 * 2673 * Args: 2674 * cp - Pointer to the control structure 2675 * 2676 * Return: 2677 * CddbResult status code 2678 */ 2679 STATIC CddbResult 2680 fcddb_language_init(cddb_control_t *cp) 2681 { 2682 cddb_language_t *lp, 2683 *prevp; 2684 int i; 2685 2686 prevp = NULL; 2687 for (i = 0; fcddb_language_tbl[i].id != NULL; i++) { 2688 lp = &fcddb_language_tbl[i]; 2689 (void) strcpy(lp->objtype, "CddbLanguage"); 2690 2691 if (i == 0) 2692 cp->languagelist.languages = lp; 2693 else 2694 prevp->next = lp; 2695 2696 cp->languagelist.count++; 2697 prevp = lp; 2698 } 2699 2700 return Cddb_OK; 2701 } 2702 2703 2704 /* 2705 * fcddb_genre_init 2706 * Initialize the genre tree 2707 * 2708 * Args: 2709 * cp - Pointer to the control structure 2710 * 2711 * Return: 2712 * CddbResult status code 2713 */ 2714 STATIC CddbResult 2715 fcddb_genre_init(cddb_control_t *cp) 2716 { 2717 fcddb_gmap_t *gmp = NULL, 2718 *gmp2 = NULL; 2719 cddb_genre_t *gp, 2720 *gp2, 2721 *gp3; 2722 FILE *fp; 2723 int i, 2724 mapvers = 0; 2725 unsigned int flags; 2726 char *p, 2727 *q, 2728 mapfile[FILE_PATH_SZ], 2729 buf[STR_BUF_SZ * 2], 2730 cddb1name[20], 2731 meta_id[8], 2732 meta_name[80], 2733 sub_id[8], 2734 sub_name[80]; 2735 bool_t use_builtin = TRUE; 2736 2737 #ifdef __VMS 2738 (void) sprintf(mapfile, "%s]genre.map", cp->options.localcachepath); 2739 #else 2740 (void) sprintf(mapfile, "%s/genre.map", cp->options.localcachepath); 2741 #endif 2742 2743 if ((fp = fopen(mapfile, "r")) != NULL) { 2744 FCDDBDBG(fcddb_errfp, "fcddb_genre_init: reading %s\n", 2745 mapfile); 2746 2747 for (i = 1; fgets(buf, sizeof(buf)-1, fp) != NULL; i++) { 2748 /* Make sure string is null terminated */ 2749 buf[sizeof(buf)-1] = '\0'; 2750 2751 /* Zap newline if necessary */ 2752 if (buf[strlen(buf)-1] == '\n') 2753 buf[strlen(buf)-1] = '\0'; 2754 2755 if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r'){ 2756 /* Comment or blank line */ 2757 (void) sscanf(buf, "# version=%d", &mapvers); 2758 continue; 2759 } 2760 2761 if (mapvers != GENREMAP_VERSION) { 2762 (void) fprintf(fcddb_errfp, 2763 "Ignored invalid genre map " 2764 "table file: %s\n", 2765 mapfile 2766 ); 2767 break; 2768 } 2769 2770 use_builtin = FALSE; 2771 2772 /* Read file entry */ 2773 q = buf; 2774 if ((p = strchr(q, ':')) == NULL) { 2775 (void) fprintf(fcddb_errfp, 2776 "Genre map table file: %s\n" 2777 "Error in line %d.\n", 2778 mapfile, i); 2779 continue; 2780 } 2781 *p = '\0'; 2782 strncpy(cddb1name, q, sizeof(cddb1name)-1); 2783 cddb1name[sizeof(cddb1name)-1] = '\0'; 2784 2785 q = p+1; 2786 if ((p = strchr(q, ':')) == NULL) { 2787 (void) fprintf(fcddb_errfp, 2788 "Genre map table file: %s\n" 2789 "Error in line %d.\n", 2790 mapfile, i); 2791 continue; 2792 } 2793 *p = '\0'; 2794 strncpy(meta_id, q, sizeof(meta_id)-1); 2795 meta_id[sizeof(meta_id)-1] = '\0'; 2796 2797 q = p+1; 2798 if ((p = strchr(q, ':')) == NULL) { 2799 (void) fprintf(fcddb_errfp, 2800 "Genre map table file: %s\n" 2801 "Error in line %d.\n", 2802 mapfile, i); 2803 continue; 2804 } 2805 *p = '\0'; 2806 strncpy(meta_name, q, sizeof(meta_name)-1); 2807 meta_name[sizeof(meta_name)-1] = '\0'; 2808 2809 q = p+1; 2810 if ((p = strchr(q, ':')) == NULL) { 2811 (void) fprintf(fcddb_errfp, 2812 "Genre map table file: %s\n" 2813 "Error in line %d.\n", 2814 mapfile, i); 2815 continue; 2816 } 2817 *p = '\0'; 2818 strncpy(sub_id, q, sizeof(sub_id)-1); 2819 sub_id[sizeof(sub_id)-1] = '\0'; 2820 2821 q = p+1; 2822 if ((p = strchr(q, ':')) == NULL) { 2823 (void) fprintf(fcddb_errfp, 2824 "Genre map table file: %s\n" 2825 "Error in line %d.\n", 2826 mapfile, i); 2827 continue; 2828 } 2829 *p = '\0'; 2830 strncpy(sub_name, q, sizeof(sub_name)-1); 2831 sub_name[sizeof(sub_name)-1] = '\0'; 2832 2833 q = p+1; 2834 if (sscanf(q, "%x", (unsigned int *) &flags) != 1) { 2835 (void) fprintf(fcddb_errfp, 2836 "Genre map table file: %s\n" 2837 "Error in line %d.\n", 2838 mapfile, i); 2839 continue; 2840 } 2841 2842 /* Allocate map entry */ 2843 gmp = (fcddb_gmap_t *) MEM_ALLOC( 2844 "CddbGenremap", sizeof(fcddb_gmap_t) 2845 ); 2846 if (gmp == NULL) { 2847 (void) fclose(fp); 2848 return CDDBTRNOutOfMemory; 2849 } 2850 (void) memset(gmp, 0, sizeof(fcddb_gmap_t)); 2851 2852 gmp->cddb1name = fcddb_strdup(cddb1name); 2853 gmp->meta.id = fcddb_strdup(meta_id); 2854 gmp->meta.name = fcddb_strdup(meta_name); 2855 gmp->sub.id = fcddb_strdup(sub_id); 2856 gmp->sub.name = fcddb_strdup(sub_name); 2857 gmp->flags = flags | CDDB_GMAP_DYN; 2858 gmp->next = NULL; 2859 2860 /* Add to list */ 2861 if (fcddb_gmap_head == NULL) 2862 fcddb_gmap_head = gmp; 2863 else 2864 gmp2->next = gmp; 2865 2866 gmp2 = gmp; 2867 } 2868 2869 (void) fclose(fp); 2870 } 2871 2872 if (use_builtin) { 2873 /* Can't initialize using genre table file, use the 2874 * built-in table instead. 2875 */ 2876 FCDDBDBG(fcddb_errfp, 2877 "fcddb_genre_init: using internal genre map table\n"); 2878 2879 fcddb_gmap_head = &fcddb_genre_map[0]; 2880 for (i = 0; fcddb_genre_map[i].cddb1name != NULL; i++) { 2881 gmp = &fcddb_genre_map[i]; 2882 2883 /* Turn this table into a linked list */ 2884 gmp->next = &fcddb_genre_map[i+1]; 2885 } 2886 if (gmp != NULL) 2887 gmp->next = NULL; 2888 2889 /* Write out the genre table file */ 2890 if ((fp = fopen(mapfile, "w")) != NULL) { 2891 FCDDBDBG(fcddb_errfp, 2892 "fcddb_genre_init: writing %s\n", mapfile); 2893 2894 /* Write out header */ 2895 (void) fprintf(fp, 2896 "# xmcd %s.%s genre mapping table\n# %s\n", 2897 VERSION_MAJ, VERSION_MIN, COPYRIGHT); 2898 (void) fprintf(fp, "# version=%d\n", GENREMAP_VERSION); 2899 (void) fprintf(fp, "#\n# %s:%s:%s:%s:%s:%s\n#\n", 2900 "cddb1name", 2901 "meta_id", "meta_name", 2902 "sub_id", "sub_name", 2903 "flags" 2904 ); 2905 2906 /* Write out map entries */ 2907 for (gmp = fcddb_gmap_head; gmp != NULL; 2908 gmp = gmp->next) { 2909 (void) fprintf(fp, "%s:%s:%s:%s:%s:%x\n", 2910 gmp->cddb1name, 2911 gmp->meta.id, 2912 gmp->meta.name, 2913 gmp->sub.id, 2914 gmp->sub.name, 2915 gmp->flags 2916 ); 2917 } 2918 2919 (void) fclose(fp); 2920 } 2921 } 2922 2923 /* Set up genre tree */ 2924 gp3 = NULL; 2925 cp->genretree.count = 0; 2926 for (gmp = fcddb_gmap_head; gmp != NULL; gmp = gmp->next) { 2927 if ((gmp->flags & CDDB_GMAP_DUP) != 0) 2928 continue; 2929 2930 /* Don't use fcddb_obj_alloc() here because we don't want 2931 * CddbReleaseObject() to free these. 2932 */ 2933 gp = (cddb_genre_t *) MEM_ALLOC( 2934 "CddbGenre", 2935 sizeof(cddb_genre_t) 2936 ); 2937 if (gp == NULL) 2938 return CDDBTRNOutOfMemory; 2939 2940 (void) memset(gp, 0, sizeof(cddb_genre_t)); 2941 (void) strcpy(gp->objtype, "CddbGenre"); 2942 2943 gp->id = (CddbStr) fcddb_strdup(gmp->meta.id); 2944 gp->name = (CddbStr) fcddb_strdup(gmp->meta.name); 2945 2946 gp2 = (cddb_genre_t *) MEM_ALLOC( 2947 "CddbGenre", 2948 sizeof(cddb_genre_t) 2949 ); 2950 if (gp2 == NULL) 2951 return CDDBTRNOutOfMemory; 2952 2953 (void) memset(gp2, 0, sizeof(cddb_genre_t)); 2954 (void) strcpy(gp2->objtype, "CddbGenre"); 2955 2956 gp2->id = (CddbStr) fcddb_strdup(gmp->sub.id); 2957 gp2->name = (CddbStr) fcddb_strdup(gmp->sub.name); 2958 gp->next = gp2; 2959 2960 if (cp->genretree.genres == NULL) 2961 cp->genretree.genres = gp; 2962 else 2963 gp3->nextmeta = gp; 2964 2965 gp3 = gp; 2966 2967 cp->genretree.count++; 2968 } 2969 2970 return Cddb_OK; 2971 } 2972 2973 2974 /* 2975 * fcddb_role_init 2976 * Initialize the role tree 2977 * 2978 * Args: 2979 * cp - Pointer to the control structure 2980 * 2981 * Return: 2982 * CddbResult status code 2983 */ 2984 STATIC CddbResult 2985 fcddb_role_init(cddb_control_t *cp) 2986 { 2987 cddb_role_t *rp, 2988 *prevrp; 2989 cddb_rolelist_t *rlp, 2990 *prevlp; 2991 int i; 2992 2993 prevrp = NULL; 2994 prevlp = NULL; 2995 rlp = NULL; 2996 for (i = 0; fcddb_role_tbl[i].id != NULL; i++) { 2997 rp = &fcddb_role_tbl[i]; 2998 2999 if (rp->roletype == NULL) 3000 continue; 3001 3002 if (rp->roletype[0] == 'm') { 3003 rlp = (cddb_rolelist_t *) MEM_ALLOC( 3004 "CddbRoleList", sizeof(cddb_rolelist_t) 3005 ); 3006 if (rlp == NULL) 3007 break; 3008 3009 (void) memset(rlp, 0, sizeof(cddb_rolelist_t)); 3010 (void) strcpy(rlp->objtype, "CddbRoleList"); 3011 3012 rlp->metarole = rp; 3013 3014 if (cp->roletree.rolelists == NULL) 3015 cp->roletree.rolelists = rlp; 3016 else 3017 prevlp->next = rlp; 3018 3019 cp->roletree.count++; 3020 prevlp = rlp; 3021 } 3022 else if (rp->roletype[0] == 's') { 3023 if (rlp == NULL) 3024 break; 3025 3026 if (rlp->subroles == NULL) 3027 rlp->subroles = rp; 3028 else 3029 prevrp->next = rp; 3030 3031 rlp->count++; 3032 prevrp = rp; 3033 } 3034 (void) strcpy(rp->objtype, "CddbRole"); 3035 } 3036 3037 return Cddb_OK; 3038 } 3039 3040 3041 /* 3042 * fcddb_url_init 3043 * Initialize the URL list 3044 * 3045 * Args: 3046 * cp - Pointer to the control structure 3047 * 3048 * Return: 3049 * CddbResult status code 3050 */ 3051 STATIC CddbResult 3052 fcddb_url_init(cddb_control_t *cp) 3053 { 3054 cddb_url_t *up; 3055 3056 /* Hard-wire the CDDB web site */ 3057 up = (cddb_url_t *) MEM_ALLOC("CddbURL", sizeof(cddb_url_t)); 3058 if (up == NULL) 3059 return CDDBTRNOutOfMemory; 3060 3061 (void) memset(up, 0, sizeof(cddb_url_t)); 3062 (void) strcpy(up->objtype, "CddbURL"); 3063 up->type = (CddbStr) fcddb_strdup("menu"); 3064 up->href = (CddbStr) fcddb_strdup("http://www.cddb.com/"); 3065 up->displaytext = 3066 (CddbStr) fcddb_strdup("CDDB, the #1 Music Info Source"); 3067 up->category = (CddbStr) fcddb_strdup("Music Sites"); 3068 3069 cp->urllist.urls = up; 3070 cp->urllist.count++; 3071 3072 return Cddb_OK; 3073 } 3074 3075 3076 /* 3077 * fcddb_region_halt 3078 * Clean up region table 3079 * 3080 * Args: 3081 * None 3082 * 3083 * Return: 3084 * Nothing 3085 */ 3086 STATIC void 3087 fcddb_region_halt(void) 3088 { 3089 int i; 3090 cddb_region_t *rp; 3091 3092 for (i = 0; fcddb_region_tbl[i].id != NULL; i++) { 3093 rp = &fcddb_region_tbl[i]; 3094 rp->objtype[0] = '\0'; 3095 rp->next = NULL; 3096 } 3097 } 3098 3099 3100 /* 3101 * fcddb_language_halt 3102 * Clean up language table 3103 * 3104 * Args: 3105 * None 3106 * 3107 * Return: 3108 * Nothing 3109 */ 3110 STATIC void 3111 fcddb_language_halt(void) 3112 { 3113 int i; 3114 cddb_language_t *lp; 3115 3116 for (i = 0; fcddb_language_tbl[i].id != NULL; i++) { 3117 lp = &fcddb_language_tbl[i]; 3118 lp->objtype[0] = '\0'; 3119 lp->next = NULL; 3120 } 3121 } 3122 3123 3124 /* 3125 * fcddb_role_halt 3126 * Clean up role table 3127 * 3128 * Args: 3129 * None 3130 * 3131 * Return: 3132 * Nothing 3133 */ 3134 STATIC void 3135 fcddb_role_halt(void) 3136 { 3137 int i; 3138 cddb_role_t *rp; 3139 3140 for (i = 0; fcddb_role_tbl[i].id != NULL; i++) { 3141 rp = &fcddb_role_tbl[i]; 3142 rp->objtype[0] = '\0'; 3143 rp->next = NULL; 3144 } 3145 } 3146 3147 3148 /* 3149 * fcddb_onsig 3150 * Generic signal handler, intended to just cause an interruption 3151 * to a blocked system call and no more. 3152 * 3153 * Args: 3154 * signo - signal number 3155 * 3156 * Return: 3157 * Nothing 3158 */ 3159 /*ARGSUSED*/ 3160 void 3161 fcddb_onsig(int signo) 3162 { 3163 /* Do nothing: we just want the side effect of causing the program 3164 * to return from a blocked system call with errno set to EINTR. 3165 */ 3166 FCDDBDBG(fcddb_errfp, "\nfcddb_onsig: Command timeout\n"); 3167 3168 #if !defined(USE_SIGACTION) && !defined(BSDCOMPAT) 3169 (void) fcddb_signal(signo, fcddb_onsig); 3170 #endif 3171 } 3172 3173 3174 /* 3175 * fcddb_signal 3176 * Wrapper around signal disposition functions to avoid 3177 * the scattering of OS-dependent code. 3178 * 3179 * Args: 3180 * Same as signal(2). 3181 * sig - The signal number 3182 * disp - The signal disposition 3183 * 3184 * Return: 3185 * The previous signal disposition. 3186 */ 3187 void 3188 (*fcddb_signal(int sig, void (*disp)(int)))(int) 3189 { 3190 #ifdef USE_SIGACTION 3191 struct sigaction sigact, 3192 oldact; 3193 3194 sigact.sa_handler = disp; 3195 sigemptyset(&sigact.sa_mask); 3196 sigact.sa_flags = 0; 3197 if (sigaction(sig, &sigact, &oldact) < 0) { 3198 perror("util_signal: sigaction failed"); 3199 _exit(1); 3200 } 3201 return (oldact.sa_handler); 3202 #endif 3203 #ifdef USE_SIGSET 3204 return (sigset(sig, disp)); 3205 #endif 3206 #ifdef USE_SIGNAL 3207 return (signal(sig, disp)); 3208 #endif 3209 } 3210 3211 3212 /* 3213 * fcddb_init 3214 * Initialize the fcddb module 3215 * 3216 * Args: 3217 * cp - Pointer to the control structure 3218 * 3219 * Return: 3220 * CddbResult status code 3221 */ 3222 CddbResult 3223 fcddb_init(cddb_control_t *cp) 3224 { 3225 CddbResult ret; 3226 char tmpuser[STR_BUF_SZ], 3227 tmphost[HOST_NAM_SZ + STR_BUF_SZ]; 3228 3229 /* Initialize debug message stream */ 3230 fcddb_errfp = stderr; 3231 3232 /* Initialize regions */ 3233 if ((ret = fcddb_region_init(cp)) != Cddb_OK) 3234 return (ret); 3235 3236 /* Initialize languages */ 3237 if ((ret = fcddb_language_init(cp)) != Cddb_OK) 3238 return (ret); 3239 3240 /* Initialize genres */ 3241 if ((ret = fcddb_genre_init(cp)) != Cddb_OK) 3242 return (ret); 3243 3244 /* Initialize roles */ 3245 if ((ret = fcddb_role_init(cp)) != Cddb_OK) 3246 return (ret); 3247 3248 /* Initialize URLs */ 3249 if ((ret = fcddb_url_init(cp)) != Cddb_OK) 3250 return (ret); 3251 3252 /* Misc initializations */ 3253 fcddb_http_xlat(cp->userinfo.userhandle, tmpuser), 3254 fcddb_http_xlat(cp->hostname, tmphost), 3255 3256 (void) sprintf(fcddb_hellostr, "hello=%.64s+%.64s+%s+%s", 3257 tmpuser, tmphost, cp->clientname, cp->clientver); 3258 (void) sprintf(fcddb_extinfo, 3259 /* Believe it or not, this is how MS Internet Explorer 3260 * does it, so don't blame me... 3261 */ 3262 "User-Agent: %s (compatible; %s %s)\r\nAccept: %s\r\n", 3263 "Mozilla/5.0", 3264 cp->clientname, 3265 cp->clientver, 3266 "text/plain"); 3267 3268 /* For SOCKS support */ 3269 SOCKSINIT(cp->clientname); 3270 3271 return Cddb_OK; 3272 } 3273 3274 3275 /* 3276 * fcddb_halt 3277 * Shut down the fcddb module 3278 * 3279 * Args: 3280 * cp - Pointer to the control structure 3281 * 3282 * Return: 3283 * CddbResult status code 3284 */ 3285 /*ARGSUSED*/ 3286 CddbResult 3287 fcddb_halt(cddb_control_t *cp) 3288 { 3289 /* Clean up internal tables */ 3290 fcddb_region_halt(); 3291 fcddb_language_halt(); 3292 fcddb_role_halt(); 3293 3294 if (fcddb_auth_buf != NULL) { 3295 MEM_FREE(fcddb_auth_buf); 3296 fcddb_auth_buf = NULL; 3297 } 3298 3299 return Cddb_OK; 3300 } 3301 3302 3303 /* 3304 * fcddb_obj_alloc 3305 * Allocate a CDDB object 3306 * 3307 * Args: 3308 * objtype - The object type to allocate 3309 * len - The size of the object 3310 * 3311 * Return: 3312 * Pointer to allocated memory 3313 */ 3314 void * 3315 fcddb_obj_alloc(char *objtype, size_t len) 3316 { 3317 void *retp; 3318 char *cp; 3319 3320 if (objtype == NULL || strlen(objtype) >= len) 3321 return NULL; 3322 3323 retp = MEM_ALLOC(objtype, len); 3324 if (retp != NULL) { 3325 cp = (char *) retp; 3326 (void) memset(cp, 0, len); 3327 (void) sprintf(cp, "+%s", objtype); 3328 } 3329 3330 return (retp); 3331 } 3332 3333 3334 /* 3335 * fcddb_obj_free 3336 * Free CDDB object 3337 * 3338 * Args: 3339 * obj - Pointer to object previously allocated via fcddb_obj_alloc() 3340 * 3341 * Return: 3342 * Nothing 3343 */ 3344 void 3345 fcddb_obj_free(void *obj) 3346 { 3347 char *cp = (char *) obj; 3348 3349 if (cp == NULL) 3350 return; 3351 3352 if (cp[0] == '+') { 3353 /* Need to add to this list if fcddb_obj_alloc is used 3354 * to allocate other types of objects 3355 */ 3356 if (strcmp(cp+1, "CddbControl") == 0) 3357 fcddb_clear_control((cddb_control_t *) obj); 3358 else if (strcmp(cp+1, "CddbDisc") == 0) 3359 fcddb_clear_disc((cddb_disc_t *) obj); 3360 else if (strcmp(cp+1, "CddbDiscs") == 0) 3361 fcddb_clear_discs((cddb_discs_t *) obj); 3362 else if (strcmp(cp+1, "CddbOptions") == 0) 3363 fcddb_clear_options((cddb_options_t *) obj); 3364 else if (strcmp(cp+1, "CddbUserInfo") == 0) 3365 fcddb_clear_userinfo((cddb_userinfo_t *) obj); 3366 else if (strcmp(cp+1, "CddbGenreTree") == 0) 3367 fcddb_clear_genretree((cddb_genretree_t *) obj); 3368 else if (strcmp(cp+1, "CddbRoleTree") == 0) 3369 fcddb_clear_roletree((cddb_roletree_t *) obj); 3370 else if (strcmp(cp+1, "CddbURLManager") == 0) 3371 fcddb_clear_urlmanager((cddb_urlmanager_t *) obj); 3372 else if (strcmp(cp+1, "CddbCredit") == 0) 3373 fcddb_clear_credit((cddb_credit_t *) obj); 3374 else if (strcmp(cp+1, "CddbSegment") == 0) 3375 fcddb_clear_segment((cddb_segment_t *) obj); 3376 3377 MEM_FREE(obj); 3378 } 3379 } 3380 3381 3382 /* 3383 * fcddb_runcmd 3384 * Spawn an external command 3385 * 3386 * Args: 3387 * cmd - The command string 3388 * 3389 * Return: 3390 * The command exit status 3391 */ 3392 int 3393 fcddb_runcmd(char *cmd) 3394 { 3395 int ret; 3396 #ifndef __VMS 3397 int fd, 3398 saverr = 0; 3399 pid_t cpid; 3400 waitret_t wstat; 3401 unsigned int oa; 3402 void (*oh)(int); 3403 3404 FCDDBDBG(fcddb_errfp, "fcddb_runcmd: [%s]\n", cmd); 3405 3406 /* Fork child to invoke external command */ 3407 switch (cpid = FORK()) { 3408 case 0: 3409 break; 3410 3411 case -1: 3412 /* Fork failed */ 3413 return EXE_ERR; 3414 3415 default: 3416 /* Parent process: wait for child to exit */ 3417 oh = fcddb_signal(SIGALRM, fcddb_onsig); 3418 oa = alarm(CMD_TIMEOUT_SECS); 3419 3420 errno = 0; 3421 ret = WAITPID(cpid, &wstat, 0); 3422 saverr = errno; 3423 3424 (void) alarm(oa); 3425 (void) fcddb_signal(SIGALRM, oh); 3426 3427 if (ret < 0) { 3428 if (saverr == EINTR) { 3429 FCDDBDBG(fcddb_errfp, 3430 "Timed out waiting for program " 3431 "to exit (pid=%d)\n", (int) cpid); 3432 } 3433 3434 ret = (saverr << 8); 3435 } 3436 else if (WIFEXITED(wstat)) 3437 ret = WEXITSTATUS(wstat); 3438 else 3439 ret = EXE_ERR; 3440 3441 FCDDBDBG(fcddb_errfp, "Command exit status %d\n", ret); 3442 return (ret); 3443 } 3444 3445 /* Child process */ 3446 for (fd = 3; fd < 255; fd++) { 3447 /* Close unneeded file descriptors */ 3448 (void) close(fd); 3449 } 3450 3451 /* Restore SIGTERM */ 3452 (void) fcddb_signal(SIGTERM, SIG_DFL); 3453 3454 /* Ignore SIGALRM */ 3455 (void) fcddb_signal(SIGALRM, SIG_IGN); 3456 3457 /* Exec a shell to run the command */ 3458 (void) execl(STR_SHPATH, STR_SHNAME, STR_SHARG, cmd, NULL); 3459 _exit(EXE_ERR); 3460 /*NOTREACHED*/ 3461 #else 3462 /* Do the command */ 3463 ret = system(cmd); 3464 3465 FCDDBDBG(fcddb_errfp, "Command exit status %d\n", ret); 3466 return (ret); 3467 #endif /* __VMS */ 3468 } 3469 3470 3471 /* 3472 * fcddb_set_auth 3473 * Set the HTTP/1.1 proxy-authorization string 3474 * 3475 * Args: 3476 * name - The proxy user name 3477 * passwd - The proxy password 3478 * 3479 * Return: 3480 * CddbResult status code 3481 */ 3482 CddbResult 3483 fcddb_set_auth(char *name, char *passwd) 3484 { 3485 int i, 3486 j, 3487 n1, 3488 n2; 3489 char *buf; 3490 3491 if (name == NULL || passwd == NULL) 3492 return Cddb_E_INVALIDARG; 3493 3494 i = strlen(name); 3495 j = strlen(passwd); 3496 n1 = i + j + 2; 3497 n2 = (n1 * 4 / 3) + 8; 3498 3499 if ((buf = (char *) MEM_ALLOC("strbuf", n1)) == NULL) 3500 return CDDBTRNOutOfMemory; 3501 3502 (void) sprintf(buf, "%s:%s", name, passwd); 3503 3504 (void) memset(name, 0, i); 3505 (void) memset(passwd, 0, j); 3506 3507 if (fcddb_auth_buf != NULL) 3508 MEM_FREE(fcddb_auth_buf); 3509 3510 fcddb_auth_buf = (char *) MEM_ALLOC("fcddb_auth_buf", n2); 3511 if (fcddb_auth_buf == NULL) 3512 return CDDBTRNOutOfMemory; 3513 3514 /* Base64 encode the name/password pair */ 3515 fcddb_b64encode( 3516 (byte_t *) buf, 3517 strlen(buf), 3518 (byte_t *) fcddb_auth_buf, 3519 FALSE 3520 ); 3521 3522 (void) memset(buf, 0, n1); 3523 MEM_FREE(buf); 3524 return Cddb_OK; 3525 } 3526 3527 3528 /* 3529 * fcddb_strdup 3530 * fcddb internal version of strdup(). 3531 * 3532 * Args: 3533 * str - source string 3534 * 3535 * Return: 3536 * Return string 3537 */ 3538 char * 3539 fcddb_strdup(char *str) 3540 { 3541 char *buf; 3542 3543 if (str == NULL) 3544 return NULL; 3545 3546 buf = (char *) MEM_ALLOC("strdup_buf", strlen(str) + 1); 3547 if (buf != NULL) 3548 (void) strcpy(buf, str); 3549 3550 return (buf); 3551 } 3552 3553 3554 /* 3555 * fcddb_basename 3556 * fcddb internal version of basename() 3557 * 3558 * Args: 3559 * path - File path 3560 * 3561 * Return: 3562 * Base name of file path 3563 */ 3564 char * 3565 fcddb_basename(char *path) 3566 { 3567 char *p; 3568 #ifdef __VMS 3569 char *q; 3570 #endif 3571 static char buf[FILE_PATH_SZ]; 3572 3573 if (path == NULL) 3574 return NULL; 3575 3576 if ((int) strlen(path) >= FILE_PATH_SZ) 3577 /* Error: path name too long */ 3578 return NULL; 3579 3580 (void) strcpy(buf, path); 3581 3582 if ((p = strrchr(buf, DIR_END)) == NULL) 3583 return (buf); 3584 3585 p++; 3586 3587 #ifdef __VMS 3588 if (*p == '\0') { 3589 /* The supplied path is a directory - special handling */ 3590 *--p = '\0'; 3591 if ((q = strrchr(buf, DIR_SEP)) != NULL) 3592 p = q + 1; 3593 else if ((q = strrchr(buf, DIR_BEG)) != NULL) 3594 p = q + 1; 3595 else if ((q = strrchr(buf, ':')) != NULL) 3596 p = q + 1; 3597 else { 3598 p = buf; 3599 *p = '\0'; 3600 } 3601 } 3602 #endif 3603 3604 return (p); 3605 } 3606 3607 3608 /* 3609 * fcddb_dirname 3610 * fcddb internal version of dirname() 3611 * 3612 * Args: 3613 * path - File path 3614 * 3615 * Return: 3616 * Directory name of file path 3617 */ 3618 char * 3619 fcddb_dirname(char *path) 3620 { 3621 char *p; 3622 #ifdef __VMS 3623 char *q; 3624 #endif 3625 static char buf[FILE_PATH_SZ]; 3626 3627 if (path == NULL) 3628 return NULL; 3629 3630 if ((int) strlen(path) >= FILE_PATH_SZ) 3631 /* Error: path name too long */ 3632 return NULL; 3633 3634 (void) strcpy(buf, path); 3635 3636 if ((p = strrchr(buf, DIR_END)) == NULL) 3637 return (buf); 3638 3639 #ifdef __VMS 3640 if (*++p == '\0') { 3641 /* The supplied path is a directory - special handling */ 3642 if ((q = strrchr(buf, DIR_SEP)) != NULL) { 3643 *q = DIR_END; 3644 *++q = '\0'; 3645 } 3646 else if ((q = strrchr(buf, ':')) != NULL) 3647 *++q = '\0'; 3648 else 3649 p = buf; 3650 } 3651 *p = '\0'; 3652 #else 3653 if (p == buf) 3654 *++p = '\0'; 3655 else 3656 *p = '\0'; 3657 #endif 3658 3659 return (buf); 3660 } 3661 3662 3663 /* 3664 * fcddb_mkdir 3665 * Wrapper for the mkdir() call. Will make all needed parent 3666 * directories leading to the target directory if they don't 3667 * already exist. 3668 * 3669 * Args: 3670 * path - The directory path to make 3671 * mode - The permissions 3672 * 3673 * Return: 3674 * CddbResult status code 3675 */ 3676 CddbResult 3677 fcddb_mkdir(char *path, mode_t mode) 3678 { 3679 CddbResult ret; 3680 char *cp, 3681 *mypath; 3682 #ifdef __VMS 3683 char *cp2, 3684 dev[STR_BUF_SZ], 3685 vpath[STR_BUF_SZ], 3686 chkpath[STR_BUF_SZ]; 3687 #endif 3688 3689 if ((mypath = fcddb_strdup(path)) == NULL) 3690 return CDDBTRNOutOfMemory; 3691 3692 #ifndef __VMS 3693 /* UNIX */ 3694 3695 /* Make parent directories, if needed */ 3696 cp = mypath; 3697 while (*(cp+1) == DIR_BEG) 3698 cp++; /* Skip extra initial '/'s if absolute path */ 3699 3700 while ((cp = strchr(cp+1, DIR_SEP)) != NULL) { 3701 *cp = '\0'; 3702 3703 if ((ret = fcddb_ckmkdir(mypath, mode)) != Cddb_OK) { 3704 MEM_FREE(mypath); 3705 return (ret); 3706 } 3707 3708 *cp = DIR_SEP; 3709 } 3710 3711 if ((ret = fcddb_ckmkdir(mypath, mode)) != Cddb_OK) { 3712 MEM_FREE(mypath); 3713 return (ret); 3714 } 3715 3716 (void) chmod(mypath, mode); 3717 3718 #else 3719 /* VMS */ 3720 3721 dev[0] = vpath[0] = '\0'; 3722 3723 /* Zap out the version number */ 3724 if ((cp = strchr(mypath, ';')) != NULL) 3725 *cp = '\0'; 3726 3727 /* Separate into disk and path parts */ 3728 3729 cp = mypath; 3730 if ((cp = strchr(cp+1, ':')) == NULL) 3731 cp = mypath; 3732 else { 3733 *cp = '\0'; 3734 (void) strcpy(dev, mypath); 3735 *cp++ = ':'; 3736 } 3737 3738 /* Check path syntax, and convert from 3739 * disk:[a.b.c]d.dir syntax to disk:[a.b.c.d] 3740 * if needed. 3741 */ 3742 cp2 = cp; 3743 if ((cp = strchr(cp, DIR_BEG)) == NULL) { 3744 cp = cp2; 3745 if ((cp2 = strrchr(cp+1, '.')) != NULL) { 3746 if (fcddb_strcasecmp(cp2, ".dir") == 0) { 3747 *cp2 = '\0'; 3748 (void) strcpy(vpath, cp); 3749 } 3750 else { 3751 FCDDBDBG(fcddb_errfp, "fcddb_mkdir: " 3752 "Illegal directory path: %s\n", 3753 mypath 3754 ); 3755 return CDDBTRNCannotCreateFile; 3756 } 3757 } 3758 } 3759 else { 3760 (void) strcpy(vpath, ++cp); 3761 if ((cp = strrchr(vpath, DIR_END)) != NULL) { 3762 *cp ='\0'; 3763 if ((cp2 = strchr(cp+1, '.')) != NULL) { 3764 if (fcddb_strcasecmp(cp2, ".dir") == 0) { 3765 *cp2 = '\0'; 3766 *cp = DIR_SEP; 3767 } 3768 else { 3769 FCDDBDBG(fcddb_errfp, "fcddb_mkdir: " 3770 "Illegal directory path: %s\n", 3771 mypath 3772 ); 3773 return CDDBTRNCannotCreateFile; 3774 } 3775 } 3776 else if (*(cp+1) != '\0') { 3777 FCDDBDBG(fcddb_errfp, "fcddb_mkdir: " 3778 "Illegal directory path: %s\n", 3779 mypath 3780 ); 3781 return CDDBTRNCannotCreateFile; 3782 } 3783 } 3784 } 3785 3786 cp = vpath; 3787 while ((cp = strchr(cp, DIR_SEP)) != NULL) { 3788 *cp = '\0'; 3789 3790 chkpath[0] = '\0'; 3791 3792 (void) sprintf(chkpath, "%s%s[%s]", 3793 dev, dev[0] == '\0' ? "" : ":", vpath 3794 ); 3795 3796 *cp++ = DIR_SEP; 3797 3798 if ((ret = fcddb_ckmkdir(chkpath, mode)) != Cddb_OK) { 3799 MEM_FREE(mypath); 3800 return (ret); 3801 } 3802 } 3803 3804 if (vpath[0] == '\0') { 3805 (void) sprintf(chkpath, "%s%s[000000]", 3806 dev, dev[0] == '\0' ? "" : ":" 3807 ); 3808 3809 if ((ret = fcddb_ckmkdir(chkpath, mode)) != Cddb_OK) { 3810 MEM_FREE(mypath); 3811 return (ret); 3812 } 3813 3814 (void) chmod(chkpath, mode); 3815 } 3816 else if (strcmp(vpath, "000000") != 0) { 3817 (void) sprintf(chkpath, "%s%s[%s]", 3818 dev, dev[0] == '\0' ? "" : ":", vpath 3819 ); 3820 3821 if ((ret = fcddb_ckmkdir(chkpath, mode)) != Cddb_OK) { 3822 MEM_FREE(mypath); 3823 return (ret); 3824 } 3825 3826 (void) chmod(chkpath, mode); 3827 } 3828 3829 #endif /* __VMS */ 3830 3831 MEM_FREE(mypath); 3832 return Cddb_OK; 3833 } 3834 3835 3836 /* 3837 * fcddb_username 3838 * Return user login name 3839 * 3840 * Args: 3841 * None 3842 * 3843 * Return: 3844 * The login name 3845 */ 3846 char * 3847 fcddb_username(void) 3848 { 3849 char *cp; 3850 #ifdef __VMS 3851 cp = getlogin(); 3852 if (cp != NULL) 3853 return (cp); 3854 #else 3855 struct passwd *pw; 3856 3857 /* Get login name from the password file if possible */ 3858 setpwent(); 3859 if ((pw = getpwuid(getuid())) != NULL) { 3860 endpwent(); 3861 return (pw->pw_name); 3862 } 3863 endpwent(); 3864 3865 /* Try the LOGNAME environment variable */ 3866 if ((cp = (char *) getenv("LOGNAME")) != NULL) 3867 return (cp); 3868 3869 /* Try the USER environment variable */ 3870 if ((cp = (char *) getenv("USER")) != NULL) 3871 return (cp); 3872 #endif 3873 /* If we still can't get the login name, just set it 3874 * to "nobody" (shrug). 3875 */ 3876 return ("nobody"); 3877 } 3878 3879 3880 /* 3881 * fcddb_homedir 3882 * Return the user's home directory 3883 * 3884 * Args: 3885 * None 3886 * 3887 * Return: 3888 * The user's home directory 3889 */ 3890 char * 3891 fcddb_homedir(void) 3892 { 3893 #ifndef __VMS 3894 struct passwd *pw; 3895 char *cp; 3896 3897 /* Get home directory from the password file if possible */ 3898 setpwent(); 3899 if ((pw = getpwuid(getuid())) != NULL) { 3900 endpwent(); 3901 return (pw->pw_dir); 3902 } 3903 endpwent(); 3904 3905 /* Try the HOME environment variable */ 3906 if ((cp = (char *) getenv("HOME")) != NULL) 3907 return (cp); 3908 3909 /* If we still can't get the home directory, just set it to the 3910 * current directory (shrug). 3911 */ 3912 return (CUR_DIR); 3913 #else 3914 char *cp; 3915 static char buf[FILE_PATH_SZ]; 3916 3917 if ((cp = (char *) getenv("HOME")) != NULL && 3918 (int) strlen(cp) < sizeof(buf)) { 3919 (void) strcpy(buf, cp); 3920 buf[strlen(buf)-1] = '\0'; /* Drop the "]" */ 3921 } 3922 else 3923 (void) strcpy(buf, "SYS$DISK:["); 3924 3925 return (buf); 3926 #endif /* __VMS */ 3927 } 3928 3929 3930 /* 3931 * fcddb_hostname 3932 * Get the host name (with fully qualified domain name if possible) 3933 * 3934 * Args: 3935 * None 3936 * 3937 * Return: 3938 * Nothing 3939 */ 3940 char * 3941 fcddb_hostname(void) 3942 { 3943 #ifndef NOREMOTE 3944 struct hostent *he; 3945 char **ap, 3946 hname[HOST_NAM_SZ+1]; 3947 static char buf[HOST_NAM_SZ+1]; 3948 3949 buf[0] = '\0'; 3950 3951 /* Try to determine host name */ 3952 if (gethostname(hname, HOST_NAM_SZ) < 0 || 3953 (he = gethostbyname(hname)) == NULL) { 3954 struct utsname un; 3955 3956 if (uname(&un) < 0) 3957 (void) strcpy(buf, "localhost"); /* shrug */ 3958 else 3959 (void) strncpy(buf, un.nodename, HOST_NAM_SZ); 3960 } 3961 else { 3962 /* Look for a a fully-qualified hostname 3963 * (with domain) 3964 */ 3965 if (strchr(he->h_name, '.') != NULL) 3966 (void) strcpy(buf, he->h_name); 3967 else { 3968 for (ap = he->h_aliases; *ap != NULL; ap++) { 3969 if (strchr(*ap, '.') != NULL) { 3970 (void) strncpy(buf, *ap, HOST_NAM_SZ); 3971 break; 3972 } 3973 } 3974 } 3975 3976 if (buf[0] == '\0') 3977 (void) strcpy(buf, hname); 3978 } 3979 3980 buf[HOST_NAM_SZ] = '\0'; 3981 return (buf); 3982 #else 3983 struct utsname un; 3984 static char buf[HOST_NAM_SZ+1]; 3985 3986 if (uname(&un) < 0) 3987 (void) strcpy(buf, "localhost"); /* shrug */ 3988 else 3989 (void) strncpy(buf, un.nodename, HOST_NAM_SZ); 3990 3991 buf[HOST_NAM_SZ] = '\0'; 3992 return (buf); 3993 #endif 3994 } 3995 3996 3997 /* 3998 * fcddb_genre_id2categ 3999 * Convert CDDB2 genre ID to CDDB1 category string. Since this could 4000 * yield multiple matches, only the first matched mapping is returned. 4001 * 4002 * Args: 4003 * id - The genre ID string 4004 * 4005 * Return: 4006 * The category string 4007 */ 4008 char * 4009 fcddb_genre_id2categ(char *id) 4010 { 4011 fcddb_gmap_t *gmp; 4012 4013 if (id == NULL) 4014 return NULL; 4015 4016 for (gmp = fcddb_gmap_head; gmp != NULL; gmp = gmp->next) { 4017 if (strcmp(gmp->sub.id, id) == 0) 4018 return (gmp->cddb1name); 4019 } 4020 return NULL; 4021 } 4022 4023 4024 /* 4025 * fcddb_genre_id2gp 4026 * Convert CDDB2 genre ID to a genre structure pointer 4027 * 4028 * Args: 4029 * cp - Pointer to the control structure 4030 * id - The genre ID string 4031 * 4032 * Return: 4033 * Pointer to the genre structure 4034 */ 4035 cddb_genre_t * 4036 fcddb_genre_id2gp(cddb_control_t *cp, char *id) 4037 { 4038 cddb_genre_t *p, 4039 *q; 4040 4041 if (cp == NULL || id == NULL) 4042 return NULL; 4043 4044 for (p = cp->genretree.genres; p != NULL; p = p->nextmeta) { 4045 if (p->id != NULL && strcmp(p->id, id) == 0) 4046 return (p); 4047 4048 for (q = p->next; q != NULL; q = q->next) { 4049 if (q->id != NULL && strcmp(q->id, id) == 0) 4050 return (q); 4051 } 4052 } 4053 4054 return NULL; 4055 } 4056 4057 4058 /* 4059 * fcddb_genre_categ2id 4060 * Convert CDDB1 category string to a CDDB2 genre ID 4061 * 4062 * Args: 4063 * categ - The category name string 4064 * 4065 * Return: 4066 * The genre ID string 4067 */ 4068 char * 4069 fcddb_genre_categ2id(char *categ) 4070 { 4071 fcddb_gmap_t *gmp; 4072 4073 if (categ == NULL) 4074 return NULL; 4075 4076 for (gmp = fcddb_gmap_head; gmp != NULL; gmp = gmp->next) { 4077 if (strcmp(gmp->cddb1name, categ) == 0) 4078 return (gmp->sub.id); 4079 } 4080 4081 return NULL; 4082 } 4083 4084 4085 /* 4086 * fcddb_genre_categ2gp 4087 * Convert CDDB1 category string to a genre structure pointer 4088 * 4089 * Args: 4090 * cp - Pointer to the control structure 4091 * categ - The category string 4092 * 4093 * Return: 4094 * Pointer to the genre structure 4095 */ 4096 cddb_genre_t * 4097 fcddb_genre_categ2gp(cddb_control_t *cp, char *categ) 4098 { 4099 fcddb_gmap_t *gmp; 4100 char *id; 4101 4102 if (cp == NULL || categ == NULL) 4103 return NULL; 4104 4105 id = NULL; 4106 for (gmp = fcddb_gmap_head; gmp != NULL; gmp = gmp->next) { 4107 if (strcmp(gmp->cddb1name, categ) == 0) { 4108 id = gmp->sub.id; 4109 break; 4110 } 4111 } 4112 4113 if (id == NULL) 4114 return NULL; 4115 4116 return (fcddb_genre_id2gp(cp, id)); 4117 } 4118 4119 4120 /* 4121 * fcddb_parse_toc 4122 * Parse a TOC string and populate an array of frame offsets 4123 * 4124 * Args: 4125 * tocstr - The TOC 4126 * addr - Return array of frame offsets 4127 * 4128 * Return: 4129 * The number of tracks 4130 */ 4131 int 4132 fcddb_parse_toc(char *tocstr, unsigned int *addr) 4133 { 4134 char *p, 4135 *q, 4136 sav; 4137 int i; 4138 4139 if (tocstr == NULL || addr == NULL) 4140 return 0; 4141 4142 i = 0; 4143 p = tocstr; 4144 SKIP_SPC(p); 4145 4146 /* Starting frames for each track */ 4147 while ((q = strchr(p, ' ')) != NULL || (q = strchr(p, '\t')) != NULL) { 4148 sav = *q; 4149 *q = '\0'; 4150 addr[i++] = (unsigned int) atoi(p); 4151 *q = sav; 4152 p = q+1; 4153 SKIP_SPC(p); 4154 } 4155 addr[i] = (unsigned int) atoi(p); /* Lead out */ 4156 4157 return (i); 4158 } 4159 4160 4161 /* 4162 * fcddb_discid 4163 * Compute CDDB1 disc ID 4164 * 4165 * Args: 4166 * ntrks - Number of tracks 4167 * addr - Array of frame offsets 4168 * 4169 * Return: 4170 * The CDDB1 disc ID string 4171 */ 4172 char * 4173 fcddb_discid(int ntrks, unsigned int *addr) 4174 { 4175 int i, 4176 t = 0, 4177 n = 0; 4178 unsigned int discid_val; 4179 char discid[12]; 4180 4181 /* For backward compatibility this algorithm must not change */ 4182 4183 for (i = 0; i < ntrks; i++) 4184 n += fcddb_sum(addr[i] / FRAME_PER_SEC); 4185 4186 t = (addr[ntrks] / FRAME_PER_SEC) - (addr[0] / FRAME_PER_SEC); 4187 4188 discid_val = ((n % 0xff) << 24 | t << 8 | ntrks); 4189 (void) sprintf(discid, "%08x", discid_val); 4190 4191 return (fcddb_strdup(discid)); 4192 } 4193 4194 4195 /* 4196 * fcddb_read_cddb 4197 * Send a read command to the CDDB server 4198 * 4199 * Args: 4200 * cp - Pointer to the control structure 4201 * discid - The disc ID 4202 * toc - The TOC 4203 * category - The CDDB1 category 4204 * conhost - Server host 4205 * conport - The server port 4206 * isproxy - Whether we're using a proxy server 4207 * 4208 * Return: 4209 * CddbResult status code 4210 */ 4211 #ifdef NOREMOTE 4212 /*ARGSUSED*/ 4213 #endif 4214 CddbResult 4215 fcddb_read_cddb( 4216 cddb_control_t *cp, 4217 char *discid, 4218 char *toc, 4219 char *category, 4220 char *conhost, 4221 unsigned short conport, 4222 bool_t isproxy 4223 ) 4224 { 4225 CddbResult ret; 4226 int fd, 4227 n, 4228 i; 4229 size_t buflen, 4230 contentlen; 4231 char *buf, 4232 *p, 4233 filepath[FILE_PATH_SZ]; 4234 time_t t; 4235 struct stat stbuf; 4236 #ifndef NOREMOTE 4237 char *q, 4238 urlstr[HOST_NAM_SZ + FILE_PATH_SZ + 12]; 4239 bool_t valid_resp; 4240 #endif 4241 4242 #ifdef __VMS 4243 (void) sprintf(filepath, "%s.%s]%s.", 4244 cp->options.localcachepath, category, discid); 4245 #else 4246 (void) sprintf(filepath, "%s/%s/%s", 4247 cp->options.localcachepath, category, discid); 4248 #endif 4249 4250 t = time(NULL); 4251 4252 /* 4253 * Attempt to read from local cache 4254 */ 4255 FCDDBDBG(fcddb_errfp, "\nfcddb_read_cddb: Checking cache\n"); 4256 4257 ret = CDDBTRNRecordNotFound; 4258 4259 if (stat(filepath, &stbuf) == 0 && S_ISREG(stbuf.st_mode) 4260 #ifndef NOREMOTE 4261 && 4262 (((cp->options.localcacheflags & CACHE_DONT_CONNECT) != 0) || 4263 ((t - stbuf.st_mtime) <= 4264 (int) (cp->options.localcachetimeout * SECS_PER_DAY))) 4265 #endif 4266 ) { 4267 /* 4268 * Use local cache 4269 */ 4270 4271 contentlen = stbuf.st_size; 4272 buflen = contentlen + 1; 4273 buf = (char *) MEM_ALLOC("cddb_buf", buflen); 4274 if (buf == NULL) 4275 return CDDBTRNOutOfMemory; 4276 4277 ret = Cddb_OK; 4278 4279 FCDDBDBG(fcddb_errfp, "fcddb_read_cddb: cache read %s/%s\n", 4280 category, discid); 4281 4282 /* Open local cache file */ 4283 if ((fd = open(filepath, O_RDONLY)) < 0) { 4284 ret = CDDBTRNRecordNotFound; 4285 } 4286 else { 4287 /* Read cache data into buf */ 4288 4289 for (i = contentlen, p = buf; i > 0; i -= n, p += n) { 4290 if ((n = read(fd, p, i)) < 0) { 4291 MEM_FREE(buf); 4292 ret = CDDBTRNRecordNotFound; 4293 break; 4294 } 4295 } 4296 *p = '\0'; 4297 4298 (void) close(fd); 4299 4300 if (ret == Cddb_OK) { 4301 ret = fcddb_parse_cddb_data( 4302 cp, buf, discid, category, toc, FALSE, 4303 &cp->disc 4304 ); 4305 } 4306 } 4307 4308 MEM_FREE(buf); 4309 buf = NULL; 4310 4311 if (ret == Cddb_OK) 4312 return (ret); 4313 4314 /* Fall through */ 4315 } 4316 4317 #ifdef NOREMOTE 4318 return (ret); 4319 #else 4320 if ((cp->options.localcacheflags & CACHE_DONT_CONNECT) != 0) 4321 return (ret); 4322 4323 /* 4324 * Do CDDB server read 4325 */ 4326 4327 FCDDBDBG(fcddb_errfp, "fcddb_read_cddb: server read %s/%s\n", 4328 category, discid); 4329 4330 buflen = CDDBP_CMDLEN; 4331 if ((buf = (char *) MEM_ALLOC("http_buf", buflen)) == NULL) 4332 return CDDBTRNOutOfMemory; 4333 4334 /* 4335 * Set up CDDB read command string 4336 */ 4337 4338 urlstr[0] = '\0'; 4339 4340 if (isproxy) { 4341 (void) sprintf(urlstr, "http://%s:%d", 4342 CDDB_SERVER_HOST, HTTP_PORT); 4343 } 4344 4345 (void) strcat(urlstr, CDDB_CGI_PATH); 4346 4347 (void) sprintf(buf, 4348 "GET %s?cmd=cddb+read+%s+%s&%s&proto=4 HTTP/1.0\r\n", 4349 urlstr, category, discid, fcddb_hellostr); 4350 4351 if (isproxy && fcddb_auth_buf != NULL) { 4352 (void) sprintf(buf, "%sProxy-Authorization: Basic %s\r\n", buf, 4353 fcddb_auth_buf); 4354 } 4355 4356 (void) sprintf(buf, "%s%s%s\r\n%s\r\n", buf, 4357 "Host: ", CDDB_SERVER_HOST, 4358 fcddb_extinfo); 4359 4360 /* Open network connection to server */ 4361 if ((ret = fcddb_connect(conhost, conport, &fd)) != Cddb_OK) { 4362 if (buf != NULL) 4363 MEM_FREE(buf); 4364 return (ret); 4365 } 4366 4367 /* 4368 * Send the command to the CDDB server and get response code 4369 */ 4370 if ((ret = fcddb_sendcmd(fd, &buf, &buflen, isproxy)) != Cddb_OK) { 4371 if (buf != NULL) 4372 MEM_FREE(buf); 4373 return (ret); 4374 } 4375 4376 /* Close server connection */ 4377 fcddb_disconnect(fd); 4378 4379 /* Check for CDDB command response */ 4380 valid_resp = FALSE; 4381 p = buf; 4382 while ((q = strchr(p, '\n')) != NULL) { 4383 *q = '\0'; 4384 4385 if (STATCODE_CHECK(p)) { 4386 valid_resp = TRUE; 4387 *q = '\n'; 4388 break; 4389 } 4390 4391 *q = '\n'; 4392 p = q + 1; 4393 SKIP_SPC(p); 4394 } 4395 4396 if (!valid_resp) { 4397 /* Server error */ 4398 return CDDBTRNBadResponseSyntax; 4399 } 4400 4401 /* 4402 * Check command status 4403 */ 4404 ret = Cddb_OK; 4405 4406 switch (STATCODE_1ST(p)) { 4407 case STAT_GEN_OK: 4408 case STAT_GEN_OKCONT: 4409 if (STATCODE_2ND(p) == '1') { 4410 if ((q = strchr(p, '\n')) == NULL) { 4411 ret = CDDBTRNBadResponseSyntax; 4412 break; 4413 } 4414 4415 p = q + 1; 4416 SKIP_SPC(p); 4417 if (*p == '\0') { 4418 ret = CDDBTRNBadResponseSyntax; 4419 break; 4420 } 4421 4422 ret = fcddb_parse_cddb_data( 4423 cp, p, discid, category, toc, TRUE, &cp->disc 4424 ); 4425 break; 4426 } 4427 4428 /*FALLTHROUGH*/ 4429 4430 case STAT_GEN_INFO: 4431 case STAT_GEN_OKFAIL: 4432 case STAT_GEN_ERROR: 4433 case STAT_GEN_CLIENT: 4434 default: 4435 /* Error */ 4436 ret = CDDBTRNRecordNotFound; 4437 break; 4438 } 4439 4440 if (buf != NULL) 4441 MEM_FREE(buf); 4442 4443 return (ret); 4444 #endif 4445 } 4446 4447 4448 /* 4449 * fcddb_lookup_cddb 4450 * High-level function to perform a CDDB server lookup 4451 * 4452 * Args: 4453 * cp - Pointer to the control structure 4454 * discid - The disc ID 4455 * addr - Array of frame offsets 4456 * toc - The TOC 4457 * conhost - The server host 4458 * conport - The server port 4459 * isproxy - Whether we're using a proxy server 4460 * matchcode - Return matchcode 4461 * 4462 * Return: 4463 * CddbResult status code 4464 */ 4465 CddbResult 4466 fcddb_lookup_cddb( 4467 cddb_control_t *cp, 4468 char *discid, 4469 unsigned int *addr, 4470 char *toc, 4471 char *conhost, 4472 unsigned short conport, 4473 bool_t isproxy, 4474 int *matchcode 4475 ) 4476 { 4477 CddbResult ret; 4478 char category[FILE_BASE_SZ]; 4479 4480 *matchcode = MATCH_NONE; 4481 fcddb_clear_disc(&cp->disc); 4482 4483 if ((ret = fcddb_query_cddb(cp, discid, toc, addr, conhost, conport, 4484 isproxy, category, matchcode)) != Cddb_OK) 4485 return (ret); 4486 4487 if (*matchcode == MATCH_EXACT) 4488 ret = fcddb_read_cddb(cp, discid, toc, category, 4489 conhost, conport, isproxy); 4490 else 4491 ret = Cddb_OK; 4492 4493 return (ret); 4494 } 4495 4496 4497 /* 4498 * fcddb_submit_cddb 4499 * Submit a disc entry to CDDB 4500 * 4501 * Args: 4502 * cp - Pointer to the control structure 4503 * dp - Pointer to the disc structure 4504 * conhost - The server host 4505 * conport - The server port 4506 * isproxy - Whether we're using a proxy server 4507 * 4508 * Return: 4509 * CddbResult status code 4510 */ 4511 #ifdef NOREMOTE 4512 /*ARGSUSED*/ 4513 #endif 4514 CddbResult 4515 fcddb_submit_cddb( 4516 cddb_control_t *cp, 4517 cddb_disc_t *dp, 4518 char *conhost, 4519 unsigned short conport, 4520 bool_t isproxy 4521 ) 4522 { 4523 CddbResult ret; 4524 char filepath[FILE_PATH_SZ]; 4525 struct stat stbuf; 4526 fcddb_gmap_t *gmp; 4527 char *p; 4528 #ifndef NOREMOTE 4529 int fd, 4530 n, 4531 i; 4532 size_t contentlen = 0, 4533 buflen; 4534 char *buf, 4535 *q, 4536 urlstr[HOST_NAM_SZ + FILE_PATH_SZ + 12]; 4537 bool_t valid_resp; 4538 #endif 4539 4540 /* Check if the genre has changed */ 4541 if (dp->category != NULL) { 4542 if ((p = fcddb_genre_categ2id(dp->category)) != NULL && 4543 strcmp(p, dp->genre->id) != 0) { 4544 MEM_FREE(dp->category); 4545 dp->category = NULL; 4546 } 4547 } 4548 4549 if (dp->category == NULL) { 4550 /* Find the appropriate category: look for an existing 4551 * local cache file. 4552 */ 4553 for (gmp = fcddb_gmap_head; gmp != NULL; gmp = gmp->next) { 4554 if (strcmp(gmp->sub.id, dp->genre->id) != 0) 4555 continue; 4556 #ifdef __VMS 4557 (void) sprintf(filepath, "%s.%s]%s.", 4558 cp->options.localcachepath, 4559 gmp->cddb1name, dp->discid); 4560 #else 4561 (void) sprintf(filepath, "%s/%s/%s", 4562 cp->options.localcachepath, 4563 gmp->cddb1name, dp->discid); 4564 #endif 4565 if (stat(filepath, &stbuf) < 0) 4566 continue; 4567 4568 if (S_ISREG(stbuf.st_mode)) { 4569 dp->category = fcddb_strdup(gmp->cddb1name); 4570 break; 4571 } 4572 } 4573 4574 if (dp->category == NULL) { 4575 /* Try reverse mapping the genre */ 4576 if ((p = fcddb_genre_id2categ(dp->genre->id)) != NULL) 4577 dp->category = fcddb_strdup(p); 4578 } 4579 4580 if (dp->category == NULL) { 4581 /* Still can't find a matching category: fail */ 4582 return Cddb_E_INVALIDARG; 4583 } 4584 } 4585 4586 #ifdef __VMS 4587 (void) sprintf(filepath, "%s.%s]%s.", 4588 cp->options.localcachepath, dp->category, dp->discid); 4589 #else 4590 (void) sprintf(filepath, "%s/%s/%s", 4591 cp->options.localcachepath, dp->category, dp->discid); 4592 #endif 4593 4594 if ((ret = fcddb_write_cddb(filepath, dp, TRUE)) != Cddb_OK) 4595 return (ret); 4596 4597 if (stat(filepath, &stbuf) < 0) 4598 return CDDBTRNFileWriteError; 4599 4600 #ifdef NOREMOTE 4601 return Cddb_OK; 4602 #else 4603 if ((cp->options.localcacheflags & CACHE_SUBMIT_OFFLINE) != 0) 4604 return Cddb_OK; 4605 4606 contentlen = (size_t) stbuf.st_size; 4607 4608 buflen = CDDBP_CMDLEN + contentlen; 4609 if ((buf = (char *) MEM_ALLOC("http_buf", buflen)) == NULL) 4610 return CDDBTRNOutOfMemory; 4611 4612 FCDDBDBG(fcddb_errfp, "\nfcddb_submit_cddb: %s/%s\n", 4613 dp->category, dp->discid); 4614 4615 /* 4616 * Set up CDDB submit command string 4617 */ 4618 4619 urlstr[0] = '\0'; 4620 4621 if (isproxy) { 4622 (void) sprintf(urlstr, "http://%s:%d", 4623 CDDB_SUBMIT_HOST, HTTP_PORT); 4624 } 4625 4626 (void) strcat(urlstr, CDDB_SUBMIT_CGI_PATH); 4627 4628 (void) sprintf(buf, "POST %s HTTP/1.0\r\n", urlstr); 4629 4630 if (isproxy && fcddb_auth_buf != NULL) { 4631 (void) sprintf(buf, "%sProxy-Authorization: Basic %s\r\n", buf, 4632 fcddb_auth_buf); 4633 } 4634 4635 (void) sprintf(buf, 4636 "%s%s%s\r\n%s%s%s\r\n%s%s\r\n" 4637 "%s%s@%s\r\n%s%s\r\n%s%u\r\n\r\n", buf, 4638 "Host: ", CDDB_SUBMIT_HOST, 4639 fcddb_extinfo, 4640 "Category: ", dp->category, 4641 "Discid: ", dp->discid, 4642 "User-Email: ", cp->userinfo.userhandle, cp->hostname, 4643 "Submit-Mode: ", 4644 cp->options.testsubmitmode ? "test" : "submit", 4645 "Content-Length: ", (unsigned int) contentlen 4646 ); 4647 4648 /* Append CDDB file data to buf here */ 4649 if ((fd = open(filepath, O_RDONLY)) < 0) { 4650 MEM_FREE(buf); 4651 return CDDBTRNRecordNotFound; 4652 } 4653 4654 for (i = contentlen, p = buf + strlen(buf); i > 0; i -= n, p += n) { 4655 if ((n = read(fd, p, i)) < 0) { 4656 MEM_FREE(buf); 4657 return CDDBTRNRecordNotFound; 4658 } 4659 } 4660 *p = '\0'; 4661 4662 (void) close(fd); 4663 4664 /* Open network connection to server */ 4665 if ((ret = fcddb_connect(conhost, conport, &fd)) != Cddb_OK) { 4666 if (buf != NULL) 4667 MEM_FREE(buf); 4668 return (ret); 4669 } 4670 4671 /* 4672 * Send the command to the CDDB server and get response code 4673 */ 4674 if ((fcddb_sendcmd(fd, &buf, &buflen, isproxy)) != Cddb_OK) { 4675 if (buf != NULL) 4676 MEM_FREE(buf); 4677 return (ret); 4678 } 4679 4680 /* Close server connection */ 4681 fcddb_disconnect(fd); 4682 4683 /* Check for CDDB command response */ 4684 valid_resp = FALSE; 4685 p = buf; 4686 while ((q = strchr(p, '\n')) != NULL) { 4687 *q = '\0'; 4688 4689 if (STATCODE_CHECK(p)) { 4690 valid_resp = TRUE; 4691 *q = '\n'; 4692 break; 4693 } 4694 4695 *q = '\n'; 4696 p = q + 1; 4697 SKIP_SPC(p); 4698 } 4699 4700 if (!valid_resp) { 4701 /* Server error */ 4702 return CDDBTRNBadResponseSyntax; 4703 } 4704 4705 /* 4706 * Check command status 4707 */ 4708 switch (STATCODE_1ST(p)) { 4709 case STAT_GEN_OK: 4710 ret = Cddb_OK; 4711 break; 4712 4713 case STAT_GEN_ERROR: 4714 ret = CDDBSVCMissingField; 4715 break; 4716 4717 case STAT_GEN_OKFAIL: 4718 ret = CDDBSVCServiceError; 4719 break; 4720 4721 case STAT_GEN_OKCONT: 4722 case STAT_GEN_INFO: 4723 default: 4724 /* Error */ 4725 ret = CDDBTRNBadResponseSyntax; 4726 break; 4727 } 4728 4729 if (buf != NULL) 4730 MEM_FREE(buf); 4731 4732 return (ret); 4733 #endif /* NOREMOTE */ 4734 } 4735 4736 4737 /* 4738 * fcddb_flush_cddb 4739 * Flush local cachestore 4740 * 4741 * Args: 4742 * cp - Pointer to the control structure 4743 * 4744 * Return: 4745 * CddbResult status code 4746 */ 4747 #ifdef NOREMOTE 4748 /*ARGSUSED*/ 4749 #endif 4750 CddbResult 4751 fcddb_flush_cddb(cddb_control_t *cp, CDDBFlushFlags flags) 4752 { 4753 #ifndef NOREMOTE 4754 fcddb_gmap_t *gmp; 4755 char cmd[FILE_PATH_SZ + STR_BUF_SZ]; 4756 4757 if (flags == FLUSH_DEFAULT || (flags & FLUSH_CACHE_MEDIA)) { 4758 for (gmp = fcddb_gmap_head; gmp != NULL; gmp = gmp->next) { 4759 #ifndef __VMS 4760 (void) sprintf(cmd, 4761 "rm -rf %s/%s >/dev/null 2>&1", 4762 cp->options.localcachepath, 4763 gmp->cddb1name); 4764 4765 if (fcddb_runcmd(cmd) != 0) 4766 return CDDBTRNFileDeleteError; 4767 #else 4768 /* For VMS simply removing the file doesn't 4769 * produce the desired effect, so do nothing 4770 * for now. 4771 */ 4772 #endif 4773 } 4774 } 4775 #endif 4776 return Cddb_OK; 4777 } 4778 4779 4780