Subversion Repositories mdb

Rev

Rev 51 | Rev 57 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 andreas 1
/*
2
 * Copyright (C) 2015 by Andreas Theofilu <andreas@theosys.at>
3
 *
4
 * All rights reserved. No warranty, explicit or implicit, provided.
5
 *
6
 * NOTICE:  All information contained herein is, and remains
7
 * the property of Andreas Theofilu and his suppliers, if any.
8
 * The intellectual and technical concepts contained
9
 * herein are proprietary to Andreas Theofilu and its suppliers and
10
 * may be covered by European and Foreign Patents, patents in process,
11
 * and are protected by trade secret or copyright law.
12
 * Dissemination of this information or reproduction of this material
13
 * is strictly forbidden unless prior written permission is obtained
14
 * from Andreas Theofilu.
15
 */
16
#include <stdio.h>
17
#include <string.h>
4 andreas 18
#include <strings.h>
2 andreas 19
#include <unistd.h>
20
#include <stdlib.h>
21
#include <libgen.h>
5 andreas 22
#include <ctype.h>
2 andreas 23
#include <math.h>
24
#include <time.h>
25
#include <signal.h>
26
#include <syslog.h>
43 andreas 27
#include <iconv.h>
2 andreas 28
#include <errno.h>
29
#include <pthread.h>
30
#include <sys/stat.h>
31
#include <sys/types.h>
32
#include <sys/socket.h>
33
#include <arpa/inet.h>
34
#include <fcntl.h>
35
#include <netdb.h>
36
#include <dirent.h>
37
#include <netinet/in.h>
38
#include <sys/param.h>
39
#include <sys/ioctl.h>
40
#include <sqlite3.h>
45 andreas 41
#include <id3tag.h>
2 andreas 42
#include "config.h"
43
#include "helplib.h"
44
#include "mdb.h"
5 andreas 45
#include "list.h"
6 andreas 46
#include "play.h"
8 andreas 47
#include "user.h"
48
#include "delete.h"
9 andreas 49
#include "search.h"
2 andreas 50
 
45 andreas 51
#define ID3_NR_OF_V1_GENRES 148
52
 
53
static const char *ID3_v1_genre_description[ID3_NR_OF_V1_GENRES] =
54
{
55
	"Blues",             //0
56
	"Classic Rock",      //1
57
	"Country",           //2
58
	"Dance",             //3
59
	"Disco",             //4
60
	"Funk",              //5
61
	"Grunge",            //6
62
	"Hip-Hop",           //7
63
	"Jazz",              //8
64
	"Metal",             //9
65
	"New Age",           //10
66
	"Oldies",            //11
67
	"Other",             //12
68
	"Pop",               //13
69
	"R&B",               //14
70
	"Rap",               //15
71
	"Reggae",            //16
72
	"Rock",              //17
73
	"Techno",            //18
74
	"Industrial",        //19
75
	"Alternative",       //20
76
	"Ska",               //21
77
	"Death Metal",       //22
78
	"Pranks",            //23
79
	"Soundtrack",        //24
80
	"Euro-Techno",       //25
81
	"Ambient",           //26
82
	"Trip-Hop",          //27
83
	"Vocal",             //28
84
	"Jazz+Funk",         //29
85
	"Fusion",            //30
86
	"Trance",            //31
87
	"Classical",         //32
88
	"Instrumental",      //33
89
	"Acid",              //34
90
	"House",             //35
91
	"Game",              //36
92
	"Sound Clip",        //37
93
	"Gospel",            //38
94
	"Noise",             //39
95
	"AlternRock",        //40
96
	"Bass",              //41
97
	"Soul",              //42
98
	"Punk",              //43
99
	"Space",             //44
100
	"Meditative",        //45
101
	"Instrumental Pop",  //46
102
	"Instrumental Rock", //47
103
	"Ethnic",            //48
104
	"Gothic",            //49
105
	"Darkwave",          //50
106
	"Techno-Industrial", //51
107
	"Electronic",        //52
108
	"Pop-Folk",          //53
109
	"Eurodance",         //54
110
	"Dream",             //55
111
	"Southern Rock",     //56
112
	"Comedy",            //57
113
	"Cult",              //58
114
	"Gangsta",           //59
115
	"Top 40",            //60
116
	"Christian Rap",     //61
117
	"Pop/Funk",          //62
118
	"Jungle",            //63
119
	"Native American",   //64
120
	"Cabaret",           //65
121
	"New Wave",          //66
122
	"Psychedelic",       //67
123
	"Rave",              //68
124
	"Showtunes",         //69
125
	"Trailer",           //70
126
	"Lo-Fi",             //71
127
	"Tribal",            //72
128
	"Acid Punk",         //73
129
	"Acid Jazz",         //74
130
	"Polka",             //75
131
	"Retro",             //76
132
	"Musical",           //77
133
	"Rock & Roll",       //78
134
	"Hard Rock",         //79
135
	// following are winamp extentions
136
	"Folk",                  //80
137
	"Folk-Rock",             //81
138
	"National Folk",         //82
139
	"Swing",                 //83
140
	"Fast Fusion",           //84
141
	"Bebob",                 //85
142
	"Latin",                 //86
143
	"Revival",               //87
144
	"Celtic",                //88
145
	"Bluegrass",             //89
146
	"Avantgarde",            //90
147
	"Gothic Rock",           //91
148
	"Progressive Rock",      //92
149
	"Psychedelic Rock",      //93
150
	"Symphonic Rock",        //94
151
	"Slow Rock",             //95
152
	"Big Band",              //96
153
	"Chorus",                //97
154
	"Easy Listening",        //98
155
	"Acoustic",              //99
156
	"Humour",                //100
157
	"Speech",                //101
158
	"Chanson",               //102
159
	"Opera",                 //103
160
	"Chamber Music",         //104
161
	"Sonata",                //105
162
	"Symphony",              //106
163
	"Booty Bass",            //107
164
	"Primus",                //108
165
	"Porn Groove",           //109
166
	"Satire",                //110
167
	"Slow Jam",              //111
168
	"Club",                  //112
169
	"Tango",                 //113
170
	"Samba",                 //114
171
	"Folklore",              //115
172
	"Ballad",                //116
173
	"Power Ballad",          //117
174
	"Rhythmic Soul",         //118
175
	"Freestyle",             //119
176
	"Duet",                  //120
177
	"Punk Rock",             //121
178
	"Drum Solo",             //122
179
	"A capella",             //123
180
	"Euro-House",            //124
181
	"Dance Hall",            //125
182
	"Goa",                   //126
183
	"Drum & Bass",           //127
184
	"Club-House",            //128
185
	"Hardcore",              //129
186
	"Terror",                //130
187
	"Indie",                 //131
188
	"Britpop",               //132
189
	"Negerpunk",             //133
190
	"Polsk Punk",            //134
191
	"Beat",                  //135
192
	"Christian Gangsta Rap", //136
193
	"Heavy Metal",           //137
194
	"Black Metal",           //138
195
	"Crossover",             //139
196
	"Contemporary Christian",//140
197
	"Christian Rock ",       //141
198
	"Merengue",              //142
199
	"Salsa",                 //143
200
	"Thrash Metal",          //144
201
	"Anime",                 //145
202
	"JPop",                  //146
203
	"Synthpop"               //147
204
};
205
 
2 andreas 206
static pthread_mutex_t fastmutex_proc = PTHREAD_MUTEX_INITIALIZER;
207
int musicFilter(const struct dirent *dir);
12 andreas 208
int grabMusicFiles(int s1, char *dir);
2 andreas 209
void evaluateMusicFile(char *file);
8 andreas 210
int cleanArchieve(int s1, char *fname);
45 andreas 211
char *getID3_Field(union id3_field *field, enum id3_field_textencoding te, char *ret, int len);
212
int checkID3FieldType(enum id3_field_type ft);
2 andreas 213
 
4 andreas 214
/* Global variables */
2 andreas 215
int file_found = 0;
4 andreas 216
char cmd_error[512];
217
char cmd_message[512];
11 andreas 218
int currentPage = PAGE_NONE;
12 andreas 219
int grabFilesCount;						/* Temporary used to count the scanned files */
9 andreas 220
struct ST_PlayPars _playPars;
56 andreas 221
int _s1[MAX_HANDLES];
2 andreas 222
 
6 andreas 223
static pthread_t pthr_play;
3 andreas 224
/*
225
 * This is called from the main listening thread as a thread of its own.
226
 * The function gets the commands from the client, work with them and give
227
 * back the result.
228
 */
