Subversion Repositories mdb

Rev

Rev 56 | Rev 58 | 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);
57 andreas 352
	pthread_exit(NULL);
14 andreas 353
	return NULL;
2 andreas 354
}
355
 
356
/*
357
 * This function parses the commands given by a network client.
358
 */
359
int parseCommand(int s1, char *cmd)
360
{
4 andreas 361
	char bef[32],par[1024], *p;
362
	char hv0[256];
14 andreas 363
	int i;
4 andreas 364
 
365
	memset(bef, 0, sizeof(bef));
366
	memset(par, 0, sizeof(par));
367
	memset(cmd_error, 0, sizeof(cmd_error));
368
 
369
	if ((p = strchr(cmd, ':')) != NULL)			/* do we have a parameter? */
370
	{
371
		strncpy(bef, cmd, p - cmd);
372
		strncpy(par, p+1, strlen(p) - 2);		/* Cut off the trailing semi colon */
373
	}
374
	else
375
		strncpy(bef, cmd, strlen(cmd) - 1);
376
 
377
	cmd_error[0] = 0;
378
	cmd_message[0] = 0;
36 andreas 379
 
4 andreas 380
	if (!strcasecmp(bef, "RESCAN"))				/* Rescan all directories */
381
	{
382
		int fd;
56 andreas 383
/*		char fname[512]; */
4 andreas 384
 
385
		if (access(configs.Pathfile, R_OK))
386
		{
387
			syslog(LOG_WARNING, "Error accessing file %s", configs.Pathfile);
388
			sprintf(cmd_error, "Error accessing file >%s<", configs.Pathfile);
389
			return FALSE;
390
		}
391
 
392
		if ((fd = open(configs.Pathfile, O_RDONLY)) < 0)
393
		{
394
			syslog(LOG_WARNING, "Error opening file %s: %s", configs.Pathfile, strerror(errno));
395
			sprintf(cmd_error, "Error opening file >%s<", configs.Pathfile);
396
			return FALSE;
397
		}
398
 
399
		i = 0;
400
 
8 andreas 401
		/* Delete the database entries before we start to scan.
5 andreas 402
		 * This makes shure, that there is no double entry.
403
		 */
42 andreas 404
/*		strcpy(fname, configs.home);
9 andreas 405
		strcat(fname, MUSICDB);
5 andreas 406
 
407
		if (!access(fname, R_OK | W_OK))
8 andreas 408
			cleanArchieve(s1, fname);
42 andreas 409
*/
12 andreas 410
		grabFilesCount = 0;
5 andreas 411
		/* Scan all directories in the config file recursievly. */
4 andreas 412
		while (readLine(fd, &hv0[0], sizeof(hv0)) != NULL)
413
		{
12 andreas 414
			grabMusicFiles(s1, hv0);
4 andreas 415
			i++;
416
		}
417
 
12 andreas 418
		sprintf(cmd_message, "SCAN:%d:%d;", i, grabFilesCount);
4 andreas 419
		close(fd);
420
	}
421
 
5 andreas 422
	/*
423
	 * Syntax: LIST:<type>:<start>:<length>;
24 andreas 424
	 *         <type>   TITLE | ARTIST | ALBUM | GENRE | QUEUE | PLAYLIST | USERS
5 andreas 425
	 *         <start>  The line to start from
426
	 *         <length> The number of lines to report
427
	 * 
12 andreas 428
	 * Returns: LINE:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>:<cover>;
5 andreas 429
	 *         <id>     The unique ID of the file. Needed to play the file!
430
	 *         <line>   The line number counting from 1
24 andreas 431
	 * 
432
	 * In case the type is USERS, the following is returned:
433
	 *          USERS:<id>:<line>:<name>;
434
	 *         <name>   The name of the user
5 andreas 435
	 */
436
	if (!strcasecmp(bef, "LIST"))				/* List content */
437
	{
438
		char p_type[16];
439
		int start, length;
440
 
441
		memset(p_type, 0, sizeof(p_type));
442
		remove_string(par, ":", &hv0[0]);
443
		start = strlen(hv0);
444
		hv0[start-1] = 0;
445
		strncpy(p_type, hv0, sizeof(p_type)-1);
446
		start = atoi(par);
447
		remove_string(par, ":", &hv0[0]);
448
		length = atoi(par);
449
		return listSongs(s1, p_type, start, length);
450
	}
451
 
452
	/*
9 andreas 453
	 * Syntax: PLIST:<user>:<playlist>:<start>:<length>;
454
	 *         <user>   The name of the user.
455
	 *         <playlist> The name of the playlist
456
	 *         <start>  The line to start from
457
	 *         <length> The number of lines to report
458
	 * 
13 andreas 459
	 * Returns: LINE:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>:<cover>;
9 andreas 460
	 *         <id>     The unique ID of the file. Needed to play the file!
461
	 *         <line>   The line number counting from 1
462
	 */
463
	if (!strcasecmp(bef, "PLIST"))				/* List content */
464
	{
465
		char user[64], playlist[64], *t;
466
		int start, length, x;
467
 
468
		memset(user, 0, sizeof(user));
469
		memset(playlist, 0, sizeof(playlist));
470
		x = 0;
471
		t = strtok (par, ":");
472
 
473
		while (t)
474
		{
475
			switch(x)
476
			{
477
				case 0: strncpy (user, t, sizeof(user)-1); break;
478
				case 1: strncpy (playlist, t, sizeof(playlist)-1); break;
479
				case 2: start = atoi(t); break;
480
				case 3: length = atoi(t); break;
481
			}
482
 
483
			x++;
484
			t = strtok (NULL, ":");
485
		}
486
 
11 andreas 487
		t = urldecode(user);
488
 
489
		if (t)
490
		{
491
			strncpy(user, t, sizeof(user)-1);
492
			free(t);
493
		}
494
 
495
		t = urldecode(playlist);
496
 
497
		if (t)
498
		{
499
			strncpy(playlist, t, sizeof(playlist)-1);
500
			free(t);
501
		}
502
 
9 andreas 503
		return listUserPlaylist(s1, user, playlist, start, length);
504
	}
505
 
506
	/*
13 andreas 507
	 * Syntax: LISTFOLDER:<type>:<name>:<start>:<length>;
508
	 *         <type>   TITLE | ARTIST | ALBUM | GENRE
509
	 *         <start>  The line to start from
510
	 *         <length> The number of lines to report
511
	 * 
512
	 * Returns: LINE:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>:<cover>;
513
	 *         <id>     The unique ID of the file. Needed to play the file!
514
	 *         <line>   The line number counting from 1
515
	 */
516
	if (!strcasecmp(bef, "LISTFOLDER"))			/* List the content of a folder */
517
	{
518
		char p_type[64], name[256], *t, *type, *nm;
519
		int x, start, length;
520
 
521
		x = 0;
522
		t = strtok(par, ":");
523
 
524
		while (t)
525
		{
526
			switch(x)
527
			{
528
				case 0: strncpy(p_type, t, sizeof(p_type)); break;
529
				case 1: strncpy(name, t, sizeof(name)); break;
530
				case 2: start = atoi(t); break;
531
				case 3: length = atoi(t); break;
532
			}
533
 
534
			x++;
535
			t = strtok(NULL, ":");
536
		}
537
 
538
		type = urldecode(p_type);
539
		nm = urldecode(name);
540
 
541
		if (type)
542
		{
543
			strncpy(p_type, type, sizeof(p_type)-1);
544
			free(type);
545
		}
546
 
547
		if (nm)
548
		{
549
			strncpy(name, nm, sizeof(name)-1);
550
			free(nm);
551
		}
552
 
553
		return listFolderContent(s1, p_type, name, start, length);
554
	}
555
 
556
	/*
5 andreas 557
	 * Syntax: FOLDER:<type>:<start>:<length>;
558
	 *         <type>   TITLE | ARTIST | ALBUM | GENRE
559
	 *         <start>  The line to start from
560
	 *         <length> The number of lines to report
561
	 * 
25 andreas 562
	 * Returns: FOLDER:<type>:<id>:<line>:<content>;
5 andreas 563
	 *         <id>       The unique ID of the file. Needed to play the file!
564
	 *         <line>     The line number counting from 1
565
	 *         <content>  The name of the folder
566
	 */
567
	if (!strcasecmp(bef, "FOLDER"))				/* List folders */
568
	{
569
		char p_type[16];
570
		int start, length;
571
 
572
		memset(p_type, 0, sizeof(p_type));
573
		remove_string(par, ":", &hv0[0]);
574
		start = strlen(hv0);
575
		hv0[start-1] = 0;
576
		strncpy(p_type, hv0, sizeof(p_type)-1);
577
		start = atoi(par);
578
		remove_string(par, ":", &hv0[0]);
579
		length = atoi(par);
580
		return listFolders(s1, p_type, start, length);
581
	}
582
 
6 andreas 583
	/*
14 andreas 584
	 * This command moves one or more files into the queue.
585
	 * If the queue already contains some files, the files are appended to
18 andreas 586
	 * the end of the queue. The command makes sure, that there are no
587
	 * duplicate entries in the queue.
14 andreas 588
	 * If the name of a folder is omitted, all files of the specified types
589
	 * are appended.
590
	 * If a folder name was specified, only the files of the folder are
591
	 * appended.
592
	 *
593
	 * Syntax: ADD:<type>:<folder>;
594
	 *          <type>     ID | TITLE | ARTIST | ALBUM | GENRE | PLAYLIST
595
	 *          <folder>   The name of a folder. This is optional.
596
	 */
597
	if (!strcasecmp(bef, "ADD"))
598
	{
599
		int x;
600
		char *t, p_type[32], folder[64], *type, *fld;
601
 
602
		memset(p_type, 0, sizeof(p_type));
603
		memset(folder, 0, sizeof(folder));
604
		x = 0;
605
		t = strtok(par, ":");
606
 
607
		while (t)
608
		{
609
			switch (x)
610
			{
611
				case 0: strncpy(p_type, t, sizeof(p_type)-1); break;
612
				case 1: strncpy(folder, t, sizeof(folder)-1); break;
613
			}
614
 
615
			x++;
616
			t = strtok(NULL, ":");
617
		}
618
 
619
		type = urldecode(p_type);
620
		fld = urldecode(folder);
621
 
622
		if (type)
623
		{
624
			strncpy(p_type, type, sizeof(p_type));
625
			free(type);
626
		}
627
 
628
		if (fld)
629
		{
630
			strncpy(folder, fld, sizeof(folder));
631
			free(fld);
632
		}
633
 
18 andreas 634
		if (strlen(p_type) && strlen(folder))
14 andreas 635
			appendToQueue(s1, p_type, folder);
636
		else
637
		{
18 andreas 638
			strcpy(cmd_message, "ERROR:ADD:Missing type or folder name;");
639
			return FALSE;
14 andreas 640
		}
641
	}
642
 
643
	/*
644
	 * This command moves one or more files into the queue and starts to play
645
	 * them.
646
	 * If the player is already playing, the file(s) are appended to the queue.
36 andreas 647
	 * If a play request for the queue is detected and the ID is 0, then the
648
	 * first or, if random is selected a random entry of the que is played. If
649
	 * the ID is grater than 0, the wanted ID is played even if random is
650
	 * selected.
49 andreas 651
	 * The type DIRECT means to play a particular file without change the current
652
	 * queue. If something is already playing, it's stopped and the file the
653
	 * parameter <what> points to is played. With DIRECT the parameter <what>
654
	 * has to contain a valid ID number.
14 andreas 655
	 *
6 andreas 656
	 * Syntax: PLAY:<type>:<what>;
49 andreas 657
	 *         <type>     ID | TITLE | ARTIST | ALBUM | GENRE | PLAYLIST | QUEUE | DIRECT
7 andreas 658
	 *         <what>     <id> or name of folder or name of playlist
6 andreas 659
	 * 
7 andreas 660
	 * Returns: PLAYING:<id>:<title>:<artist>:<album>:<genre>;
49 andreas 661
	 *          POSITION:<time>:<time left>:<total>;
6 andreas 662
	 */
663
	if (!strcasecmp(bef, "PLAY"))				/* Plays one or more files */
664
	{
665
		char p_type[16];
666
		char what[256];
667
		int i;
668
 
669
		memset(p_type, 0, sizeof(p_type));
36 andreas 670
		memset(hv0, 0, sizeof(hv0));
6 andreas 671
		remove_string(par, ":", &hv0[0]);
672
		i = strlen(hv0);
37 andreas 673
 
36 andreas 674
		if (i > 0)
675
		{
676
			hv0[i-1] = 0;
677
			strncpy(p_type, hv0, sizeof(p_type) - 1);
678
		}
679
		else
680
			strcpy(p_type, "QUEUE");
681
 
49 andreas 682
		if (strcasecmp(p_type, "ID") && strcasecmp(p_type, "DIRECT"))	/* If the type is not "ID" and not "DIRECT" then */
683
		{																/* decode the text */
9 andreas 684
			char *w;
7 andreas 685
 
9 andreas 686
			w = urldecode(par);
687
 
688
			if (w)
689
			{
690
				strncpy (what, w, sizeof(what));
691
				i = sizeof(what) - 1;
692
				what[i] = 0;
693
				free (w);
694
			}
695
			else
696
				strncpy(what, par, sizeof(what));
697
		}
36 andreas 698
		else if (strlen(par))
699
			strncpy(what, par, sizeof(what));
9 andreas 700
		else
36 andreas 701
			strcpy(what, "0");
37 andreas 702
 
49 andreas 703
		if (playerActive && playStatus == PLAY_STATUS_PAUSE && strcasecmp(p_type, "DIRECT"))
8 andreas 704
			nextCommand = PLAY_PLAY;
49 andreas 705
		else if (playerActive && strcasecmp(p_type, "DIRECT"))		/* Only if type is not DIRECT */
7 andreas 706
			appendToQueue(s1, p_type, what);
707
		else
6 andreas 708
		{
7 andreas 709
			_playPars.s1 = s1;
9 andreas 710
			strcpy(_playPars.type, p_type);
711
			strcpy(_playPars.what, what);
49 andreas 712
 
713
			if (!strcasecmp(p_type, "DIRECT") && playerActive)
714
			{
50 andreas 715
				qstackAdd(atoi(what));
716
				nextCommand = PLAY_STACK;
717
			}
51 andreas 718
			else if (!playerActive)
50 andreas 719
			{
57 andreas 720
				int e;
50 andreas 721
				/* start a new thread to play the file(s) */
57 andreas 722
				if ((e = pthread_create(&pthr_play, NULL, pthr_playfile, (void *)&_playPars)) != 0)
49 andreas 723
				{
57 andreas 724
					switch (e)
725
					{
726
						case EAGAIN: strcpy (hv0, "Insufficient resources to create another thread."); break;
727
						case EINVAL: strcpy (hv0, "Invalid settings in \"attr\"."); break;
728
						case EPERM:  strcpy (hv0, "No permission to set the scheduling policy and parameters specified in \"attr\"."); break;
729
						default:     sprintf (hv0, "Unknown error %d.", e);
730
					}
731
 
732
					syslog (LOG_DAEMON,"Creation of thread \"pthr_play\" failed: %s", hv0);
50 andreas 733
					strcpy(cmd_error, "Error playing a file!");
49 andreas 734
					return FALSE;
735
				}
736
			}
6 andreas 737
		}
738
	}
739
 
7 andreas 740
	if (!strcasecmp(bef, "STOP") && playerActive)				/* Stop the currently playing file */
741
		nextCommand = PLAY_STOP;
742
 
743
	if (!strcasecmp(bef, "PAUSE") && playerActive)				/* Pause the currently playing file */
744
		nextCommand = PLAY_PAUSE;
745
 
40 andreas 746
	if (!strcasecmp(bef, "PLAYPAUSE"))							/* Play/Pause the currently playing file, */
747
	{															/* or play one in the queue if there are any. */
748
		if (playerActive && (playStatus == PLAY_STATUS_PAUSE || playStatus == PLAY_STATUS_PLAY))
749
			nextCommand = PLAY_PLAYPAUSE;
750
		else if (playerActive && playStatus == PLAY_STATUS_STOP)
751
			nextCommand = PLAY_PLAY;
752
		else if (!playerActive)
753
		{
754
			_playPars.s1 = s1;
755
			strcpy(_playPars.type, "QUEUE");					/* Select the queue to play. */
756
			strcpy(_playPars.what, "0");						/* Play the first or a random file, if random is selected, in the queue. */
757
			/* start a new thread to play the file(s) */
758
			if (pthread_create(&pthr_play, NULL, pthr_playfile, (void *)&_playPars) != 0)
759
			{
760
				syslog (LOG_DAEMON,"Create of thread \"pthr_play\" failed!");
761
				strcpy(cmd_error, "Error playing a file!");
762
				return FALSE;
763
			}
764
		}
765
	}
7 andreas 766
 
767
	if (!strcasecmp(bef, "FORWARD") && playerActive)			/* Fast forward */
768
		nextCommand = PLAY_FWD;
769
 
770
	if (!strcasecmp(bef, "REWIND") && playerActive)				/* Fast rewind */
771
		nextCommand = PLAY_REW;
772
 
773
	if (!strcasecmp(bef, "SKIPFWD") && playerActive)			/* Skip to next file in queue */
774
		nextCommand = PLAY_SKIP_FWD;
775
 
776
	if (!strcasecmp(bef, "SKIPREW") && playerActive)			/* Skip to previous file in queue */
777
		nextCommand = PLAY_SKIP_REW;
778
 
11 andreas 779
	if (!strcasecmp(bef, "RANDOM"))								/* Toggle random play */
780
	{
781
		playerRandom = !playerRandom;
782
		sprintf(cmd_message, "RANDOM:%s;", playerRandom ? "TRUE" : "FALSE");
783
	}
784
 
785
	if (!strcasecmp(bef, "REPEAT"))								/* Toggle repeat */
786
	{
787
		playerRepeat = !playerRepeat;
788
		sprintf(cmd_message, "REPEAT:%s;", playerRepeat ? "TRUE" : "FALSE");
789
	}
790
 
7 andreas 791
	/*
14 andreas 792
	 * This command loads a user. If the user doesn't exist it creates
793
	 * the user. In this case the name of a playlist is needed.
794
	 * If the user already exist, the name of a playlist is optional.
795
	 * 
796
	 * If this command is called without the name of a playlist and the
797
	 * user doesn't exist, an error occurs.
798
	 *
799
	 * This command should only be called when a new playlist is to be
800
	 * saved. After this command the command "SAVEQUEUE" should be called.
801
	 *
8 andreas 802
	 * Syntax: USER:<name>:[<playlist>];
803
	 *         <name>     the name of the user.
804
	 *         <playlist> the name of the playlist; This is optional.
7 andreas 805
	 */
806
	if (!strcasecmp(bef, "USER"))								/* In case the user doesn't exist, create a new one. Activate this user */
807
	{
8 andreas 808
		char user[64], playlist[64], *t;
809
		int x;
7 andreas 810
 
8 andreas 811
		memset(user, 0, sizeof(user));
812
		memset(playlist, 0, sizeof(playlist));
813
		x = 0;
814
		t = strtok (par, ":");
7 andreas 815
 
8 andreas 816
		while (t)
817
		{
818
			char *hv;
819
 
820
			switch(x)
821
			{
822
				case 0:
823
					strncpy (user, t, sizeof(user));
824
					user[63] = 0;
825
					hv = urldecode(user);
826
 
827
					if (hv != NULL)
828
					{
829
						strcpy(user, hv);
830
						free(hv);
831
					}
832
				break;
833
 
834
				case 1:
835
					strncpy (playlist, t, sizeof(playlist));
836
					playlist[63] = 0;
837
					hv = urldecode(playlist);
838
 
839
					if (hv != NULL)
840
					{
841
						strcpy(playlist, hv);
842
						free(hv);
843
					}
844
				break;
845
			}
846
 
847
			x++;
848
			t = strtok(NULL, ":");
849
		}
850
 
851
		if (strlen(user) && strlen(playlist))
852
		{
853
			if (!createUser(s1, user, playlist))
854
				return FALSE;
855
 
856
			return selectUser(s1, user);
857
		}
858
		else if (strlen(user))
859
			return selectUser(s1, user);
860
		else
861
		{
862
			strcpy (cmd_error, "Missing the username and the name of the playlist");
863
			return FALSE;
864
		}
7 andreas 865
	}
866
 
8 andreas 867
	/*
868
	 * Syntax: SAVEQUEUE:<user>:<playlist>;
869
	 *         <user>     The name of the user. If this is empty, the actual
870
	 *                    user is taken, if any. If there's no user selected,
871
	 *                    an error occurs.
872
	 *         <playlist> The name of the playlist. If the playlist already
873
	 *                    exist, it'll be overwritten.
874
	 * 
875
	 * Return: TRANSFERED:<num>;
876
	 *         <num>      The number of entries transfered.
877
	 */
14 andreas 878
	if (!strcasecmp(bef, "SAVEQUEUE"))			/* Saves the content of the queue into a playlist */
8 andreas 879
	{
880
		char user[64], playlist[64];
881
		char *t;
882
		int x;
883
 
25 andreas 884
		if (!queueTotal)
885
		{
886
			readQueue();
887
 
888
			if (!queueTotal)
889
			{
890
				strcpy(cmd_error, "Queue is empty");
891
				return FALSE;
892
			}
893
		}
894
 
8 andreas 895
		x = 0;
896
		t = strtok(par, ":");
897
 
898
		while (t)
899
		{
900
			char *hv;
901
 
902
			switch(x)
903
			{
904
				case 0:
905
					strncpy (user, t, sizeof(user));
906
					user[63] = 0;
907
					hv = urldecode(user);
908
 
909
					if (hv != NULL)
910
					{
911
						strcpy(user, hv);
912
						free(hv);
913
					}
914
				break;
915
 
916
				case 1:
917
					strncpy (playlist, t, sizeof(playlist));
918
					playlist[63] = 0;
919
					hv = urldecode(playlist);
920
 
921
					if (hv != NULL)
922
					{
923
						strcpy(playlist, hv);
924
						free(hv);
925
					}
926
				break;
927
			}
928
 
929
			x++;
930
			t = strtok(NULL, ":");
931
		}
932
 
933
		if (!strlen(user) && userchain != NULL)
934
			strcpy(user, userchain->uname);
25 andreas 935
		else if (!selectUser(s1, user) && strlen(user) && strlen(playlist))
936
		{
937
			if (!createUser(s1, user, playlist))
938
				return FALSE;
939
 
940
			return QueueToPlaylist(s1, user, playlist);
941
		}
8 andreas 942
		else if (!selectUser(s1, user))
943
		{
944
			strcpy (cmd_error, "No or invalid user");
945
			return FALSE;
946
		}
947
 
948
		if (!strlen(playlist))
949
		{
950
			strcpy (cmd_error, "No or invalid playlist");
951
			return FALSE;
952
		}
953
 
954
		if (!QueueToPlaylist(s1, user, playlist))
955
			return FALSE;
956
	}
957
 
958
	/*
959
	 * Syntax: DELETE:<type>:<id>;
13 andreas 960
	 *      <type>     PTITLE | QUEUE | PLAYLIST
961
	 *                 PTITLE: Delete one title from a playlist
962
	 *                 QUEUE: If the given ID is -1, then the whole
963
	 *                        queue is deleted. Otherwise only a title
964
	 *                        in the queue.
965
	 *                 PLAYLIST Delete a whole playlist from a user
8 andreas 966
	 * 
13 andreas 967
	 *      <id>       -1 = Delete whole QUEUE or PLAYLIST
968
	 *                 >= 0 Delete only a particular entry
8 andreas 969
	 */
9 andreas 970
	if (!strcasecmp(bef, "DELETE"))
8 andreas 971
	{
972
		char p_type[32], *t;
973
		int id, x;
974
 
975
		x = 0;
976
		t = strtok(par, ":");
977
 
978
		while (t)
979
		{
980
			switch (x)
981
			{
982
				case 0:
983
					strncpy(p_type, t, sizeof(p_type));
984
					p_type[31] = 0;
985
				break;
986
 
987
				case 1: id = atoi(t); break;
988
			}
989
 
990
			x++;
991
			t = strtok(NULL, ":");
992
		}
993
 
994
		if (!strcasecmp(p_type, "QUEUE"))
995
			return deleteQueue(s1, id);
996
		else if (!strcasecmp(p_type, "PTITLE"))
997
			return deletePlaylistEntry(s1, id);
998
		else if (!strcasecmp(p_type, "PLAYLIST"))
999
			return deletePlaylist(s1, id);
1000
		else
1001
			return FALSE;
1002
	}
1003
 
1004
	/*
9 andreas 1005
	 * Syntax: SEARCH:<type>:<expression>:<start>:<lines>;
8 andreas 1006
	 *            <type>   TITLE | ARTIST | ALBUM | GENRE
1007
	 *            <expression> This is what to search for
9 andreas 1008
	 *            <start>  the data to start to
1009
	 *            <lines>  number of lines to return
1010
	 * 
18 andreas 1011
	 * Return: SEARCH:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>;
8 andreas 1012
	 */
1013
	if (!strcasecmp(bef, "SEARCH"))
1014
	{
9 andreas 1015
		int start, lines, x;
1016
		char *t, p_type[32], expr[128];
1017
 
1018
		memset(p_type, 0, sizeof(p_type));
1019
		memset(expr, 0, sizeof(expr));
1020
		start = lines = 0;
1021
		x = 0;
1022
		t = strtok (par, ":");
1023
 
1024
		while (t)
1025
		{
11 andreas 1026
			char *hv;
1027
 
9 andreas 1028
			switch(x)
1029
			{
11 andreas 1030
				case 0: strncpy(p_type, t, sizeof(p_type)-1); break;
9 andreas 1031
 
1032
				case 1:
11 andreas 1033
					strncpy(expr, t, sizeof(expr)-1);
1034
					hv = urldecode(expr);
1035
 
1036
					if (hv != NULL)
1037
					{
1038
						strncpy(expr, hv, sizeof(expr) - 1);
1039
						free(hv);
1040
					}
9 andreas 1041
				break;
1042
 
1043
				case 2: start = atoi(t); break;
1044
				case 3: lines = atoi(t); break;
1045
			}
1046
 
1047
			x++;
1048
			t = strtok (NULL, ":");
1049
		}
13 andreas 1050
 
9 andreas 1051
		if (x != 4 || !strlen(p_type) || !strlen(expr) || !lines || !start)
1052
		{
1053
			strcpy (cmd_error, "Missing one ore more parameters");
1054
			return FALSE;
1055
		}
1056
 
1057
		return searchTerm(s1, p_type, expr, start, lines);
8 andreas 1058
	}
1059
 
28 andreas 1060
	/*
1061
	 * This command reread the config file.
1062
	 */
1063
	if (!strcasecmp(bef, "RESET"))
1064
	{
1065
		if (playerActive)
1066
			nextCommand = PLAY_STOP;
1067
 
1068
		readConf();
1069
	}
1070
 
4 andreas 1071
	return TRUE;		/* cmd was OK */
2 andreas 1072
}
1073
 
