1 /* $OpenBSD: cddb.c,v 1.23 2020/06/26 19:51:14 naddy 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/socket.h> 28 #include <netinet/in.h> 29 #include <sys/cdio.h> 30 #include <err.h> 31 #include <netdb.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <limits.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[HOST_NAME_MAX+1]; 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 fflush(f); 98 } 99 100 #define MAXSIZE 256 101 char copy_buffer[MAXSIZE]; 102 103 void 104 safe_copy(char **p, const char *title) 105 { 106 strnvis(copy_buffer, title, MAXSIZE-1, VIS_TAB|VIS_NL); 107 if (*p == NULL) 108 *p = strdup(copy_buffer); 109 else { 110 char *n; 111 112 if (asprintf(&n, "%s%s", *p, copy_buffer) == -1) 113 return; 114 free(*p); 115 *p = n; 116 } 117 } 118 119 int 120 further_query(FILE *cout, char *line) 121 { 122 char *key; 123 char *title; 124 125 key = strchr(line, ' '); 126 if (!key) 127 return 0; 128 *key++ = 0; 129 title = strchr(key, ' '); 130 if (!title) 131 return 0; 132 *title++ = 0; 133 strnvis(copy_buffer, title, MAXSIZE-1, VIS_TAB|VIS_NL); 134 printf("%s", copy_buffer); 135 strnvis(copy_buffer, line, MAXSIZE-1, VIS_TAB|VIS_NL); 136 printf("(%s)\n", copy_buffer); 137 fprintf(cout, "CDDB READ %s %s\r\n", line, key); 138 fflush(cout); 139 return 1; 140 } 141 142 143 int 144 connect_to(const char *host, const char *serv) 145 { 146 int s = -1; 147 struct addrinfo hints, *res0 = NULL, *res; 148 int error; 149 150 memset(&hints, 0, sizeof hints); 151 hints.ai_family = PF_UNSPEC; 152 hints.ai_socktype = SOCK_STREAM; 153 154 error = getaddrinfo(host, serv, &hints, &res0); 155 if (error) { 156 warnx("%s", gai_strerror(error)); 157 return -1; 158 } 159 160 for (res = res0; res; res = res->ai_next) { 161 s = socket(res->ai_family, res->ai_socktype, 162 res->ai_protocol); 163 if (s == -1) 164 continue; 165 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { 166 close(s); 167 s = -1; 168 continue; 169 } 170 break; 171 } 172 if (s == -1) 173 warn("cddb"); 174 freeaddrinfo(res0); 175 return s; 176 } 177 178 int 179 parse_connect_to(const char *host_port, const char *port) 180 { 181 int s; 182 char *last, *host; 183 184 host = (char *)host_port; 185 186 last = strrchr(host_port, ':'); 187 if (last != 0 && !(last != host && last[-1] == ':')) { 188 port = last + 1; 189 host = malloc(last - host_port + 1); 190 if (!host) 191 return -1; 192 memcpy(host, host_port, last-host_port); 193 host[last-host_port] = 0; 194 } 195 s = connect_to(host, port); 196 if (host != host_port) 197 free(host); 198 return s; 199 } 200 201 char * 202 get_line(FILE *cin) 203 { 204 char *line; 205 size_t len; 206 207 line = fgetln(cin, &len); 208 if (!line) 209 return NULL; 210 if (len == 0) 211 return NULL; 212 if (line[len-1] == '\n') 213 line[--len] = 0; 214 if (len != 0 && line[len-1] == '\r') 215 line[--len] = 0; 216 if (line[len] != 0) 217 return NULL; 218 return line; 219 } 220 221 char * 222 get_answer(FILE *cin) 223 { 224 char *line; 225 226 line = get_line(cin); 227 if (!line || *line != '2') 228 return NULL; 229 else 230 return line; 231 } 232 233 void 234 verify_track_names(char **names, int n, struct cd_toc_entry *e) 235 { 236 int i; 237 238 for (i = 0; i < n; i++) { 239 if (names[i] == 0) 240 names[i] = strdup(e->control & 4 ? "data" : "audio"); 241 } 242 } 243 244 char ** 245 cddb(const char *host_port, int n, struct cd_toc_entry *e, char *arg) 246 { 247 int s = -1; 248 int s2 = -1; 249 FILE *cin = NULL; 250 FILE *cout = NULL; 251 char *type; 252 char *line; 253 char **result = NULL; 254 int i; 255 const char *errstr; 256 257 s = parse_connect_to(host_port, "8880"); 258 if (s == -1) 259 goto end; 260 s2 = dup(s); 261 if (s2 == -1) 262 goto end; 263 cin = fdopen(s, "r"); 264 if (!cin) { 265 warn("cddb: fdopen"); 266 goto end; 267 } 268 s = -1; 269 cout = fdopen(s2, "w"); 270 if (!cout) { 271 warn("cddb: fdopen"); 272 goto end; 273 } 274 s2 = -1; 275 line = get_answer(cin); 276 if (!line) { 277 warnx("cddb: won't talk to us"); 278 goto end; 279 } 280 281 send_hello(cout); 282 line = get_answer(cin); 283 if (!line) { 284 warnx("cddb: problem in hello"); 285 goto end; 286 } 287 288 send_query(cout, n, e); 289 line = get_answer(cin); 290 if (!line) { 291 warnx("cddb: problem in query"); 292 goto end; 293 } 294 type = strchr(line, ' '); 295 if (!type) 296 goto end; 297 *type++ = 0; 298 /* no match or other issue */ 299 if (strcmp(line, "202") == 0) { 300 printf("cddb: No match in database\n"); 301 goto end; 302 } 303 if (strcmp(line, "211") == 0 || strcmp(line, "212") == 0) { 304 int number = strtonum(arg, 0, INT_MAX, &errstr); 305 if (errstr != NULL && *arg != '\0') { 306 warnx("cddb: invalid index"); 307 goto end; 308 } 309 if (number == 0) { 310 if (strcmp(line, "211") == 0) 311 printf("cddb: multiple matches\n"); 312 else { 313 printf("cddb: inexact match\n"); 314 number = 1; 315 } 316 } 317 if (number == 0) { 318 for (i = 1;; i++) { 319 line = get_line(cin); 320 if (!line || strcmp(line, ".") == 0) 321 goto end; 322 printf("%d: %s\n", i, line); 323 } 324 } else { 325 int ok = 0; 326 327 for (i = 1;; i++) { 328 line = get_line(cin); 329 if (!line) 330 break; 331 if (strcmp(line, ".") == 0) 332 break; 333 if (i == number) 334 ok = further_query(cout, line); 335 } 336 if (!ok) 337 goto end; 338 } 339 } else if (strcmp(line, "200") != 0 || !further_query(cout, type)) 340 goto end; 341 result = calloc(sizeof(char *), n + 1); 342 if (!result) 343 goto end; 344 for (i = 0; i <= n; i++) 345 result[i] = NULL; 346 line = get_answer(cin); 347 if (!line) 348 goto end2; 349 for (;;) { 350 int k; 351 char *end; 352 353 line = get_line(cin); 354 if (!line) 355 goto end2; 356 if (strcmp(line, ".") == 0) 357 break; 358 if (strncmp(line, "TTITLE", 6) != 0) 359 continue; 360 line += 6; 361 end = strchr(line, '='); 362 if (end == NULL) 363 continue; 364 *end++ = '\0'; 365 k = strtonum(line, 0, n - 1, &errstr); 366 if (errstr != NULL) 367 continue; 368 safe_copy(&result[k], end); 369 } 370 fprintf(cout, "QUIT\r\n"); 371 verify_track_names(result, n, e); 372 goto end; 373 end2: 374 free(result); 375 result = NULL; 376 end: 377 if (cout) 378 fclose(cout); 379 if (cin) 380 fclose(cin); 381 if (s != -1) 382 close(s); 383 if (s2 != -1) 384 close(s2); 385 return result; 386 } 387 388 void 389 free_names(char **names) 390 { 391 int i; 392 393 for (i = 0; names[i]; i++) 394 free(names[i]); 395 free(names); 396 } 397