2 andreas 229
void *processCommands(void *pV_data)
230
{
4 andreas 231
	char ch, buf[512];
2 andreas 232
	int i, s1, s;
233
	struct SOCKETS *soc;
3 andreas 234
 
2 andreas 235
	soc = (struct SOCKETS *)pV_data;
236
	s1 = soc->newfd;
237
	s = soc->sockfd;
56 andreas 238
 
11 andreas 239
	i = 0;
240
	/* Report the actual status to the client */
19 andreas 241
/*	pthread_mutex_lock (&fastmutex_proc); */
11 andreas 242
	sprintf(buf, "# MDB v%s\n", VERSION);
243
	strcat (buf, "# (C) Copyright 2015 by Andreas Theofilu <andreas@theosys.at>\n");
244
	strcat (buf, "# All rights reserved. No warranty, explicit or implicit, provided.\n");
245
	write (s1, buf, strlen(buf));
246
 
247
	if (userchain != NULL)
248
	{
249
		sprintf(buf, "USER:%s;", userchain->uname);
250
		write (s1, buf, strlen(buf));
251
	}
252
 
253
	strcpy (buf, "PAGE:");
254
 
255
	switch (currentPage)
256
	{
257
		case PAGE_NONE:		strcat (buf, "NONE;"); break;
258
		case PAGE_TITLE:	strcat (buf, "TITLE;"); break;
259
		case PAGE_ARTIST:	strcat (buf, "ARTIST;"); break;
260
		case PAGE_ALBUM:	strcat (buf, "ALBUM;"); break;
261
		case PAGE_GENRE:	strcat (buf, "GENRE;"); break;
262
		case PAGE_QUEUE:	strcat (buf, "QUEUE;"); break;
263
		case PAGE_PLAYLIST:	strcat (buf, "PLAYLIST;"); break;
264
	}
265
 
266
	write (s1, buf, strlen(buf));
267
	sprintf(buf, "REPEAT:%s;", playerRepeat ? "TRUE" : "FALSE");
268
	write (s1, buf, strlen(buf));
269
	sprintf(buf, "RANDOM:%s;", playerRandom ? "TRUE" : "FALSE");
270
	write (s1, buf, strlen(buf));
271
	/* In case the player is playing, we send the data of the
272
	 * current song */
273
	if (playerActive)
274
	{
275
		char buffer[8192];
276
		char *title, *artist, *album, *genre;
277
 
278
		title = urlencode(playCurrent.title);
279
		artist = urlencode(playCurrent.artist);
280
		album = urlencode(playCurrent.album);
281
		genre = urlencode(playCurrent.genre);
12 andreas 282
		sprintf(buffer, "PLAYING:%d:%s:%s:%s:%s:%s;", playCurrent.id, title, artist, album, genre, playCurrent.cover);
11 andreas 283
		write (s1, buffer, strlen(buffer));
284
 
285
		if (title != NULL) free(title);
286
		if (artist != NULL) free(artist);
287
		if (album != NULL) free(album);
288
		if (genre != NULL) free(genre);
289
	}
290
	/* Signal client, that we are finished the initial report */
291
	strcpy (buf, "DONE;");
12 andreas 292
	write (s1, buf, strlen(buf));
56 andreas 293
	handleAdd(s1);
11 andreas 294
 
19 andreas 295
	memset(buf, 0, sizeof(buf));
4 andreas 296
	memset(cmd_message, 0, sizeof(cmd_message));
11 andreas 297
 
2 andreas 298
	while (read(s1,&ch,1) > 0)
299
	{
4 andreas 300
		if (i < (int)(sizeof(buf) - 1))
2 andreas 301
		{
302
			buf[i] = ch;
3 andreas 303
 
2 andreas 304
			if (ch == ';' || ch == 0x0d)
305
			{
306
				int pstat;
3 andreas 307
 
19 andreas 308
/*				pthread_mutex_lock (&fastmutex_proc); */
3 andreas 309
 
2 andreas 310
				if (!strncmp(buf, "quit", 4))
311
					break;
3 andreas 312
 
4 andreas 313
				if ((pstat = parseCommand(s1, buf)) == FALSE)
2 andreas 314
				{
315
					char hv0[128];
3 andreas 316
 
4 andreas 317
					char *p = urlencode(cmd_error);
318
 
319
					if (p != NULL)
320
					{
321
						sprintf(&hv0[0], "INVALID COMMAND: %s: %s;", buf, p);
322
						free(p);
323
					}
324
					else
325
						sprintf(&hv0[0], "INVALID COMMAND: %s;", buf);
326
 
327
					write(s1, hv0, strlen(hv0));
2 andreas 328
				}
329
				else if (pstat == 2)
330
					write(s1, "NAK;", 4);
331
				else
4 andreas 332
				{
333
					if (strlen(cmd_message) > 0)
5 andreas 334
						write(s1, cmd_message, strlen(cmd_message));
4 andreas 335
					else
336
						write(s1, "OK;", 3);
337
				}
338
 
2 andreas 339
				memset(&buf[0], 0, sizeof(buf));
12 andreas 340
				memset(cmd_message, 0, sizeof(cmd_message));
2 andreas 341
				i = 0;
19 andreas 342
/*				pthread_mutex_unlock(&fastmutex_proc); */
2 andreas 343
				continue;
344
			}
345
		}
3 andreas 346
 
2 andreas 347
		i++;
348
	}
3 andreas 349
 
56 andreas 350
	handleDelete(s1);
2 andreas 351
	close(s1);
14 andreas 352
	return NULL;
2 andreas 353
}
354
 
355
/*
356
 * This function parses the commands given by a network client.
357
 */