1074
/*
8 andreas 1075
 * This function opens the database and deletes the whole archive of scanned
1076
 * MP3 files.
1077
 * This is called when the command "RESCAN" was detected. When this function
1078
 * is finished, the database is ready for new data.
1079
 */
1080
int cleanArchieve(int s1, char *fname)
1081
{
1082
	char query[1024];
1083
	sqlite3 *db;
1084
	char *zErrMsg = 0;
1085
	int rc;
1086
 
1087
	rc = sqlite3_open(fname, &db);
1088
 
1089
	if (rc)
1090
	{
1091
		syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
1092
		strcpy(query, "ERROR:MDB:Error opening database;");
1093
		write (s1, query, strlen(query));
1094
		return FALSE;
1095
	}
1096
 
29 andreas 1097
	strcpy (query, "delete from \"main\".\"playlists\"");
1098
 
1099
	if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
1100
	{
1101
		syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
1102
		sqlite3_free(zErrMsg);
1103
		sqlite3_close(db);
1104
		strcpy(query, "ERROR:MDB:SQL error;");
1105
		write (s1, query, strlen(query));
1106
		return FALSE;
1107
	}
1108
 
8 andreas 1109
	strcpy (query, "delete from \"main\".\"musicdb\"");
1110
 
1111
	if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
1112
	{
1113
		syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
1114
		sqlite3_free(zErrMsg);
1115
		sqlite3_close(db);
1116
		strcpy(query, "ERROR:MDB:SQL error;");
1117
		write (s1, query, strlen(query));
1118
		return FALSE;
1119
	}
1120
 
1121
	sqlite3_close(db);
1122
	return TRUE;
1123
}
1124
 
