1 /*
2  * $Id: cddb.c,v 1.2 1999/02/14 09:50:42 dirk Exp $
3  *
4  * This file is part of WorkMan, the civilized CD player library
5  * (c) 1991-1997 by Steven Grimm (original author)
6  * (c) by Dirk F�rsterling (current 'author' = maintainer)
7  * The maintainer can be contacted by his e-mail address:
8  * milliByte@DeathsDoor.com
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the Free
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  *
25  * establish connection to cddb server and get data
26  * socket stuff gotten from gnu port of the finger command
27  *
28  * provided by Sven Oliver Moll
29  *
30  */
31 
32 static char cddb_id[] = "$Id: cddb.c,v 1.2 1999/02/14 09:50:42 dirk Exp $";
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <unistd.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/signal.h>
41 #include <ctype.h>
42 #include <sys/time.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <netdb.h>
46 
47 #include "include/wm_config.h"
48 #include "include/wm_struct.h"
49 #include "include/wm_cdinfo.h"
50 #include "include/wm_helpers.h"
51 
52 /*
53  * This is for identifying WorkMan at CDDB servers
54  */
55 #define PROGRAM WORKMAN_NAME
56 #define VERSION WORKMAN_VERSION
57 
58 struct wm_cddb cddb;
59 
60 int cur_cddb_protocol;
61 char *cur_cddb_server;
62 char *cur_cddb_mail_adress;
63 char *cur_cddb_path_to_cgi;
64 char *cur_cddb_proxy_server;
65 
66 int Socket;
67 FILE *Connection;
68 
69 /*
70  *
71  */
72 void
cddb_cur2struct(void)73 cddb_cur2struct(void)
74 {
75 	cddb.protocol = cur_cddb_protocol;
76 	strcpy(cddb.cddb_server,  cur_cddb_server);
77 	strcpy(cddb.mail_adress,  cur_cddb_mail_adress);
78 	strcpy(cddb.path_to_cgi,  cur_cddb_path_to_cgi);
79 	strcpy(cddb.proxy_server, cur_cddb_proxy_server);
80 } /* cddb_cur2struct() */
81 
82 /*
83  *
84  */
85 void
cddb_struct2cur(void)86 cddb_struct2cur(void)
87 {
88 	cur_cddb_protocol = cddb.protocol;
89 	cur_cddb_server =       (cddb.cddb_server);
90 	cur_cddb_mail_adress =  (cddb.mail_adress);
91 	cur_cddb_path_to_cgi =  (cddb.path_to_cgi);
92 	cur_cddb_proxy_server = (cddb.proxy_server);
93 } /* cddb_struct2cur() */
94 
95 
96 /*
97  * Subroutine from cddb_discid
98  */
99 int
cddb_sum(int n)100 cddb_sum(int n)
101 {
102 	char	buf[12],
103 		*p;
104 	int	ret = 0;
105 
106 	/* For backward compatibility this algorithm must not change */
107 	sprintf(buf, "%lu", (unsigned long)n);
108 	for (p = buf; *p != '\0'; p++)
109 	  ret += (*p - '0');
110 
111 	return (ret);
112 } /* cddb_sum() */
113 
114 
115 /*
116  * Calculate the discid of a CD according to cddb
117  */
118 unsigned long
cddb_discid(void)119 cddb_discid(void)
120 {
121 	int	i,
122 		t,
123 		n = 0;
124 
125 	/* For backward compatibility this algorithm must not change */
126 	for (i = 0; i < thiscd.ntracks; i++) {
127 
128 		n += cddb_sum(thiscd.trk[i].start / 75);
129 	/*
130 	 * Just for demonstration (See below)
131 	 *
132 	 *	t += (thiscd.trk[i+1].start / 75) -
133 	 *	     (thiscd.trk[i  ].start / 75);
134 	 */
135 	}
136 
137 	/*
138 	 * Mathematics can be fun. Example: How to reduce a full loop to
139 	 * a simple statement. The discid algorhythm is so half-hearted
140 	 * developed that it doesn't even use the full 32bit range.
141 	 * But it seems to be always this way: The bad standards will be
142 	 * accepted, the good ones turned down.
143 	 */
144 
145 	t = (thiscd.trk[thiscd.ntracks].start-thiscd.trk[0].start)/75;
146 	return ((n % 0xff) << 24 | t << 8 | thiscd.ntracks);
147 } /* cddb_discid() */
148 
149 /*
150  * Split a string into two components according to the first occurance of
151  * the delimiter.
152  */
153 char *
string_split(char * line,char delim)154 string_split(char *line, char delim)
155 {
156 	char *p1;
157 
158 	for (p1=line;*p1;p1++)
159 	{
160 		if(*p1 == delim)
161 		{
162 			*p1 = 0;
163 			return ++p1;
164 		}
165 	}
166 	return (NULL);
167 } /* string_split() */
168 
169 /*
170  * Generate the hello string according to the cddb protocol
171  * delimiter is either ' ' (cddbp) or '+' (http)
172  */
173 void
string_makehello(char * line,char delim)174 string_makehello(char *line,char delim)
175 {
176 	char mail[84],*host;
177 
178 	strcpy(mail,cddb.mail_adress);
179 	host=string_split(mail,'@');
180 
181 	sprintf(line,"%shello%c%s%c%s%c%s%c%s",
182 		delim == ' ' ? "cddb " : "&",
183 		delim == ' ' ? ' ' : '=',
184 		mail,delim,
185 		host,delim,
186 		PROGRAM,delim,
187 		VERSION);
188 } /* string_makehello() */
189 
190 /*
191  * Open the TCP connection to the cddb/proxy server
192  */
193 int
connect_open(void)194 connect_open(void)
195 {
196 	char *host;
197 	struct hostent *hp;
198 	struct sockaddr_in soc_in;
199 	int port;
200 
201 	if(cddb.protocol == 3) /* http proxy */
202 	  host = strdup(cddb.proxy_server);
203 	else
204 	  host = strdup(cddb.cddb_server);
205 	/*
206 	 * t=string_split(host,':');
207 	 */
208 	port=atoi(string_split(host,':'));
209 	if(!port)
210 	  port=8880;
211 
212 	printf("%s:%d\n",host,port);
213 	hp  =gethostbyname(host);
214 
215 	if (hp == NULL)
216 	{
217 		static struct hostent def;
218 		static struct in_addr defaddr;
219 		static char *alist[1];
220 		static char namebuf[128];
221 		int inet_addr();
222 
223 		defaddr.s_addr = inet_addr(host);
224 		if (defaddr.s_addr == -1)
225 		{
226 			printf("unknown host: %s\n", host);
227 			return (-1);
228 		}
229 		strcpy(namebuf, host);
230 		def.h_name = namebuf;
231 		def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
232 		def.h_length = sizeof (struct in_addr);
233 		def.h_addrtype = AF_INET;
234 		def.h_aliases = 0;
235 		hp = &def;
236 	}
237 	soc_in.sin_family = hp->h_addrtype;
238 	bcopy(hp->h_addr, (char *)&soc_in.sin_addr, hp->h_length);
239 	soc_in.sin_port = htons(port);
240 	Socket = socket(hp->h_addrtype, SOCK_STREAM, 0);
241 	if (Socket < 0)
242 	{
243 		perror("socket");
244 		return (-1);
245 	}
246 	fflush(stdout);
247 	if (connect(Socket, (struct sockaddr *)&soc_in, sizeof (soc_in)) < 0)
248 	{
249 		perror("connect");
250 		close(Socket);
251 		return (-1);
252 	}
253 
254 	Connection = fdopen(Socket, "r");
255 	return (0);
256 } /* connect_open() */
257 
258 
259 /*
260  * Close the connection
261  */
262 void
connect_close(void)263 connect_close(void)
264 {
265 	(void)fclose(Connection);
266 	close(Socket);
267 } /* connect_close() */
268 
269 /*
270  * Get a line from the connection with CR and LF stripped
271  */
272 void
connect_getline(char * line)273 connect_getline(char *line)
274 {
275 	char c;
276 
277 	while ((c = getc(Connection)) != '\n')
278 	{
279 		*line = c;
280 		if ((c != '\r') && (c != (char)0xff))
281 		  line++;
282 	}
283 	*line=0;
284 } /* connect_getline() */
285 
286 /*
287  * Read the CD data from the server and place them into the cd struct
288  */
289 void
connect_read_entry(void)290 connect_read_entry(void)
291 {
292 	char type;
293 	int trknr;
294 
295 	char *t,*t2,tempbuf[2000];
296 
297 	while(strcmp(tempbuf,"."))
298 	{
299 		connect_getline(tempbuf);
300 
301 		t=string_split(tempbuf,'=');
302 		if(t != NULL)
303 		{
304 			type=tempbuf[0];
305 
306 			if(strncmp("TITLE",tempbuf+1,5))
307 			  continue;
308 
309 			if('D' == type)
310 			{
311 				/*
312 				 * Annahme: "Interpret / Titel" ist falsch.
313 				 * Daher: NULL-String erwarten.
314                                  */
315 				t2=string_split(t,'/');
316 				if(t2 == NULL)
317 					t2 = t;
318 				if(*t2 == ' ')
319 				  t2++;
320 				strcpy(cd->cdname,t2);
321 
322 				for(t2=t;*t2;t2++)
323 				{
324 					if((*t2 == ' ') && (*(t2+1) == 0))
325 					  *t2=0;
326 				}
327 				strcpy(cd->artist,t);
328 			}
329 
330 			if('T' == type)
331 			{
332 				trknr=atoi(tempbuf+6);
333 				/*
334 				 * printf("Track %d:%s\n",trknr,t);
335 				 */
336 				wm_strmcpy(&cd->trk[trknr].songname,t);
337 			}
338 			/*
339 			 * fprintf(stderr, "%s %s\n",tempbuf,t);
340 			 */
341 		}
342 	}
343 } /* connect_read_entry() */
344 
345 /*
346  * Send a command to the server using cddbp
347  */
348 void
cddbp_send(char * line)349 cddbp_send(char *line)
350 {
351 	write(Socket, line, strlen(line));
352 	write(Socket, "\n", 1);
353 } /* cddbp_send() */
354 
355 /*
356  * Send the "read from cddb" command to the server using cddbp
357  */
358 void
cddbp_read(char * category,unsigned int id)359 cddbp_read(char *category, unsigned int id)
360 {
361 	char tempbuf[84];
362 	sprintf(tempbuf, "cddb read %s %08x", category, id);
363 	cddbp_send(tempbuf);
364 } /* cddbp_read() */
365 
366 /*
367  * Send a command to the server using http
368  */
369 void
http_send(char * line)370 http_send(char* line)
371 {
372 	char tempbuf[2000];
373 
374 	write(Socket, "GET ", 4);
375 	printf("GET ");
376 	if(cddb.protocol == 3)
377 	{
378 		write(Socket, "http://", 7);
379 		write(Socket, cddb.cddb_server, strlen(cddb.cddb_server));
380 		printf("http://%s",cddb.cddb_server);
381 	}
382 	write(Socket, cddb.path_to_cgi, strlen(cddb.path_to_cgi));
383 	write(Socket, "?cmd=" ,5);
384 	write(Socket, line, strlen(line));
385 	printf("%s?cmd=%s",cddb.path_to_cgi,line);
386 	string_makehello(tempbuf,'+');
387 	write(Socket, tempbuf, strlen(tempbuf));
388 	printf("%s",tempbuf);
389 	write(Socket, "&proto=1 HTTP/1.0\n\n", 19);
390 	printf("&proto=1 HTTP/1.0\n");
391 	do
392 	  connect_getline(tempbuf);
393 	while(strcmp(tempbuf,""));
394 } /* http_send() */
395 
396 /*
397  * Send the "read from cddb" command to the server using http
398  */
399 void
http_read(char * category,unsigned int id)400 http_read(char *category, unsigned int id)
401 {
402 	char tempbuf[84];
403 	sprintf(tempbuf, "cddb+read+%s+%08x", category, id);
404 	http_send(tempbuf);
405 } /* http_read() */
406 
407 /*
408  * The main routine called from the ui
409  */
410 void
cddb_request(void)411 cddb_request(void)
412 {
413 	int i;
414 	char tempbuf[2000];
415 	extern int cur_ntracks;
416 
417 	int status;
418 	char category[20];
419 	unsigned int id;
420 
421 	strcpy(cddb.cddb_server,"localhost:888");
422 	strcpy(cddb.mail_adress,"svolli@bigfoot.com");
423 	/*
424 	 * cddb.protocol = 1;
425 	 */
426 	wipe_cdinfo();
427 
428 	switch(cddb.protocol)
429 	{
430 	 case 1: /* cddbp */
431 		printf("USING CDDBP\n");
432 		printf("open\n");
433 		connect_open();
434 		connect_getline(tempbuf);
435 		printf("[%s]\n",tempbuf);
436 		/*
437 		 * if(atoi(tempbuf) == 201) return;
438 		 */
439 
440 		/*
441 		 * strcpy(tempbuf,"cddb hello svolli bigfoot.com Eierkratzer eins");
442 		 */
443 		string_makehello(tempbuf,' ');
444 		fprintf(stderr, "%s\n", tempbuf);
445 		cddbp_send(tempbuf);
446 		connect_getline(tempbuf);
447 		printf("[%s]\n",tempbuf);
448 
449 		printf("query\n");
450 		sprintf(tempbuf, "cddb query %08x %d",thiscd.cddbid,thiscd.ntracks);
451 		for (i = 0; i < cur_ntracks; i++)
452 		  if (thiscd.trk[i].section < 2)
453 		    sprintf(tempbuf + strlen(tempbuf), " %d",
454 			    thiscd.trk[i].start);
455 		sprintf(tempbuf + strlen(tempbuf), " %d\n", thiscd.length);
456 		printf(">%s<\n",tempbuf);
457 		cddbp_send(tempbuf);
458 		connect_getline(tempbuf);
459 		printf("[%s]\n",tempbuf);
460 
461 		status=atoi(tempbuf);
462 		/*
463 		 * fprintf(stderr, "status:%d\n",status);
464 		 * fprintf(stderr,"category:%s\n",category);
465 		 * fprintf(stderr,"id:%s\n",id);
466 		 */
467 		if(status == 200) /* Exact match */
468 		{
469 			sscanf(tempbuf,"%d %s %08x",&status,category,&id);
470 			cddbp_read(category,id);
471 			connect_read_entry();
472 		}
473 
474 		if(status == 211) /* Unexact match, multiple possible
475 				   * Hack: always use first. */
476 		{
477 			connect_getline(tempbuf);
478 			sscanf(tempbuf,"%s %08x",category,&id);
479 			while(strcmp(tempbuf,"."))
480 			  connect_getline(tempbuf);
481 			cddbp_read(category,id);
482 			connect_read_entry();
483 		}
484 
485 		cddbp_send("quit");
486 		connect_close();
487 		printf("close\n");
488 		break;
489 	 case 2: /* http */
490 	 case 3: /* http proxy */
491 		printf("USING HTTP%s\n",
492 		       (cddb.protocol == 3) ? " WITH PROXY" : "");
493 		printf("query\n");
494 		sprintf(tempbuf, "cddb+query+%08x+%d",thiscd.cddbid,thiscd.ntracks);
495 		for (i = 0; i < cur_ntracks; i++)
496 		  if (thiscd.trk[i].section < 2)
497 		    sprintf(tempbuf + strlen(tempbuf), "+%d",
498 			    thiscd.trk[i].start);
499 		sprintf(tempbuf + strlen(tempbuf), "+%d", thiscd.length);
500 		printf(">%s<\n",tempbuf);
501 		connect_open();
502 		http_send(tempbuf);
503 		connect_getline(tempbuf);
504 		printf("[%s]\n",tempbuf);
505 
506 		status=atoi(tempbuf);
507 		/*
508 		 * fprintf(stderr, "status:%d\n",status);
509 		 * fprintf(stderr, "category:%s\n",category);
510 		 * fprintf(stderr, "id:%s\n",id);
511 		 */
512 
513 		if(status == 200) /* Exact match */
514 		{
515 			connect_close();
516 			connect_open();
517 			sscanf(tempbuf,"%d %s %08x",&status,category,&id);
518 			http_read(category,id);
519 			connect_read_entry();
520 		}
521 
522 		if(status == 211) /* Unexact match, multiple possible
523 				   * Hack: always use first. */
524 		{
525 			connect_getline(tempbuf);
526 			sscanf(tempbuf,"%s %08x",category,&id);
527 			while(strcmp(tempbuf,"."))
528 			  connect_getline(tempbuf);
529 			connect_close();
530 			connect_open();
531 			http_read(category,id);
532 			connect_read_entry();
533 		}
534 		/* moved close above break */
535 		connect_close();
536 		break;
537 	 default: /* off */
538 		break;
539 	}
540 } /* cddb_request() */
541 
542