358
int parseCommand(int s1, char *cmd)
359
{
4 andreas 360
	char bef[32],par[1024], *p;
361
	char hv0[256];
14 andreas 362
	int i;
4 andreas 363
 
364
	memset(bef, 0, sizeof(bef));
365
	memset(par, 0, sizeof(par));
366
	memset(cmd_error, 0, sizeof(cmd_error));
367
 
368
	if ((p = strchr(cmd, ':')) != NULL)			/* do we have a parameter? */
369
	{
370
		strncpy(bef, cmd, p - cmd);
371
		strncpy(par, p+1, strlen(p) - 2);		/* Cut off the trailing semi colon */
372
	}
373
	else
374
		strncpy(bef, cmd, strlen(cmd) - 1);
375
 
376
	cmd_error[0] = 0;
377
	cmd_message[0] = 0;
36 andreas 378
 
4 andreas 379
	if (!strcasecmp(bef, "RESCAN"))				/* Rescan all directories */
380
	{
381
		int fd;
56 andreas 382
/*		char fname[512]; */
4 andreas 383
 
384
		if (access(configs.Pathfile, R_OK))
385
		{
386
			syslog(LOG_WARNING, "Error accessing file %s", configs.Pathfile);
387
			sprintf(cmd_error, "Error accessing file >%s<", configs.Pathfile);
388
			return FALSE;
389
		}
390
 
391
		if ((fd = open(configs.Pathfile, O_RDONLY)) < 0)
392
		{
393
			syslog(LOG_WARNING, "Error opening file %s: %s", configs.Pathfile, strerror(errno));
394
			sprintf(cmd_error, "Error opening file >%s<", configs.Pathfile);
395
			return FALSE;
396
		}
397
 
398
		i = 0;
399
 
8 andreas 400
		/* Delete the database entries before we start to scan.
5 andreas 401
		 * This makes shure, that there is no double entry.
402
		 */
42 andreas 403
/*		strcpy(fname, configs.home);
9 andreas 404
		strcat(fname, MUSICDB);
5 andreas 405
 
406
		if (!access(fname, R_OK | W_OK))
8 andreas 407
			cleanArchieve(s1, fname);
42 andreas 408
*/
12 andreas 409
		grabFilesCount = 0;
5 andreas 410
		/* Scan all directories in the config file recursievly. */
4 andreas 411
		while (readLine(fd, &hv0[0], sizeof(hv0)) != NULL)
412
		{
12 andreas 413
			grabMusicFiles(s1, hv0);
4 andreas 414
			i++;
415
		}
416
 
12 andreas 417
		sprintf(cmd_message, "SCAN:%d:%d;", i, grabFilesCount);
4 andreas 418
		close(fd);
419
	}
420
 
5 andreas 421
	/*
422
	 * Syntax: LIST:<type>:<start>:<length>;
24 andreas 423
	 *         <type>   TITLE | ARTIST | ALBUM | GENRE | QUEUE | PLAYLIST | USERS
5 andreas 424
	 *         <start>  The line to start from
425
	 *         <length> The number of lines to report
426
	 * 
12 andreas 427
	 * Returns: LINE:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>:<cover>;
5 andreas 428
	 *         <id>     The unique ID of the file. Needed to play the file!
429
	 *         <line>   The line number counting from 1
24 andreas 430
	 * 
431
	 * In case the type is USERS, the following is returned:
432
	 *          USERS:<id>:<line>:<name>;
433
	 *         <name>   The name of the user
5 andreas 434
	 */
435
	if (!strcasecmp(bef, "LIST"))				/* List content */
436
	{
437
		char p_type[16];
438
		int start, length;
439
 
440
		memset(p_type, 0, sizeof(p_type));
441
		remove_string(par, ":", &hv0[0]);
442
		start = strlen(hv0);
443
		hv0[start-1] = 0;
444
		strncpy(p_type, hv0, sizeof(p_type)-1);
445
		start = atoi(par);
446
		remove_string(par, ":", &hv0[0]);
447
		length = atoi(par);
448
		return listSongs(s1, p_type, start, length);
449
	}
450
 
451
	/*
9 andreas 452
	 * Syntax: PLIST:<user>:<playlist>:<start>:<length>;
453
	 *         <user>   The name of the user.
454
	 *         <playlist> The name of the playlist
455
	 *         <start>  The line to start from
456
	 *         <length> The number of lines to report
457
	 * 
13 andreas 458
	 * Returns: LINE:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>:<cover>;
9 andreas 459
	 *         <id>     The unique ID of the file. Needed to play the file!
460
	 *         <line>   The line number counting from 1
461
	 */
462
	if (!strcasecmp(bef, "PLIST"))				/* List content */
463
	{
464
		char user[64], playlist[64], *t;
465
		int start, length, x;
466
 
467
		memset(user, 0, sizeof(user));
468
		memset(playlist, 0, sizeof(playlist));
469
		x = 0;
470
		t = strtok (par, ":");
471
 
472
		while (t)
473
		{
474
			switch(x)
475
			{
476
				case 0: strncpy (user, t, sizeof(user)-1); break;
477
				case 1: strncpy (playlist, t, sizeof(playlist)-1); break;
478
				case 2: start = atoi(t); break;
479
				case 3: length = atoi(t); break;
480
			}
481
 
482
			x++;
483
			t = strtok (NULL, ":");
484
		}
485
 
11 andreas 486
		t = urldecode(user);
487
 
488
		if (t)
489
		{
490
			strncpy(user, t, sizeof(user)-1);
491
			free(t);
492
		}
493
 
494
		t = urldecode(playlist);
495
 
496
		if (t)
497
		{
498
			strncpy(playlist, t, sizeof(playlist)-1);
499
			free(t);
500
		}
501
 
9 andreas 502
		return listUserPlaylist(s1, user, playlist, start, length);
503
	}
504
 
505
	/*
13 andreas 506
	 * Syntax: LISTFOLDER:<type>:<name>:<start>:<length>;
507
	 *         <type>   TITLE | ARTIST | ALBUM | GENRE
508
	 *         <start>  The line to start from
509
	 *         <length> The number of lines to report
510
	 * 
511
	 * Returns: LINE:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>:<cover>;
512
	 *         <id>     The unique ID of the file. Needed to play the file!
513
	 *         <line>   The line number counting from 1
514
	 */
515
	if (!strcasecmp(bef, "LISTFOLDER"))			/* List the content of a folder */
516
	{
517
		char p_type[64], name[256], *t, *type, *nm;
518
		int x, start, length;
519
 
520
		x = 0;
521
		t = strtok(par, ":");
522
 
523
		while (t)
524
		{
525
			switch(x)
526
			{
527
				case 0: strncpy(p_type, t, sizeof(p_type)); break;
528
				case 1: strncpy(name, t, sizeof(name)); break;
529
				case 2: start = atoi(t); break;
530
				case 3: length = atoi(t); break;
531
			}
532
 
533
			x++;
534
			t = strtok(NULL, ":");
535
		}
536
 
537
		type = urldecode(p_type);
538
		nm = urldecode(name);
539
 
540
		if (type)
541
		{
542
			strncpy(p_type, type, sizeof(p_type)-1);
543
			free(type);
544
		}
545
 
546
		if (nm)
547
		{
548
			strncpy(name, nm, sizeof(name)-1);
549
			free(nm);
550
		}
551
 
552
		return listFolderContent(s1, p_type, name, start, length);
553
	}
554
 
555
	/*
5 andreas 556
	 * Syntax: FOLDER:<type>:<start>:<length>;
557
	 *         <type>   TITLE | ARTIST | ALBUM | GENRE
558
	 *         <start>  The line to start from
559
	 *         <length> The number of lines to report
560
	 * 
25 andreas 561
	 * Returns: FOLDER:<type>:<id>:<line>:<content>;
5 andreas 562
	 *         <id>       The unique ID of the file. Needed to play the file!
563
	 *         <line>     The line number counting from 1
564
	 *         <content>  The name of the folder
565
	 */
566
	if (!strcasecmp(bef, "FOLDER"))				/* List folders */
567
	{
568
		char p_type[16];
569
		int start, length;
570
 
571
		memset(p_type, 0, sizeof(p_type));
572
		remove_string(par, ":", &hv0[0]);
573
		start = strlen(hv0);
574
		hv0[start-1] = 0;
575
		strncpy(p_type, hv0, sizeof(p_type)-1);
576
		start = atoi(par);
577
		remove_string(par, ":", &hv0[0]);
578
		length = atoi(par);
579
		return listFolders(s1, p_type, start, length);
580
	}
581
 
6 andreas 582
	/*
14 andreas 583
	 * This command moves one or more files into the queue.
584
	 * If the queue already contains some files, the files are appended to
18 andreas 585
	 * the end of the queue. The command makes sure, that there are no
586
	 * duplicate entries in the queue.
14 andreas 587
	 * If the name of a folder is omitted, all files of the specified types
588
	 * are appended.
589
	 * If a folder name was specified, only the files of the folder are
590
	 * appended.
591
	 *
592
	 * Syntax: ADD:<type>:<folder>;
593
	 *          <type>     ID | TITLE | ARTIST | ALBUM | GENRE | PLAYLIST
594
	 *          <folder>   The name of a folder. This is optional.
595
	 */
596
	if (!strcasecmp(bef, "ADD"))
597
	{
598
		int x;
599
		char *t, p_type[32], folder[64], *type, *fld;
600
 
601
		memset(p_type, 0, sizeof(p_type));
602
		memset(folder, 0, sizeof(folder));
603
		x = 0;
604
		t = strtok(par, ":");
605
 
606
		while (t)
607
		{
608
			switch (x)
609
			{
610
				case 0: strncpy(p_type, t, sizeof(p_type)-1); break;
611
				case 1: strncpy(folder, t, sizeof(folder)-1); break;
612
			}
613
 
614
			x++;
615
			t = strtok(NULL, ":");
616
		}
617
 
618
		type = urldecode(p_type);
619
		fld = urldecode(folder);
620
 
621
		if (type)
622
		{
623
			strncpy(p_type, type, sizeof(p_type));
624
			free(type);
625
		}
626
 
627
		if (fld)
628
		{
629
			strncpy(folder, fld, sizeof(folder));
630
			free(fld);
631
		}
632
 
18 andreas 633
		if (strlen(p_type) && strlen(folder))
14 andreas 634
			appendToQueue(s1, p_type, folder);
635
		else
636
		{
18 andreas 637
			strcpy(cmd_message, "ERROR:ADD:Missing type or folder name;");
638
			return FALSE;
14 andreas 639
		}
640
	}
641
 
642
	/*
643
	 * This command moves one or more files into the queue and starts to play
644
	 * them.
645
	 * If the player is already playing, the file(s) are appended to the queue.
36 andreas 646
	 * If a play request for the queue is detected and the ID is 0, then the
647
	 * first or, if random is selected a random entry of the que is played. If
648
	 * the ID is grater than 0, the wanted ID is played even if random is
649
	 * selected.
49 andreas 650
	 * The type DIRECT means to play a particular file without change the current
651
	 * queue. If something is already playing, it's stopped and the file the
652
	 * parameter <what> points to is played. With DIRECT the parameter <what>
653
	 * has to contain a valid ID number.
14 andreas 654
	 *
6 andreas 655
	 * Syntax: PLAY:<type>:<what>;
49 andreas 656
	 *         <type>     ID | TITLE | ARTIST | ALBUM | GENRE | PLAYLIST | QUEUE | DIRECT
7 andreas 657
	 *         <what>     <id> or name of folder or name of playlist
6 andreas 658
	 * 
7 andreas 659
	 * Returns: PLAYING:<id>:<title>:<artist>:<album>:<genre>;
49 andreas 660
	 *          POSITION:<time>:<time left>:<total>;
6 andreas 661
	 */
662
	if (!strcasecmp(bef, "PLAY"))				/* Plays one or more files */
663
	{
664
		char p_type[16];
665
		char what[256];
666
		int i;
667
 
668
		memset(p_type, 0, sizeof(p_type));
36 andreas 669
		memset(hv0, 0, sizeof(hv0));
6 andreas 670
		remove_string(par, ":", &hv0[0]);
671
		i = strlen(hv0);
37 andreas 672
 
36 andreas 673
		if (i > 0)
674
		{
675
			hv0[i-1] = 0;
676
			strncpy(p_type, hv0, sizeof(p_type) - 1);
677
		}
678
		else
679
			strcpy(p_type, "QUEUE");
680
 
49 andreas 681
		if (strcasecmp(p_type, "ID") && strcasecmp(p_type, "DIRECT"))	/* If the type is not "ID" and not "DIRECT" then */
682
		{																/* decode the text */
9 andreas 683
			char *w;
7 andreas 684
 
9 andreas 685
			w = urldecode(par);
686
 
687
			if (w)
688
			{
689
				strncpy (what, w, sizeof(what));
690
				i = sizeof(what) - 1;
691
				what[i] = 0;
692
				free (w);
693
			}
694
			else
695
				strncpy(what, par, sizeof(what));
696
		}
36 andreas 697
		else if (strlen(par))
698
			strncpy(what, par, sizeof(what));
9 andreas 699
		else
36 andreas 700
			strcpy(what, "0");
37 andreas 701
 
49 andreas 702
		if (playerActive && playStatus == PLAY_STATUS_PAUSE && strcasecmp(p_type, "DIRECT"))
8 andreas 703
			nextCommand = PLAY_PLAY;
49 andreas 704
		else if (playerActive && strcasecmp(p_type, "DIRECT"))		/* Only if type is not DIRECT */
7 andreas 705
			appendToQueue(s1, p_type, what);
706
		else
6 andreas 707
		{
7 andreas 708
			_playPars.s1 = s1;
9 andreas 709
			strcpy(_playPars.type, p_type);
710
			strcpy(_playPars.what, what);
49 andreas 711
 
712
			if (!strcasecmp(p_type, "DIRECT") && playerActive)
713
			{
50 andreas 714
				qstackAdd(atoi(what));
715
				nextCommand = PLAY_STACK;
716
			}
51 andreas 717
			else if (!playerActive)
50 andreas 718
			{
719
				/* start a new thread to play the file(s) */
720
				if (pthread_create(&pthr_play, NULL, pthr_playfile, (void *)&_playPars) != 0)
49 andreas 721
				{
50 andreas 722
					syslog (LOG_DAEMON,"Creation of thread \"pthr_play\" failed!");
723
					strcpy(cmd_error, "Error playing a file!");
49 andreas 724
					return FALSE;
725
				}
726
			}
6 andreas 727
		}
728
	}
729
 
7 andreas 730
	if (!strcasecmp(bef, "STOP") && playerActive)				/* Stop the currently playing file */
731
		nextCommand = PLAY_STOP;
732
 
733
	if (!strcasecmp(bef, "PAUSE") && playerActive)				/* Pause the currently playing file */
734
		nextCommand = PLAY_PAUSE;
735
 
40 andreas 736
	if (!strcasecmp(bef, "PLAYPAUSE"))							/* Play/Pause the currently playing file, */
737
	{															/* or play one in the queue if there are any. */
738
		if (playerActive && (playStatus == PLAY_STATUS_PAUSE || playStatus == PLAY_STATUS_PLAY))
739
			nextCommand = PLAY_PLAYPAUSE;
740
		else if (playerActive && playStatus == PLAY_STATUS_STOP)
741
			nextCommand = PLAY_PLAY;
742
		else if (!playerActive)
743
		{
744
			_playPars.s1 = s1;
745
			strcpy(_playPars.type, "QUEUE");					/* Select the queue to play. */
746
			strcpy(_playPars.what, "0");						/* Play the first or a random file, if random is selected, in the queue. */
747
			/* start a new thread to play the file(s) */
748
			if (pthread_create(&pthr_play, NULL, pthr_playfile, (void *)&_playPars) != 0)
749
			{
750
				syslog (LOG_DAEMON,"Create of thread \"pthr_play\" failed!");
751
				strcpy(cmd_error, "Error playing a file!");
752
				return FALSE;
753
			}
754
		}
755
	}
7 andreas 756
 
757
	if (!strcasecmp(bef, "FORWARD") && playerActive)			/* Fast forward */
758
		nextCommand = PLAY_FWD;
759
 
760
	if (!strcasecmp(bef, "REWIND") && playerActive)				/* Fast rewind */
761
		nextCommand = PLAY_REW;
762
 
763
	if (!strcasecmp(bef, "SKIPFWD") && playerActive)			/* Skip to next file in queue */
764
		nextCommand = PLAY_SKIP_FWD;
765
 
766
	if (!strcasecmp(bef, "SKIPREW") && playerActive)			/* Skip to previous file in queue */
767
		nextCommand = PLAY_SKIP_REW;
768
 
11 andreas 769
	if (!strcasecmp(bef, "RANDOM"))								/* Toggle random play */
770
	{
771
		playerRandom = !playerRandom;
772
		sprintf(cmd_message, "RANDOM:%s;", playerRandom ? "TRUE" : "FALSE");
773
	}
774
 
775
	if (!strcasecmp(bef, "REPEAT"))								/* Toggle repeat */
776
	{
777
		playerRepeat = !playerRepeat;
778
		sprintf(cmd_message, "REPEAT:%s;", playerRepeat ? "TRUE" : "FALSE");
779
	}
780
 
7 andreas 781
	/*
14 andreas 782
	 * This command loads a user. If the user doesn't exist it creates
783
	 * the user. In this case the name of a playlist is needed.
784
	 * If the user already exist, the name of a playlist is optional.
785
	 * 
786
	 * If this command is called without the name of a playlist and the
787
	 * user doesn't exist, an error occurs.
788
	 *
789
	 * This command should only be called when a new playlist is to be
790
	 * saved. After this command the command "SAVEQUEUE" should be called.
791
	 *
8 andreas 792
	 * Syntax: USER:<name>:[<playlist>];
793
	 *         <name>     the name of the user.
794
	 *         <playlist> the name of the playlist; This is optional.
7 andreas 795
	 */
796
	if (!strcasecmp(bef, "USER"))								/* In case the user doesn't exist, create a new one. Activate this user */
797
	{
8 andreas 798
		char user[64], playlist[64], *t;
799
		int x;
7 andreas 800
 
8 andreas 801
		memset(user, 0, sizeof(user));
802
		memset(playlist, 0, sizeof(playlist));
803
		x = 0;
804
		t = strtok (par, ":");
7 andreas 805
 
8 andreas 806
		while (t)
807
		{
808
			char *hv;
809
 
810
			switch(x)
811
			{
812
				case 0:
813
					strncpy (user, t, sizeof(user));
814
					user[63] = 0;
815
					hv = urldecode(user);
816
 
817
					if (hv != NULL)
818
					{
819
						strcpy(user, hv);
820
						free(hv);
821
					}
822
				break;
823
 
824
				case 1:
825
					strncpy (playlist, t, sizeof(playlist));
826
					playlist[63] = 0;
827
					hv = urldecode(playlist);
828
 
829
					if (hv != NULL)
830
					{
831
						strcpy(playlist, hv);
832
						free(hv);
833
					}
834
				break;
835
			}
836
 
837
			x++;
838
			t = strtok(NULL, ":");
839
		}
840
 
841
		if (strlen(user) && strlen(playlist))
842
		{
843
			if (!createUser(s1, user, playlist))
844
				return FALSE;
845
 
846
			return selectUser(s1, user);
847
		}
848
		else if (strlen(user))
849
			return selectUser(s1, user);
850
		else
851
		{
852
			strcpy (cmd_error, "Missing the username and the name of the playlist");
853
			return FALSE;
854
		}
7 andreas 855
	}
856
 
8 andreas 857
	/*
858
	 * Syntax: SAVEQUEUE:<user>:<playlist>;
859
	 *         <user>     The name of the user. If this is empty, the actual
860
	 *                    user is taken, if any. If there's no user selected,
861
	 *                    an error occurs.
862
	 *         <playlist> The name of the playlist. If the playlist already
863
	 *                    exist, it'll be overwritten.
864
	 * 
865
	 * Return: TRANSFERED:<num>;
866
	 *         <num>      The number of entries transfered.
867
	 */
14 andreas 868
	if (!strcasecmp(bef, "SAVEQUEUE"))			/* Saves the content of the queue into a playlist */
8 andreas 869
	{
870
		char user[64], playlist[64];
871
		char *t;
872
		int x;
873
 
25 andreas 874
		if (!queueTotal)
875
		{
876
			readQueue();
877
 
878
			if (!queueTotal)
879
			{
880
				strcpy(cmd_error, "Queue is empty");
881
				return FALSE;
882
			}
883
		}
884
 
8 andreas 885
		x = 0;
886
		t = strtok(par, ":");
887
 
888
		while (t)
889
		{
890
			char *hv;
891
 
892
			switch(x)
893
			{
894
				case 0:
895
					strncpy (user, t, sizeof(user));
896
					user[63] = 0;
897
					hv = urldecode(user);
898
 
899
					if (hv != NULL)
900
					{
901
						strcpy(user, hv);
902
						free(hv);
903
					}
904
				break;
905
 
906
				case 1:
907
					strncpy (playlist, t, sizeof(playlist));
908
					playlist[63] = 0;
909
					hv = urldecode(playlist);
910
 
911
					if (hv != NULL)
912
					{
913
						strcpy(playlist, hv);
914
						free(hv);
915
					}
916
				break;
917
			}
918
 
919
			x++;
920
			t = strtok(NULL, ":");
921
		}
922
 
923
		if (!strlen(user) && userchain != NULL)
924
			strcpy(user, userchain->uname);
25 andreas 925
		else if (!selectUser(s1, user) && strlen(user) && strlen(playlist))
926
		{
927
			if (!createUser(s1, user, playlist))
928
				return FALSE;
929
 
930
			return QueueToPlaylist(s1, user, playlist);
931
		}
8 andreas 932
		else if (!selectUser(s1, user))
933
		{
934
			strcpy (cmd_error, "No or invalid user");
935
			return FALSE;
936
		}
937
 
938
		if (!strlen(playlist))
939
		{
940
			strcpy (cmd_error, "No or invalid playlist");
941
			return FALSE;
942
		}
943
 
944
		if (!QueueToPlaylist(s1, user, playlist))
945
			return FALSE;
946
	}
947
 
948
	/*
949
	 * Syntax: DELETE:<type>:<id>;
13 andreas 950
	 *      <type>     PTITLE | QUEUE | PLAYLIST
951
	 *                 PTITLE: Delete one title from a playlist
952
	 *                 QUEUE: If the given ID is -1, then the whole
953
	 *                        queue is deleted. Otherwise only a title
954
	 *                        in the queue.
955
	 *                 PLAYLIST Delete a whole playlist from a user
8 andreas 956
	 * 
13 andreas 957
	 *      <id>       -1 = Delete whole QUEUE or PLAYLIST
958
	 *                 >= 0 Delete only a particular entry
8 andreas 959
	 */
9 andreas 960
	if (!strcasecmp(bef, "DELETE"))
8 andreas 961
	{
962
		char p_type[32], *t;
963
		int id, x;
964
 
965
		x = 0;
966
		t = strtok(par, ":");
967
 
968
		while (t)
969
		{
970
			switch (x)
971
			{
972
				case 0:
973
					strncpy(p_type, t, sizeof(p_type));
974
					p_type[31] = 0;
975
				break;
976
 
977
				case 1: id = atoi(t); break;
978
			}
979
 
980
			x++;
981
			t = strtok(NULL, ":");
982
		}
983
 
984
		if (!strcasecmp(p_type, "QUEUE"))
985
			return deleteQueue(s1, id);
986
		else if (!strcasecmp(p_type, "PTITLE"))
987
			return deletePlaylistEntry(s1, id);
988
		else if (!strcasecmp(p_type, "PLAYLIST"))
989
			return deletePlaylist(s1, id);
990
		else
991
			return FALSE;
992
	}
993
 
994
	/*
9 andreas 995
	 * Syntax: SEARCH:<type>:<expression>:<start>:<lines>;
8 andreas 996
	 *            <type>   TITLE | ARTIST | ALBUM | GENRE
997
	 *            <expression> This is what to search for
9 andreas 998
	 *            <start>  the data to start to
999
	 *            <lines>  number of lines to return
1000
	 * 
18 andreas 1001
	 * Return: SEARCH:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>;
8 andreas 1002
	 */
1003
	if (!strcasecmp(bef, "SEARCH"))
1004
	{
9 andreas 1005
		int start, lines, x;
1006
		char *t, p_type[32], expr[128];
1007
 
1008
		memset(p_type, 0, sizeof(p_type));
1009
		memset(expr, 0, sizeof(expr));
1010
		start = lines = 0;
1011
		x = 0;
1012
		t = strtok (par, ":");
1013
 
1014
		while (t)
1015
		{
11 andreas 1016
			char *hv;
1017
 
9 andreas 1018
			switch(x)
1019
			{
11 andreas 1020
				case 0: strncpy(p_type, t, sizeof(p_type)-1); break;
9 andreas 1021
 
1022
				case 1:
11 andreas 1023
					strncpy(expr, t, sizeof(expr)-1);
1024
					hv = urldecode(expr);
1025
 
1026
					if (hv != NULL)
1027
					{
1028
						strncpy(expr, hv, sizeof(expr) - 1);
1029
						free(hv);
1030
					}
9 andreas 1031
				break;
1032
 
1033
				case 2: start = atoi(t); break;
1034
				case 3: lines = atoi(t); break;
1035
			}
1036
 
1037
			x++;
1038
			t = strtok (NULL, ":");
1039
		}
13 andreas 1040
 
9 andreas 1041
		if (x != 4 || !strlen(p_type) || !strlen(expr) || !lines || !start)
1042
		{
1043
			strcpy (cmd_error, "Missing one ore more parameters");
1044
			return FALSE;
1045
		}
1046
 
1047
		return searchTerm(s1, p_type, expr, start, lines);
8 andreas 1048
	}
1049
 
28 andreas 1050
	/*
1051
	 * This command reread the config file.
1052
	 */
1053
	if (!strcasecmp(bef, "RESET"))
1054
	{
1055
		if (playerActive)
1056
			nextCommand = PLAY_STOP;
1057
 
1058
		readConf();
1059
	}
1060
 
4 andreas 1061
	return TRUE;		/* cmd was OK */
2 andreas 1062
}
1063
 