1125
/*
2 andreas 1126
 * Callback function of directory scan.
1127
 * This function desides whether a directory entry will be treated as a
1128
 * music file or not.
1129
 */
1130
int musicFilter(const struct dirent *dir)
1131
{
4 andreas 1132
	if (dir == NULL)
1133
		return 0;
2 andreas 1134
 
4 andreas 1135
	if (dir->d_type == DT_REG)
1136
	{
32 andreas 1137
		char *p = getFileExtension(dir->d_name);
1138
 
1139
		if (p != NULL && (strcasecmp(p, "mp3") == 0 || strcasecmp(p, "flac") == 0))
1140
			return 1;
4 andreas 1141
	}
1142
	else if (dir->d_type == DT_DIR)
1143
	{
1144
		if (strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0)
1145
			return 1;
1146
	}
1147
 
2 andreas 1148
	return 0;
1149
}
1150
 
1151
/*
1152
 * This function scans a directory for music files. If it finds some file name
1153
 * who is a possible music file, it gives it to a function to evaluate and
1154
 * store it.
1155
 */
12 andreas 1156
int grabMusicFiles(int s1, char *dir)
2 andreas 1157
{
1158
	struct dirent **namelist;
1159
	int n;
1160
 
1161
	if (dir == NULL || strlen(dir) == 0)
1162
		return FALSE;
1163
 
1164
	n = scandir(dir, &namelist, musicFilter, alphasort);
1165
 
1166
	if (n < 0)
1167
	{
4 andreas 1168
		syslog(LOG_DAEMON, "Error scanning directory %s: %s", dir, strerror(errno));
2 andreas 1169
		return FALSE;
1170
	}
1171
	else
1172
	{ 
1173
		while(n--)
1174
		{
42 andreas 1175
			char hv0[1024];
1176
			int len;
1177
 
5 andreas 1178
			strcpy(hv0, dir);
1179
			strcat(hv0, "/");
42 andreas 1180
			len = sizeof(hv0) - strlen(hv0) - 1;
5 andreas 1181
 
42 andreas 1182
			if (len > 0)
1183
				strncat(hv0, namelist[n]->d_name, len);
1184
			else
1185
			{
1186
				free(namelist[n]);
1187
				continue;
1188
			}
1189
 
4 andreas 1190
			if (namelist[n]->d_type == DT_DIR)
12 andreas 1191
				grabMusicFiles(s1, hv0);
1192
			else
4 andreas 1193
			{
1194
				evaluateMusicFile(hv0);
1195
 
12 andreas 1196
				if (!(grabFilesCount % 100))
1197
				{
1198
					sprintf(hv0, "SCANNING:%d;", grabFilesCount);
1199
					write (s1, hv0, strlen(hv0));
1200
				}
1201
			}
1202
 
2 andreas 1203
			free(namelist[n]); 
1204
		} 
1205
 
1206
		free(namelist); 
14 andreas 1207
	}
1208
 
1209
	return TRUE;
2 andreas 1210
}
1211
 
