1 /*
2  * $Id: helper.c,v 1.29 2002/12/08 14:10:34 alphix Exp $
3  *
4  * This file is part of Ample.
5  *
6  * Ample is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Ample is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Ample; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 #include <config.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <libgen.h>
27 #include <string.h>
28 #ifdef HAVE_SYSLOG_H
29 #include <syslog.h>
30 #endif
31 #ifdef HAVE_STDINT_H
32 #include <stdint.h>
33 #endif
34 #include <stdarg.h>
35 #include <errno.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <netdb.h>
40 
41 #include "ample.h"
42 #include "entries.h"
43 #include "client.h"
44 #include "helper.h"
45 
46 /* Used to signal errors in hostname lookups */
47 extern int h_errno;
48 
49 /* Some utility macros used in getsonglength() */
50 #define CHECKSYNC(x) (((x >> 21) & 0x07FF) == 0x7FF)
51 #define BYTE0(x) ((x >> 24) & 0xFF)
52 #define BYTE1(x) ((x >> 16) & 0xFF)
53 #define BYTE2(x) ((x >> 8)  & 0xFF)
54 #define BYTE3(x) ((x >> 0)  & 0xFF)
55 
56 /* Table of bitrates for MP3 files, all values in kilo.
57  * Indexed by version, layer and value of bit 15-12 in header.
58  */
59 const int bitrate_table[2][3][16] =
60 {
61 	{
62 		{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},
63 		{0,32,48,56, 64,80, 96, 112,128,160,192,224,256,320,384,0},
64 		{0,32,40,48, 56,64, 80, 96, 112,128,160,192,224,256,320,0}
65 	},
66 	{
67 		{0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0},
68 		{0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0},
69 		{0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0}
70 	}
71 };
72 
73 /* Table of samples per frame for MP3 files.
74  * Indexed by layer.
75  */
76 const int bs[4] = {0, 384, 1152, 1152};
77 
78 
79 /* Table of sample frequency for MP3 files.
80  * Indexed by version and layer.
81  */
82 const int freqtab[2][4] =
83 {
84 	{44100, 48000, 32000, 0},
85 	{22050, 24000, 16000, 0},
86 };
87 
88 
89 /*
90  * Allows children to send status reports to the parent process.
91  *
92  * Argument: to - the udp socket to send the message to
93  *           fmt - the format string of the message
94  *           argp - the list of arguments to complement the format string
95  *
96  * Returns: void
97  */
98 void
sendstatusmsg(int to,char * fmt,...)99 sendstatusmsg(int to, char *fmt, ...)
100 {
101 	char buffer[1000];
102 	int length;
103 	va_list argp;
104 
105 	if(to >= 0) {
106 		va_start(argp, fmt);
107 		length = vsnprintf(buffer, 1000, fmt, argp);
108 		sprintf(&buffer[992], "<TRUNC>");
109 		send(to, buffer, strlen(buffer) + 1, 0);
110 		va_end(argp);
111 	}
112 }
113 
114 
115 /*
116  * Generic function to deal with log, debug and death messages.
117  *
118  * Argument: type - the type of the message (see header)
119  *           fmt - the format string of the message
120  *           argp - the list of arguments to complement the format string
121  *
122  * Returns: void
123  */
124 static void
printlogmsg(int type,char * fmt,va_list argp)125 printlogmsg(int type, char *fmt, va_list argp)
126 {
127 	char buffer[1000];
128 	int length;
129 
130 	length = vsnprintf(buffer, 1000, fmt, argp);
131 	sprintf(&buffer[992], "<TRUNC>");
132 
133 #ifdef HAVE_SYSLOG_H
134 	if(!gconf.trace) {
135 		if(type == TYPE_LOG) {
136 			syslog(LOG_INFO, "%s", buffer);
137 		} else if(type == TYPE_DEBUG) {
138 			syslog(LOG_DEBUG, "%s", buffer);
139 		} else if(type == TYPE_DIE) {
140 			if(errno != 0)
141 				syslog(LOG_ERR, "died - %s, errno: %s", buffer,
142 				       strerror(errno));
143 			else
144 				syslog(LOG_ERR, "died - %s", buffer);
145 			exit(EXIT_FAILURE);
146 		}
147 		return;
148 	}
149 #endif
150 
151 	if(type == TYPE_LOG) {
152 		printf("LOG[%d]: %s", getpid(), buffer);
153 	} else if(type == TYPE_DEBUG) {
154 		printf("DBG[%d]: %s", getpid(), buffer);
155 	} else if(type == TYPE_DIE) {
156 		if(errno != 0)
157 			printf("DIE[%d]: (errno: %s) %s", getpid(),
158 			       strerror(errno), buffer);
159 		else
160 			printf("DIE[%d]: %s", getpid(), buffer);
161 		exit(EXIT_FAILURE);
162 	}
163 	fflush(stdout);
164 }
165 
166 
167 /*
168  * Wrapper function to printlogmsg that is used to print ordinary log messages.
169  *
170  * Arguments: fmt - the format string of the message
171  *            ... - the variables to complement the format string
172  *
173  * Returns: void
174  */
175 void
logmsg(char * fmt,...)176 logmsg(char *fmt, ...)
177 {
178 	va_list argp;
179 
180 	va_start(argp, fmt);
181 	printlogmsg(TYPE_LOG, fmt, argp);
182 	va_end(argp);
183 }
184 
185 
186 /*
187  * Wrapper function to printlogmsg that is used to print debug messages if
188  * the debug level config option is high enough.
189  *
190  * Arguments: priority - the level of the debug message, higher number
191  *                       generally means more verbose debugging, if this
192  *                       number is higher than the debuglevel config option,
193  *                       nothing is printed
194  *            fmt - the format string of the message
195  *            ... - the variables to complement the format string
196  *
197  * Returns: void
198  */
199 void
debug(int priority,char * fmt,...)200 debug(int priority, char *fmt, ...)
201 {
202 	va_list argp;
203 
204 	if(priority > gconf.debuglevel)
205 		return;
206 
207 	va_start(argp, fmt);
208 	printlogmsg(TYPE_DEBUG, fmt, argp);
209 	va_end(argp);
210 }
211 
212 
213 /*
214  * Wrapper function to printlogmsg that is used to print death messages
215  * and then exit.
216  *
217  * Arguments: fmt - the format string of the message
218  *            ... - the variables to complement the format string
219  *
220  * Returns: void
221  */
222 void
die(char * fmt,...)223 die(char *fmt, ...)
224 {
225 	va_list argp;
226 
227 	va_start(argp, fmt);
228 	printlogmsg(TYPE_DIE, fmt, argp);
229 	va_end(argp);
230 }
231 
232 
233 static void
expandmalloc(void ** old,size_t * size,int line)234 expandmalloc(void **old, size_t *size, int line) {
235 	*size = ((*size) * 2);
236 	debug(4, "called from %i, gonna realloc with size %i and old %p\n",
237 	      line, *size, *old);
238 	*old = realloc(*old, *size);
239 	if(*old == NULL)
240 		die("realloc failed\n");
241 }
242 
243 
244 /*
245  * Implements the behaviour of GNU getcwd using standard getcwd
246  * (GNU cwd auto-malloc's a buffer to hold the result)
247  *
248  * Argument: none
249  *
250  * Returns: A pointer to the newly allocated buffer
251  */
252 char *
mgetcwd()253 mgetcwd()
254 {
255 	size_t size = 100;
256 	char *buffer = (char *)malloc(size);
257 
258 	while (TRUE) {
259 		if(getcwd (buffer, size) == buffer)
260 			return buffer;
261 		if(errno != ERANGE)
262 			die("getcwd");
263 		expandmalloc((void **)&buffer, &size, __LINE__);
264 	}
265 }
266 
267 
268 /*
269  * The opposite of atoi, converts an integer to an ASCII string.
270  *
271  * Arguments: integer - the integer to convert
272  *
273  * Returns: an ASCII string stored in malloc:ed memory
274  */
275 char *
itoa(int integer)276 itoa(int integer) {
277 	int tmp = integer;
278 	int size;
279 	char *result;
280 
281 	if(integer == 0)
282 		size = 2;
283 	else if(integer < 0)
284 		size = 2;
285 	else if(integer > 0)
286 		size = 1;
287 
288 	while(tmp) {
289 		tmp /= 10;
290 		size++;
291 	}
292 
293 	result = malloc(size);
294 	snprintf(result, size, "%i", integer);
295 	return result;
296 }
297 
298 
299 /*
300  * Encode function common for both HTML and URL encodings.
301  *
302  * Arguments: toencode - string to convert
303  *            url - if TRUE, urlencode the string, otherwise htmlencode it
304  *
305  * Returns: an encoded ASCII string stored in malloc:ed memory
306  */
307 static char *
commonencode(char * toencode,bool url)308 commonencode(char *toencode, bool url) {
309 	char *validchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
310 	char *urlprefix = "%";
311 	char *htmlprefix = "&#x";
312 	char *urlsuffix = "";
313 	char *htmlsuffix = ";";
314 	char *prefix;
315 	char *suffix;
316 	char *tmp;
317 	char *tmp2;
318 	char *retval;
319 	char buffer[3];
320 	size_t size = 100;
321 	size_t used = 1;
322 
323 	if(url) {
324 		prefix = urlprefix;
325 		suffix = urlsuffix;
326 	} else {
327 		prefix = htmlprefix;
328 		suffix = htmlsuffix;
329 	}
330 
331 	retval = malloc(size);
332 	if(retval == NULL)
333 		die("malloc failed\n");
334 	*retval = '\0';
335 
336 	for(tmp = toencode; *tmp != '\0'; tmp++) {
337 		if((size - used) < (strlen(prefix) + 2 + strlen(suffix)))
338 			expandmalloc((void **)&retval, &size, __LINE__);
339 		for(tmp2 = validchars; *tmp2 != '\0'; tmp2++) {
340 			if((*tmp) == (*tmp2))
341 				break;
342 		}
343 
344 		if(*tmp2 != '\0') {
345 			buffer[0] = *tmp2;
346 			buffer[1] = '\0';
347 			strcat(retval, buffer);
348 			used++;
349 		} else {
350 			snprintf(buffer, 3, "%.2x", (int)(unsigned char)*tmp);
351 			strcat(retval, prefix);
352 			strcat(retval, buffer);
353 			strcat(retval, suffix);
354 			used += (strlen(prefix) + 2 + strlen(suffix));
355 		}
356 	}
357 
358 	return(retval);
359 }
360 
361 
362 /*
363  * urlencodes a string.
364  *
365  * Arguments: toencode - string to convert
366  *
367  * Returns: an encoded ASCII string stored in malloc:ed memory
368  */
369 char *
urlencode(char * toencode)370 urlencode(char *toencode) {
371 	return(commonencode(toencode, TRUE));
372 }
373 
374 
375 /*
376  * htmlencodes a string.
377  *
378  * Arguments: toencode - string to convert
379  *
380  * Returns: an encoded ASCII string stored in malloc:ed memory
381  */
382 char *
htmlencode(char * toencode)383 htmlencode(char *toencode) {
384 	return(commonencode(toencode, FALSE));
385 }
386 
387 
388 /*
389  * Takes a string and replaces variables with their values, then returns
390  * the adress of the new string to the client. This string will be
391  * valid until replacevariables is called again and doesn't need to
392  * be free:d. Used to create dynamic HTML pages.
393  *
394  * Arguments: input - the line to use as a base for the conversion
395  *            cconf - session information
396  *            entry - an (optional) entry to retrieve some of the
397  *                    variable values from
398  *
399  * Returns: void
400  */
401 char *
replacevariables(char * input,struct client_config * cconf,mp3entry * entry)402 replacevariables(char *input, struct client_config *cconf, mp3entry *entry)
403 {
404 	static char *result = NULL;
405 
406 	char *str_nbsp = "&nbsp;";
407 	char *str_err = "Variable not allowed here";
408 	char *str_dir = "DIR";
409 	char *str_file = "FILE";
410 	char *str_sep = "@";
411 
412 	char *start = input;
413 	char *end;
414 	char *variable;
415 	char *encvariable;
416 
417 	/*
418 	 * VARIABLES USED:
419 	 *
420 	 * Static: (Can be used anywhere)
421 	 * @SERVERNAME@
422 	 * @PORT@
423 	 * @PATH@
424 	 *
425 	 * Non-Static: (Can only be used if tmp defined)
426 	 * FILE:                           DIR:
427 	 * =====                           ====
428 	 * @NAME@   = tmp->name            tmp->name
429 	 * @URL@    = tmp->name            tmp->name + /index.html
430 	 * @LENGTH@ = tmp->length          "&nbsp;"
431 	 * @TITLE@  = tmp->title           "DIR"
432 	 * @SIZE@   = tmp->filesize        "&nbsp;"
433 	 * @TYPE@   = "FILE"               "DIR"
434 	 * @FPATH@  = tmp->path            "&nbsp;"
435 	 */
436 	char *variables[][2] = {
437 		{"@SERVERNAME@", gconf.servername},
438 		{"@PORT@", NULL},
439 		{"@PATH@", cconf->requestpath},
440 		{"@NAME@", str_err},
441 		{"@URL@", str_err},
442 		{"@LENGTH@", str_err},
443 		{"@TITLE@", str_err},
444 		{"@SIZE@", str_err},
445 		{"@TYPE@", str_err},
446 		{"@FPATH@", str_err},
447 		{NULL, NULL}
448 	};
449 
450 	int i;
451 	size_t size = 100;
452 	int used = 1;
453 
454 	/* Clean up after earlier invocations */
455 	if(result != NULL) {
456 		free(result);
457 		result = NULL;
458 	}
459 
460 	result = malloc(size);
461 	if(result == NULL)
462 		die("malloc failed\n");
463 	*result = '\0';
464 
465 	variables[1][1] = itoa(gconf.port);
466 	if(!entry) {
467 		/* Do nothing */
468 	} else if(IS_DIR(entry)) {
469 		variables[3][1] = entry->name;
470 		variables[4][1] = malloc(strlen(entry->name) +
471 					 strlen("/index.html") + 1);
472 		sprintf(variables[4][1], "%s/index.html", entry->name);
473 		variables[5][1] = str_nbsp;
474 		variables[6][1] = str_dir;
475 		variables[7][1] = str_nbsp;
476 		variables[8][1] = str_dir;
477 		variables[9][1] = str_nbsp;
478 	} else {
479 		variables[3][1] = entry->name;
480 		variables[4][1] = entry->name;
481 		variables[5][1] = itoa(entry->length);
482 		variables[6][1] = entry->title;
483 		variables[7][1] = itoa(entry->filesize);
484 		variables[8][1] = str_file;
485 		variables[9][1] = entry->path;
486 	}
487 
488 	while((end = strchr(start, '@')) != NULL) {
489 		debug(1, "end is %s\n", end);
490 		debug(1, "end is %p, start is %p, diff %i, used is %i, size is %i\n", end, start, (int)(end - start), used, size);
491 		while((used + end - start) > size) {
492 			expandmalloc((void **)&result, &size, __LINE__);
493 		}
494 		strncat(result, start, (end - start));
495 		used += (end - start);
496 		variable = NULL;
497 
498 		for(i = 0; variables[i][0] != NULL; i++) {
499 			if(!strncmp(end, variables[i][0],
500 				    strlen(variables[i][0]))) {
501 				start = end + strlen(variables[i][0]);
502 				variable = variables[i][1];
503 				if(!strncmp(variables[i][0], "@URL@", strlen(variables[i][0])))
504 					encvariable = urlencode(variable);
505 				else
506 					encvariable = htmlencode(variable);
507 			}
508 		}
509 
510 		if(!variable) {
511 			start = end + 1;
512 			variable = str_sep;
513 			encvariable = str_sep;
514 		}
515 
516 		while((used + strlen(encvariable)) > size)
517 			expandmalloc((void **)&result, &size, __LINE__);
518 		strcat(result, encvariable);
519 		used += strlen(encvariable);
520 
521 		if(encvariable != str_sep)
522 			free(encvariable);
523 	}
524 
525 	while((used + strlen(start)) > size)
526 		expandmalloc((void **)&result, &size, __LINE__);
527 	strcat(result, start);
528 	used += strlen(start);
529 
530 	free(variables[1][1]);
531 	if(!entry) {
532 		/* Do nothing */
533 	} else if(IS_DIR(entry)) {
534 		free(variables[4][1]);
535 	} else {
536 		free(variables[5][1]);
537 		free(variables[7][1]);
538 	}
539 
540 	return(result);
541 }
542 
543 
544 /*
545  * Given a hostentry, finds the FQDN (Fully Qualified Domain Name)
546  *
547  * Arguments: host - the hostentry to base the search on
548  *
549  * Returns: the FQDN as a string stored in malloc:ed memory
550  */
551 static char *
findfqdn(struct hostent * host)552 findfqdn(struct hostent *host)
553 {
554 	int i;
555 
556 	if (strchr(host->h_name, '.'))
557 		return strdup(host->h_name);
558 
559 	for (i=0; host->h_aliases[i] != NULL; i++) {
560 		if (!strchr(host->h_aliases[i], '.'))
561 			continue;
562 		else if (!strncasecmp(host->h_aliases[i], host->h_name, strlen(host->h_name)))
563 			return strdup(host->h_aliases[i]);
564 	}
565 
566         return NULL;
567 }
568 
569 
570 /*
571  * Finds the name (or address) which clients should use when connecting to
572  * the server.
573  *
574  * Arguments: none
575  *
576  * Returns: the name or address as a string stored in malloc:ed memory
577  */
578 char *
getservername()579 getservername()
580 {
581 #ifndef MAXHOSTNAMELEN
582 #define MAXHOSTNAMELEN 256
583 #endif
584 
585 	char buf[MAXHOSTNAMELEN + 1];
586 	char *hostname = NULL;
587 	struct hostent *host;
588 
589 	if (gethostname(buf, sizeof(buf) - 1) == 0) {
590 		buf[sizeof(buf) - 1] = '\0';
591 
592 		if ((host = gethostbyname(buf)) &&
593 		    (hostname = findfqdn(host))) {
594 			return hostname;
595 		}
596 
597 		if (host && host->h_addr_list[0] != NULL)
598 			return strdup(inet_ntoa(*(struct in_addr *)host->h_addr_list[0]));
599 	}
600 
601 	logmsg("Failed to get hostname, please set serveraddress in configfile\n");
602 	return NULL;
603 }
604 
605 
606 /*
607  * Writes ShoutCast type metadata to the client (for format information, see
608  * the developer docs at the Ample homepage).
609  *
610  * Arguments: to - the stream to write the data to
611  *            entry - the entry that is currently playing
612  *            metaflag - has information about this particular entry
613  *                       been written before?
614  *
615  * Returns: TRUE if the operation was successful, else FALSE
616  */
617 bool
writemetadata(FILE * to,mp3entry * entry,bool * metaflag)618 writemetadata(FILE *to, mp3entry *entry, bool *metaflag)
619 {
620 	int msglen;
621 	int padding;
622 	int towrite;
623 	int written;
624 	char *buf;
625 
626 	if(*metaflag) {
627 		*metaflag = FALSE;
628 
629 		msglen = strlen(entry->title) + 28;
630 		padding = 16 - msglen % 16;
631 		towrite = msglen + padding + 1;
632 
633 		buf = malloc(towrite);
634 		memset(buf, 0, towrite);
635 		sprintf(buf, "%cStreamTitle='%s';StreamUrl='';",
636 			(msglen + padding)/16, entry->title);
637 	} else {
638 		towrite = 1;
639 		buf = malloc(towrite);
640 		memset(buf, 0, towrite);
641 	}
642 
643 	written = fwrite(buf, sizeof(char), towrite, to);
644 	free(buf);
645 	if(written == towrite)
646 		return(TRUE);
647 	else
648 		return(FALSE);
649 }
650 
651 
652 /*
653  * Removes trailing spaces from a string.
654  *
655  * Arguments: buffer - the string to process
656  *
657  * Returns: void
658  */
659 static void
stripspaces(char * buffer)660 stripspaces(char *buffer)
661 {
662 	int i = 0;
663 
664 	while(*(buffer + i) != '\0')
665 		i++;
666 
667 	for(;i >= 0; i--) {
668 		if(*(buffer + i) == ' ')
669 			*(buffer + i) = '\0';
670 		else if(*(buffer + i) == '\0')
671 			continue;
672 		else
673 			break;
674 	}
675 }
676 
677 
678 /*
679  * Sets the title of an MP3 entry based on its ID3v1 tag.
680  *
681  * Arguments: file - the MP3 file to scen for a ID3v1 tag
682  *            entry - the entry to set the title in
683  *
684  * Returns: TRUE if a title was found and created, else FALSE
685  */
686 static bool
setid3v1title(FILE * file,mp3entry * entry)687 setid3v1title(FILE *file, mp3entry *entry)
688 {
689 	char buffer[31];
690 	int offsets[3] = {-95,-65,-125};
691 	int i;
692 	char *result;
693 
694 	result = (char *)malloc(3*30 + 2*3 + 1);
695 	*result = '\0';
696 
697 	for(i=0;i<3;i++) {
698 		if(fseek(file, offsets[i], SEEK_END) != 0) {
699 			free(result);
700 			return FALSE;
701 		}
702 
703 
704 		fgets(buffer, 31, file);
705 		stripspaces(buffer);
706 		if(buffer[0] != '\0' && *result != '\0')
707 			strcat(result, " - ");
708 		strcat(result, buffer);
709 	}
710 
711 	if(*result == '\0') {
712 		free(result);
713 		return(FALSE);
714 	} else {
715 		entry->title = (char *)realloc(result, strlen(result) + 1);
716 		return(TRUE);
717 	}
718 }
719 
720 
721 /*
722  * Sets the title of an MP3 entry based on its ID3v2 tag.
723  *
724  * Arguments: file - the MP3 file to scen for a ID3v2 tag
725  *            entry - the entry to set the title in
726  *
727  * Returns: TRUE if a title was found and created, else FALSE
728  */
729 static bool
setid3v2title(FILE * file,mp3entry * entry)730 setid3v2title(FILE *file, mp3entry *entry)
731 {
732 	char *buffer;
733 	int minframesize;
734 	int size, readsize = 0, headerlen;
735 	char *title = NULL;
736 	char *artist = NULL;
737 	char header[10];
738 	unsigned short int version;
739 
740 	/* 10 = headerlength */
741 	if(entry->id3v2len < 10)
742 		return(FALSE);
743 
744 	/* Check version */
745 	fseek(file, 0, SEEK_SET);
746 	fread(header, sizeof(char), 10, file);
747 	version = (unsigned short int)header[3];
748 
749 	/* Read all frames in the tag */
750 	size = entry->id3v2len - 10;
751 	buffer = malloc(size + 1);
752 	if(size != (int)fread(buffer, sizeof(char), size, file)) {
753 		free(buffer);
754 		return(FALSE);
755 	}
756 	*(buffer + size) = '\0';
757 
758 	/* Set minimun frame size according to ID3v2 version */
759 	if(version > 2)
760 		minframesize = 12;
761 	else
762 		minframesize = 8;
763 
764 	/*
765 	 * We must have at least minframesize bytes left for the
766 	 * remaining frames to be interesting
767 	 */
768 	while(size - readsize > minframesize) {
769 
770                 /* Read frame header and check length */
771 		if(version > 2) {
772 			memcpy(header, (buffer + readsize), 10);
773 			readsize += 10;
774 			headerlen = UNSYNC(header[4], header[5],
775 					   header[6], header[7]);
776 		} else {
777 			memcpy(header, (buffer + readsize), 6);
778 			readsize += 6;
779 			headerlen = (header[3] << 16) +
780 				    (header[4] << 8) +
781 				    (header[5]);
782 		}
783 		if(headerlen < 1)
784 			continue;
785 
786 		/* Check for certain frame headers */
787 		if(!strncmp(header, "TPE1", strlen("TPE1")) ||
788 		   !strncmp(header, "TP1", strlen("TP1"))) {
789 			readsize++;
790 			headerlen--;
791 			if(headerlen > (size - readsize))
792 				headerlen = (size - readsize);
793 			artist = malloc(headerlen + 1);
794 			snprintf(artist, headerlen + 1, "%s",
795 				 (buffer + readsize));
796 			readsize += headerlen;
797 		} else if(!strncmp(header, "TIT2", strlen("TIT2")) ||
798 			  !strncmp(header, "TT2", strlen("TT2"))) {
799 			readsize++;
800 			headerlen--;
801 			if(headerlen > (size - readsize))
802 				headerlen = (size - readsize);
803 			title = malloc(headerlen + 1);
804 			snprintf(title, headerlen + 1, "%s",
805 				 (buffer + readsize));
806 			readsize += headerlen;
807 		}
808 	}
809 
810 	/* Done, let's clean up */
811 	if(artist && title) {
812 		entry->title = malloc(strlen(artist) + strlen(title) + 4);
813 		snprintf(entry->title, strlen(artist) + strlen(title) + 4,
814 			 "%s - %s", artist, title);
815 		free(artist);
816 		free(title);
817 	} else if(artist) {
818 		entry->title = artist;
819 	} else if(title) {
820 		entry->title = title;
821 	}
822 
823 	free(buffer);
824 	return(entry->title != NULL);
825 }
826 
827 
828 /*
829  * Calculates the size of the ID3v2 tag.
830  *
831  * Arguments: file - the file to search for a tag.
832  *
833  * Returns: the size of the tag or 0 if none was found
834  */
835 static int
getid3v2len(FILE * file)836 getid3v2len(FILE *file)
837 {
838 	char buf[6];
839 	int offset;
840 
841 	/* Make sure file has a ID3 tag */
842 	if((fseek(file, 0, SEEK_SET) != 0) ||
843 	   (fread(buf, sizeof(char), 6, file) != 6) ||
844 	   (strncmp(buf, "ID3", strlen("ID3")) != 0))
845 		offset = 0;
846 	/* Now check what the ID3v2 size field says */
847 	else if(fread(buf, sizeof(char), 4, file) != 4)
848 		offset = 0;
849 	else
850 		offset = UNSYNC(buf[0], buf[1], buf[2], buf[3]) + 10;
851 
852 	return(offset);
853 }
854 
855 
856 /*
857  * Calculates the size of the ID3v1 tag.
858  *
859  * Arguments: file - the file to search for a tag.
860  *
861  * Returns: the size of the tag or 0 if none was found
862  */
863 static int
getid3v1len(FILE * file)864 getid3v1len(FILE *file)
865 {
866 	char buf[3];
867 	int offset;
868 
869 	/* Check if we find "TAG" 128 bytes from EOF */
870 	if((fseek(file, -128, SEEK_END) != 0) ||
871 	   (fread(buf, sizeof(char), 3, file) != 3) ||
872 	   (strncmp(buf, "TAG", 3) != 0))
873 		offset = 0;
874 	else
875 		offset = 128;
876 
877 	return offset;
878 }
879 
880 
881 /*
882  * Calculates the length (in seconds) of an MP3 file. Currently this code
883  * doesn't care about VBR (Variable BitRate) files since it would have to
884  * scan through the entire file but this should become a config option
885  * in the future.
886  *
887  * Arguments: file - the file to calculate the length upon
888  *            entry - the entry to update with the length
889  *
890  * Returns: the song length in seconds,
891  *          -1 means that it couldn't be calculated
892  */
893 static int
getsonglength(FILE * file,mp3entry * entry)894 getsonglength(FILE *file, mp3entry *entry) {
895 
896 #ifdef HAVE_STDINT_H
897 	uint32_t header;
898 #else
899 	long header;
900 #endif
901 	int version;
902 	int layer;
903 	int bitindex;
904 	int bitrate;
905 	int freqindex;
906 	int frequency;
907 
908 	double bpf;
909 	double tpf;
910 	int i;
911 
912 	/* Start searching after ID3v2 header */
913 	if(fseek(file, entry->id3v2len, SEEK_SET))
914 		return -1;
915 
916 	/* Fill up header with first 24 bits */
917 	for(version = 0; version < 3; version++) {
918 		header <<= 8;
919 		if(!fread(&header, 1, 1, file))
920 			return -1;
921 	}
922 
923 	/* Loop trough file until we find a frame header */
924  restart:
925 	do {
926 		header <<= 8;
927 		if(!fread(&header, 1, 1, file))
928 			return -1;
929 	} while(!CHECKSYNC(header));
930 
931 
932 	/*
933 	 * Some files are filled with garbage in the beginning,
934 	 * if the bitrate index of the header is binary 1111
935 	 * that is a good indicator
936 	 */
937 	if((header & 0xF000) == 0xF000)
938 		goto restart;
939 
940 	debug(5, "We found %x-%x-%x-%x and checksync %i and test %x\n",
941 	      BYTE0(header), BYTE1(header), BYTE2(header), BYTE3(header),
942 	      CHECKSYNC(header), (header & 0xF000) == 0xF000);
943 
944 	/* MPEG Audio Version */
945 	switch((header & 0x180000) >> 19) {
946 	case 2:
947 		version = 2;
948 		break;
949 	case 3:
950 		version = 1;
951 		break;
952 	default:
953 		return -1;
954 	}
955 
956 	/* Layer */
957 	switch((header & 0x060000) >> 17) {
958 	case 1:
959 		layer = 3;
960 		break;
961 	case 2:
962 		layer = 2;
963 		break;
964 	case 3:
965 		layer = 1;
966 		break;
967 	default:
968 		return -1;
969 	}
970 
971 	/* Bitrate */
972 	bitindex = (header & 0xF000) >> 12;
973 	bitrate = bitrate_table[version-1][layer-1][bitindex];
974 	if(bitrate == 0)
975 		return -1;
976 
977 	/* Sampling frequency */
978 	freqindex = (header & 0x0C00) >> 10;
979 	frequency = freqtab[version-1][freqindex];
980 	if(frequency == 0)
981 		return -1;
982 
983 	debug(2, "Version %i, lay %i, biti %i, bitr %i, freqi %i, freq %i\n",
984 	      version, layer, bitindex, bitrate, freqindex, frequency);
985 
986 	/* Calculate bytes per frame, calculation depends on layer */
987         switch(layer) {
988 	case 1:
989 		bpf = bitrate_table[version - 1][layer - 1][bitindex];
990 		bpf *= 12000.0 * 4.0;
991 		bpf /= freqtab[version-1][freqindex] << (version - 1);
992 		break;
993 	case 2:
994 	case 3:
995 		bpf = bitrate_table[version - 1][layer - 1][bitindex];
996 		bpf *= 144000;
997 		bpf /= freqtab[version-1][freqindex] << (version - 1);
998 		break;
999 	default:
1000 		bpf = 1.0;
1001         }
1002 
1003 	/* Calculate time per frame */
1004         tpf = bs[layer];
1005         tpf /= freqtab[version-1][freqindex] << (version - 1);
1006 
1007 	debug(3, "BitRate is %i, FileLength is %i, TPF is %f and BPF is %f, we have %f frames in one second\n", bitrate, entry->filesize, tpf, bpf, 1/tpf);
1008 
1009 	/*
1010 	 * Now song length is
1011 	 * ((filesize)/(bytes per frame))*(time per frame)
1012 	 */
1013 	return (int)(((float)entry->filesize/bpf)*tpf);
1014 }
1015 
1016 
1017 /*
1018  * Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc)
1019  * about an MP3 file and updates it's entry accordingly.
1020  *
1021  * Arguments: entry - the entry to check and update with the new information
1022  *
1023  * Returns: void
1024  */
1025 void
checkmp3info(mp3entry * entry)1026 checkmp3info(mp3entry *entry)
1027 {
1028 	FILE *file;
1029 	char *copy;
1030 	char *title;
1031 
1032 	if((file = fopen(entry->path, "r")) == NULL)
1033 		return;
1034 
1035 	entry->id3v2len = getid3v2len(file);
1036 	entry->id3v1len = getid3v1len(file);
1037 	entry->length = getsonglength(file, entry);
1038 	entry->title = NULL;
1039 
1040 	if(HASID3V2(entry))
1041 		setid3v2title(file, entry);
1042 
1043 	if(HASID3V1(entry) && !entry->title)
1044 		setid3v1title(file, entry);
1045 
1046 	if(!entry->title) {
1047 		copy = strdup(entry->path);
1048 		title = basename(copy);
1049 		entry->title = malloc(strlen(title) - 3);
1050 		snprintf(entry->title, strlen(title) - 3, "%s", title);
1051 		free(copy);
1052 	}
1053 
1054 	fclose(file);
1055 }
1056