1064
/*
8 andreas 1065
 * This function opens the database and deletes the whole archive of scanned
1066
 * MP3 files.
1067
 * This is called when the command "RESCAN" was detected. When this function
1068
 * is finished, the database is ready for new data.
1069
 */
1070
int cleanArchieve(int s1, char *fname)
1071
{
1072
	char query[1024];
1073
	sqlite3 *db;
1074
	char *zErrMsg = 0;
1075
	int rc;
1076
 
1077
	rc = sqlite3_open(fname, &db);
1078
 
1079
	if (rc)
1080
	{
1081
		syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
1082
		strcpy(query, "ERROR:MDB:Error opening database;");
1083
		write (s1, query, strlen(query));
1084
		return FALSE;
1085
	}
1086
 
29 andreas 1087
	strcpy (query, "delete from \"main\".\"playlists\"");
1088
 
1089
	if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
1090
	{
1091
		syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
1092
		sqlite3_free(zErrMsg);
1093
		sqlite3_close(db);
1094
		strcpy(query, "ERROR:MDB:SQL error;");
1095
		write (s1, query, strlen(query));
1096
		return FALSE;
1097
	}
1098
 
8 andreas 1099
	strcpy (query, "delete from \"main\".\"musicdb\"");
1100
 
1101
	if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
1102
	{
1103
		syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
1104
		sqlite3_free(zErrMsg);
1105
		sqlite3_close(db);
1106
		strcpy(query, "ERROR:MDB:SQL error;");
1107
		write (s1, query, strlen(query));
1108
		return FALSE;
1109
	}
1110
 
1111
	sqlite3_close(db);
1112
	return TRUE;
1113
}
1114
 