1212
void evaluateMusicFile(char *file)
1213
{
42 andreas 1214
sqlite3 *db;
1215
sqlite3_stmt *res;
1216
char *zErrMsg = 0;
43 andreas 1217
int rc, fType, update, id;
42 andreas 1218
char fname[256], hv0[64];
1219
char query[8192];
45 andreas 1220
struct id3_tag *id3;
1221
struct id3_file *ifile;
1222
struct id3_frame *frame;
1223
union id3_field *field;
1224
id3_byte_t const *bt;
42 andreas 1225
char id3_title[256];
1226
char id3_singer[256];
1227
char id3_album[256];
1228
char id3_genre[256];
1229
char id3_cover[256];
56 andreas 1230
char /* *title, *singer, *album,*/ *ext;
45 andreas 1231
enum id3_field_type enc;
3 andreas 1232
 
2 andreas 1233
	if (access(file, R_OK))
1234
		return;
1235
 
45 andreas 1236
	bt = NULL;
1237
 
2 andreas 1238
	strcpy(fname, configs.home);
9 andreas 1239
	strcat(fname, MUSICDB);
4 andreas 1240
 
5 andreas 1241
	if (!access(fname, R_OK | W_OK))
2 andreas 1242
		file_found = TRUE;
4 andreas 1243
	else						/* create the directory, if it doesn't exitst */
1244
	{
1245
		if (access(configs.home, R_OK | W_OK))
1246
		{
1247
			if (mkdir(configs.home, 0775) != 0)
1248
			{
1249
				syslog(LOG_WARNING, "Error creating directory %s: %s", configs.home, strerror(errno));
1250
				return;
1251
			}
1252
			else
1253
				syslog(LOG_INFO, "Directory %s was created.", configs.home);
1254
		}
5 andreas 1255
 
1256
		sprintf(query, "%s/Covers", configs.home);
1257
 
1258
		if (access(query, R_OK | W_OK))
1259
		{
1260
			if (mkdir(query, 0775) != 0)
1261
				syslog(LOG_WARNING, "Error creating directory %s: %s", query, strerror(errno));
1262
			else
1263
				syslog(LOG_INFO, "Directory %s was created.", query);
1264
		}
4 andreas 1265
	}
2 andreas 1266
 
1267
	rc = sqlite3_open(fname, &db);
1268
 
1269
	if (rc)
1270
	{
1271
		syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
1272
		return;
1273
	}
1274
 
1275
	if (!file_found)
1276
	{
32 andreas 1277
		/* id        autoincrement unique index
1278
		 * path      path to file containing an MP3 or FLAC file
1279
		 * type      1 = MP3, 2 = FLAC
1280
		 * title     title of song
1281
		 * interpret singer
1282
		 * album     the album the is from
1283
		 * genre     genre of the song
1284
		 * cover     name of file containing the cover
1285
		 */
2 andreas 1286
		strcpy(query, "create table \"main\".\"musicdb\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,");
32 andreas 1287
		strcat(query, "\"path\" TEXT NOT NULL, \"type\" INTEGER, \"title\" TEXT, \"interpret\" TEXT,");
5 andreas 1288
		strcat(query, "\"album\" TEXT, \"genre\" TEXT, \"cover\" TEXT)");
8 andreas 1289
		rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg);
2 andreas 1290
 
1291
		if (rc != SQLITE_OK)
1292
		{
5 andreas 1293
			syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
2 andreas 1294
			sqlite3_free(zErrMsg);
1295
			sqlite3_close(db);
1296
			return;
1297
		}
4 andreas 1298
		else
7 andreas 1299
			syslog(LOG_INFO, "Database \"musicdb\" was successfully created.");
1300
 
1301
		strcpy(query, "CREATE TABLE \"main\".\"users\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT,");
1302
		strcat(query, "\"uname\" TEXT NOT NULL, \"playlist\" TEXT)");
8 andreas 1303
		rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg);
