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