1115
/*
2 andreas 1116
 * Callback function of directory scan.
1117
 * This function desides whether a directory entry will be treated as a
1118
 * music file or not.
1119
 */
1120
int musicFilter(const struct dirent *dir)
1121
{
4 andreas 1122
	if (dir == NULL)
1123
		return 0;
2 andreas 1124
 
4 andreas 1125
	if (dir->d_type == DT_REG)
1126
	{
32 andreas 1127
		char *p = getFileExtension(dir->d_name);
1128
 
1129
		if (p != NULL && (strcasecmp(p, "mp3") == 0 || strcasecmp(p, "flac") == 0))
1130
			return 1;
4 andreas 1131
	}
1132
	else if (dir->d_type == DT_DIR)
1133
	{
1134
		if (strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0)
1135
			return 1;
1136
	}
1137
 
2 andreas 1138
	return 0;
1139
}
1140
 
1141
/*
1142
 * This function scans a directory for music files. If it finds some file name
1143
 * who is a possible music file, it gives it to a function to evaluate and
1144
 * store it.
1145
 */
12 andreas 1146
int grabMusicFiles(int s1, char *dir)
2 andreas 1147
{
1148
	struct dirent **namelist;
1149
	int n;
1150
 
1151
	if (dir == NULL || strlen(dir) == 0)
1152
		return FALSE;
1153
 
1154
	n = scandir(dir, &namelist, musicFilter, alphasort);
1155
 
1156
	if (n < 0)
1157
	{
4 andreas 1158
		syslog(LOG_DAEMON, "Error scanning directory %s: %s", dir, strerror(errno));
2 andreas 1159
		return FALSE;
1160
	}
1161
	else
1162
	{ 
1163
		while(n--)
1164
		{
42 andreas 1165
			char hv0[1024];
1166
			int len;
1167
 
5 andreas 1168
			strcpy(hv0, dir);
1169
			strcat(hv0, "/");
42 andreas 1170
			len = sizeof(hv0) - strlen(hv0) - 1;
5 andreas 1171
 
42 andreas 1172
			if (len > 0)
1173
				strncat(hv0, namelist[n]->d_name, len);
1174
			else
1175
			{
1176
				free(namelist[n]);
1177
				continue;
1178
			}
1179
 
4 andreas 1180
			if (namelist[n]->d_type == DT_DIR)
12 andreas 1181
				grabMusicFiles(s1, hv0);
1182
			else
4 andreas 1183
			{
1184
				evaluateMusicFile(hv0);
1185
 
12 andreas 1186
				if (!(grabFilesCount % 100))
1187
				{
1188
					sprintf(hv0, "SCANNING:%d;", grabFilesCount);
1189
					write (s1, hv0, strlen(hv0));
1190
				}
1191
			}
1192
 
2 andreas 1193
			free(namelist[n]); 
1194
		} 
1195
 
1196
		free(namelist); 
14 andreas 1197
	}
1198
 
1199
	return TRUE;
2 andreas 1200
}
1201
 
1202
void evaluateMusicFile(char *file)
1203
{
42 andreas 1204
sqlite3 *db;
1205
sqlite3_stmt *res;
1206
char *zErrMsg = 0;
43 andreas 1207
int rc, fType, update, id;
42 andreas 1208
char fname[256], hv0[64];
1209
char query[8192];
45 andreas 1210
struct id3_tag *id3;
1211
struct id3_file *ifile;
1212
struct id3_frame *frame;
1213
union id3_field *field;
1214
id3_byte_t const *bt;
42 andreas 1215
char id3_title[256];
1216
char id3_singer[256];
1217
char id3_album[256];
1218
char id3_genre[256];
1219
char id3_cover[256];
56 andreas 1220
char /* *title, *singer, *album,*/ *ext;
45 andreas 1221
enum id3_field_type enc;
3 andreas 1222
 
2 andreas 1223
	if (access(file, R_OK))
1224
		return;
1225
 
45 andreas 1226
	bt = NULL;
1227
 
2 andreas 1228
	strcpy(fname, configs.home);
9 andreas 1229
	strcat(fname, MUSICDB);
4 andreas 1230
 
5 andreas 1231
	if (!access(fname, R_OK | W_OK))
2 andreas 1232
		file_found = TRUE;
4 andreas 1233
	else						/* create the directory, if it doesn't exitst */
1234
	{
1235
		if (access(configs.home, R_OK | W_OK))
1236
		{
1237
			if (mkdir(configs.home, 0775) != 0)
1238
			{
1239
				syslog(LOG_WARNING, "Error creating directory %s: %s", configs.home, strerror(errno));
1240
				return;
1241
			}
1242
			else
1243
				syslog(LOG_INFO, "Directory %s was created.", configs.home);
1244
		}
5 andreas 1245
 
1246
		sprintf(query, "%s/Covers", configs.home);
1247
 
1248
		if (access(query, R_OK | W_OK))
1249
		{
1250
			if (mkdir(query, 0775) != 0)
1251
				syslog(LOG_WARNING, "Error creating directory %s: %s", query, strerror(errno));
1252
			else
1253
				syslog(LOG_INFO, "Directory %s was created.", query);
1254
		}
4 andreas 1255
	}
2 andreas 1256
 
1257
	rc = sqlite3_open(fname, &db);
1258
 
1259
	if (rc)
1260
	{
1261
		syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
1262
		return;
1263
	}
1264
 
1265
	if (!file_found)
1266
	{
32 andreas 1267
		/* id        autoincrement unique index
1268
		 * path      path to file containing an MP3 or FLAC file
1269
		 * type      1 = MP3, 2 = FLAC
1270
		 * title     title of song
1271
		 * interpret singer
1272
		 * album     the album the is from
1273
		 * genre     genre of the song
1274
		 * cover     name of file containing the cover
1275
		 */
2 andreas 1276
		strcpy(query, "create table \"main\".\"musicdb\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,");
32 andreas 1277
		strcat(query, "\"path\" TEXT NOT NULL, \"type\" INTEGER, \"title\" TEXT, \"interpret\" TEXT,");
5 andreas 1278
		strcat(query, "\"album\" TEXT, \"genre\" TEXT, \"cover\" TEXT)");
8 andreas 1279
		rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg);
2 andreas 1280
 
1281
		if (rc != SQLITE_OK)
1282
		{
5 andreas 1283
			syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
2 andreas 1284
			sqlite3_free(zErrMsg);
1285
			sqlite3_close(db);
1286
			return;
1287
		}
4 andreas 1288
		else
7 andreas 1289
			syslog(LOG_INFO, "Database \"musicdb\" was successfully created.");
1290
 
1291
		strcpy(query, "CREATE TABLE \"main\".\"users\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT,");
1292
		strcat(query, "\"uname\" TEXT NOT NULL, \"playlist\" TEXT)");
8 andreas 1293
		rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg);