7 andreas 1304
 
1305
		if (rc != SQLITE_OK)
1306
		{
1307
			syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
1308
			sqlite3_free(zErrMsg);
1309
			sqlite3_close(db);
1310
			return;
1311
		}
1312
		else
1313
			syslog(LOG_INFO, "Database \"users\" was successfully created.");
8 andreas 1314
 
1315
		strcpy (query, "CREATE TABLE \"main\".\"playlists\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,");
1316
		strcat (query, "\"userid\" INTEGER NOT NULL, \"musicid\" INTEGER NOT NULL)");
1317
 
1318
		rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg);
1319
 
1320
		if (rc != SQLITE_OK)
1321
		{
1322
			syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
1323
			sqlite3_free(zErrMsg);
1324
			sqlite3_close(db);
1325
			return;
1326
		}
1327
		else
1328
			syslog(LOG_INFO, "Database \"users\" was successfully created.");
2 andreas 1329
	}
1330
 
4 andreas 1331
	/* Make sure, the file is not already in the database */
45 andreas 1332
	sprintf(query, "select id, path, cover from musicdb where path = \"%s\"", file);
2 andreas 1333
 
8 andreas 1334
	if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
2 andreas 1335
	{
8 andreas 1336
		syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
2 andreas 1337
		sqlite3_close(db);
1338
		return;
1339
	}
42 andreas 1340
 
8 andreas 1341
	rc = sqlite3_step(res);
45 andreas 1342
	id3_cover[0] = 0;
42 andreas 1343
 
8 andreas 1344
	if (rc == SQLITE_ROW)			/* If we've a row, the path exists and we'll not add it again */
43 andreas 1345
	{								/* Instead we'll update the information */
1346
		update = 1;
1347
		id = sqlite3_column_int(res, 0);
45 andreas 1348
 
1349
		if ((ext = (char *)sqlite3_column_text(res, 2)) == NULL)
1350
		{
1351
			syslog(LOG_WARNING, "Error getting path of picture from database!");
1352
			id3_cover[0] = 0;
1353
		}
1354
		else
1355
			strncpy(&id3_cover[0], ext, sizeof(id3_cover));
2 andreas 1356
	}
43 andreas 1357
	else
1358
		update = 0;
2 andreas 1359
 
43 andreas 1360
	sqlite3_finalize(res);
32 andreas 1361
	/* Check the file extension and decide whether it's an MP3 or FLAC file. */
1362
	ext = getFileExtension(file);
1363
 
1364
	if (ext != NULL)
1365
	{
1366
		if (strcasecmp(ext, "flac") == 0)
1367
			fType = FILE_TYPE_FLAC;
1368
		else
1369
			fType = FILE_TYPE_MP3;
1370
	}
1371
	else
1372
		fType = FILE_TYPE_MP3;
1373
 
4 andreas 1374
	/* Open the file and get the ID3 tag. */
45 andreas 1375
	if ((ifile = id3_file_open(file, ID3_FILE_MODE_READONLY)) == NULL)
1376
	{
1377
		syslog(LOG_WARNING, "Error opening file %s!", file);
1378
		sqlite3_close(db);
1379
		return;
1380
	}
1381
 
1382
	if ((id3 = id3_file_tag(ifile)) == NULL)
1383
	{
1384
		syslog(LOG_WARNING, "Error initializing MP3 file %s", file);
1385
		sqlite3_close(db);
1386
		return;
1387
	}
1388
 
2 andreas 1389
	memset(&id3_title, 0, sizeof(id3_title));
1390
	memset(&id3_singer, 0, sizeof(id3_singer));
1391
	memset(&id3_album, 0, sizeof(id3_album));
1392
	memset(&id3_genre, 0, sizeof(id3_genre));
45 andreas 1393
 
1394
	if (!strlen(id3_cover))
