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 __RCSID("$MirOS: src/usr.bin/cdio/cddb.c,v 1.2 2010/03/06 19:57:40 tg Exp $");
41
42 unsigned long cddb_sum(unsigned long);
43 void send_hello(FILE *);
44 void send_query(FILE *, int, struct cd_toc_entry *);
45 int further_query(FILE *, char *);
46 int connect_to(const char *, const char *);
47 int parse_connect_to(const char *, const char *);
48 char * get_line(FILE *);
49 char * get_answer(FILE *);
50 void verify_track_names(char **, int, struct cd_toc_entry *);
51 void safe_copy(char **, const char *);
52
53 unsigned long
cddb_sum(unsigned long v)54 cddb_sum(unsigned long v)
55 {
56 unsigned long sum = 0;
57
58 while (v > 0) {
59 sum += v % 10;
60 v /= 10;
61 }
62 return (sum);
63 }
64
65 unsigned long
cddb_discid(int n,struct cd_toc_entry * e)66 cddb_discid(int n, struct cd_toc_entry *e)
67 {
68 unsigned long sum;
69 int i;
70
71 sum = 0;
72 for (i =0; i < n; i++)
73 sum += cddb_sum(entry2time(e+i));
74 return (((sum % 0xff) << 24) |
75 ((entry2time(e+n) - entry2time(e)) << 8) | n);
76 }
77
78 void
send_hello(FILE * cout)79 send_hello(FILE *cout)
80 {
81 char hostname[MAXHOSTNAMELEN];
82
83 if (gethostname(hostname, sizeof(hostname)) == -1)
84 strlcpy(hostname, "unknown", sizeof hostname);
85 fprintf(cout, "CDDB HELLO %s %s cdio " VERSION "\r\n",
86 getlogin(), hostname);
87 fflush(cout);
88 }
89
90 void
send_query(FILE * f,int n,struct cd_toc_entry * e)91 send_query(FILE *f, int n, struct cd_toc_entry *e)
92 {
93 int i;
94
95 fprintf(f, "cddb query %8lx %d", cddb_discid(n, e), n);
96 for (i = 0; i < n; i++)
97 fprintf(f, " %lu", entry2frames(e+i));
98 fprintf(f, " %lu\r\n", (entry2frames(e+n)-entry2frames(e)) /75);
99 }
100
101 #define MAXSIZE 256
102 char copy_buffer[MAXSIZE];
103
104 void
safe_copy(char ** p,const char * title)105 safe_copy(char **p, const char *title)
106 {
107 strnvis(copy_buffer, title, MAXSIZE-1, VIS_TAB|VIS_NL);
108 if (*p == NULL)
109 *p = strdup(copy_buffer);
110 else {
111 char *n;
112
113 if (asprintf(&n, "%s%s", *p, copy_buffer) == -1)
114 return;
115 free(*p);
116 *p = n;
117 }
118 }
119
120 int
further_query(FILE * cout,char * line)121 further_query(FILE *cout, char *line)
122 {
123 char *key;
124 char *title;
125
126 key = strchr(line, ' ');
127 if (!key)
128 return 0;
129 *key++ = 0;
130 title = strchr(key, ' ');
131 if (!title)
132 return 0;
133 *title++ = 0;
134 strnvis(copy_buffer, title, MAXSIZE-1, VIS_TAB|VIS_NL);
135 printf("%s", copy_buffer);
136 strnvis(copy_buffer, line, MAXSIZE-1, VIS_TAB|VIS_NL);
137 printf("(%s)\n", copy_buffer);
138 fprintf(cout, "CDDB READ %s %s\r\n", line, key);
139 fflush(cout);
140 return 1;
141 }
142
143
144 int
connect_to(const char * host,const char * serv)145 connect_to(const char *host, const char *serv)
146 {
147 int s = -1;
148 struct addrinfo hints, *res0 = NULL, *res;
149 int error;
150
151 memset(&hints, 0, sizeof hints);
152 hints.ai_family = PF_UNSPEC;
153 hints.ai_socktype = SOCK_STREAM;
154
155 error = getaddrinfo(host, serv, &hints, &res0);
156 if (error) {
157 warnx("%s", gai_strerror(error));
158 return -1;
159 }
160
161 for (res = res0; res; res = res->ai_next) {
162 s = socket(res->ai_family, res->ai_socktype,
163 res->ai_protocol);
164 if (s == -1)
165 continue;
166 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
167 close(s);
168 s = -1;
169 continue;
170 }
171 break;
172 }
173 if (s == -1)
174 warn("cddb");
175 freeaddrinfo(res0);
176 return s;
177 }
178
179 int
parse_connect_to(const char * host_port,const char * port)180 parse_connect_to(const char *host_port, const char *port)
181 {
182 int s;
183 char *last, *host;
184
185 host = (char *)host_port;
186
187 last = strrchr(host_port, ':');
188 if (last != 0 && !(last != host && last[-1] == ':')) {
189 port = last + 1;
190 host = malloc(last - host_port + 1);
191 if (!host)
192 return -1;
193 memcpy(host, host_port, last-host_port);
194 host[last-host_port] = 0;
195 }
196 s = connect_to(host, port);
197 if (host != host_port)
198 free(host);
199 return s;
200 }
201
202 char *
get_line(FILE * cin)203 get_line(FILE *cin)
204 {
205 char *line;
206 size_t len;
207
208 line = fgetln(cin, &len);
209 if (!line)
210 return NULL;
211 if (len == 0)
212 return NULL;
213 if (line[len-1] == '\n')
214 line[--len] = 0;
215 if (len != 0 && line[len-1] == '\r')
216 line[--len] = 0;
217 return line;
218 }
219
220 char *
get_answer(FILE * cin)221 get_answer(FILE *cin)
222 {
223 char *line;
224
225 line = get_line(cin);
226 if (!line || *line != '2')
227 return NULL;
228 else
229 return line;
230 }
231
232 void
verify_track_names(char ** names,int n,struct cd_toc_entry * e)233 verify_track_names(char **names, int n, struct cd_toc_entry *e)
234 {
235 int i;
236
237 for (i = 0; i < n; i++) {
238 if (names[i] == 0)
239 names[i] = strdup(e->control & 4 ? "data" : "audio");
240 }
241 }
242
243 char **
cddb(const char * host_port,int n,struct cd_toc_entry * e,char * arg)244 cddb(const char *host_port, int n, struct cd_toc_entry *e, char *arg)
245 {
246 int s = -1;
247 FILE *cin = NULL;
248 FILE *cout = NULL;
249 char *type;
250 char *line;
251 char **result = NULL;
252 int i;
253 const char *errstr;
254
255 s = parse_connect_to(host_port, "cddb");
256 if (s == -1)
257 goto end;
258 cin = fdopen(s, "r");
259 if (!cin) {
260 warn("cddb: fdopen");
261 goto end;
262 }
263 cout = fdopen(s, "w");
264 s = -1;
265 if (!cout) {
266 warn("cddb: fdopen");
267 goto end;
268 }
269 line = get_answer(cin);
270 if (!line) {
271 warnx("cddb: won't talk to us");
272 goto end;
273 }
274
275 send_hello(cout);
276 line = get_answer(cin);
277 if (!line) {
278 warnx("cddb: problem in hello");
279 goto end;
280 }
281
282 send_query(cout, n, e);
283 fflush(cout);
284 line = get_answer(cin);
285 if (!line) {
286 warnx("cddb: problem in query");
287 goto end;
288 }
289 type = strchr(line, ' ');
290 if (!type)
291 goto end;
292 *type++ = 0;
293 /* no match or other issue */
294 if (strcmp(line, "202") == 0) {
295 printf("cddb: No match in database\n");
296 goto end;
297 }
298 if (strcmp(line, "211") == 0 || strcmp(line, "212") == 0) {
299 int number = strtonum(arg, 0, INT_MAX, &errstr);
300 if (errstr != NULL && *arg != '\0') {
301 warnx("cddb: invalid index");
302 goto end;
303 }
304 if (number == 0) {
305 if (strcmp(line, "211") == 0)
306 printf("cddb: multiple matches\n");
307 else {
308 printf("cddb: inexact match\n");
309 number = 1;
310 }
311 }
312 if (number == 0) {
313 for (i = 1;; i++) {
314 line = get_line(cin);
315 if (!line || strcmp(line, ".") == 0)
316 goto end;
317 printf("%d: %s\n", i, line);
318 }
319 } else {
320 int ok = 0;
321
322 for (i = 1;; i++) {
323 line = get_line(cin);
324 if (!line)
325 break;
326 if (strcmp(line, ".") == 0)
327 break;
328 if (i == number)
329 ok = further_query(cout, line);
330 }
331 if (!ok)
332 goto end;
333 }
334 } else if (strcmp(line, "200") != 0 || !further_query(cout, type))
335 goto end;
336 result = calloc(sizeof(char *), n + 1);
337 if (!result)
338 goto end;
339 for (i = 0; i <= n; i++)
340 result[i] = NULL;
341 line = get_answer(cin);
342 if (!line)
343 goto end2;
344 for (;;) {
345 long k;
346 char *end;
347
348 line = get_line(cin);
349 if (!line)
350 goto end2;
351 if (strcmp(line, ".") == 0)
352 break;
353 if (strncmp(line, "TTITLE", 6) != 0)
354 continue;
355 line += 6;
356 k = strtol(line, &end, 10);
357 if (*end++ != '=')
358 continue;
359 if (k >= n)
360 continue;
361 safe_copy(&result[k], end);
362 }
363 fprintf(cout, "QUIT\r\n");
364 verify_track_names(result, n, e);
365 goto end;
366 end2:
367 free(result);
368 result = NULL;
369 end:
370 if (cout)
371 fclose(cout);
372 if (cin)
373 fclose(cin);
374 if (s != -1)
375 close(s);
376 return result;
377 }
378
379 void
free_names(char ** names)380 free_names(char **names)
381 {
382 int i;
383
384 for (i = 0; names[i]; i++)
385 free(names[i]);
386 free(names);
387 }
388