7 andreas 1294
 
1295
		if (rc != SQLITE_OK)
1296
		{
1297
			syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
1298
			sqlite3_free(zErrMsg);
1299
			sqlite3_close(db);
1300
			return;
1301
		}
1302
		else
1303
			syslog(LOG_INFO, "Database \"users\" was successfully created.");
8 andreas 1304
 
1305
		strcpy (query, "CREATE TABLE \"main\".\"playlists\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,");
1306
		strcat (query, "\"userid\" INTEGER NOT NULL, \"musicid\" INTEGER NOT NULL)");
1307
 
1308
		rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg);
1309
 
1310
		if (rc != SQLITE_OK)
1311
		{
1312
			syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
1313
			sqlite3_free(zErrMsg);
1314
			sqlite3_close(db);
1315
			return;
1316
		}
1317
		else
1318
			syslog(LOG_INFO, "Database \"users\" was successfully created.");
2 andreas 1319
	}
1320
 
4 andreas 1321
	/* Make sure, the file is not already in the database */
45 andreas 1322
	sprintf(query, "select id, path, cover from musicdb where path = \"%s\"", file);
2 andreas 1323
 
8 andreas 1324
	if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
2 andreas 1325
	{
8 andreas 1326
		syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
2 andreas 1327
		sqlite3_close(db);
1328
		return;
1329
	}
42 andreas 1330
 
8 andreas 1331
	rc = sqlite3_step(res);
45 andreas 1332
	id3_cover[0] = 0;
42 andreas 1333
 
8 andreas 1334
	if (rc == SQLITE_ROW)			/* If we've a row, the path exists and we'll not add it again */
43 andreas 1335
	{								/* Instead we'll update the information */
1336
		update = 1;
1337
		id = sqlite3_column_int(res, 0);
45 andreas 1338
 
1339
		if ((ext = (char *)sqlite3_column_text(res, 2)) == NULL)
1340
		{
1341
			syslog(LOG_WARNING, "Error getting path of picture from database!");
1342
			id3_cover[0] = 0;
1343
		}
1344
		else
1345
			strncpy(&id3_cover[0], ext, sizeof(id3_cover));
2 andreas 1346
	}
43 andreas 1347
	else
1348
		update = 0;
2 andreas 1349
 
43 andreas 1350
	sqlite3_finalize(res);
32 andreas 1351
	/* Check the file extension and decide whether it's an MP3 or FLAC file. */
1352
	ext = getFileExtension(file);
1353
 
1354
	if (ext != NULL)
1355
	{
1356
		if (strcasecmp(ext, "flac") == 0)
1357
			fType = FILE_TYPE_FLAC;
1358
		else
1359
			fType = FILE_TYPE_MP3;
1360
	}
1361
	else
1362
		fType = FILE_TYPE_MP3;
1363
 
4 andreas 1364
	/* Open the file and get the ID3 tag. */
45 andreas 1365
	if ((ifile = id3_file_open(file, ID3_FILE_MODE_READONLY)) == NULL)
1366
	{
1367
		syslog(LOG_WARNING, "Error opening file %s!", file);
1368
		sqlite3_close(db);
1369
		return;
1370
	}
1371
 
1372
	if ((id3 = id3_file_tag(ifile)) == NULL)
1373
	{
1374
		syslog(LOG_WARNING, "Error initializing MP3 file %s", file);
1375
		sqlite3_close(db);
1376
		return;
1377
	}
1378
 
2 andreas 1379
	memset(&id3_title, 0, sizeof(id3_title));
1380
	memset(&id3_singer, 0, sizeof(id3_singer));
1381
	memset(&id3_album, 0, sizeof(id3_album));
1382
	memset(&id3_genre, 0, sizeof(id3_genre));
45 andreas 1383
 
1384
	if (!strlen(id3_cover))
1385
		memset(&id3_cover, 0, sizeof(id3_cover));
1386
 
4 andreas 1387
	/* Get the required frames */
45 andreas 1388
	if ((frame = id3_tag_findframe(id3, ID3_FRAME_TITLE, 0)) != NULL)
2 andreas 1389
	{
45 andreas 1390
		int i;
1391
 
1392
		for (i = 0; i < (int)frame->nfields; i++)
1393
		{
1394
			field = id3_frame_field(frame, i);
1395
 
1396
			if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
1397
				enc = id3_field_gettextencoding(field);
1398
 
1399
			if (field && checkID3FieldType(field->type))
1400
				break;
1401
		}
1402
 
1403
		getID3_Field(field, enc, &id3_title[0], sizeof(id3_title));
5 andreas 1404
		char_replace(id3_title, '"', '`');
1405
		char_replace(id3_title, '\\', ' ');
2 andreas 1406
	}
1407
 
45 andreas 1408
	if ((frame = id3_tag_findframe(id3, ID3_FRAME_ARTIST, 0)) != NULL)
2 andreas 1409
	{
45 andreas 1410
		int i;
1411
 
1412
		for (i = 0; i < (int)frame->nfields; i++)
1413
		{
1414
			field = id3_frame_field(frame, i);
1415
 
1416
			if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
1417
				enc = id3_field_gettextencoding(field);
1418
 
1419
			if (field && checkID3FieldType(field->type))
1420
				break;
1421
		}
1422
 
1423
		getID3_Field(field, enc, &id3_singer[0], sizeof(id3_singer));
5 andreas 1424
		char_replace(id3_singer, '"', '`');
1425
		char_replace(id3_singer, '\\', ' ');
2 andreas 1426
	}
1427
 
45 andreas 1428
	if ((frame = id3_tag_findframe(id3, ID3_FRAME_ALBUM, 0)) != NULL)
2 andreas 1429
	{
45 andreas 1430
		int i;
1431
 
1432
		for (i = 0; i < (int)frame->nfields; i++)
1433
		{
1434
			field = id3_frame_field(frame, i);
1435
 
1436
			if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
1437
				enc = id3_field_gettextencoding(field);
1438
 
1439
			if (field && checkID3FieldType(field->type))
1440
				break;
1441
		}
1442
 
1443
		getID3_Field(field, enc, &id3_album[0], sizeof(id3_album));
5 andreas 1444
		char_replace(id3_album, '"', '`');
1445
		char_replace(id3_album, '\\', ' ');
2 andreas 1446
	}
1447
 
45 andreas 1448
	if ((frame = id3_tag_findframe(id3, "APIC", 0)) != NULL)