1395
		memset(&id3_cover, 0, sizeof(id3_cover));
1396
 
4 andreas 1397
	/* Get the required frames */
45 andreas 1398
	if ((frame = id3_tag_findframe(id3, ID3_FRAME_TITLE, 0)) != NULL)
2 andreas 1399
	{
45 andreas 1400
		int i;
1401
 
1402
		for (i = 0; i < (int)frame->nfields; i++)
1403
		{
1404
			field = id3_frame_field(frame, i);
1405
 
1406
			if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
1407
				enc = id3_field_gettextencoding(field);
1408
 
1409
			if (field && checkID3FieldType(field->type))
1410
				break;
1411
		}
1412
 
1413
		getID3_Field(field, enc, &id3_title[0], sizeof(id3_title));
5 andreas 1414
		char_replace(id3_title, '"', '`');
1415
		char_replace(id3_title, '\\', ' ');
2 andreas 1416
	}
1417
 
45 andreas 1418
	if ((frame = id3_tag_findframe(id3, ID3_FRAME_ARTIST, 0)) != NULL)
2 andreas 1419
	{
45 andreas 1420
		int i;
1421
 
1422
		for (i = 0; i < (int)frame->nfields; i++)
1423
		{
1424
			field = id3_frame_field(frame, i);
1425
 
1426
			if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
1427
				enc = id3_field_gettextencoding(field);
1428
 
1429
			if (field && checkID3FieldType(field->type))
1430
				break;
1431
		}
1432
 
1433
		getID3_Field(field, enc, &id3_singer[0], sizeof(id3_singer));
5 andreas 1434
		char_replace(id3_singer, '"', '`');
1435
		char_replace(id3_singer, '\\', ' ');
2 andreas 1436
	}
1437
 
45 andreas 1438
	if ((frame = id3_tag_findframe(id3, ID3_FRAME_ALBUM, 0)) != NULL)
2 andreas 1439
	{
45 andreas 1440
		int i;
1441
 
1442
		for (i = 0; i < (int)frame->nfields; i++)
1443
		{
1444
			field = id3_frame_field(frame, i);
1445
 
1446
			if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
1447
				enc = id3_field_gettextencoding(field);
1448
 
1449
			if (field && checkID3FieldType(field->type))
1450
				break;
1451
		}
1452
 
1453
		getID3_Field(field, enc, &id3_album[0], sizeof(id3_album));
5 andreas 1454
		char_replace(id3_album, '"', '`');
1455
		char_replace(id3_album, '\\', ' ');
2 andreas 1456
	}
1457
 
45 andreas 1458
	if ((frame = id3_tag_findframe(id3, "APIC", 0)) != NULL)
