xref: /openbsd/usr.bin/cdio/cddb.c (revision 143054fa)
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