1 /* $OpenBSD: cddb.c,v 1.15 2008/04/27 23:06:40 fgsch Exp $ */ 2 /* 3 * Copyright (c) 2002 Marc Espie. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS 15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD 18 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/socket.h> 29 #include <netinet/in.h> 30 #include <sys/cdio.h> 31 #include <err.h> 32 #include <netdb.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include <vis.h> 38 #include "extern.h" 39 40 unsigned long cddb_sum(unsigned long); 41 void send_hello(FILE *); 42 void send_query(FILE *, int, struct cd_toc_entry *); 43 int further_query(FILE *, char *); 44 int connect_to(const char *, const char *); 45 int parse_connect_to(const char *, const char *); 46 char * get_line(FILE *); 47 char * get_answer(FILE *); 48 void verify_track_names(char **, int, struct cd_toc_entry *); 49 void safe_copy(char **, const char *); 50 51 unsigned long 52 cddb_sum(unsigned long v) 53 { 54 unsigned long sum = 0; 55 56 while (v > 0) { 57 sum += v % 10; 58 v /= 10; 59 } 60 return (sum); 61 } 62 63 unsigned long 64 cddb_discid(int n, struct cd_toc_entry *e) 65 { 66 unsigned long sum; 67 int i; 68 69 sum = 0; 70 for (i =0; i < n; i++) 71 sum += cddb_sum(entry2time(e+i)); 72 return (((sum % 0xff) << 24) | 73 ((entry2time(e+n) - entry2time(e)) << 8) | n); 74 } 75 76 void 77 send_hello(FILE *cout) 78 { 79 char hostname[MAXHOSTNAMELEN]; 80 81 if (gethostname(hostname, sizeof(hostname)) == -1) 82 strlcpy(hostname, "unknown", sizeof hostname); 83 fprintf(cout, "CDDB HELLO %s %s cdio " VERSION "\r\n", 84 getlogin(), hostname); 85 fflush(cout); 86 } 87 88 void 89 send_query(FILE *f, int n, struct cd_toc_entry *e) 90 { 91 int i; 92 93 fprintf(f, "cddb query %8lx %d", cddb_discid(n, e), n); 94 for (i = 0; i < n; i++) 95 fprintf(f, " %lu", entry2frames(e+i)); 96 fprintf(f, " %lu\r\n", (entry2frames(e+n)-entry2frames(e)) /75); 97 } 98 99 #define MAXSIZE 256 100 char copy_buffer[MAXSIZE]; 101 102 void 103 safe_copy(char **p, const char *title) 104 { 105 strnvis(copy_buffer, title, MAXSIZE-1, VIS_TAB|VIS_NL); 106 if (*p == NULL) 107 *p = strdup(copy_buffer); 108 else { 109 char *n; 110 111 if (asprintf(&n, "%s%s", *p, copy_buffer) == -1) 112 return; 113 free(*p); 114 *p = n; 115 } 116 } 117 118 int 119 further_query(FILE *cout, char *line) 120 { 121 char *key; 122 char *title; 123 124 key = strchr(line, ' '); 125 if (!key) 126 return 0; 127 *key++ = 0; 128 title = strchr(key, ' '); 129 if (!title) 130 return 0; 131 *title++ = 0; 132 strnvis(copy_buffer, title, MAXSIZE-1, VIS_TAB|VIS_NL); 133 printf("%s", copy_buffer); 134 strnvis(copy_buffer, line, MAXSIZE-1, VIS_TAB|VIS_NL); 135 printf("(%s)\n", copy_buffer); 136 fprintf(cout, "CDDB READ %s %s\r\n", line, key); 137 fflush(cout); 138 return 1; 139 } 140 141 142 int 143 connect_to(const char *host, const char *serv) 144 { 145 int s = -1; 146 struct addrinfo hints, *res0 = NULL, *res; 147 int error; 148 149 memset(&hints, 0, sizeof hints); 150 hints.ai_family = PF_UNSPEC; 151 hints.ai_socktype = SOCK_STREAM; 152 153 error = getaddrinfo(host, serv, &hints, &res0); 154 if (error) { 155 warnx("%s", gai_strerror(error)); 156 return -1; 157 } 158 159 for (res = res0; res; res = res->ai_next) { 160 s = socket(res->ai_family, res->ai_socktype, 161 res->ai_protocol); 162 if (s == -1) 163 continue; 164 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { 165 close(s); 166 s = -1; 167 continue; 168 } 169 break; 170 } 171 if (s == -1) 172 warn("cddb"); 173 freeaddrinfo(res0); 174 return s; 175 } 176 177 int 178 parse_connect_to(const char *host_port, const char *port) 179 { 180 int s; 181 char *last, *host; 182 183 host = (char *)host_port; 184 185 last = strrchr(host_port, ':'); 186 if (last != 0 && !(last != host && last[-1] == ':')) { 187 port = last + 1; 188 host = malloc(last - host_port + 1); 189 if (!host) 190 return -1; 191 memcpy(host, host_port, last-host_port); 192 host[last-host_port] = 0; 193 } 194 s = connect_to(host, port); 195 if (host != host_port) 196 free(host); 197 return s; 198 } 199 200 char * 201 get_line(FILE *cin) 202 { 203 char *line; 204 size_t len; 205 206 line = fgetln(cin, &len); 207 if (!line) 208 return NULL; 209 if (len == 0) 210 return NULL; 211 if (line[len-1] == '\n') 212 line[--len] = 0; 213 if (len != 0 && line[len-1] == '\r') 214 line[--len] = 0; 215 return line; 216 } 217 218 char * 219 get_answer(FILE *cin) 220 { 221 char *line; 222 223 line = get_line(cin); 224 if (!line || *line != '2') 225 return NULL; 226 else 227 return line; 228 } 229 230 void 231 verify_track_names(char **names, int n, struct cd_toc_entry *e) 232 { 233 int i; 234 235 for (i = 0; i < n; i++) { 236 if (names[i] == 0) 237 names[i] = strdup(e->control & 4 ? "data" : "audio"); 238 } 239 } 240 241 char ** 242 cddb(const char *host_port, int n, struct cd_toc_entry *e, char *arg) 243 { 244 int s = -1; 245 FILE *cin = NULL; 246 FILE *cout = NULL; 247 char *type; 248 char *line; 249 char **result = NULL; 250 int i; 251 const char *errstr; 252 253 s = parse_connect_to(host_port, "cddb"); 254 if (s == -1) 255 goto end; 256 cin = fdopen(s, "r"); 257 if (!cin) { 258 warn("cddb: fdopen"); 259 goto end; 260 } 261 cout = fdopen(s, "w"); 262 s = -1; 263 if (!cout) { 264 warn("cddb: fdopen"); 265 goto end; 266 } 267 line = get_answer(cin); 268 if (!line) { 269 warnx("cddb: won't talk to us"); 270 goto end; 271 } 272 273 send_hello(cout); 274 line = get_answer(cin); 275 if (!line) { 276 warnx("cddb: problem in hello"); 277 goto end; 278 } 279 280 send_query(cout, n, e); 281 fflush(cout); 282 line = get_answer(cin); 283 if (!line) { 284 warnx("cddb: problem in query"); 285 goto end; 286 } 287 type = strchr(line, ' '); 288 if (!type) 289 goto end; 290 *type++ = 0; 291 /* no match or other issue */ 292 if (strcmp(line, "202") == 0) { 293 printf("cddb: No match in database\n"); 294 goto end; 295 } 296 if (strcmp(line, "211") == 0 || strcmp(line, "212") == 0) { 297 int number = strtonum(arg, 0, INT_MAX, &errstr); 298 if (errstr != NULL && *arg != NULL) { 299 warnx("cddb: invalid index"); 300 goto end; 301 } 302 if (number == 0) { 303 if (strcmp(line, "211") == 0) 304 printf("cddb: multiple matches\n"); 305 else { 306 printf("cddb: inexact match\n"); 307 number = 1; 308 } 309 } 310 if (number == 0) { 311 for (i = 1;; i++) { 312 line = get_line(cin); 313 if (!line || strcmp(line, ".") == 0) 314 goto end; 315 printf("%d: %s\n", i, line); 316 } 317 } else { 318 int ok = 0; 319 320 for (i = 1;; i++) { 321 line = get_line(cin); 322 if (!line) 323 break; 324 if (strcmp(line, ".") == 0) 325 break; 326 if (i == number) 327 ok = further_query(cout, line); 328 } 329 if (!ok) 330 goto end; 331 } 332 } else if (strcmp(line, "200") != 0 || !further_query(cout, type)) 333 goto end; 334 result = calloc(sizeof(char *), n + 1); 335 if (!result) 336 goto end; 337 for (i = 0; i <= n; i++) 338 result[i] = NULL; 339 line = get_answer(cin); 340 if (!line) 341 goto end2; 342 for (;;) { 343 long k; 344 char *end; 345 346 line = get_line(cin); 347 if (!line) 348 goto end2; 349 if (strcmp(line, ".") == 0) 350 break; 351 if (strncmp(line, "TTITLE", 6) != 0) 352 continue; 353 line += 6; 354 k = strtol(line, &end, 10); 355 if (*end++ != '=') 356 continue; 357 if (k >= n) 358 continue; 359 safe_copy(&result[k], end); 360 } 361 fprintf(cout, "QUIT\r\n"); 362 verify_track_names(result, n, e); 363 goto end; 364 end2: 365 free(result); 366 result = NULL; 367 end: 368 if (cout) 369 fclose(cout); 370 if (cin) 371 fclose(cin); 372 if (s != -1) 373 close(s); 374 return result; 375 } 376 377 void 378 free_names(char **names) 379 { 380 int i; 381 382 for (i = 0; names[i]; i++) 383 free(names[i]); 384 free(names); 385 } 386