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
cddb_sum(unsigned long v)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
cddb_discid(int n,struct cd_toc_entry * e)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
send_hello(FILE * cout)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
send_query(FILE * f,int n,struct cd_toc_entry * e)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
safe_copy(char ** p,const char * title)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
further_query(FILE * cout,char * line)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
connect_to(const char * host,const char * serv)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
parse_connect_to(const char * host_port,const char * port)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 *
get_line(FILE * cin)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 *
get_answer(FILE * cin)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
verify_track_names(char ** names,int n,struct cd_toc_entry * e)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 **
cddb(const char * host_port,int n,struct cd_toc_entry * e,char * arg)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
free_names(char ** names)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