12 andreas 1449
	{
1450
		char fpname[512], uid[128], hv0[256];
56 andreas 1451
/*		struct id3_frame *fpframe; */
45 andreas 1452
		int i, fd, np;
1453
		long unsigned len;
12 andreas 1454
 
40 andreas 1455
		memset(uid, 0, sizeof(uid));
12 andreas 1456
		strcpy(fpname, configs.home);
1457
		strcat(fpname, "/Covers");
1458
 
1459
		if (access(fpname, R_OK | W_OK | X_OK))
1460
			mkdir(fpname, 0775);
1461
 
45 andreas 1462
		if (!strlen(id3_cover))
12 andreas 1463
		{
14 andreas 1464
			char *md5;
12 andreas 1465
 
22 andreas 1466
			if ((md5 = str2hash(file, (int)strlen(file))) != NULL)
14 andreas 1467
			{
1468
				sprintf(uid, "Picture-%s", md5);
1469
				free(md5);
1470
			}
1471
			else
1472
				sprintf(uid, "Picture-%ld", random());
1473
		}
1474
 
12 andreas 1475
		strcat(fpname, "/");
45 andreas 1476
		len = 0;
1477
		np = 0;			/* 1 = a new picture was found */
1478
		bt = NULL;
12 andreas 1479
		/* Get the picture and store it into a file */
45 andreas 1480
		for (i = 0; i < (int)frame->nfields; i++)
1481
		{
1482
			if ((field = id3_frame_field(frame, i)) != NULL)
1483
			{
1484
				id3_latin1_t const *lt1;
40 andreas 1485
 
45 andreas 1486
				switch (field->type)
1487
				{
1488
					case ID3_FIELD_TYPE_LATIN1:			// MIME type
1489
						if ((lt1 = id3_field_getlatin1(field)) != NULL)
1490
							strncpy (&hv0[0], (char *)lt1, sizeof(hv0)-1);
1491
					break;
1492
 
1493
					case ID3_FIELD_TYPE_LATIN1FULL:
1494
						if ((lt1 = id3_field_getfulllatin1(field)) != NULL)
1495
							strncpy (&hv0[0], (char *)lt1, sizeof(hv0)-1);
1496
					break;
1497
 
1498
					case ID3_FIELD_TYPE_BINARYDATA:		// The picture
1499
						bt = id3_field_getbinarydata(field, &len);
1500
						np = 1;
1501
					break;
1502
				}
1503
			}
1504
		}
1505
 
1506
		if (!strlen(id3_cover) && np)
12 andreas 1507
		{
45 andreas 1508
			if (strstr(hv0, "jpeg") != NULL)
1509
				strcat(uid, ".jpg");
1510
			else if (strstr(hv0, "png") != NULL)
1511
				strcat(uid, ".png");
1512
			else if (strstr(hv0, "gif") != NULL)
1513
				strcat(uid, ".gif");
1514
			else if (strstr(hv0, "tiff") != NULL)
1515
				strcat(uid, ".tiff");
1516
			else
1517
			{
1518
				char *pos;
12 andreas 1519
 
45 andreas 1520
				if ((pos = strrchr(hv0, '/')) != NULL)
1521
				{
1522
					strcat(uid, ".");
1523
					strcat(uid, pos+1);
1524
				}
1525
			}
1526
 
1527
			strcat(fpname, uid);
1528
			strcpy (id3_cover, uid);
1529
		}
1530
		else if (strlen(id3_cover) && !np)
1531
		{
1532
			strcat (fpname, id3_cover);
1533
			unlink(fpname);
1534
		}
1535
		else if (strlen(id3_cover))
1536
			strcat (fpname, id3_cover);
1537
 
1538
		if (np)
1539
		{
1540
			if ((fd = open(fpname, O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
1541
				syslog(LOG_WARNING, "Error opening file %s for saving a picture: %s", fpname, strerror(errno));
1542
			else
12 andreas 1543
			{
45 andreas 1544
				write(fd, bt, len);
1545
				close(fd);
12 andreas 1546
			}
1547
		}
1548
	}
1549
 
45 andreas 1550
	if ((frame = id3_tag_findframe(id3, ID3_FRAME_GENRE, 0)) != NULL)
2 andreas 1551
	{
45 andreas 1552
		int i;
1553
 
1554
		for (i = 0; i < (int)frame->nfields; i++)
1555
		{
1556
			field = id3_frame_field(frame, i);
5 andreas 1557
 
45 andreas 1558
			if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
1559
				enc = id3_field_gettextencoding(field);
1560
 
1561
			if (field && checkID3FieldType(field->type))
1562
				break;
1563
		}
1564
 
1565
		if (field != NULL)
1566
			getID3_Field(field, enc, &id3_genre[0], sizeof(id3_genre));
1567
		else
1568
			id3_genre[0] = 0;
1569
 
5 andreas 1570
		if (strchr(id3_genre, '(') && strchr(id3_genre, ')') && isdigit(id3_genre[1]))
1571
		{
45 andreas 1572
			int gen;
5 andreas 1573
 
45 andreas 1574
			gen = atoi(&id3_genre[1]);
5 andreas 1575
 
46 andreas 1576
			if (gen >= 0 && gen < ID3_NR_OF_V1_GENRES)
5 andreas 1577
				strncpy (id3_genre, ID3_v1_genre_description[gen], sizeof(id3_genre));
1578
			else
1579
				strcpy (id3_genre, "Pop");
1580
		}
1581
 
1582
		char_replace(id3_genre, '"', '`');
1583
		char_replace(id3_genre, '\\', ' ');
2 andreas 1584
	}
5 andreas 1585
 
45 andreas 1586
	id3_tag_delete(id3);
1587
	id3_file_close(ifile);
5 andreas 1588
 
12 andreas 1589
	if (!strlen(id3_title) && !strlen(id3_singer))		/* grab title and artist from file name */
1590
	{
1591
		char hv0[256], *p;
43 andreas 1592
		int x, flag;
12 andreas 1593
 
1594
		memset(hv0, 0, sizeof(hv0));
46 andreas 1595
		p = strrchr(file, '/');							/* find the last directory separator */
12 andreas 1596
 
46 andreas 1597
		if (p)											/* If there is a seperator ... */
1598
			strncpy(hv0, p+1, sizeof(hv0)-1);			/* Copy the part after it (this is the file name) */
1599
		else											/* There seems to be no path ... */
1600
			strncpy(hv0, file, sizeof(hv0)-1);			/* Copy the whole file name (this IS the file name) */
12 andreas 1601
 
46 andreas 1602
		if ((p = strchr(hv0, '-')) != NULL)				/* Do we have a dash as seperator? */
1603
		{												/* Yes, so look how many components the file name have */
1604
			char *p2;
1605
 
12 andreas 1606
			*p = 0;
1607
			p++;
1608
 
46 andreas 1609
			if ((p2 = strchr(p, '-')) != NULL)
1610
			{
1611
				*p2 = 0;
1612
				p2++;
1613
				strncpy(id3_album, hv0, sizeof(id3_album) - 1);
1614
				strncpy(id3_singer, p, sizeof(id3_singer) - 1);
1615
				strncpy(id3_title, p2, sizeof(id3_title) - 1);
1616
				trim(id3_album);
1617
			}
1618
			else
1619
			{
1620
				strncpy(id3_singer, hv0, sizeof(id3_singer) - 1);
1621
				strncpy(id3_title, p, sizeof(id3_title) - 1);
1622
			}
1623
 
12 andreas 1624
			if ((p = strrchr(id3_title, '.')) != NULL)
1625
				*p = 0;
1626
 
1627
			trim(id3_singer);
1628
			trim(id3_title);
43 andreas 1629
			/* Make upper / lower case */
46 andreas 1630
			flag = 1;
43 andreas 1631
 
1632
			for (x = 0; x < (int)strlen(id3_singer); x++)
1633
			{
1634
				if (id3_singer[x] == ' ')
1635
					flag = 1;
1636
 
1637
				if (flag && id3_singer[x] != ' ')
1638
				{
1639
					id3_singer[x] = (char)toupper((int)id3_singer[x]);
1640
					flag = 0;
1641
				}
1642
				else
1643
					id3_singer[x] = (char)tolower((int)id3_singer[x]);
1644
			}
1645
 
46 andreas 1646
			flag = 1;
43 andreas 1647
 
1648
			for (x = 0; x < (int)strlen(id3_title); x++)
1649
			{
1650
				if (id3_title[x] == ' ')
1651
					flag = 1;
1652
 
1653
				if (flag && id3_title[x] != ' ')
1654
				{
1655
					id3_title[x] = (char)toupper((int)id3_title[x]);
1656
					flag = 0;
1657
				}
1658
				else
1659
					id3_title[x] = (char)tolower((int)id3_title[x]);
1660
			}
46 andreas 1661
 
1662
			if (p2)
1663
			{
1664
				flag = 1;
1665
 
1666
				for (x = 0; x < (int)strlen(id3_album); x++)
1667
				{
1668
					if (id3_album[x] == ' ')
1669
						flag = 1;
1670
 
1671
					if (flag && id3_album[x] != ' ')
1672
					{
1673
						id3_album[x] = (char)toupper((int)id3_album[x]);
1674
						flag = 0;
1675
					}
1676
					else
1677
						id3_album[x] = (char)tolower((int)id3_album[x]);
1678
				}
1679
			}
12 andreas 1680
		}
1681
		else
1682
		{
1683
			strncpy(id3_title, hv0, sizeof(hv0) - 1);
1684
 
1685
			if ((p = strrchr(id3_title, '.')) != NULL)
1686
				*p = 0;
1687
 
1688
			trim(id3_title);
43 andreas 1689
			flag = 0;
1690
 
1691
			for (x = 0; x < (int)strlen(id3_title); x++)
1692
			{
1693
				if (id3_title[x] == ' ')
1694
					flag = 1;
1695
 
1696
				if (flag && id3_title[x] != ' ')
1697
				{
1698
					id3_title[x] = (char)toupper((int)id3_title[x]);
1699
					flag = 0;
1700
				}
1701
				else
1702
					id3_title[x] = (char)tolower((int)id3_title[x]);
1703
			}
12 andreas 1704
		}
1705
	}
1706
 
43 andreas 1707
	if (!update)
5 andreas 1708
	{
43 andreas 1709
		strcpy (query, "insert into musicdb (\"path\", \"type\", \"title\", \"interpret\", \"album\", \"genre\", \"cover\") values ");
1710
		strcat (query, "(\"");
1711
		strcat (query, file);
1712
		strcat (query, "\", ");
1713
		sprintf(hv0, "%d", fType);
1714
		strcat (query, hv0);
1715
		strcat (query, ", \"");
5 andreas 1716
		strcat (query, id3_title);
43 andreas 1717
		strcat (query, "\", \"");
1718
		strcat (query, id3_singer);
1719
		strcat (query, "\", \"");
1720
		strcat (query, id3_album);
1721
		strcat (query, "\", \"");
1722
		strcat (query, id3_genre);
1723
		strcat (query, "\", \"");
1724
		strcat (query, id3_cover);
1725
		strcat (query, "\")");
5 andreas 1726
	}
1727
	else
1728
	{
43 andreas 1729
		strcpy(query, "update musicdb set \"title\" = \"");
1730
		strcat(query, id3_title);
1731
		strcat(query, "\", \"interpret\" = \"");
1732
		strcat(query, id3_singer);
1733
		strcat(query, "\", \"album\" = \"");
1734
		strcat(query, id3_album);
1735
		strcat(query, "\", \"genre\" = \"");
1736
		strcat(query, id3_genre);
1737
		strcat(query, "\", \"cover\" = \"");
1738
		strcat(query, id3_cover);
1739
		strcat(query, "\" where id = ");
1740
		sprintf(hv0, "%d", id);
1741
		strcat(query, hv0);
5 andreas 1742
	}
1743
 
8 andreas 1744
	if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
2 andreas 1745
	{
5 andreas 1746
		syslog(LOG_DAEMON, "SQL Error  [%s]: %s", query, zErrMsg);
2 andreas 1747
		sqlite3_free(zErrMsg);
1748
		sqlite3_close(db);
1749
		return;
1750
	}
1751
 
1752
	sqlite3_close(db);
12 andreas 1753
	grabFilesCount++;
2 andreas 1754
}
43 andreas 1755
 
45 andreas 1756
int checkID3FieldType(enum id3_field_type ft)
43 andreas 1757
{
45 andreas 1758
	switch (ft)
1759
	{
1760
		case ID3_FIELD_TYPE_LATIN1:
1761
		case ID3_FIELD_TYPE_LATIN1FULL:
1762
		case ID3_FIELD_TYPE_LATIN1LIST:
1763
		case ID3_FIELD_TYPE_STRING:
1764
		case ID3_FIELD_TYPE_STRINGFULL:
1765
		case ID3_FIELD_TYPE_STRINGLIST:
1766
			return 1;
1767
 
1768
		case ID3_FIELD_TYPE_INT8:
1769
		case ID3_FIELD_TYPE_INT16:
1770
		case ID3_FIELD_TYPE_INT24:
1771
		case ID3_FIELD_TYPE_INT32:
1772
		case ID3_FIELD_TYPE_INT32PLUS:
1773
			return 1;
1774
	}
1775
 
1776
	return 0;
1777
}
1778
 
1779
char *getID3_Field(union id3_field *field, enum id3_field_textencoding te, char *ret, int len)
1780
{
44 andreas 1781
char *p, *pg;
45 andreas 1782
char wc[256];
1783
id3_utf8_t *uc;
1784
id3_ucs4_t const *ucs;
1785
id3_latin1_t const *lt1;
43 andreas 1786
iconv_t ic;
1787
int i;
45 andreas 1788
size_t length;
1789
long num;
43 andreas 1790
 
45 andreas 1791
	if (field == NULL)
1792
	{
1793
		if (configs.debug)
1794
			syslog(LOG_DEBUG, "Missing valid field pointer!");
1795
 
1796
		*ret = 0;
1797
		return NULL;
1798
	}
1799
 
43 andreas 1800
	*ret = 0;
1801
 
1802
	switch (te)
1803
	{
45 andreas 1804
		case ID3_FIELD_TEXTENCODING_ISO_8859_1:
1805
			if (field->type == ID3_FIELD_TYPE_LATIN1)
43 andreas 1806
			{
45 andreas 1807
				if ((lt1 = id3_field_getlatin1(field)) == NULL)
1808
				{
1809
					syslog(LOG_WARNING, "Couldn't get latin1 field!");
1810
					return NULL;
1811
				}
1812
			}
1813
			else if (field->type == ID3_FIELD_TYPE_LATIN1FULL)
1814
			{
1815
				if ((lt1 = id3_field_getfulllatin1(field)) == NULL)
1816
				{
1817
					syslog(LOG_WARNING, "Couldn't get full latin1 field!");
1818
					return NULL;
1819
				}
1820
			}
1821
			else if (field->type == ID3_FIELD_TYPE_LATIN1LIST)
1822
			{
1823
				int n;
43 andreas 1824
 
45 andreas 1825
				n = id3_field_getnstrings(field);
1826
 
1827
				for (i = 0; i < n; i++)
1828
				{
1829
					if ((ucs = id3_field_getstrings(field, i)) != NULL)
1830
						break;
1831
				}
1832
 
1833
				if (ucs == NULL)
1834
				{
1835
					syslog(LOG_WARNING, "Couldn't get Latin1 field out of a list of %d", n);
1836
					return NULL;
1837
				}
1838
 
1839
				uc = id3_ucs4_utf8duplicate(ucs);
1840
 
1841
				if (uc)
1842
				{
1843
					strncpy(ret, (char *)uc, len);
1844
					free (uc);
1845
					return ret;
1846
				}
1847
 
1848
				return NULL;
43 andreas 1849
			}
45 andreas 1850
			else
1851
			{
1852
				syslog(LOG_WARNING, "No parseable field type found!");
1853
				return NULL;
1854
			}
43 andreas 1855
 
1856
			if ((ic = iconv_open("UTF-8", "ISO-8859-1")) == (iconv_t)-1)
1857
			{
1858
				syslog(LOG_WARNING, "Error initializing ICONV to convert ASCII into UTF-8: %s", strerror(errno));
1859
				return ret;
1860
			}
1861
 
1862
			length = sizeof(wc);
1863
			p = wc;
45 andreas 1864
			pg = (char *)lt1;
1865
			i = strlen((char *)lt1);
43 andreas 1866
 
44 andreas 1867
			iconv(ic, &pg, (size_t *)&i, &p, &length);
43 andreas 1868
			iconv_close(ic);
1869
			strncpy (ret, wc, len);
1870
		break;
1871
 
45 andreas 1872
		case ID3_FIELD_TEXTENCODING_UTF_16:
1873
		case ID3_FIELD_TEXTENCODING_UTF_16BE:
1874
		case ID3_FIELD_TEXTENCODING_UTF_8:
1875
			if (field->type == ID3_FIELD_TYPE_STRINGFULL)
43 andreas 1876
			{
45 andreas 1877
				if ((ucs = id3_field_getfullstring(field)) == NULL)
1878
				{
1879
					syslog(LOG_WARNING, "Couldn't get full UTF-? field!");
1880
					return NULL;
1881
				}
43 andreas 1882
			}
45 andreas 1883
			else if (field->type == ID3_FIELD_TYPE_STRING)
43 andreas 1884
			{
45 andreas 1885
				if ((ucs = id3_field_getstring(field)) == NULL)
1886
				{
1887
					syslog(LOG_WARNING, "Couldn't get UTF-? field!");
1888
					return NULL;
1889
				}
43 andreas 1890
			}
45 andreas 1891
			else if (field->type == ID3_FIELD_TYPE_STRINGLIST)
43 andreas 1892
			{
45 andreas 1893
				int n;
43 andreas 1894
 
45 andreas 1895
				n = id3_field_getnstrings(field);
43 andreas 1896
 
45 andreas 1897
				for (i = 0; i < n; i++)
1898
				{
1899
					if ((ucs = id3_field_getstrings(field, i)) != NULL)
1900
						break;
1901
				}
43 andreas 1902
 
45 andreas 1903
				if (ucs == NULL)
1904
				{
1905
					syslog(LOG_WARNING, "Couldn't get UTF-? field out of a list of %d", n);
1906
					return NULL;
1907
				}
1908
			}
1909
			else
43 andreas 1910
			{
45 andreas 1911
				syslog(LOG_WARNING, "No parseable field type found!");
44 andreas 1912
				return NULL;
43 andreas 1913
			}
1914
 
45 andreas 1915
			uc = id3_ucs4_utf8duplicate(ucs);
43 andreas 1916
 
45 andreas 1917
			if (uc)
1918
			{
1919
				strncpy(ret, (char *)uc, len);
1920
				free (uc);
1921
			}
43 andreas 1922
		break;
1923
 
45 andreas 1924
		case ID3_FIELD_TYPE_INT8:
1925
		case ID3_FIELD_TYPE_INT16:
1926
		case ID3_FIELD_TYPE_INT24:
1927
		case ID3_FIELD_TYPE_INT32:
1928
		case ID3_FIELD_TYPE_INT32PLUS:
1929
			num = id3_field_getint(field);
1930
			sprintf(ret, "(%ld)", num);
44 andreas 1931
		break;
1932
 
43 andreas 1933
		default:
45 andreas 1934
			if (configs.debug)
1935
				syslog(LOG_DEBUG, "Unkown field encoding: %d", te);
1936
 
1937
			return NULL;
43 andreas 1938
	}
1939
 
1940
	return ret;
1941
}
56 andreas 1942
 
1943
/*
1944
 * This are functions to handle all the handles for connected clients.
1945
 */
1946
void handleInit()
1947
{
1948
int i;
1949
 
1950
	for (i = 0; i < MAX_HANDLES; i++)
1951
		_s1[i] = -1;
1952
}
1953
 
1954
void handleAdd(int fd)
1955
{
1956
int i;
1957
 
1958
	for (i = 0; i < MAX_HANDLES; i++)
1959
	{
1960
		if (_s1[i] == fd)
1961
			return;
1962
	}
1963
 
1964
	for (i = 0; i < MAX_HANDLES; i++)
1965
	{
1966
		if (_s1[i] < 0)
1967
		{
1968
			_s1[i] = fd;
1969
			return;
1970
		}
1971
	}
1972
}
1973
 
1974
void handleDelete(int fd)
1975
{
1976
int i;
1977
 
1978
	for (i = 0; i < MAX_HANDLES; i++)
1979
	{
1980
		if (_s1[i] == fd)
1981
		{
1982
			_s1[i] = -1;
1983
			return;
1984
		}
1985
	}
1986
}
1987
 
1988
void handleWrite(char *txt)
1989
{
1990
	int i;
1991
 
1992
	for (i = 0; i < MAX_HANDLES; i++)
1993
	{
1994
		if (_s1[i] >= 0)
1995
			write(_s1[i], txt, strlen(txt));
1996
	}
1997
}