1 /* libmpdclient
2    (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
3    This project's homepage is: http://www.musicpd.org
4 
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
8 
9    - Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11 
12    - Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15 
16    - Neither the name of the Music Player Daemon nor the names of its
17    contributors may be used to endorse or promote products derived from
18    this software without specific prior written permission.
19 
20    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
24    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 
33 
34 #include "libmpdclient.h"
35 #include <errno.h>
36 #include <ctype.h>
37 #include <sys/types.h>
38 #include <stdio.h>
39 #include <sys/param.h>
40 
41 #include <string.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
45 #include <limits.h>
46 
47 #include <glib.h>
48 
49 #ifdef WIN32
50 #  include <ws2tcpip.h>
51 #  include <winsock.h>
52 #else
53 #  include <netinet/in.h>
54 #  include <arpa/inet.h>
55 #  include <sys/socket.h>
56 #  include <netdb.h>
57 #endif
58 
59 /* (bits+1)/3 (plus the sign character) */
60 #define INTLEN      ((sizeof(int)       * CHAR_BIT + 1) / 3 + 1)
61 #define LONGLONGLEN ((sizeof(long long) * CHAR_BIT + 1) / 3 + 1)
62 
63 #define COMMAND_LIST    1
64 #define COMMAND_LIST_OK 2
65 
66 #ifndef MPD_NO_GAI
67 #  ifdef AI_ADDRCONFIG
68 #    define MPD_HAVE_GAI
69 #  endif
70 #endif
71 
72 #ifndef WIN32
73 #include <sys/un.h>
74 #endif
75 
76 #ifndef MSG_DONTWAIT
77 #  define MSG_DONTWAIT 0
78 #endif
79 
80 #ifdef WIN32
81 #  define SELECT_ERRNO_IGNORE   (errno == WSAEINTR || errno == WSAEINPROGRESS)
82 #  define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
83 #else
84 #  define SELECT_ERRNO_IGNORE   (errno == EINTR)
85 #  define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
86 #  define winsock_dll_error(c)  0
87 #  define closesocket(s)        close(s)
88 #  define WSACleanup()          do { /* nothing */ } while (0)
89 #endif
90 
91 #ifdef WIN32
winsock_dll_error(mpd_Connection * connection)92 static int winsock_dll_error(mpd_Connection *connection)
93 {
94 	WSADATA wsaData;
95 	if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
96 			LOBYTE(wsaData.wVersion) != 2 ||
97 			HIBYTE(wsaData.wVersion) != 2 ) {
98 		strcpy(connection->errorStr,
99 		       "Could not find usable WinSock DLL.");
100 		connection->error = MPD_ERROR_SYSTEM;
101 		return 1;
102 	}
103 	return 0;
104 }
105 
do_connect_fail(mpd_Connection * connection,const struct sockaddr * serv_addr,int addrlen)106 static int do_connect_fail(mpd_Connection *connection,
107                            const struct sockaddr *serv_addr, int addrlen)
108 {
109 	int iMode = 1; /* 0 = blocking, else non-blocking */
110 	if (connect(connection->sock, serv_addr, addrlen) == SOCKET_ERROR)
111 		return 1;
112 	ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
113 	return 0;
114 }
115 #elif defined(__solaris__) || defined(__sun__)
do_connect_fail(mpd_Connection * connection,const struct sockaddr * serv_addr,int addrlen)116 static int do_connect_fail(mpd_Connection *connection,
117 		const struct sockaddr *serv_addr, int addrlen)
118 {
119 	int flags;
120 	if (connect(connection->sock, serv_addr, addrlen) < 0)
121 		return 1;
122 	flags = fcntl(connection->sock, F_GETFL, 0);
123 	fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
124 	return 0;
125 }
126 #else /* !WIN32 (sane operating systems) */
do_connect_fail(mpd_Connection * connection,const struct sockaddr * serv_addr,int addrlen)127 static int do_connect_fail(mpd_Connection *connection,
128                            const struct sockaddr *serv_addr, int addrlen)
129 {
130 	int flags, res,valopt;
131 	struct timeval tv;
132 	fd_set myset;
133 	flags = fcntl(connection->sock, F_GETFL, 0);
134 	fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
135 	/*if (connect(connection->sock, serv_addr, addrlen) < 0)
136 		return 1;*/
137     /* Do a non blocking connect */
138     res = connect(connection->sock, serv_addr, addrlen);
139     if (res < 0) {
140         if (errno == EINPROGRESS) {
141             tv.tv_sec = connection->timeout.tv_sec;
142             tv.tv_usec = connection->timeout.tv_usec;
143             FD_ZERO(&myset);
144             FD_SET(connection->sock, &myset);
145             if (select(connection->sock+1, NULL, &myset, NULL, &tv) > 0) {
146                 socklen_t lon;
147                 lon = sizeof(int);
148                 /* Check for errors */
149                 getsockopt(connection->sock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
150                 if (valopt) {
151                     fprintf(stderr, "Error in connection() %d - %s\n", valopt, strerror(valopt));
152                     return 1;
153                 }
154             }
155             else {
156                 /* Connecting timed out. */
157                 fprintf(stderr, "Timeout or error() %d - %s\n", valopt, strerror(valopt));
158                 return 1;
159             }
160         }
161         else {
162             /* Failed to connect */
163             fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno));
164             return 1;
165         }
166     }
167 
168 	return 0;
169 }
170 #endif /* !WIN32 */
171 
172 #ifdef MPD_HAVE_GAI
mpd_connect(mpd_Connection * connection,const char * host,int port,float timeout)173 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
174                        float timeout)
175 {
176 	int error;
177 	char service[INTLEN+1];
178 	struct addrinfo hints;
179 	struct addrinfo *res = NULL;
180 	struct addrinfo *addrinfo = NULL;
181 
182 	/**
183 	 * Setup hints
184 	 */
185 	hints.ai_flags     = AI_ADDRCONFIG;
186 	hints.ai_family    = AF_UNSPEC;
187 	hints.ai_socktype  = SOCK_STREAM;
188 	hints.ai_protocol  = IPPROTO_TCP;
189 	hints.ai_addrlen   = 0;
190 	hints.ai_addr      = NULL;
191 	hints.ai_canonname = NULL;
192 	hints.ai_next      = NULL;
193 
194 	snprintf(service, sizeof(service), "%i", port);
195 
196 	error = getaddrinfo(host, service, &hints, &addrinfo);
197 
198 	if (error) {
199 		snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
200 		         "host \"%s\" not found: %s",
201 		         host, gai_strerror(error));
202 		connection->error = MPD_ERROR_UNKHOST;
203 		return -1;
204 	}
205 
206 	for (res = addrinfo; res; res = res->ai_next) {
207 		/* create socket */
208 		if (connection->sock >= 0)
209 			closesocket(connection->sock);
210 		connection->sock = socket(res->ai_family, SOCK_STREAM,
211 		                          res->ai_protocol);
212 		if (connection->sock < 0) {
213 			snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
214 			         "problems creating socket: %s",
215 			         strerror(errno));
216 			connection->error = MPD_ERROR_SYSTEM;
217 			freeaddrinfo(addrinfo);
218 			return -1;
219 		}
220 
221 		mpd_setConnectionTimeout(connection, timeout);
222 
223 		/* connect stuff */
224  		if (do_connect_fail(connection,
225 		                    res->ai_addr, res->ai_addrlen)) {
226  			/* try the next address */
227  			closesocket(connection->sock);
228  			connection->sock = -1;
229  			continue;
230 		}
231 
232 		break;
233 	}
234 
235 	freeaddrinfo(addrinfo);
236 
237 	if (connection->sock < 0) {
238 		snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
239 		         "problems connecting to \"%s\" on port %i: %s",
240 		         host, port, strerror(errno));
241 		connection->error = MPD_ERROR_CONNPORT;
242 
243 		return -1;
244 	}
245 
246 	return 0;
247 }
248 #else /* !MPD_HAVE_GAI */
mpd_connect(mpd_Connection * connection,const char * host,int port,float timeout)249 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
250                        float timeout)
251 {
252 	struct hostent * he;
253 	struct sockaddr * dest;
254 	int destlen;
255 	struct sockaddr_in sin;
256 
257 	if(!(he=gethostbyname(host))) {
258 		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
259 				"host \"%s\" not found",host);
260 		connection->error = MPD_ERROR_UNKHOST;
261 		return -1;
262 	}
263 
264 	memset(&sin,0,sizeof(struct sockaddr_in));
265 	/*dest.sin_family = he->h_addrtype;*/
266 	sin.sin_family = AF_INET;
267 	sin.sin_port = htons(port);
268 
269 	switch(he->h_addrtype) {
270 	case AF_INET:
271 		memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
272 				he->h_length);
273 		dest = (struct sockaddr *)&sin;
274 		destlen = sizeof(struct sockaddr_in);
275 		break;
276 	default:
277 		strcpy(connection->errorStr,"address type is not IPv4");
278 		connection->error = MPD_ERROR_SYSTEM;
279 		return -1;
280 		break;
281 	}
282 
283 	if (connection->sock >= 0)
284 		closesocket(connection->sock);
285 	if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
286 		strcpy(connection->errorStr,"problems creating socket");
287 		connection->error = MPD_ERROR_SYSTEM;
288 		return -1;
289 	}
290 
291 	mpd_setConnectionTimeout(connection,timeout);
292 
293 	/* connect stuff */
294 	if (do_connect_fail(connection, dest, destlen)) {
295 		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
296 				"problems connecting to \"%s\" on port"
297 				" %i",host,port);
298 		connection->error = MPD_ERROR_CONNPORT;
299 		return -1;
300 	}
301 
302 	return 0;
303 }
304 #endif /* !MPD_HAVE_GAI */
305 
306 char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
307 {
308 	"Artist",
309 	"Album",
310 	"Title",
311 	"Track",
312 	"Name",
313 	"Genre",
314 	"Date",
315 	"Composer",
316 	"Performer",
317 	"Comment",
318 	"Disc",
319 	"Filename",
320     "AlbumArtist",
321 	"Any"
322 };
323 
mpd_sanitizeArg(const char * arg)324 static char * mpd_sanitizeArg(const char * arg) {
325 	size_t i;
326 	char * ret;
327 	register const char *c;
328 	register char *rc;
329 
330 	/* instead of counting in that loop above, just
331 	 * use a bit more memory and half running time
332 	 */
333 	ret = malloc(strlen(arg) * 2 + 1);
334 
335 	c = arg;
336 	rc = ret;
337 	for(i = strlen(arg)+1; i != 0; --i) {
338 		if(*c=='"' || *c=='\\')
339 			*rc++ = '\\';
340 		*(rc++) = *(c++);
341 	}
342 
343 	return ret;
344 }
345 
mpd_newReturnElement(const char * name,const char * value)346 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
347 {
348 	mpd_ReturnElement* ret = g_slice_new(mpd_ReturnElement);
349 
350 	ret->name = strdup(name);
351 	ret->value = strdup(value);
352 
353 	return ret;
354 }
355 
mpd_freeReturnElement(mpd_ReturnElement * re)356 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
357 	free(re->name);
358 	free(re->value);
359 	g_slice_free(mpd_ReturnElement, re);
360 }
361 
mpd_setConnectionTimeout(mpd_Connection * connection,float timeout)362 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
363 	connection->timeout.tv_sec = (int)timeout;
364 	connection->timeout.tv_usec = (int)(timeout*1e6 -
365 	                                    connection->timeout.tv_sec*1000000 +
366 					    0.5);
367 }
368 
mpd_parseWelcome(mpd_Connection * connection,const char * host,int port,char * output)369 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
370                             char * output) {
371 	char * tmp;
372 	char * test;
373 	int i;
374 
375 	if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
376 		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
377 				"mpd not running on port %i on host \"%s\"",
378 				port,host);
379 		connection->error = MPD_ERROR_NOTMPD;
380 		return 1;
381 	}
382 
383 	tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
384 
385 	for(i=0;i<3;i++) {
386 		if(tmp) connection->version[i] = strtol(tmp,&test,10);
387 
388 		if (!tmp || (test[0] != '.' && test[0] != '\0')) {
389 			snprintf(connection->errorStr,
390 			         MPD_ERRORSTR_MAX_LENGTH,
391 			         "error parsing version number at "
392 			         "\"%s\"",
393 			         &output[strlen(MPD_WELCOME_MESSAGE)]);
394 			connection->error = MPD_ERROR_NOTMPD;
395 			return 1;
396 		}
397 		tmp = ++test;
398 	}
399 
400 	return 0;
401 }
402 
403 #ifndef WIN32
mpd_connect_un(mpd_Connection * connection,const char * host,float timeout)404 static int mpd_connect_un(mpd_Connection * connection,
405 			  const char * host, float timeout)
406 {
407 	int error, flags;
408 	size_t path_length;
409 	struct sockaddr_un saun;
410 
411 	path_length = strlen(host);
412 	if (path_length >= sizeof(saun.sun_path)) {
413 		strcpy(connection->errorStr, "unix socket path is too long");
414 		connection->error = MPD_ERROR_UNKHOST;
415 		return -1;
416 	}
417 
418 	saun.sun_family = AF_UNIX;
419 	memcpy(saun.sun_path, host, path_length + 1);
420 
421 	connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
422 	if (connection->sock < 0) {
423 		strcpy(connection->errorStr, "problems creating socket");
424 		connection->error = MPD_ERROR_SYSTEM;
425 		return -1;
426 	}
427 
428 	mpd_setConnectionTimeout(connection, timeout);
429 
430 	flags = fcntl(connection->sock, F_GETFL, 0);
431 	fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
432 
433 	error = connect(connection->sock, (struct sockaddr*)&saun, sizeof(saun));
434 	if (error < 0) {
435 		/* try the next address family */
436 		close(connection->sock);
437 		connection->sock = 0;
438 
439 		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
440 			 "problems connecting to \"%s\": %s",
441 			 host, strerror(errno));
442 		connection->error = MPD_ERROR_CONNPORT;
443 		return -1;
444 	}
445 
446 	return 0;
447 }
448 #endif /* WIN32 */
449 
mpd_newConnection(const char * host,int port,float timeout)450 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
451 	int err;
452 	char * rt;
453 	char * output =  NULL;
454 	mpd_Connection * connection = g_slice_new0(mpd_Connection);
455 	struct timeval tv;
456 	fd_set fds;
457 	strcpy(connection->buffer,"");
458 	connection->sock = -1;
459 	strcpy(connection->errorStr,"");
460 
461 	if (winsock_dll_error(connection))
462 		return connection;
463 
464 #ifndef WIN32
465 	if (host[0] == '/')
466 		err = mpd_connect_un(connection, host, timeout);
467 	else
468 #endif
469 		err = mpd_connect(connection, host, port, timeout);
470 	if (err < 0)
471 		return connection;
472 
473 	while(!(rt = strstr(connection->buffer,"\n"))) {
474 		tv.tv_sec = connection->timeout.tv_sec;
475 		tv.tv_usec = connection->timeout.tv_usec;
476 		FD_ZERO(&fds);
477 		FD_SET(connection->sock,&fds);
478 		if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
479 			int readed;
480 			readed = recv(connection->sock,
481 					&(connection->buffer[connection->buflen]),
482 					MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
483 			if(readed<=0) {
484 				snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
485 						"problems getting a response from"
486 						" \"%s\" on port %i : %s",host,
487 						port, strerror(errno));
488 				connection->error = MPD_ERROR_NORESPONSE;
489 				return connection;
490 			}
491 			connection->buflen+=readed;
492 			connection->buffer[connection->buflen] = '\0';
493 		}
494 		else if(err<0) {
495  			if (SELECT_ERRNO_IGNORE)
496 				continue;
497 			snprintf(connection->errorStr,
498 					MPD_ERRORSTR_MAX_LENGTH,
499 					"problems connecting to \"%s\" on port"
500 					" %i",host,port);
501 			connection->error = MPD_ERROR_CONNPORT;
502 			return connection;
503 		}
504 		else {
505 			snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
506 					"timeout in attempting to get a response from"
507 					" \"%s\" on port %i",host,port);
508 			connection->error = MPD_ERROR_NORESPONSE;
509 			return connection;
510 		}
511 	}
512 
513 	*rt = '\0';
514 	output = strdup(connection->buffer);
515 	strcpy(connection->buffer,rt+1);
516 	connection->buflen = strlen(connection->buffer);
517 
518 	if(mpd_parseWelcome(connection,host,port,output) == 0) connection->doneProcessing = 1;
519 
520 	free(output);
521 
522 	return connection;
523 }
524 
mpd_clearError(mpd_Connection * connection)525 void mpd_clearError(mpd_Connection * connection) {
526 	connection->error = 0;
527 	connection->errorStr[0] = '\0';
528 }
529 
mpd_closeConnection(mpd_Connection * connection)530 void mpd_closeConnection(mpd_Connection * connection) {
531 	closesocket(connection->sock);
532 	if(connection->returnElement) free(connection->returnElement);
533 	if(connection->request) free(connection->request);
534 	g_slice_free(mpd_Connection, connection);
535 	WSACleanup();
536 }
537 
mpd_executeCommand(mpd_Connection * connection,const char * command)538 static void mpd_executeCommand(mpd_Connection * connection,const char * command) {
539 	int ret;
540 	struct timeval tv;
541 	fd_set fds;
542 	const char * commandPtr = command;
543 	int commandLen = strlen(command);
544 
545 	if(!connection->doneProcessing && !connection->commandList) {
546 		strcpy(connection->errorStr,"not done processing current command");
547 		connection->error = 1;
548 		return;
549 	}
550 
551 	mpd_clearError(connection);
552 
553 	FD_ZERO(&fds);
554 	FD_SET(connection->sock,&fds);
555 	tv.tv_sec = connection->timeout.tv_sec;
556 	tv.tv_usec = connection->timeout.tv_usec;
557     while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
558 			(ret==-1 && SELECT_ERRNO_IGNORE)) {
559         fflush(NULL);
560 		ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
561 		if(ret<=0)
562 		{
563 			if (SENDRECV_ERRNO_IGNORE) continue;
564 			snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
565 			         "problems giving command \"%s\"",command);
566 			connection->error = MPD_ERROR_SENDING;
567 			return;
568 		}
569 		else {
570 			commandPtr+=ret;
571 			commandLen-=ret;
572 		}
573 
574 		if(commandLen<=0) break;
575 	}
576 	if(commandLen>0) {
577 		perror("");
578 		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
579 		         "timeout sending command \"%s\"",command);
580 		connection->error = MPD_ERROR_TIMEOUT;
581 		return;
582 	}
583 
584 	if(!connection->commandList) connection->doneProcessing = 0;
585 	else if(connection->commandList == COMMAND_LIST_OK) {
586 		connection->listOks++;
587 	}
588 }
589 
mpd_getNextReturnElement(mpd_Connection * connection)590 static void mpd_getNextReturnElement(mpd_Connection * connection) {
591 	char * output = NULL;
592 	char * rt = NULL;
593 	char * name = NULL;
594 	char * value = NULL;
595 	fd_set fds;
596 	struct timeval tv;
597 	char * tok = NULL;
598 	int readed;
599 	char * bufferCheck = NULL;
600 	int err;
601 	int pos;
602 
603 	if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
604 	connection->returnElement = NULL;
605 
606 	if(connection->doneProcessing || (connection->listOks &&
607 	   connection->doneListOk))
608 	{
609 		strcpy(connection->errorStr,"already done processing current command");
610 		connection->error = 1;
611 		return;
612 	}
613 
614 	bufferCheck = connection->buffer+connection->bufstart;
615 	while(connection->bufstart>=connection->buflen ||
616 			!(rt = strchr(bufferCheck,'\n'))) {
617 		if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
618 			memmove(connection->buffer,
619 					connection->buffer+
620 					connection->bufstart,
621 					connection->buflen-
622 					connection->bufstart+1);
623 			connection->buflen-=connection->bufstart;
624 			connection->bufstart = 0;
625 		}
626 		if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
627 			strcpy(connection->errorStr,"buffer overrun");
628 			connection->error = MPD_ERROR_BUFFEROVERRUN;
629 			connection->doneProcessing = 1;
630 			connection->doneListOk = 0;
631 			return;
632 		}
633 		bufferCheck = connection->buffer+connection->buflen;
634 		tv.tv_sec = connection->timeout.tv_sec;
635 		tv.tv_usec = connection->timeout.tv_usec;
636 		FD_ZERO(&fds);
637 		FD_SET(connection->sock,&fds);
638 		if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
639 			readed = recv(connection->sock,
640 					connection->buffer+connection->buflen,
641 					MPD_BUFFER_MAX_LENGTH-connection->buflen,
642 					MSG_DONTWAIT);
643 			if(readed<0 && SENDRECV_ERRNO_IGNORE) {
644 				continue;
645 			}
646 			if(readed<=0) {
647 				strcpy(connection->errorStr,"connection"
648 				       " closed");
649 				connection->error = MPD_ERROR_CONNCLOSED;
650 				connection->doneProcessing = 1;
651 				connection->doneListOk = 0;
652 				return;
653 			}
654 			connection->buflen+=readed;
655 			connection->buffer[connection->buflen] = '\0';
656 		}
657 		else if(err<0 && SELECT_ERRNO_IGNORE) continue;
658 		else {
659 			strcpy(connection->errorStr,"connection timeout");
660 			connection->error = MPD_ERROR_TIMEOUT;
661 			connection->doneProcessing = 1;
662 			connection->doneListOk = 0;
663 			return;
664 		}
665 	}
666 
667 	*rt = '\0';
668 	output = connection->buffer+connection->bufstart;
669 	connection->bufstart = rt - connection->buffer + 1;
670 
671 	if(strcmp(output,"OK")==0) {
672 		if(connection->listOks > 0) {
673 			strcpy(connection->errorStr, "expected more list_OK's");
674 			connection->error = 1;
675 		}
676 		connection->listOks = 0;
677 		connection->doneProcessing = 1;
678 		connection->doneListOk = 0;
679 		return;
680 	}
681 
682 	if(strcmp(output, "list_OK") == 0) {
683 		if(!connection->listOks) {
684 			strcpy(connection->errorStr,
685 					"got an unexpected list_OK");
686 			connection->error = 1;
687 		}
688 		else {
689 			connection->doneListOk = 1;
690 			connection->listOks--;
691 		}
692 		return;
693 	}
694 
695 	if(strncmp(output,"ACK",strlen("ACK"))==0) {
696 		char * test;
697 		char * needle;
698 		int val;
699 
700 		strcpy(connection->errorStr, output);
701 		connection->error = MPD_ERROR_ACK;
702 		connection->errorCode = MPD_ACK_ERROR_UNK;
703 		connection->errorAt = MPD_ERROR_AT_UNK;
704 		connection->doneProcessing = 1;
705 		connection->doneListOk = 0;
706 
707 		needle = strchr(output, '[');
708 		if(!needle) return;
709 		val = strtol(needle+1, &test, 10);
710 		if(*test != '@') return;
711 		connection->errorCode = val;
712 		val = strtol(test+1, &test, 10);
713 		if(*test != ']') return;
714 		connection->errorAt = val;
715 		return;
716 	}
717 
718 	tok = strchr(output, ':');
719 	if (!tok) return;
720 	pos = tok - output;
721 	value = ++tok;
722 	name = output;
723 	name[pos] = '\0';
724 
725 	if(value[0]==' ') {
726 		connection->returnElement = mpd_newReturnElement(name,&(value[1]));
727 	}
728 	else {
729 		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
730 					"error parsing: %s:%s",name,value);
731 		connection->error = 1;
732 	}
733 }
734 
mpd_finishCommand(mpd_Connection * connection)735 void mpd_finishCommand(mpd_Connection * connection) {
736 	while(!connection->doneProcessing) {
737 		if(connection->doneListOk) connection->doneListOk = 0;
738 		mpd_getNextReturnElement(connection);
739 	}
740 }
741 
mpd_finishListOkCommand(mpd_Connection * connection)742 static void mpd_finishListOkCommand(mpd_Connection * connection) {
743 	while(!connection->doneProcessing && connection->listOks &&
744 			!connection->doneListOk)
745 	{
746 		mpd_getNextReturnElement(connection);
747 	}
748 }
749 
mpd_nextListOkCommand(mpd_Connection * connection)750 int mpd_nextListOkCommand(mpd_Connection * connection) {
751 	mpd_finishListOkCommand(connection);
752 	if(!connection->doneProcessing) connection->doneListOk = 0;
753 	if(connection->listOks == 0 || connection->doneProcessing) return -1;
754 	return 0;
755 }
756 
mpd_sendStatusCommand(mpd_Connection * connection)757 void mpd_sendStatusCommand(mpd_Connection * connection) {
758 	mpd_executeCommand(connection,"status\n");
759 }
760 
mpd_getStatus(mpd_Connection * connection)761 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
762 	/*mpd_executeCommand(connection,"status\n");
763 
764 	if(connection->error) return NULL;*/
765 
766 	if(connection->doneProcessing || (connection->listOks &&
767 	   connection->doneListOk))
768 	{
769 		return NULL;
770 	}
771 
772 	if(!connection->returnElement) mpd_getNextReturnElement(connection);
773 
774 	mpd_Status* status = g_slice_new0(mpd_Status);
775 	status->volume = -1;
776 	status->playlist = -1;
777 	status->storedplaylist = -1;
778 	status->playlistLength = -1;
779 	status->state = -1;
780 	status->nextsong = -1;
781 	status->nextsongid = -1;
782 	status->crossfade = -1;
783 
784 	if(connection->error) {
785 		g_slice_free(mpd_Status, status);
786 		return NULL;
787 	}
788 	while(connection->returnElement) {
789 		mpd_ReturnElement * re = connection->returnElement;
790 		if(strcmp(re->name,"volume")==0) {
791 			status->volume = atoi(re->value);
792 		}
793 		else if(strcmp(re->name,"repeat")==0) {
794 			status->repeat = atoi(re->value);
795 		}
796 		else if(strcmp(re->name,"single")==0) {
797 			status->single = atoi(re->value);
798 		}
799 		else if(strcmp(re->name,"consume")==0) {
800 			status->consume = atoi(re->value);
801 		}
802 		else if(strcmp(re->name,"random")==0) {
803 			status->random = atoi(re->value);
804 		}
805 		else if(strcmp(re->name,"playlist")==0) {
806 			status->playlist = strtol(re->value,NULL,10);
807 		}
808 		else if(strcmp(re->name,"playlistlength")==0) {
809 			status->playlistLength = atoi(re->value);
810 		}
811 		else if(strcmp(re->name,"bitrate")==0) {
812 			status->bitRate = atoi(re->value);
813 		}
814 		else if(strcmp(re->name,"state")==0) {
815 			if(strcmp(re->value,"play")==0) {
816 				status->state = MPD_STATUS_STATE_PLAY;
817 			}
818 			else if(strcmp(re->value,"stop")==0) {
819 				status->state = MPD_STATUS_STATE_STOP;
820 			}
821 			else if(strcmp(re->value,"pause")==0) {
822 				status->state = MPD_STATUS_STATE_PAUSE;
823 			}
824 			else {
825 				status->state = MPD_STATUS_STATE_UNKNOWN;
826 			}
827 		}
828 		else if(strcmp(re->name,"song")==0) {
829 			status->song = atoi(re->value);
830 		}
831 		else if(strcmp(re->name,"songid")==0) {
832 			status->songid = atoi(re->value);
833 		}
834 		else if(strcmp(re->name,"nextsong")==0) {
835 			status->nextsong = atoi(re->value);
836 		}
837 		else if(strcmp(re->name,"nextsongid")==0) {
838 			status->nextsongid = atoi(re->value);
839 		}
840 		else if(strcmp(re->name,"time")==0) {
841 			char * tok = strchr(re->value,':');
842 			/* the second strchr below is a safety check */
843 			if (tok && (strchr(tok,0) > (tok+1))) {
844 				/* atoi stops at the first non-[0-9] char: */
845 				status->elapsedTime = atoi(re->value);
846 				status->totalTime = atoi(tok+1);
847 			}
848 		}
849 		else if(strcmp(re->name,"error")==0) {
850 			status->error = strdup(re->value);
851 		}
852 		else if(strcmp(re->name,"xfade")==0) {
853 			status->crossfade = atoi(re->value);
854 		}
855 		else if(strcmp(re->name,"updating_db")==0) {
856 			status->updatingDb = atoi(re->value);
857 		}
858 		else if(strcmp(re->name,"audio")==0) {
859 			char * tok = strchr(re->value,':');
860 			if (tok && (strchr(tok,0) > (tok+1))) {
861 				status->sampleRate = atoi(re->value);
862 				status->bits = atoi(++tok);
863 				tok = strchr(tok,':');
864 				if (tok && (strchr(tok,0) > (tok+1)))
865 					status->channels = atoi(tok+1);
866 			}
867 		}
868 
869 		mpd_getNextReturnElement(connection);
870 		if(connection->error) {
871 			g_slice_free(mpd_Status, status);
872 			return NULL;
873 		}
874 	}
875 
876 	if(connection->error) {
877 		g_slice_free(mpd_Status, status);
878 		return NULL;
879 	}
880 	else if(status->state<0) {
881 		strcpy(connection->errorStr,"state not found");
882 		connection->error = 1;
883 		g_slice_free(mpd_Status, status);
884 		return NULL;
885 	}
886 
887 	return status;
888 }
889 
mpd_freeStatus(mpd_Status * status)890 void mpd_freeStatus(mpd_Status * status) {
891 	if(status->error) free(status->error);
892 	g_slice_free(mpd_Status, status);
893 }
894 
mpd_sendStatsCommand(mpd_Connection * connection)895 void mpd_sendStatsCommand(mpd_Connection * connection) {
896 	mpd_executeCommand(connection,"stats\n");
897 }
898 
mpd_getStats(mpd_Connection * connection)899 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
900 	/*mpd_executeCommand(connection,"stats\n");
901 
902 	if(connection->error) return NULL;*/
903 
904 	if(connection->doneProcessing || (connection->listOks &&
905 	   connection->doneListOk))
906 	{
907 		return NULL;
908 	}
909 
910 	if(!connection->returnElement) mpd_getNextReturnElement(connection);
911 
912 	mpd_Stats* stats = g_slice_new0(mpd_Stats);
913 
914 	if(connection->error) {
915 		mpd_freeStats(stats);
916 		return NULL;
917 	}
918 	while(connection->returnElement) {
919 		mpd_ReturnElement * re = connection->returnElement;
920 		if(strcmp(re->name,"artists")==0) {
921 			stats->numberOfArtists = atoi(re->value);
922 		}
923 		else if(strcmp(re->name,"albums")==0) {
924 			stats->numberOfAlbums = atoi(re->value);
925 		}
926 		else if(strcmp(re->name,"songs")==0) {
927 			stats->numberOfSongs = atoi(re->value);
928 		}
929 		else if(strcmp(re->name,"uptime")==0) {
930 			stats->uptime = strtol(re->value,NULL,10);
931 		}
932 		else if(strcmp(re->name,"db_update")==0) {
933 			stats->dbUpdateTime = strtol(re->value,NULL,10);
934 		}
935 		else if(strcmp(re->name,"playtime")==0) {
936 			stats->playTime = strtol(re->value,NULL,10);
937 		}
938 		else if(strcmp(re->name,"db_playtime")==0) {
939 			stats->dbPlayTime = strtol(re->value,NULL,10);
940 		}
941 
942 		mpd_getNextReturnElement(connection);
943 		if(connection->error) {
944 			mpd_freeStats(stats);
945 			return NULL;
946 		}
947 	}
948 
949 	if(connection->error) {
950 		mpd_freeStats(stats);
951 		return NULL;
952 	}
953 
954 	return stats;
955 }
956 
mpd_freeStats(mpd_Stats * stats)957 void mpd_freeStats(mpd_Stats * stats) {
958 	g_slice_free(mpd_Stats, stats);
959 }
960 
mpd_getSearchStats(mpd_Connection * connection)961 mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection)
962 {
963 	if (connection->doneProcessing ||
964 	    (connection->listOks && connection->doneListOk)) {
965 		return NULL;
966 	}
967 
968 	if (!connection->returnElement) mpd_getNextReturnElement(connection);
969 
970 	if (connection->error)
971 		return NULL;
972 
973 	mpd_ReturnElement* re;
974 	mpd_SearchStats* stats = g_slice_new0(mpd_SearchStats);
975 
976 	while (connection->returnElement) {
977 		re = connection->returnElement;
978 
979 		if (strcmp(re->name, "songs") == 0) {
980 			stats->numberOfSongs = atoi(re->value);
981 		} else if (strcmp(re->name, "playtime") == 0) {
982 			stats->playTime = strtol(re->value, NULL, 10);
983 		}
984 
985 		mpd_getNextReturnElement(connection);
986 		if (connection->error) {
987 			mpd_freeSearchStats(stats);
988 			return NULL;
989 		}
990 	}
991 
992 	if (connection->error) {
993 		mpd_freeSearchStats(stats);
994 		return NULL;
995 	}
996 
997 	return stats;
998 }
999 
mpd_freeSearchStats(mpd_SearchStats * stats)1000 void mpd_freeSearchStats(mpd_SearchStats * stats)
1001 {
1002 	g_slice_free(mpd_SearchStats, stats);
1003 }
1004 
mpd_finishSong(mpd_Song * song)1005 static void mpd_finishSong(mpd_Song * song) {
1006 	if(song->file) free(song->file);
1007 	if(song->artist) free(song->artist);
1008 	if(song->album) free(song->album);
1009 	if(song->title) free(song->title);
1010 	if(song->track) free(song->track);
1011 	if(song->name) free(song->name);
1012 	if(song->date) free(song->date);
1013 	if(song->genre) free(song->genre);
1014 	if(song->composer) free(song->composer);
1015 	if(song->performer) free(song->performer);
1016 	if(song->disc) free(song->disc);
1017 	if(song->comment) free(song->comment);
1018     if(song->albumartist) free(song->albumartist);
1019 }
1020 
mpd_newSong(void)1021 mpd_Song * mpd_newSong(void) {
1022 	mpd_Song * ret = g_slice_new0(mpd_Song);
1023 	ret->time = MPD_SONG_NO_TIME;
1024 	ret->pos = MPD_SONG_NO_NUM;
1025 	ret->id = MPD_SONG_NO_ID;
1026 	return ret;
1027 }
1028 
mpd_freeSong(mpd_Song * song)1029 void mpd_freeSong(mpd_Song * song) {
1030 	mpd_finishSong(song);
1031 	g_slice_free(mpd_Song, song);
1032 }
1033 
mpd_songDup(const mpd_Song * song)1034 mpd_Song * mpd_songDup(const mpd_Song * song) {
1035 	mpd_Song * ret = mpd_newSong();
1036 
1037 	if(song->file) ret->file = strdup(song->file);
1038 	if(song->artist) ret->artist = strdup(song->artist);
1039 	if(song->album) ret->album = strdup(song->album);
1040 	if(song->title) ret->title = strdup(song->title);
1041 	if(song->track) ret->track = strdup(song->track);
1042 	if(song->name) ret->name = strdup(song->name);
1043 	if(song->date) ret->date = strdup(song->date);
1044 	if(song->genre) ret->genre= strdup(song->genre);
1045 	if(song->composer) ret->composer= strdup(song->composer);
1046 	if(song->performer) ret->performer = strdup(song->performer);
1047 	if(song->disc) ret->disc = strdup(song->disc);
1048 	if(song->comment) ret->comment = strdup(song->comment);
1049     if(song->albumartist) ret->albumartist = strdup(song->albumartist);
1050 	ret->time = song->time;
1051 	ret->pos = song->pos;
1052 	ret->id = song->id;
1053 
1054 	return ret;
1055 }
1056 
1057 
mpd_finishDirectory(mpd_Directory * directory)1058 static void mpd_finishDirectory(mpd_Directory * directory) {
1059 	if(directory->path) free(directory->path);
1060 }
1061 
mpd_newDirectory(void)1062 mpd_Directory * mpd_newDirectory(void) {
1063 	return g_slice_new0(mpd_Directory);
1064 }
1065 
mpd_freeDirectory(mpd_Directory * directory)1066 void mpd_freeDirectory(mpd_Directory * directory) {
1067 	mpd_finishDirectory(directory);
1068 	g_slice_free(mpd_Directory, directory);
1069 }
1070 
mpd_directoryDup(mpd_Directory * directory)1071 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
1072 	mpd_Directory * ret = mpd_newDirectory();
1073 
1074 	if(directory->path) ret->path = strdup(directory->path);
1075 
1076 	return ret;
1077 }
1078 
mpd_finishPlaylistFile(mpd_PlaylistFile * playlist)1079 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
1080 	if(playlist->path) free(playlist->path);
1081     if(playlist->mtime) free(playlist->mtime);
1082 }
1083 
mpd_newPlaylistFile(void)1084 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
1085 	return g_slice_new0(mpd_PlaylistFile);
1086 }
1087 
mpd_freePlaylistFile(mpd_PlaylistFile * playlist)1088 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
1089 	mpd_finishPlaylistFile(playlist);
1090 	g_slice_free(mpd_PlaylistFile, playlist);
1091 }
1092 
mpd_playlistFileDup(mpd_PlaylistFile * playlist)1093 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1094 	mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1095 
1096 	if(playlist->path) ret->path = strdup(playlist->path);
1097     if(playlist->mtime) ret->mtime = strdup(playlist->mtime);
1098 
1099 	return ret;
1100 }
1101 
mpd_finishInfoEntity(mpd_InfoEntity * entity)1102 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1103 	if(entity->info.directory) {
1104 		if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1105 			mpd_freeDirectory(entity->info.directory);
1106 		}
1107 		else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1108 			mpd_freeSong(entity->info.song);
1109 		}
1110 		else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1111 			mpd_freePlaylistFile(entity->info.playlistFile);
1112 		}
1113 	}
1114 }
1115 
mpd_newInfoEntity(void)1116 mpd_InfoEntity * mpd_newInfoEntity(void) {
1117 	return g_slice_new0(mpd_InfoEntity);
1118 }
1119 
mpd_freeInfoEntity(mpd_InfoEntity * entity)1120 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1121 	mpd_finishInfoEntity(entity);
1122 	g_slice_free(mpd_InfoEntity, entity);
1123 }
1124 
mpd_sendInfoCommand(mpd_Connection * connection,const char * command)1125 static void mpd_sendInfoCommand(mpd_Connection * connection,const char * command) {
1126 	mpd_executeCommand(connection,command);
1127 }
1128 
mpd_getNextInfoEntity(mpd_Connection * connection)1129 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1130 	mpd_InfoEntity * entity = NULL;
1131 
1132 	if(connection->doneProcessing || (connection->listOks &&
1133 	   connection->doneListOk))
1134 	{
1135 		return NULL;
1136 	}
1137 
1138 	if(!connection->returnElement) mpd_getNextReturnElement(connection);
1139 
1140 	if(connection->returnElement) {
1141 		if(strcmp(connection->returnElement->name,"file")==0) {
1142 			entity = mpd_newInfoEntity();
1143 			entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1144 			entity->info.song = mpd_newSong();
1145 			entity->info.song->file =
1146 				strdup(connection->returnElement->value);
1147 		}
1148 		else if(strcmp(connection->returnElement->name,
1149 					"directory")==0) {
1150 			entity = mpd_newInfoEntity();
1151 			entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1152 			entity->info.directory = mpd_newDirectory();
1153 			entity->info.directory->path =
1154 				strdup(connection->returnElement->value);
1155 		}
1156 		else if(strcmp(connection->returnElement->name,"playlist")==0) {
1157 			entity = mpd_newInfoEntity();
1158 			entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1159 			entity->info.playlistFile = mpd_newPlaylistFile();
1160 			entity->info.playlistFile->path =
1161 				strdup(connection->returnElement->value);
1162 		}
1163 		else if(strcmp(connection->returnElement->name, "cpos") == 0){
1164 			entity = mpd_newInfoEntity();
1165 			entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1166 			entity->info.song = mpd_newSong();
1167 			entity->info.song->pos = atoi(connection->returnElement->value);
1168 		}
1169 		else {
1170 			connection->error = 1;
1171 			strcpy(connection->errorStr,"problem parsing song info");
1172 			return NULL;
1173 		}
1174 	}
1175 	else return NULL;
1176 
1177 	mpd_getNextReturnElement(connection);
1178 	while(connection->returnElement) {
1179 		mpd_ReturnElement * re = connection->returnElement;
1180 
1181 		if(strcmp(re->name,"file")==0) return entity;
1182 		else if(strcmp(re->name,"directory")==0) return entity;
1183 		else if(strcmp(re->name,"playlist")==0) return entity;
1184 		else if(strcmp(re->name,"cpos")==0) return entity;
1185 
1186 		if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1187 				strlen(re->value)) {
1188 			if(strcmp(re->name,"Artist")==0) {
1189 				if(entity->info.song->artist) {
1190 					int length = strlen(entity->info.song->artist);
1191 					entity->info.song->artist = realloc(entity->info.song->artist,
1192 					                                    length + strlen(re->value) + 3);
1193 					strcpy(&((entity->info.song->artist)[length]), ", ");
1194 					strcpy(&((entity->info.song->artist)[length + 2]), re->value);
1195 				}
1196 				else {
1197 					entity->info.song->artist = strdup(re->value);
1198 				}
1199 			}
1200 			else if(!entity->info.song->album &&
1201 					strcmp(re->name,"Album")==0) {
1202 				entity->info.song->album = strdup(re->value);
1203 			}
1204 			else if(!entity->info.song->title &&
1205 					strcmp(re->name,"Title")==0) {
1206 				entity->info.song->title = strdup(re->value);
1207 			}
1208 			else if(!entity->info.song->track &&
1209 					strcmp(re->name,"Track")==0) {
1210 				entity->info.song->track = strdup(re->value);
1211 			}
1212 			else if(!entity->info.song->name &&
1213 					strcmp(re->name,"Name")==0) {
1214 				entity->info.song->name = strdup(re->value);
1215 			}
1216 			else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1217 					strcmp(re->name,"Time")==0) {
1218 				entity->info.song->time = atoi(re->value);
1219 			}
1220 			else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1221 					strcmp(re->name,"Pos")==0) {
1222 				entity->info.song->pos = atoi(re->value);
1223 			}
1224 			else if(entity->info.song->id==MPD_SONG_NO_ID &&
1225 					strcmp(re->name,"Id")==0) {
1226 				entity->info.song->id = atoi(re->value);
1227 			}
1228 			else if(!entity->info.song->date &&
1229 					strcmp(re->name, "Date") == 0) {
1230 				entity->info.song->date = strdup(re->value);
1231 			}
1232 			else if(!entity->info.song->genre &&
1233 					strcmp(re->name, "Genre") == 0) {
1234 				if(entity->info.song->genre) {
1235 					int length = strlen(entity->info.song->genre);
1236 					entity->info.song->genre = realloc(entity->info.song->genre,
1237 					                                   length + strlen(re->value) + 4);
1238 					strcpy(&((entity->info.song->genre)[length]), ", ");
1239 					strcpy(&((entity->info.song->genre)[length + 3]), re->value);
1240 				}
1241 				else {
1242 					entity->info.song->genre = strdup(re->value);
1243 				}
1244 			}
1245 			else if(strcmp(re->name, "Composer") == 0) {
1246 				if(entity->info.song->composer) {
1247 					int length = strlen(entity->info.song->composer);
1248 					entity->info.song->composer = realloc(entity->info.song->composer,
1249 					                                      length + strlen(re->value) + 3);
1250 					strcpy(&((entity->info.song->composer)[length]), ", ");
1251 					strcpy(&((entity->info.song->composer)[length + 2]), re->value);
1252 				}
1253 				else {
1254 					entity->info.song->composer = strdup(re->value);
1255 				}
1256 			}
1257 			else if(strcmp(re->name, "Performer") == 0) {
1258 				if(entity->info.song->performer) {
1259 					int length = strlen(entity->info.song->performer);
1260 					entity->info.song->performer = realloc(entity->info.song->performer,
1261 					                                       length + strlen(re->value) + 3);
1262 					strcpy(&((entity->info.song->performer)[length]), ", ");
1263 					strcpy(&((entity->info.song->performer)[length + 2]), re->value);
1264 				}
1265 				else {
1266 					entity->info.song->performer = strdup(re->value);
1267 				}
1268 			}
1269 			else if(!entity->info.song->disc &&
1270 					strcmp(re->name, "Disc") == 0) {
1271 				entity->info.song->disc = strdup(re->value);
1272 			}
1273 			else if(!entity->info.song->comment &&
1274 					strcmp(re->name, "Comment") == 0) {
1275 				entity->info.song->comment = strdup(re->value);
1276 			}
1277 
1278 			else if(!entity->info.song->albumartist &&
1279 					strcmp(re->name, "AlbumArtist") == 0) {
1280 				entity->info.song->albumartist = strdup(re->value);
1281 			}
1282 		}
1283 		else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1284 		}
1285 		else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1286             if(!entity->info.playlistFile->mtime &&
1287                     strcmp(re->name, "Last-Modified") == 0) {
1288                     entity->info.playlistFile->mtime = strdup(re->value);
1289             }
1290 		}
1291 
1292 		mpd_getNextReturnElement(connection);
1293 	}
1294 
1295 	return entity;
1296 }
1297 
mpd_getNextReturnElementNamed(mpd_Connection * connection,const char * name)1298 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1299 		const char * name)
1300 {
1301 	if(connection->doneProcessing || (connection->listOks &&
1302 				connection->doneListOk))
1303 	{
1304 		return NULL;
1305 	}
1306 
1307 	mpd_getNextReturnElement(connection);
1308 	while(connection->returnElement) {
1309 		mpd_ReturnElement * re = connection->returnElement;
1310 
1311 		if(strcmp(re->name,name)==0) return strdup(re->value);
1312 		mpd_getNextReturnElement(connection);
1313 	}
1314 
1315 	return NULL;
1316 }
1317 
mpd_getNextTag(mpd_Connection * connection,int type)1318 char *mpd_getNextTag(mpd_Connection *connection, int type)
1319 {
1320 	if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES ||
1321 	    type == MPD_TAG_ITEM_ANY)
1322 		return NULL;
1323 	if (type == MPD_TAG_ITEM_FILENAME)
1324 		return mpd_getNextReturnElementNamed(connection, "file");
1325 	return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
1326 }
1327 
mpd_getNextArtist(mpd_Connection * connection)1328 char * mpd_getNextArtist(mpd_Connection * connection) {
1329 	return mpd_getNextReturnElementNamed(connection,"Artist");
1330 }
1331 
mpd_getNextAlbum(mpd_Connection * connection)1332 char * mpd_getNextAlbum(mpd_Connection * connection) {
1333 	return mpd_getNextReturnElementNamed(connection,"Album");
1334 }
1335 
mpd_sendPlaylistInfoCommand(mpd_Connection * connection,int songPos)1336 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1337 	int len = strlen("playlistinfo")+2+INTLEN+3;
1338 	char *string = malloc(len);
1339 	snprintf(string, len, "playlistinfo \"%i\"\n", songPos);
1340 	mpd_sendInfoCommand(connection,string);
1341 	free(string);
1342 }
1343 
mpd_sendPlaylistIdCommand(mpd_Connection * connection,int id)1344 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1345 	int len = strlen("playlistid")+2+INTLEN+3;
1346 	char *string = malloc(len);
1347 	snprintf(string, len, "playlistid \"%i\"\n", id);
1348 	mpd_sendInfoCommand(connection, string);
1349 	free(string);
1350 }
1351 
mpd_sendPlChangesCommand(mpd_Connection * connection,long long playlist)1352 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1353 	int len = strlen("plchanges")+2+LONGLONGLEN+3;
1354 	char *string = malloc(len);
1355 	snprintf(string, len, "plchanges \"%lld\"\n", playlist);
1356 	mpd_sendInfoCommand(connection,string);
1357 	free(string);
1358 }
1359 
mpd_sendPlChangesPosIdCommand(mpd_Connection * connection,long long playlist)1360 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1361 	int len = strlen("plchangesposid")+2+LONGLONGLEN+3;
1362 	char *string = malloc(len);
1363 	snprintf(string, len, "plchangesposid \"%lld\"\n", playlist);
1364 	mpd_sendInfoCommand(connection,string);
1365 	free(string);
1366 }
1367 
mpd_sendListallCommand(mpd_Connection * connection,const char * dir)1368 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1369 	char * sDir = mpd_sanitizeArg(dir);
1370 	int len = strlen("listall")+2+strlen(sDir)+3;
1371 	char *string = malloc(len);
1372 	snprintf(string, len, "listall \"%s\"\n", sDir);
1373 	mpd_sendInfoCommand(connection,string);
1374 	free(string);
1375 	free(sDir);
1376 }
1377 
mpd_sendListallInfoCommand(mpd_Connection * connection,const char * dir)1378 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1379 	char * sDir = mpd_sanitizeArg(dir);
1380 	int len = strlen("listallinfo")+2+strlen(sDir)+3;
1381 	char *string = malloc(len);
1382 	snprintf(string, len, "listallinfo \"%s\"\n", sDir);
1383 	mpd_sendInfoCommand(connection,string);
1384 	free(string);
1385 	free(sDir);
1386 }
1387 
mpd_sendLsInfoCommand(mpd_Connection * connection,const char * dir)1388 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1389 	char * sDir = mpd_sanitizeArg(dir);
1390 	int len = strlen("lsinfo")+2+strlen(sDir)+3;
1391 	char *string = malloc(len);
1392 	snprintf(string, len, "lsinfo \"%s\"\n", sDir);
1393 	mpd_sendInfoCommand(connection,string);
1394 	free(string);
1395 	free(sDir);
1396 }
1397 
mpd_sendCurrentSongCommand(mpd_Connection * connection)1398 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1399 	mpd_executeCommand(connection,"currentsong\n");
1400 }
1401 
mpd_sendSearchCommand(mpd_Connection * connection,int table,const char * str)1402 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1403 		const char * str)
1404 {
1405 	mpd_startSearch(connection, 0);
1406 	mpd_addConstraintSearch(connection, table, str);
1407 	mpd_commitSearch(connection);
1408 }
1409 
mpd_sendFindCommand(mpd_Connection * connection,int table,const char * str)1410 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1411 		const char * str)
1412 {
1413 	mpd_startSearch(connection, 1);
1414 	mpd_addConstraintSearch(connection, table, str);
1415 	mpd_commitSearch(connection);
1416 }
1417 
mpd_sendListCommand(mpd_Connection * connection,int table,const char * arg1)1418 void mpd_sendListCommand(mpd_Connection * connection, int table,
1419 		const char * arg1)
1420 {
1421 	char st[10];
1422 	int len;
1423 	char *string;
1424 	if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1425 	else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1426 	else {
1427 		connection->error = 1;
1428 		strcpy(connection->errorStr,"unknown table for list");
1429 		return;
1430 	}
1431 	if(arg1) {
1432 		char * sanitArg1 = mpd_sanitizeArg(arg1);
1433 		len = strlen("list")+1+strlen(sanitArg1)+2+strlen(st)+3;
1434 		string = malloc(len);
1435 		snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1);
1436 		free(sanitArg1);
1437 	}
1438 	else {
1439 		len = strlen("list")+1+strlen(st)+2;
1440 		string = malloc(len);
1441 		snprintf(string, len, "list %s\n", st);
1442 	}
1443 	mpd_sendInfoCommand(connection,string);
1444 	free(string);
1445 }
1446 
mpd_sendAddCommand(mpd_Connection * connection,const char * file)1447 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1448 	char * sFile = mpd_sanitizeArg(file);
1449 	int len = strlen("add")+2+strlen(sFile)+3;
1450 	char *string = malloc(len);
1451 	snprintf(string, len, "add \"%s\"\n", sFile);
1452 	mpd_executeCommand(connection,string);
1453 	free(string);
1454 	free(sFile);
1455 }
1456 
mpd_sendAddIdCommand(mpd_Connection * connection,const char * file)1457 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1458 {
1459 	int retval = -1;
1460 	char *sFile = mpd_sanitizeArg(file);
1461 	int len = strlen("addid")+2+strlen(sFile)+3;
1462 	char *string = malloc(len);
1463 
1464 	snprintf(string, len, "addid \"%s\"\n", sFile);
1465 	mpd_sendInfoCommand(connection, string);
1466 	free(string);
1467 	free(sFile);
1468 
1469 	string = mpd_getNextReturnElementNamed(connection, "Id");
1470 	if (string) {
1471 		retval = atoi(string);
1472 		free(string);
1473 	}
1474 
1475 	return retval;
1476 }
1477 
mpd_sendDeleteCommand(mpd_Connection * connection,int songPos)1478 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1479 	int len = strlen("delete")+2+INTLEN+3;
1480 	char *string = malloc(len);
1481 	snprintf(string, len, "delete \"%i\"\n", songPos);
1482 	mpd_sendInfoCommand(connection,string);
1483 	free(string);
1484 }
1485 
mpd_sendDeleteIdCommand(mpd_Connection * connection,int id)1486 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1487 	int len = strlen("deleteid")+2+INTLEN+3;
1488 	char *string = malloc(len);
1489 	snprintf(string, len, "deleteid \"%i\"\n", id);
1490 	mpd_sendInfoCommand(connection,string);
1491 	free(string);
1492 }
1493 
mpd_sendSaveCommand(mpd_Connection * connection,const char * name)1494 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1495 	char * sName = mpd_sanitizeArg(name);
1496 	int len = strlen("save")+2+strlen(sName)+3;
1497 	char *string = malloc(len);
1498 	snprintf(string, len, "save \"%s\"\n", sName);
1499 	mpd_executeCommand(connection,string);
1500 	free(string);
1501 	free(sName);
1502 }
1503 
mpd_sendLoadCommand(mpd_Connection * connection,const char * name)1504 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1505 	char * sName = mpd_sanitizeArg(name);
1506 	int len = strlen("load")+2+strlen(sName)+3;
1507 	char *string = malloc(len);
1508 	snprintf(string, len, "load \"%s\"\n", sName);
1509 	mpd_executeCommand(connection,string);
1510 	free(string);
1511 	free(sName);
1512 }
1513 
mpd_sendRmCommand(mpd_Connection * connection,const char * name)1514 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1515 	char * sName = mpd_sanitizeArg(name);
1516 	int len = strlen("rm")+2+strlen(sName)+3;
1517 	char *string = malloc(len);
1518 	snprintf(string, len, "rm \"%s\"\n", sName);
1519 	mpd_executeCommand(connection,string);
1520 	free(string);
1521 	free(sName);
1522 }
1523 
mpd_sendRenameCommand(mpd_Connection * connection,const char * from,const char * to)1524 void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
1525                            const char *to)
1526 {
1527 	char *sFrom = mpd_sanitizeArg(from);
1528 	char *sTo = mpd_sanitizeArg(to);
1529 	int len = strlen("rename")+2+strlen(sFrom)+3+strlen(sTo)+3;
1530 	char *string = malloc(len);
1531 	snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo);
1532 	mpd_executeCommand(connection, string);
1533 	free(string);
1534 	free(sFrom);
1535 	free(sTo);
1536 }
1537 
mpd_sendShuffleCommand(mpd_Connection * connection)1538 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1539 	mpd_executeCommand(connection,"shuffle\n");
1540 }
1541 
mpd_sendClearCommand(mpd_Connection * connection)1542 void mpd_sendClearCommand(mpd_Connection * connection) {
1543 	mpd_executeCommand(connection,"clear\n");
1544 }
1545 
mpd_sendPlayCommand(mpd_Connection * connection,int songPos)1546 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1547 	int len = strlen("play")+2+INTLEN+3;
1548 	char *string = malloc(len);
1549 	snprintf(string, len, "play \"%i\"\n", songPos);
1550 	mpd_sendInfoCommand(connection,string);
1551 	free(string);
1552 }
1553 
mpd_sendPlayIdCommand(mpd_Connection * connection,int id)1554 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1555 	int len = strlen("playid")+2+INTLEN+3;
1556 	char *string = malloc(len);
1557 	snprintf(string, len, "playid \"%i\"\n", id);
1558 	mpd_sendInfoCommand(connection,string);
1559 	free(string);
1560 }
1561 
mpd_sendStopCommand(mpd_Connection * connection)1562 void mpd_sendStopCommand(mpd_Connection * connection) {
1563 	mpd_executeCommand(connection,"stop\n");
1564 }
1565 
mpd_sendPauseCommand(mpd_Connection * connection,int pauseMode)1566 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1567 	int len = strlen("pause")+2+INTLEN+3;
1568 	char *string = malloc(len);
1569 	snprintf(string, len, "pause \"%i\"\n", pauseMode);
1570 	mpd_executeCommand(connection,string);
1571 	free(string);
1572 }
1573 
mpd_sendNextCommand(mpd_Connection * connection)1574 void mpd_sendNextCommand(mpd_Connection * connection) {
1575 	mpd_executeCommand(connection,"next\n");
1576 }
1577 
mpd_sendMoveCommand(mpd_Connection * connection,int from,int to)1578 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1579 	int len = strlen("move")+2+INTLEN+3+INTLEN+3;
1580 	char *string = malloc(len);
1581 	snprintf(string, len, "move \"%i\" \"%i\"\n", from, to);
1582 	mpd_sendInfoCommand(connection,string);
1583 	free(string);
1584 }
1585 
mpd_sendMoveIdCommand(mpd_Connection * connection,int id,int to)1586 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1587 	int len = strlen("moveid")+2+INTLEN+3+INTLEN+3;
1588 	char *string = malloc(len);
1589 	snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to);
1590 	mpd_sendInfoCommand(connection,string);
1591 	free(string);
1592 }
1593 
mpd_sendSwapCommand(mpd_Connection * connection,int song1,int song2)1594 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1595 	int len = strlen("swap")+2+INTLEN+3+INTLEN+3;
1596 	char *string = malloc(len);
1597 	snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2);
1598 	mpd_sendInfoCommand(connection,string);
1599 	free(string);
1600 }
1601 
mpd_sendSwapIdCommand(mpd_Connection * connection,int id1,int id2)1602 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1603 	int len = strlen("swapid")+2+INTLEN+3+INTLEN+3;
1604 	char *string = malloc(len);
1605 	snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2);
1606 	mpd_sendInfoCommand(connection,string);
1607 	free(string);
1608 }
1609 
mpd_sendSeekCommand(mpd_Connection * connection,int song,int seek_time)1610 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int seek_time) {
1611 	int len = strlen("seek")+2+INTLEN+3+INTLEN+3;
1612 	char *string = malloc(len);
1613 	snprintf(string, len, "seek \"%i\" \"%i\"\n", song, seek_time);
1614 	mpd_sendInfoCommand(connection,string);
1615 	free(string);
1616 }
1617 
mpd_sendSeekIdCommand(mpd_Connection * connection,int id,int seek_time)1618 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int seek_time) {
1619 	int len = strlen("seekid")+2+INTLEN+3+INTLEN+3;
1620 	char *string = malloc(len);
1621 	snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, seek_time);
1622 	mpd_sendInfoCommand(connection,string);
1623 	free(string);
1624 }
1625 
mpd_sendUpdateCommand(mpd_Connection * connection,const char * path)1626 void mpd_sendUpdateCommand(mpd_Connection * connection,const char * path) {
1627 	char * sPath = mpd_sanitizeArg(path);
1628 	int len = strlen("update")+2+strlen(sPath)+3;
1629 	char *string = malloc(len);
1630 	snprintf(string, len, "update \"%s\"\n", sPath);
1631 	mpd_sendInfoCommand(connection,string);
1632 	free(string);
1633 	free(sPath);
1634 }
1635 
mpd_getUpdateId(mpd_Connection * connection)1636 int mpd_getUpdateId(mpd_Connection * connection) {
1637 	char * jobid;
1638 	int ret = 0;
1639 
1640 	jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1641 	if(jobid) {
1642 		ret = atoi(jobid);
1643 		free(jobid);
1644 	}
1645 
1646 	return ret;
1647 }
1648 
mpd_sendPrevCommand(mpd_Connection * connection)1649 void mpd_sendPrevCommand(mpd_Connection * connection) {
1650 	mpd_executeCommand(connection,"previous\n");
1651 }
1652 
mpd_sendSingleCommand(mpd_Connection * connection,int singleMode)1653 void mpd_sendSingleCommand(mpd_Connection * connection, int singleMode) {
1654 	int len = strlen("repeat")+2+INTLEN+3;
1655 	char *string = malloc(len);
1656 	snprintf(string, len, "single \"%i\"\n", singleMode);
1657 	mpd_executeCommand(connection,string);
1658 	free(string);
1659 }
1660 
mpd_sendConsumeCommand(mpd_Connection * connection,int consumeMode)1661 void mpd_sendConsumeCommand(mpd_Connection * connection, int consumeMode) {
1662 	int len = strlen("repeat")+2+INTLEN+3;
1663 	char *string = malloc(len);
1664 	snprintf(string, len, "consume \"%i\"\n", consumeMode);
1665 	mpd_executeCommand(connection,string);
1666 	free(string);
1667 }
1668 
mpd_sendRepeatCommand(mpd_Connection * connection,int repeatMode)1669 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1670 	int len = strlen("repeat")+2+INTLEN+3;
1671 	char *string = malloc(len);
1672 	snprintf(string, len, "repeat \"%i\"\n", repeatMode);
1673 	mpd_executeCommand(connection,string);
1674 	free(string);
1675 }
1676 
mpd_sendRandomCommand(mpd_Connection * connection,int randomMode)1677 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1678 	int len = strlen("random")+2+INTLEN+3;
1679 	char *string = malloc(len);
1680 	snprintf(string, len, "random \"%i\"\n", randomMode);
1681 	mpd_executeCommand(connection,string);
1682 	free(string);
1683 }
1684 
mpd_sendSetvolCommand(mpd_Connection * connection,int volumeChange)1685 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1686 	int len = strlen("setvol")+2+INTLEN+3;
1687 	char *string = malloc(len);
1688 	snprintf(string, len, "setvol \"%i\"\n", volumeChange);
1689 	mpd_executeCommand(connection,string);
1690 	free(string);
1691 }
1692 
mpd_sendCrossfadeCommand(mpd_Connection * connection,int seconds)1693 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1694 	int len = strlen("crossfade")+2+INTLEN+3;
1695 	char *string = malloc(len);
1696 	snprintf(string, len, "crossfade \"%i\"\n", seconds);
1697 	mpd_executeCommand(connection,string);
1698 	free(string);
1699 }
1700 
mpd_sendPasswordCommand(mpd_Connection * connection,const char * pass)1701 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1702 	char * sPass = mpd_sanitizeArg(pass);
1703 	int len = strlen("password")+2+strlen(sPass)+3;
1704 	char *string = malloc(len);
1705 	snprintf(string, len, "password \"%s\"\n", sPass);
1706 	mpd_executeCommand(connection,string);
1707 	free(string);
1708 	free(sPass);
1709 }
1710 
mpd_sendCommandListBegin(mpd_Connection * connection)1711 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1712 	if(connection->commandList) {
1713 		strcpy(connection->errorStr,"already in command list mode");
1714 		connection->error = 1;
1715 		return;
1716 	}
1717 	connection->commandList = COMMAND_LIST;
1718 	mpd_executeCommand(connection,"command_list_begin\n");
1719 }
1720 
mpd_sendCommandListOkBegin(mpd_Connection * connection)1721 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1722 	if(connection->commandList) {
1723 		strcpy(connection->errorStr,"already in command list mode");
1724 		connection->error = 1;
1725 		return;
1726 	}
1727 	connection->commandList = COMMAND_LIST_OK;
1728 	mpd_executeCommand(connection,"command_list_ok_begin\n");
1729 	connection->listOks = 0;
1730 }
1731 
mpd_sendCommandListEnd(mpd_Connection * connection)1732 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1733 	if(!connection->commandList) {
1734 		strcpy(connection->errorStr,"not in command list mode");
1735 		connection->error = 1;
1736 		return;
1737 	}
1738 	connection->commandList = 0;
1739 	mpd_executeCommand(connection,"command_list_end\n");
1740 }
1741 
mpd_sendOutputsCommand(mpd_Connection * connection)1742 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1743 	mpd_executeCommand(connection,"outputs\n");
1744 }
1745 
mpd_getNextOutput(mpd_Connection * connection)1746 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1747 	if(connection->doneProcessing || (connection->listOks &&
1748 				connection->doneListOk))
1749 	{
1750 		return NULL;
1751 	}
1752 
1753 	if(connection->error) return NULL;
1754 
1755 	mpd_OutputEntity* output = g_slice_new0(mpd_OutputEntity);
1756 	output->id = -10;
1757 
1758 	if(!connection->returnElement) mpd_getNextReturnElement(connection);
1759 
1760 	while(connection->returnElement) {
1761 		mpd_ReturnElement * re = connection->returnElement;
1762 		if(strcmp(re->name,"outputid")==0) {
1763 			if(output!=NULL && output->id>=0) return output;
1764 			output->id = atoi(re->value);
1765 		}
1766 		else if(strcmp(re->name,"outputname")==0) {
1767 			output->name = strdup(re->value);
1768 		}
1769 		else if(strcmp(re->name,"outputenabled")==0) {
1770 			output->enabled = atoi(re->value);
1771 		}
1772 
1773 		mpd_getNextReturnElement(connection);
1774 		if(connection->error) {
1775 			mpd_freeOutputElement(output);
1776 			return NULL;
1777 		}
1778 	}
1779 
1780 	return output;
1781 }
1782 
mpd_sendEnableOutputCommand(mpd_Connection * connection,int outputId)1783 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1784 	int len = strlen("enableoutput")+2+INTLEN+3;
1785 	char *string = malloc(len);
1786 	snprintf(string, len, "enableoutput \"%i\"\n", outputId);
1787 	mpd_executeCommand(connection,string);
1788 	free(string);
1789 }
1790 
mpd_sendDisableOutputCommand(mpd_Connection * connection,int outputId)1791 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1792 	int len = strlen("disableoutput")+2+INTLEN+3;
1793 	char *string = malloc(len);
1794 	snprintf(string, len, "disableoutput \"%i\"\n", outputId);
1795 	mpd_executeCommand(connection,string);
1796 	free(string);
1797 }
1798 
mpd_freeOutputElement(mpd_OutputEntity * output)1799 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1800 	if(output->name)
1801 		free(output->name);
1802 	g_slice_free(mpd_OutputEntity, output);
1803 }
1804 
1805 /**
1806  * mpd_sendNotCommandsCommand
1807  * odd naming, but it gets the not allowed commands
1808  */
1809 
mpd_sendNotCommandsCommand(mpd_Connection * connection)1810 void mpd_sendNotCommandsCommand(mpd_Connection * connection)
1811 {
1812 	mpd_executeCommand(connection, "notcommands\n");
1813 }
1814 
1815 /**
1816  * mpd_sendCommandsCommand
1817  * odd naming, but it gets the allowed commands
1818  */
mpd_sendCommandsCommand(mpd_Connection * connection)1819 void mpd_sendCommandsCommand(mpd_Connection * connection)
1820 {
1821 	mpd_executeCommand(connection, "commands\n");
1822 }
1823 
1824 /**
1825  * Get the next returned command
1826  */
mpd_getNextCommand(mpd_Connection * connection)1827 char * mpd_getNextCommand(mpd_Connection * connection)
1828 {
1829 	return mpd_getNextReturnElementNamed(connection, "command");
1830 }
1831 
mpd_sendUrlHandlersCommand(mpd_Connection * connection)1832 void mpd_sendUrlHandlersCommand(mpd_Connection * connection)
1833 {
1834 	mpd_executeCommand(connection, "urlhandlers\n");
1835 }
1836 
mpd_getNextHandler(mpd_Connection * connection)1837 char * mpd_getNextHandler(mpd_Connection * connection)
1838 {
1839 	return mpd_getNextReturnElementNamed(connection, "handler");
1840 }
1841 
mpd_sendTagTypesCommand(mpd_Connection * connection)1842 void mpd_sendTagTypesCommand(mpd_Connection * connection)
1843 {
1844 	mpd_executeCommand(connection, "tagtypes\n");
1845 }
1846 
mpd_getNextTagType(mpd_Connection * connection)1847 char * mpd_getNextTagType(mpd_Connection * connection)
1848 {
1849 	return mpd_getNextReturnElementNamed(connection, "tagtype");
1850 }
1851 
mpd_startSearch(mpd_Connection * connection,int exact)1852 void mpd_startSearch(mpd_Connection *connection, int exact)
1853 {
1854 	if (connection->request) {
1855 		strcpy(connection->errorStr, "search already in progress");
1856 		connection->error = 1;
1857 		return;
1858 	}
1859 
1860 	if (exact) connection->request = strdup("find");
1861 	else connection->request = strdup("search");
1862 }
1863 
mpd_startStatsSearch(mpd_Connection * connection)1864 void mpd_startStatsSearch(mpd_Connection *connection)
1865 {
1866 	if (connection->request) {
1867 		strcpy(connection->errorStr, "search already in progress");
1868 		connection->error = 1;
1869 		return;
1870 	}
1871 
1872 	connection->request = strdup("count");
1873 }
1874 
mpd_startPlaylistSearch(mpd_Connection * connection,int exact)1875 void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
1876 {
1877 	if (connection->request) {
1878 		strcpy(connection->errorStr, "search already in progress");
1879 		connection->error = 1;
1880 		return;
1881 	}
1882 
1883 	if (exact) connection->request = strdup("playlistfind");
1884 	else connection->request = strdup("playlistsearch");
1885 }
1886 
mpd_startFieldSearch(mpd_Connection * connection,int type)1887 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1888 {
1889 	char *strtype;
1890 	int len;
1891 
1892 	if (connection->request) {
1893 		strcpy(connection->errorStr, "search already in progress");
1894 		connection->error = 1;
1895 		return;
1896 	}
1897 
1898 	if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1899 		strcpy(connection->errorStr, "invalid type specified");
1900 		connection->error = 1;
1901 		return;
1902 	}
1903 
1904 	strtype = mpdTagItemKeys[type];
1905 
1906 	len = 5+strlen(strtype)+1;
1907 	connection->request = malloc(len);
1908 
1909 	snprintf(connection->request, len, "list %c%s",
1910 	         tolower(strtype[0]), strtype+1);
1911 }
1912 
mpd_addConstraintSearch(mpd_Connection * connection,int type,const char * name)1913 void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name)
1914 {
1915 	char *strtype;
1916 	char *arg;
1917 	int len;
1918 	char *string;
1919 
1920 	if (!connection->request) {
1921 		strcpy(connection->errorStr, "no search in progress");
1922 		connection->error = 1;
1923 		return;
1924 	}
1925 
1926 	if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1927 		strcpy(connection->errorStr, "invalid type specified");
1928 		connection->error = 1;
1929 		return;
1930 	}
1931 
1932 	if (name == NULL) {
1933 		strcpy(connection->errorStr, "no name specified");
1934 		connection->error = 1;
1935 		return;
1936 	}
1937 
1938 	string = strdup(connection->request);
1939 	strtype = mpdTagItemKeys[type];
1940 	arg = mpd_sanitizeArg(name);
1941 
1942 	len = strlen(string)+1+strlen(strtype)+2+strlen(arg)+2;
1943 	connection->request = realloc(connection->request, len);
1944 	snprintf(connection->request, len, "%s %c%s \"%s\"",
1945 	         string, tolower(strtype[0]), strtype+1, arg);
1946 
1947 	free(string);
1948 	free(arg);
1949 }
1950 
mpd_commitSearch(mpd_Connection * connection)1951 void mpd_commitSearch(mpd_Connection *connection)
1952 {
1953 	int len;
1954 
1955 	if (!connection->request) {
1956 		strcpy(connection->errorStr, "no search in progress");
1957 		connection->error = 1;
1958 		return;
1959 	}
1960 
1961 	len = strlen(connection->request)+2;
1962 	connection->request = realloc(connection->request, len);
1963 	connection->request[len-2] = '\n';
1964 	connection->request[len-1] = '\0';
1965 	mpd_sendInfoCommand(connection, connection->request);
1966 
1967 	free(connection->request);
1968 	connection->request = NULL;
1969 }
1970 
1971 /**
1972  * @param connection a MpdConnection
1973  * @param path	the path to the playlist.
1974  *
1975  * List the content, with full metadata, of a stored playlist.
1976  *
1977  */
mpd_sendListPlaylistInfoCommand(mpd_Connection * connection,const char * path)1978 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection,const char *path)
1979 {
1980 	char *arg = mpd_sanitizeArg(path);
1981 	int len = strlen("listplaylistinfo")+2+strlen(arg)+3;
1982 	char *query = malloc(len);
1983 	snprintf(query, len, "listplaylistinfo \"%s\"\n", arg);
1984 	mpd_sendInfoCommand(connection, query);
1985 	free(arg);
1986 	free(query);
1987 }
1988 
1989 /**
1990  * @param connection a MpdConnection
1991  * @param path	the path to the playlist.
1992  *
1993  * List the content of a stored playlist.
1994  *
1995  */
mpd_sendListPlaylistCommand(mpd_Connection * connection,const char * path)1996 void mpd_sendListPlaylistCommand(mpd_Connection *connection,const char *path)
1997 {
1998 	char *arg = mpd_sanitizeArg(path);
1999 	int len = strlen("listplaylist")+2+strlen(arg)+3;
2000 	char *query = malloc(len);
2001 	snprintf(query, len, "listplaylist \"%s\"\n", arg);
2002 	mpd_sendInfoCommand(connection, query);
2003 	free(arg);
2004 	free(query);
2005 }
2006 
mpd_sendPlaylistClearCommand(mpd_Connection * connection,const char * path)2007 void mpd_sendPlaylistClearCommand(mpd_Connection *connection,const char *path)
2008 {
2009 	char *sPath = mpd_sanitizeArg(path);
2010 	int len = strlen("playlistclear")+2+strlen(sPath)+3;
2011 	char *string = malloc(len);
2012 	snprintf(string, len, "playlistclear \"%s\"\n", sPath);
2013 	mpd_executeCommand(connection, string);
2014 	free(sPath);
2015 	free(string);
2016 }
2017 
mpd_sendPlaylistAddCommand(mpd_Connection * connection,const char * playlist,const char * path)2018 void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
2019                                 const char *playlist,const char *path)
2020 {
2021 	char *sPlaylist = mpd_sanitizeArg(playlist);
2022 	char *sPath = mpd_sanitizeArg(path);
2023 	int len = strlen("playlistadd")+2+strlen(sPlaylist)+3+strlen(sPath)+3;
2024 	char *string = malloc(len);
2025 	snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath);
2026 	mpd_executeCommand(connection, string);
2027 	free(sPlaylist);
2028 	free(sPath);
2029 	free(string);
2030 }
2031 
mpd_sendPlaylistMoveCommand(mpd_Connection * connection,const char * playlist,int from,int to)2032 void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
2033                                  const char *playlist, int from, int to)
2034 {
2035 	char *sPlaylist = mpd_sanitizeArg(playlist);
2036 	int len = strlen("playlistmove")+
2037 	          2+strlen(sPlaylist)+3+INTLEN+3+INTLEN+3;
2038 	char *string = malloc(len);
2039 	snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2040 	         sPlaylist, from, to);
2041 	mpd_executeCommand(connection, string);
2042 	free(sPlaylist);
2043 	free(string);
2044 }
2045 
mpd_sendPlaylistDeleteCommand(mpd_Connection * connection,const char * playlist,int pos)2046 void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
2047                                    const char *playlist, int pos)
2048 {
2049 	char *sPlaylist = mpd_sanitizeArg(playlist);
2050 	int len = strlen("playlistdelete")+2+strlen(sPlaylist)+3+INTLEN+3;
2051 	char *string = malloc(len);
2052 	snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos);
2053 	mpd_executeCommand(connection, string);
2054 	free(sPlaylist);
2055 	free(string);
2056 }
mpd_sendClearErrorCommand(mpd_Connection * connection)2057 void mpd_sendClearErrorCommand(mpd_Connection * connection) {
2058 	mpd_executeCommand(connection,"clearerror\n");
2059 }
2060 
2061 
mpd_sendGetEventsCommand(mpd_Connection * connection)2062 void mpd_sendGetEventsCommand(mpd_Connection *connection) {
2063     mpd_executeCommand(connection, "idle\nnoidle\n");
2064 //    mpd_executeCommand(connection, "noidle\n");
2065 }
2066 
mpd_getNextEvent(mpd_Connection * connection)2067 char * mpd_getNextEvent(mpd_Connection *connection)
2068 {
2069     return mpd_getNextReturnElementNamed(connection, "changed");
2070 }
2071 
mpd_sendListPlaylistsCommand(mpd_Connection * connection)2072 void mpd_sendListPlaylistsCommand(mpd_Connection * connection) {
2073     mpd_sendInfoCommand(connection, "listplaylists\n");
2074 }
2075 
mpd_getNextSticker(mpd_Connection * connection)2076 char * mpd_getNextSticker (mpd_Connection * connection)
2077 {
2078 	return mpd_getNextReturnElementNamed(connection, "sticker");
2079 }
mpd_sendGetSongSticker(mpd_Connection * connection,const char * song_path,const char * sticker)2080 void  mpd_sendGetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker)
2081 {
2082     char *sSong = mpd_sanitizeArg(song_path);
2083     char *sSticker = mpd_sanitizeArg(sticker);
2084     int len = strlen("sticker get song ")+strlen(sSong)+3+strlen(sSticker)+4;
2085     char *string = malloc(len);
2086     snprintf(string, len, "sticker get song \"%s\" \"%s\"\n", sSong, sSticker);
2087     mpd_executeCommand(connection, string);
2088     free(string);
2089     free(sSong);
2090     free(sSticker);
2091 }
2092 
mpd_sendSetSongSticker(mpd_Connection * connection,const char * song_path,const char * sticker,const char * value)2093 void mpd_sendSetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker, const char *value)
2094 {
2095 
2096     char *sSong = mpd_sanitizeArg(song_path);
2097     char *sSticker = mpd_sanitizeArg(sticker);
2098     char *sValue = mpd_sanitizeArg(value);
2099     int len = strlen("sticker set song ")+strlen(sSong)+3+strlen(sSticker)+3+strlen(sValue)+4;
2100     char *string = malloc(len);
2101     snprintf(string, len, "sticker set song \"%s\" \"%s\" \"%s\"\n", sSong, sSticker,sValue);
2102     mpd_sendInfoCommand(connection, string);
2103     free(string);
2104     free(sSong);
2105     free(sSticker);
2106     free(sValue);
2107 }
2108 
mpd_sendSetReplayGainMode(mpd_Connection * connection,const char * mode)2109 void mpd_sendSetReplayGainMode(mpd_Connection *connection, const char *mode)
2110 {
2111     char *smode= mpd_sanitizeArg(mode);
2112     int len = strlen("replay_gain_mode ")+strlen(smode)+4;
2113     char *string = malloc(len);
2114     snprintf(string, len, "replay_gain_mode \"%s\"\n", smode);
2115     mpd_sendInfoCommand(connection, string);
2116     free(smode);
2117     free(string);
2118 }
mpd_sendReplayGainModeCommand(mpd_Connection * connection)2119 void mpd_sendReplayGainModeCommand(mpd_Connection *connection)
2120 {
2121 	mpd_executeCommand(connection, "replay_gain_status\n");
2122 }
mpd_getReplayGainMode(mpd_Connection * connection)2123 char *mpd_getReplayGainMode(mpd_Connection *connection)
2124 {
2125     return mpd_getNextReturnElementNamed(connection, "replay_gain_mode");
2126 }
2127