12 andreas 1459
	{
1460
		char fpname[512], uid[128], hv0[256];
56 andreas 1461
/*		struct id3_frame *fpframe; */
45 andreas 1462
		int i, fd, np;
1463
		long unsigned len;
12 andreas 1464
 
40 andreas 1465
		memset(uid, 0, sizeof(uid));
12 andreas 1466
		strcpy(fpname, configs.home);
1467
		strcat(fpname, "/Covers");
1468
 
1469
		if (access(fpname, R_OK | W_OK | X_OK))
1470
			mkdir(fpname, 0775);
1471
 
45 andreas 1472
		if (!strlen(id3_cover))
12 andreas 1473
		{
14 andreas 1474
			char *md5;
12 andreas 1475
 
22 andreas 1476
			if ((md5 = str2hash(file, (int)strlen(file))) != NULL)
14 andreas 1477
			{
1478
				sprintf(uid, "Picture-%s", md5);
1479
				free(md5);
1480
			}
1481
			else
1482
				sprintf(uid, "Picture-%ld", random());
1483
		}
1484
 
12 andreas 1485
		strcat(fpname, "/");
45 andreas 1486
		len = 0;
1487
		np = 0;			/* 1 = a new picture was found */
1488
		bt = NULL;
12 andreas 1489
		/* Get the picture and store it into a file */
45 andreas 1490
		for (i = 0; i < (int)frame->nfields; i++)
1491
		{
1492
			if ((field = id3_frame_field(frame, i)) != NULL)
1493
			{
1494
				id3_latin1_t const *lt1;
40 andreas 1495
 
45 andreas 1496
				switch (field->type)
1497
				{
1498
					case ID3_FIELD_TYPE_LATIN1:			// MIME type
1499
						if ((lt1 = id3_field_getlatin1(field)) != NULL)
1500
							strncpy (&hv0[0], (char *)lt1, sizeof(hv0)-1);
1501
					break;
1502
 
1503
					case ID3_FIELD_TYPE_LATIN1FULL:
1504
						if ((lt1 = id3_field_getfulllatin1(field)) != NULL)
1505
							strncpy (&hv0[0], (char *)lt1, sizeof(hv0)-1);
1506
					break;
1507
 
1508
					case ID3_FIELD_TYPE_BINARYDATA:		// The picture
1509
						bt = id3_field_getbinarydata(field, &len);
1510
						np = 1;
1511
					break;
1512
				}
1513
			}
1514
		}
1515
 
1516
		if (!strlen(id3_cover) && np)
12 andreas 1517
		{
45 andreas 1518
			if (strstr(hv0, "jpeg") != NULL)
1519
				strcat(uid, ".jpg");
1520
			else if (strstr(hv0, "png") != NULL)
1521
				strcat(uid, ".png");
1522
			else if (strstr(hv0, "gif") != NULL)
1523
				strcat(uid, ".gif");
1524
			else if (strstr(hv0, "tiff") != NULL)
1525
				strcat(uid, ".tiff");
1526
			else
1527
			{
1528
				char *pos;
12 andreas 1529
 
45 andreas 1530
				if ((pos = strrchr(hv0, '/')) != NULL)
1531
				{
1532
					strcat(uid, ".");
1533
					strcat(uid, pos+1);
1534
				}
1535
			}
1536
 
1537
			strcat(fpname, uid);
1538
			strcpy (id3_cover, uid);
1539
		}
1540
		else if (strlen(id3_cover) && !np)
1541
		{
1542
			strcat (fpname, id3_cover);
1543
			unlink(fpname);
1544
		}
1545
		else if (strlen(id3_cover))
1546
			strcat (fpname, id3_cover);
1547
 
1548
		if (np)
1549
		{
1550
			if ((fd = open(fpname, O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
1551
				syslog(LOG_WARNING, "Error opening file %s for saving a picture: %s", fpname, strerror(errno));
1552
			else
12 andreas 1553
			{
45 andreas 1554
				write(fd, bt, len);
1555
				close(fd);
12 andreas 1556
			}
1557
		}
1558
	}
1559
 
45 andreas 1560
	if ((frame = id3_tag_findframe(id3, ID3_FRAME_GENRE, 0)) != NULL)
2 andreas 1561
	{
45 andreas 1562
		int i;
1563
 
1564
		for (i = 0; i < (int)frame->nfields; i++)
1565
		{
1566
			field = id3_frame_field(frame, i);
5 andreas 1567
 
45 andreas 1568
			if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
1569
				enc = id3_field_gettextencoding(field);
1570
 
1571
			if (field && checkID3FieldType(field->type))
1572
				break;
1573
		}
1574
 
1575
		if (field != NULL)
1576
			getID3_Field(field, enc, &id3_genre[0], sizeof(id3_genre));
1577
		else
1578
			id3_genre[0] = 0;
1579
 
5 andreas 1580
		if (strchr(id3_genre, '(') && strchr(id3_genre, ')') && isdigit(id3_genre[1]))
1581
		{
45 andreas 1582
			int gen;
5 andreas 1583
 
45 andreas 1584
			gen = atoi(&id3_genre[1]);
5 andreas 1585
 
46 andreas 1586
			if (gen >= 0 && gen < ID3_NR_OF_V1_GENRES)
5 andreas 1587
				strncpy (id3_genre, ID3_v1_genre_description[gen], sizeof(id3_genre));
1588
			else
1589
				strcpy (id3_genre, "Pop");
1590
		}
1591
 
1592
		char_replace(id3_genre, '"', '`');
1593
		char_replace(id3_genre, '\\', ' ');
2 andreas 1594
	}
5 andreas 1595
 
45 andreas 1596
	id3_tag_delete(id3);
1597
	id3_file_close(ifile);
5 andreas 1598
 
12 andreas 1599
	if (!strlen(id3_title) && !strlen(id3_singer))		/* grab title and artist from file name */
1600
	{
1601
		char hv0[256], *p;
43 andreas 1602
		int x, flag;
12 andreas 1603
 
1604
		memset(hv0, 0, sizeof(hv0));
46 andreas 1605
		p = strrchr(file, '/');							/* find the last directory separator */
12 andreas 1606
 
46 andreas 1607
		if (p)											/* If there is a seperator ... */
1608
			strncpy(hv0, p+1, sizeof(hv0)-1);			/* Copy the part after it (this is the file name) */
1609
		else											/* There seems to be no path ... */
1610
			strncpy(hv0, file, sizeof(hv0)-1);			/* Copy the whole file name (this IS the file name) */
12 andreas 1611
 
46 andreas 1612
		if ((p = strchr(hv0, '-')) != NULL)				/* Do we have a dash as seperator? */
1613
		{												/* Yes, so look how many components the file name have */
1614
			char *p2;
1615
 
12 andreas 1616
			*p = 0;
1617
			p++;
1618
 
46 andreas 1619
			if ((p2 = strchr(p, '-')) != NULL)
1620
			{
1621
				*p2 = 0;
1622
				p2++;
1623
				strncpy(id3_album, hv0, sizeof(id3_album) - 1);
1624
				strncpy(id3_singer, p, sizeof(id3_singer) - 1);
1625
				strncpy(id3_title, p2, sizeof(id3_title) - 1);
1626
				trim(id3_album);
1627
			}
1628
			else
1629
			{
1630
				strncpy(id3_singer, hv0, sizeof(id3_singer) - 1);
1631
				strncpy(id3_title, p, sizeof(id3_title) - 1);
1632
			}
1633
 
12 andreas 1634
			if ((p = strrchr(id3_title, '.')) != NULL)
1635
				*p = 0;
1636
 
1637
			trim(id3_singer);
1638
			trim(id3_title);
43 andreas 1639
			/* Make upper / lower case */
46 andreas 1640
			flag = 1;
43 andreas 1641
 
1642
			for (x = 0; x < (int)strlen(id3_singer); x++)
1643
			{
1644
				if (id3_singer[x] == ' ')
1645
					flag = 1;
1646
 
1647
				if (flag && id3_singer[x] != ' ')
1648
				{
1649
					id3_singer[x] = (char)toupper((int)id3_singer[x]);
1650
					flag = 0;
1651
				}
1652
				else
1653
					id3_singer[x] = (char)tolower((int)id3_singer[x]);
1654
			}
1655
 
46 andreas 1656
			flag = 1;
43 andreas 1657
 
1658
			for (x = 0; x < (int)strlen(id3_title); x++)
1659
			{
1660
				if (id3_title[x] == ' ')
1661
					flag = 1;
1662
 
1663
				if (flag && id3_title[x] != ' ')
1664
				{
1665
					id3_title[x] = (char)toupper((int)id3_title[x]);
1666
					flag = 0;
1667
				}
1668
				else
1669
					id3_title[x] = (char)tolower((int)id3_title[x]);
1670
			}
46 andreas 1671
 
1672
			if (p2)
1673
			{
1674
				flag = 1;
1675
 
1676
				for (x = 0; x < (int)strlen(id3_album); x++)
1677
				{
1678
					if (id3_album[x] == ' ')
1679
						flag = 1;
1680
 
1681
					if (flag && id3_album[x] != ' ')
1682
					{
1683
						id3_album[x] = (char)toupper((int)id3_album[x]);
1684
						flag = 0;
1685
					}
1686
					else
1687
						id3_album[x] = (char)tolower((int)id3_album[x]);
1688
				}
1689
			}
12 andreas 1690
		}
1691
		else
1692
		{
1693
			strncpy(id3_title, hv0, sizeof(hv0) - 1);
1694
 
1695
			if ((p = strrchr(id3_title, '.')) != NULL)
1696
				*p = 0;
1697
 
1698
			trim(id3_title);
43 andreas 1699
			flag = 0;
1700
 
1701
			for (x = 0; x < (int)strlen(id3_title); x++)
1702
			{
1703
				if (id3_title[x] == ' ')
1704
					flag = 1;
1705
 
1706
				if (flag && id3_title[x] != ' ')
1707
				{
1708
					id3_title[x] = (char)toupper((int)id3_title[x]);
1709
					flag = 0;
1710
				}
1711
				else
1712
					id3_title[x] = (char)tolower((int)id3_title[x]);
1713
			}
12 andreas 1714
		}
1715
	}
1716
 
43 andreas 1717
	if (!update)
5 andreas 1718
	{
43 andreas 1719
		strcpy (query, "insert into musicdb (\"path\", \"type\", \"title\", \"interpret\", \"album\", \"genre\", \"cover\") values ");
1720
		strcat (query, "(\"");
1721
		strcat (query, file);
1722
		strcat (query, "\", ");
1723
		sprintf(hv0, "%d", fType);
1724
		strcat (query, hv0);
1725
		strcat (query, ", \"");
5 andreas 1726
		strcat (query, id3_title);
43 andreas 1727
		strcat (query, "\", \"");
1728
		strcat (query, id3_singer);
1729
		strcat (query, "\", \"");
1730
		strcat (query, id3_album);
1731
		strcat (query, "\", \"");
1732
		strcat (query, id3_genre);
1733
		strcat (query, "\", \"");
1734
		strcat (query, id3_cover);
1735
		strcat (query, "\")");
5 andreas 1736
	}
1737
	else
1738
	{
43 andreas 1739
		strcpy(query, "update musicdb set \"title\" = \"");
1740
		strcat(query, id3_title);
1741
		strcat(query, "\", \"interpret\" = \"");
1742
		strcat(query, id3_singer);
1743
		strcat(query, "\", \"album\" = \"");
1744
		strcat(query, id3_album);
1745
		strcat(query, "\", \"genre\" = \"");
1746
		strcat(query, id3_genre);
1747
		strcat(query, "\", \"cover\" = \"");
1748
		strcat(query, id3_cover);
1749
		strcat(query, "\" where id = ");
1750
		sprintf(hv0, "%d", id);
1751
		strcat(query, hv0);
5 andreas 1752
	}
1753
 
8 andreas 1754
	if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
2 andreas 1755
	{
5 andreas 1756
		syslog(LOG_DAEMON, "SQL Error  [%s]: %s", query, zErrMsg);
2 andreas 1757
		sqlite3_free(zErrMsg);
1758
		sqlite3_close(db);
1759
		return;
1760
	}
1761
 
1762
	sqlite3_close(db);
12 andreas 1763
	grabFilesCount++;
2 andreas 1764
}
43 andreas 1765
 
45 andreas 1766
int checkID3FieldType(enum id3_field_type ft)
43 andreas 1767
{
45 andreas 1768
	switch (ft)
1769
	{
1770
		case ID3_FIELD_TYPE_LATIN1:
1771
		case ID3_FIELD_TYPE_LATIN1FULL:
1772
		case ID3_FIELD_TYPE_LATIN1LIST:
1773
		case ID3_FIELD_TYPE_STRING:
1774
		case ID3_FIELD_TYPE_STRINGFULL:
1775
		case ID3_FIELD_TYPE_STRINGLIST:
1776
			return 1;
1777
 
1778
		case ID3_FIELD_TYPE_INT8:
1779
		case ID3_FIELD_TYPE_INT16:
1780
		case ID3_FIELD_TYPE_INT24:
1781
		case ID3_FIELD_TYPE_INT32:
1782
		case ID3_FIELD_TYPE_INT32PLUS:
1783
			return 1;
1784
	}
1785
 
1786
	return 0;
1787
}
1788
 
1789
char *getID3_Field(union id3_field *field, enum id3_field_textencoding te, char *ret, int len)
1790
{
44 andreas 1791
char *p, *pg;
45 andreas 1792
char wc[256];
1793
id3_utf8_t *uc;
1794
id3_ucs4_t const *ucs;
1795
id3_latin1_t const *lt1;
43 andreas 1796
iconv_t ic;
1797
int i;
45 andreas 1798
size_t length;
1799
long num;
43 andreas 1800
 
45 andreas 1801
	if (field == NULL)
1802
	{
1803
		if (configs.debug)
1804
			syslog(LOG_DEBUG, "Missing valid field pointer!");
1805
 
1806
		*ret = 0;
1807
		return NULL;
1808
	}
1809
 
43 andreas 1810
	*ret = 0;
1811
 
1812
	switch (te)
1813
	{
45 andreas 1814
		case ID3_FIELD_TEXTENCODING_ISO_8859_1:
1815
			if (field->type == ID3_FIELD_TYPE_LATIN1)
43 andreas 1816
			{
45 andreas 1817
				if ((lt1 = id3_field_getlatin1(field)) == NULL)
1818
				{
1819
					syslog(LOG_WARNING, "Couldn't get latin1 field!");
1820
					return NULL;
1821
				}
1822
			}
1823
			else if (field->type == ID3_FIELD_TYPE_LATIN1FULL)
1824
			{
1825
				if ((lt1 = id3_field_getfulllatin1(field)) == NULL)
1826
				{
1827
					syslog(LOG_WARNING, "Couldn't get full latin1 field!");
1828
					return NULL;
1829
				}
1830
			}
1831
			else if (field->type == ID3_FIELD_TYPE_LATIN1LIST)
1832
			{
1833
				int n;
43 andreas 1834
 
45 andreas 1835
				n = id3_field_getnstrings(field);
1836
 
1837
				for (i = 0; i < n; i++)
1838
				{
1839
					if ((ucs = id3_field_getstrings(field, i)) != NULL)
1840
						break;
1841
				}
1842
 
1843
				if (ucs == NULL)
1844
				{
1845
					syslog(LOG_WARNING, "Couldn't get Latin1 field out of a list of %d", n);
1846
					return NULL;
1847
				}
1848
 
1849
				uc = id3_ucs4_utf8duplicate(ucs);
1850
 
1851
				if (uc)
1852
				{
1853
					strncpy(ret, (char *)uc, len);
1854
					free (uc);
1855
					return ret;
1856
				}
1857
 
1858
				return NULL;
43 andreas 1859
			}
45 andreas 1860
			else
1861
			{
1862
				syslog(LOG_WARNING, "No parseable field type found!");
1863
				return NULL;
1864
			}
43 andreas 1865
 
1866
			if ((ic = iconv_open("UTF-8", "ISO-8859-1")) == (iconv_t)-1)
1867
			{
1868
				syslog(LOG_WARNING, "Error initializing ICONV to convert ASCII into UTF-8: %s", strerror(errno));
1869
				return ret;
1870
			}
1871
 
1872
			length = sizeof(wc);
1873
			p = wc;
45 andreas 1874
			pg = (char *)lt1;
1875
			i = strlen((char *)lt1);
43 andreas 1876
 
44 andreas 1877
			iconv(ic, &pg, (size_t *)&i, &p, &length);
43 andreas 1878
			iconv_close(ic);
1879
			strncpy (ret, wc, len);
1880
		break;
1881
 
45 andreas 1882
		case ID3_FIELD_TEXTENCODING_UTF_16:
1883
		case ID3_FIELD_TEXTENCODING_UTF_16BE:
1884
		case ID3_FIELD_TEXTENCODING_UTF_8:
1885
			if (field->type == ID3_FIELD_TYPE_STRINGFULL)
43 andreas 1886
			{
45 andreas 1887
				if ((ucs = id3_field_getfullstring(field)) == NULL)
1888
				{
1889
					syslog(LOG_WARNING, "Couldn't get full UTF-? field!");
1890
					return NULL;
1891
				}
43 andreas 1892
			}
45 andreas 1893
			else if (field->type == ID3_FIELD_TYPE_STRING)
43 andreas 1894
			{
45 andreas 1895
				if ((ucs = id3_field_getstring(field)) == NULL)
1896
				{
1897
					syslog(LOG_WARNING, "Couldn't get UTF-? field!");
1898
					return NULL;
1899
				}
43 andreas 1900
			}
45 andreas 1901
			else if (field->type == ID3_FIELD_TYPE_STRINGLIST)
43 andreas 1902
			{
45 andreas 1903
				int n;
43 andreas 1904
 
45 andreas 1905
				n = id3_field_getnstrings(field);
43 andreas 1906
 
45 andreas 1907
				for (i = 0; i < n; i++)
1908
				{
1909
					if ((ucs = id3_field_getstrings(field, i)) != NULL)
1910
						break;
1911
				}
43 andreas 1912
 
45 andreas 1913
				if (ucs == NULL)
1914
				{
1915
					syslog(LOG_WARNING, "Couldn't get UTF-? field out of a list of %d", n);
1916
					return NULL;
1917
				}
1918
			}
1919
			else
43 andreas 1920
			{
45 andreas 1921
				syslog(LOG_WARNING, "No parseable field type found!");
44 andreas 1922
				return NULL;
43 andreas 1923
			}
1924
 
45 andreas 1925
			uc = id3_ucs4_utf8duplicate(ucs);
43 andreas 1926
 
45 andreas 1927
			if (uc)
1928
			{
1929
				strncpy(ret, (char *)uc, len);
1930
				free (uc);
1931
			}
43 andreas 1932
		break;
1933
 
45 andreas 1934
		case ID3_FIELD_TYPE_INT8:
1935
		case ID3_FIELD_TYPE_INT16:
1936
		case ID3_FIELD_TYPE_INT24:
1937
		case ID3_FIELD_TYPE_INT32:
1938
		case ID3_FIELD_TYPE_INT32PLUS:
1939
			num = id3_field_getint(field);
1940
			sprintf(ret, "(%ld)", num);
44 andreas 1941
		break;
1942
 
43 andreas 1943
		default:
45 andreas 1944
			if (configs.debug)
1945
				syslog(LOG_DEBUG, "Unkown field encoding: %d", te);
1946
 
1947
			return NULL;
43 andreas 1948
	}
1949
 
1950
	return ret;
1951
}
56 andreas 1952
 
1953
/*
1954
 * This are functions to handle all the handles for connected clients.
1955
 */
1956
void handleInit()
1957
{
1958
int i;
1959
 
1960
	for (i = 0; i < MAX_HANDLES; i++)
1961
		_s1[i] = -1;
1962
}
1963
 
1964
void handleAdd(int fd)
1965
{
1966
int i;
1967
 
1968
	for (i = 0; i < MAX_HANDLES; i++)
1969
	{
1970
		if (_s1[i] == fd)
1971
			return;
1972
	}
1973
 
1974
	for (i = 0; i < MAX_HANDLES; i++)
1975
	{
1976
		if (_s1[i] < 0)
1977
		{
1978
			_s1[i] = fd;
1979
			return;
1980
		}
1981
	}
1982
}
1983
 
1984
void handleDelete(int fd)
1985
{
1986
int i;
1987
 
1988
	for (i = 0; i < MAX_HANDLES; i++)
1989
	{
1990
		if (_s1[i] == fd)
1991
		{
1992
			_s1[i] = -1;
1993
			return;
1994
		}
1995
	}
1996
}
1997
 
1998
void handleWrite(char *txt)
1999
{
2000
	int i;
2001
 
2002
	for (i = 0; i < MAX_HANDLES; i++)
2003
	{
2004
		if (_s1[i] >= 0)
2005
			write(_s1[i], txt, strlen(txt));
2006
	}
2007
}