Hack in priority support. Patch 1.
[libmpd.git] / src / libmpdclient.c
blob97306122b4625d00dbb9ab560856fcaa0f47f980
1 /* libmpdclient
2 (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
3 This project's homepage is: http://www.musicpd.org
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
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.
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.
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.
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>
41 #include <string.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
45 #include <limits.h>
47 #include <glib.h>
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
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)
63 #define COMMAND_LIST 1
64 #define COMMAND_LIST_OK 2
66 #ifndef MPD_NO_GAI
67 # ifdef AI_ADDRCONFIG
68 # define MPD_HAVE_GAI
69 # endif
70 #endif
72 #ifndef WIN32
73 #include <sys/un.h>
74 #endif
76 #ifndef MSG_DONTWAIT
77 # define MSG_DONTWAIT 0
78 #endif
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
91 #ifdef WIN32
92 static int winsock_dll_error(mpd_Connection *connection)
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;
103 return 0;
106 static int do_connect_fail(mpd_Connection *connection,
107 const struct sockaddr *serv_addr, int addrlen)
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;
115 #elif defined(__solaris__) || defined(__sun__)
116 static int do_connect_fail(mpd_Connection *connection,
117 const struct sockaddr *serv_addr, int addrlen)
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;
126 #else /* !WIN32 (sane operating systems) */
127 static int do_connect_fail(mpd_Connection *connection,
128 const struct sockaddr *serv_addr, int addrlen)
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;
155 else {
156 /* Connecting timed out. */
157 fprintf(stderr, "Timeout or error() %d - %s\n", valopt, strerror(valopt));
158 return 1;
161 else {
162 /* Failed to connect */
163 fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno));
164 return 1;
168 return 0;
170 #endif /* !WIN32 */
172 #ifdef MPD_HAVE_GAI
173 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
174 float timeout)
176 int error;
177 char service[INTLEN+1];
178 struct addrinfo hints;
179 struct addrinfo *res = NULL;
180 struct addrinfo *addrinfo = NULL;
183 * Setup hints
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;
194 snprintf(service, sizeof(service), "%i", port);
196 error = getaddrinfo(host, service, &hints, &addrinfo);
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;
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;
221 mpd_setConnectionTimeout(connection, timeout);
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;
232 break;
235 freeaddrinfo(addrinfo);
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;
243 return -1;
246 return 0;
248 #else /* !MPD_HAVE_GAI */
249 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
250 float timeout)
252 struct hostent * he;
253 struct sockaddr * dest;
254 int destlen;
255 struct sockaddr_in sin;
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;
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);
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;
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;
291 mpd_setConnectionTimeout(connection,timeout);
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;
302 return 0;
304 #endif /* !MPD_HAVE_GAI */
306 char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
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"
324 static char * mpd_sanitizeArg(const char * arg) {
325 size_t i;
326 char * ret;
327 register const char *c;
328 register char *rc;
330 /* instead of counting in that loop above, just
331 * use a bit more memory and half running time
333 ret = malloc(strlen(arg) * 2 + 1);
335 c = arg;
336 rc = ret;
337 for(i = strlen(arg)+1; i != 0; --i) {
338 if(*c=='"' || *c=='\\')
339 *rc++ = '\\';
340 *(rc++) = *(c++);
343 return ret;
346 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
348 mpd_ReturnElement* ret = g_slice_new(mpd_ReturnElement);
350 ret->name = strdup(name);
351 ret->value = strdup(value);
353 return ret;
356 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
357 free(re->name);
358 free(re->value);
359 g_slice_free(mpd_ReturnElement, re);
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);
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;
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;
383 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
385 for(i=0;i<3;i++) {
386 if(tmp) connection->version[i] = strtol(tmp,&test,10);
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;
397 tmp = ++test;
400 return 0;
403 #ifndef WIN32
404 static int mpd_connect_un(mpd_Connection * connection,
405 const char * host, float timeout)
407 int error, flags;
408 size_t path_length;
409 struct sockaddr_un saun;
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;
418 saun.sun_family = AF_UNIX;
419 memcpy(saun.sun_path, host, path_length + 1);
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;
428 mpd_setConnectionTimeout(connection, timeout);
430 flags = fcntl(connection->sock, F_GETFL, 0);
431 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
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;
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;
446 return 0;
448 #endif /* WIN32 */
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,"");
461 if (winsock_dll_error(connection))
462 return connection;
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;
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;
491 connection->buflen+=readed;
492 connection->buffer[connection->buflen] = '\0';
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;
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;
513 *rt = '\0';
514 output = strdup(connection->buffer);
515 strcpy(connection->buffer,rt+1);
516 connection->buflen = strlen(connection->buffer);
518 if(mpd_parseWelcome(connection,host,port,output) == 0) connection->doneProcessing = 1;
520 free(output);
522 return connection;
525 void mpd_clearError(mpd_Connection * connection) {
526 connection->error = 0;
527 connection->errorStr[0] = '\0';
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();
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);
545 if(!connection->doneProcessing && !connection->commandList) {
546 strcpy(connection->errorStr,"not done processing current command");
547 connection->error = 1;
548 return;
551 mpd_clearError(connection);
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)
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;
569 else {
570 commandPtr+=ret;
571 commandLen-=ret;
574 if(commandLen<=0) break;
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;
584 if(!connection->commandList) connection->doneProcessing = 0;
585 else if(connection->commandList == COMMAND_LIST_OK) {
586 connection->listOks++;
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;
603 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
604 connection->returnElement = NULL;
606 if(connection->doneProcessing || (connection->listOks &&
607 connection->doneListOk))
609 strcpy(connection->errorStr,"already done processing current command");
610 connection->error = 1;
611 return;
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;
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;
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;
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;
654 connection->buflen+=readed;
655 connection->buffer[connection->buflen] = '\0';
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;
667 *rt = '\0';
668 output = connection->buffer+connection->bufstart;
669 connection->bufstart = rt - connection->buffer + 1;
671 if(strcmp(output,"OK")==0) {
672 if(connection->listOks > 0) {
673 strcpy(connection->errorStr, "expected more list_OK's");
674 connection->error = 1;
676 connection->listOks = 0;
677 connection->doneProcessing = 1;
678 connection->doneListOk = 0;
679 return;
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;
688 else {
689 connection->doneListOk = 1;
690 connection->listOks--;
692 return;
695 if(strncmp(output,"ACK",strlen("ACK"))==0) {
696 char * test;
697 char * needle;
698 int val;
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;
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;
718 tok = strchr(output, ':');
719 if (!tok) return;
720 pos = tok - output;
721 value = ++tok;
722 name = output;
723 name[pos] = '\0';
725 if(value[0]==' ') {
726 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
728 else {
729 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
730 "error parsing: %s:%s",name,value);
731 connection->error = 1;
735 void mpd_finishCommand(mpd_Connection * connection) {
736 while(!connection->doneProcessing) {
737 if(connection->doneListOk) connection->doneListOk = 0;
738 mpd_getNextReturnElement(connection);
742 static void mpd_finishListOkCommand(mpd_Connection * connection) {
743 while(!connection->doneProcessing && connection->listOks &&
744 !connection->doneListOk)
746 mpd_getNextReturnElement(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;
757 void mpd_sendStatusCommand(mpd_Connection * connection) {
758 mpd_executeCommand(connection,"status\n");
761 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
762 /*mpd_executeCommand(connection,"status\n");
764 if(connection->error) return NULL;*/
766 if(connection->doneProcessing || (connection->listOks &&
767 connection->doneListOk))
769 return NULL;
772 if(!connection->returnElement) mpd_getNextReturnElement(connection);
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;
784 if(connection->error) {
785 g_slice_free(mpd_Status, status);
786 return NULL;
788 while(connection->returnElement) {
789 mpd_ReturnElement * re = connection->returnElement;
790 if(strcmp(re->name,"volume")==0) {
791 status->volume = atoi(re->value);
793 else if(strcmp(re->name,"repeat")==0) {
794 status->repeat = atoi(re->value);
796 else if(strcmp(re->name,"single")==0) {
797 status->single = atoi(re->value);
799 else if(strcmp(re->name,"consume")==0) {
800 status->consume = atoi(re->value);
802 else if(strcmp(re->name,"random")==0) {
803 status->random = atoi(re->value);
805 else if(strcmp(re->name,"playlist")==0) {
806 status->playlist = strtol(re->value,NULL,10);
808 else if(strcmp(re->name,"playlistlength")==0) {
809 status->playlistLength = atoi(re->value);
811 else if(strcmp(re->name,"bitrate")==0) {
812 status->bitRate = atoi(re->value);
814 else if(strcmp(re->name,"state")==0) {
815 if(strcmp(re->value,"play")==0) {
816 status->state = MPD_STATUS_STATE_PLAY;
818 else if(strcmp(re->value,"stop")==0) {
819 status->state = MPD_STATUS_STATE_STOP;
821 else if(strcmp(re->value,"pause")==0) {
822 status->state = MPD_STATUS_STATE_PAUSE;
824 else {
825 status->state = MPD_STATUS_STATE_UNKNOWN;
828 else if(strcmp(re->name,"song")==0) {
829 status->song = atoi(re->value);
831 else if(strcmp(re->name,"songid")==0) {
832 status->songid = atoi(re->value);
834 else if(strcmp(re->name,"nextsong")==0) {
835 status->nextsong = atoi(re->value);
837 else if(strcmp(re->name,"nextsongid")==0) {
838 status->nextsongid = atoi(re->value);
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);
849 else if(strcmp(re->name,"error")==0) {
850 status->error = strdup(re->value);
852 else if(strcmp(re->name,"xfade")==0) {
853 status->crossfade = atoi(re->value);
855 else if(strcmp(re->name,"updating_db")==0) {
856 status->updatingDb = atoi(re->value);
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);
869 mpd_getNextReturnElement(connection);
870 if(connection->error) {
871 g_slice_free(mpd_Status, status);
872 return NULL;
876 if(connection->error) {
877 g_slice_free(mpd_Status, status);
878 return NULL;
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;
887 return status;
890 void mpd_freeStatus(mpd_Status * status) {
891 if(status->error) free(status->error);
892 g_slice_free(mpd_Status, status);
895 void mpd_sendStatsCommand(mpd_Connection * connection) {
896 mpd_executeCommand(connection,"stats\n");
899 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
900 /*mpd_executeCommand(connection,"stats\n");
902 if(connection->error) return NULL;*/
904 if(connection->doneProcessing || (connection->listOks &&
905 connection->doneListOk))
907 return NULL;
910 if(!connection->returnElement) mpd_getNextReturnElement(connection);
912 mpd_Stats* stats = g_slice_new0(mpd_Stats);
914 if(connection->error) {
915 mpd_freeStats(stats);
916 return NULL;
918 while(connection->returnElement) {
919 mpd_ReturnElement * re = connection->returnElement;
920 if(strcmp(re->name,"artists")==0) {
921 stats->numberOfArtists = atoi(re->value);
923 else if(strcmp(re->name,"albums")==0) {
924 stats->numberOfAlbums = atoi(re->value);
926 else if(strcmp(re->name,"songs")==0) {
927 stats->numberOfSongs = atoi(re->value);
929 else if(strcmp(re->name,"uptime")==0) {
930 stats->uptime = strtol(re->value,NULL,10);
932 else if(strcmp(re->name,"db_update")==0) {
933 stats->dbUpdateTime = strtol(re->value,NULL,10);
935 else if(strcmp(re->name,"playtime")==0) {
936 stats->playTime = strtol(re->value,NULL,10);
938 else if(strcmp(re->name,"db_playtime")==0) {
939 stats->dbPlayTime = strtol(re->value,NULL,10);
942 mpd_getNextReturnElement(connection);
943 if(connection->error) {
944 mpd_freeStats(stats);
945 return NULL;
949 if(connection->error) {
950 mpd_freeStats(stats);
951 return NULL;
954 return stats;
957 void mpd_freeStats(mpd_Stats * stats) {
958 g_slice_free(mpd_Stats, stats);
961 mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection)
963 if (connection->doneProcessing ||
964 (connection->listOks && connection->doneListOk)) {
965 return NULL;
968 if (!connection->returnElement) mpd_getNextReturnElement(connection);
970 if (connection->error)
971 return NULL;
973 mpd_ReturnElement* re;
974 mpd_SearchStats* stats = g_slice_new0(mpd_SearchStats);
976 while (connection->returnElement) {
977 re = connection->returnElement;
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);
985 mpd_getNextReturnElement(connection);
986 if (connection->error) {
987 mpd_freeSearchStats(stats);
988 return NULL;
992 if (connection->error) {
993 mpd_freeSearchStats(stats);
994 return NULL;
997 return stats;
1000 void mpd_freeSearchStats(mpd_SearchStats * stats)
1002 g_slice_free(mpd_SearchStats, stats);
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);
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 ret->priority = MPD_SONG_NO_PRIORITY;;
1027 return ret;
1030 void mpd_freeSong(mpd_Song * song) {
1031 mpd_finishSong(song);
1032 g_slice_free(mpd_Song, song);
1035 mpd_Song * mpd_songDup(const mpd_Song * song) {
1036 mpd_Song * ret = mpd_newSong();
1038 if(song->file) ret->file = strdup(song->file);
1039 if(song->artist) ret->artist = strdup(song->artist);
1040 if(song->album) ret->album = strdup(song->album);
1041 if(song->title) ret->title = strdup(song->title);
1042 if(song->track) ret->track = strdup(song->track);
1043 if(song->name) ret->name = strdup(song->name);
1044 if(song->date) ret->date = strdup(song->date);
1045 if(song->genre) ret->genre= strdup(song->genre);
1046 if(song->composer) ret->composer= strdup(song->composer);
1047 if(song->performer) ret->performer = strdup(song->performer);
1048 if(song->disc) ret->disc = strdup(song->disc);
1049 if(song->comment) ret->comment = strdup(song->comment);
1050 if(song->albumartist) ret->albumartist = strdup(song->albumartist);
1051 ret->time = song->time;
1052 ret->pos = song->pos;
1053 ret->id = song->id;
1054 ret->priority = song->priority;
1056 return ret;
1060 static void mpd_finishDirectory(mpd_Directory * directory) {
1061 if(directory->path) free(directory->path);
1064 mpd_Directory * mpd_newDirectory(void) {
1065 return g_slice_new0(mpd_Directory);
1068 void mpd_freeDirectory(mpd_Directory * directory) {
1069 mpd_finishDirectory(directory);
1070 g_slice_free(mpd_Directory, directory);
1073 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
1074 mpd_Directory * ret = mpd_newDirectory();
1076 if(directory->path) ret->path = strdup(directory->path);
1078 return ret;
1081 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
1082 if(playlist->path) free(playlist->path);
1083 if(playlist->mtime) free(playlist->mtime);
1086 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
1087 return g_slice_new0(mpd_PlaylistFile);
1090 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
1091 mpd_finishPlaylistFile(playlist);
1092 g_slice_free(mpd_PlaylistFile, playlist);
1095 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1096 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1098 if(playlist->path) ret->path = strdup(playlist->path);
1099 if(playlist->mtime) ret->mtime = strdup(playlist->mtime);
1101 return ret;
1104 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1105 if(entity->info.directory) {
1106 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1107 mpd_freeDirectory(entity->info.directory);
1109 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1110 mpd_freeSong(entity->info.song);
1112 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1113 mpd_freePlaylistFile(entity->info.playlistFile);
1118 mpd_InfoEntity * mpd_newInfoEntity(void) {
1119 return g_slice_new0(mpd_InfoEntity);
1122 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1123 mpd_finishInfoEntity(entity);
1124 g_slice_free(mpd_InfoEntity, entity);
1127 static void mpd_sendInfoCommand(mpd_Connection * connection,const char * command) {
1128 mpd_executeCommand(connection,command);
1131 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1132 mpd_InfoEntity * entity = NULL;
1134 if(connection->doneProcessing || (connection->listOks &&
1135 connection->doneListOk))
1137 return NULL;
1140 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1142 if(connection->returnElement) {
1143 if(strcmp(connection->returnElement->name,"file")==0) {
1144 entity = mpd_newInfoEntity();
1145 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1146 entity->info.song = mpd_newSong();
1147 entity->info.song->file =
1148 strdup(connection->returnElement->value);
1150 else if(strcmp(connection->returnElement->name,
1151 "directory")==0) {
1152 entity = mpd_newInfoEntity();
1153 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1154 entity->info.directory = mpd_newDirectory();
1155 entity->info.directory->path =
1156 strdup(connection->returnElement->value);
1158 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1159 entity = mpd_newInfoEntity();
1160 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1161 entity->info.playlistFile = mpd_newPlaylistFile();
1162 entity->info.playlistFile->path =
1163 strdup(connection->returnElement->value);
1165 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1166 entity = mpd_newInfoEntity();
1167 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1168 entity->info.song = mpd_newSong();
1169 entity->info.song->pos = atoi(connection->returnElement->value);
1171 else {
1172 connection->error = 1;
1173 strcpy(connection->errorStr,"problem parsing song info");
1174 return NULL;
1177 else return NULL;
1179 mpd_getNextReturnElement(connection);
1180 while(connection->returnElement) {
1181 mpd_ReturnElement * re = connection->returnElement;
1183 if(strcmp(re->name,"file")==0) return entity;
1184 else if(strcmp(re->name,"directory")==0) return entity;
1185 else if(strcmp(re->name,"playlist")==0) return entity;
1186 else if(strcmp(re->name,"cpos")==0) return entity;
1188 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1189 strlen(re->value)) {
1190 if(strcmp(re->name,"Artist")==0) {
1191 if(entity->info.song->artist) {
1192 int length = strlen(entity->info.song->artist);
1193 entity->info.song->artist = realloc(entity->info.song->artist,
1194 length + strlen(re->value) + 3);
1195 strcpy(&((entity->info.song->artist)[length]), ", ");
1196 strcpy(&((entity->info.song->artist)[length + 2]), re->value);
1198 else {
1199 entity->info.song->artist = strdup(re->value);
1202 else if(!entity->info.song->album &&
1203 strcmp(re->name,"Album")==0) {
1204 entity->info.song->album = strdup(re->value);
1206 else if(!entity->info.song->title &&
1207 strcmp(re->name,"Title")==0) {
1208 entity->info.song->title = strdup(re->value);
1210 else if(!entity->info.song->track &&
1211 strcmp(re->name,"Track")==0) {
1212 entity->info.song->track = strdup(re->value);
1214 else if(!entity->info.song->name &&
1215 strcmp(re->name,"Name")==0) {
1216 entity->info.song->name = strdup(re->value);
1218 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1219 strcmp(re->name,"Time")==0) {
1220 entity->info.song->time = atoi(re->value);
1222 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1223 strcmp(re->name,"Pos")==0) {
1224 entity->info.song->pos = atoi(re->value);
1226 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1227 strcmp(re->name,"Id")==0) {
1228 entity->info.song->id = atoi(re->value);
1230 else if (!(entity->info.song->priority >= 0) &&
1231 strcmp(re->name, "Prio") == 0) {
1232 entity->info.song->priority = atoi(re->value);
1234 else if(!entity->info.song->date &&
1235 strcmp(re->name, "Date") == 0) {
1236 entity->info.song->date = strdup(re->value);
1238 else if(!entity->info.song->genre &&
1239 strcmp(re->name, "Genre") == 0) {
1240 if(entity->info.song->genre) {
1241 int length = strlen(entity->info.song->genre);
1242 entity->info.song->genre = realloc(entity->info.song->genre,
1243 length + strlen(re->value) + 4);
1244 strcpy(&((entity->info.song->genre)[length]), ", ");
1245 strcpy(&((entity->info.song->genre)[length + 3]), re->value);
1247 else {
1248 entity->info.song->genre = strdup(re->value);
1251 else if(strcmp(re->name, "Composer") == 0) {
1252 if(entity->info.song->composer) {
1253 int length = strlen(entity->info.song->composer);
1254 entity->info.song->composer = realloc(entity->info.song->composer,
1255 length + strlen(re->value) + 3);
1256 strcpy(&((entity->info.song->composer)[length]), ", ");
1257 strcpy(&((entity->info.song->composer)[length + 2]), re->value);
1259 else {
1260 entity->info.song->composer = strdup(re->value);
1263 else if(strcmp(re->name, "Performer") == 0) {
1264 if(entity->info.song->performer) {
1265 int length = strlen(entity->info.song->performer);
1266 entity->info.song->performer = realloc(entity->info.song->performer,
1267 length + strlen(re->value) + 3);
1268 strcpy(&((entity->info.song->performer)[length]), ", ");
1269 strcpy(&((entity->info.song->performer)[length + 2]), re->value);
1271 else {
1272 entity->info.song->performer = strdup(re->value);
1275 else if(!entity->info.song->disc &&
1276 strcmp(re->name, "Disc") == 0) {
1277 entity->info.song->disc = strdup(re->value);
1279 else if(!entity->info.song->comment &&
1280 strcmp(re->name, "Comment") == 0) {
1281 entity->info.song->comment = strdup(re->value);
1284 else if(!entity->info.song->albumartist &&
1285 strcmp(re->name, "AlbumArtist") == 0) {
1286 entity->info.song->albumartist = strdup(re->value);
1289 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1291 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1292 if(!entity->info.playlistFile->mtime &&
1293 strcmp(re->name, "Last-Modified") == 0) {
1294 entity->info.playlistFile->mtime = strdup(re->value);
1298 mpd_getNextReturnElement(connection);
1301 return entity;
1304 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1305 const char * name)
1307 if(connection->doneProcessing || (connection->listOks &&
1308 connection->doneListOk))
1310 return NULL;
1313 mpd_getNextReturnElement(connection);
1314 while(connection->returnElement) {
1315 mpd_ReturnElement * re = connection->returnElement;
1317 if(strcmp(re->name,name)==0) return strdup(re->value);
1318 mpd_getNextReturnElement(connection);
1321 return NULL;
1324 char *mpd_getNextTag(mpd_Connection *connection, int type)
1326 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES ||
1327 type == MPD_TAG_ITEM_ANY)
1328 return NULL;
1329 if (type == MPD_TAG_ITEM_FILENAME)
1330 return mpd_getNextReturnElementNamed(connection, "file");
1331 return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
1334 char * mpd_getNextArtist(mpd_Connection * connection) {
1335 return mpd_getNextReturnElementNamed(connection,"Artist");
1338 char * mpd_getNextAlbum(mpd_Connection * connection) {
1339 return mpd_getNextReturnElementNamed(connection,"Album");
1342 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1343 int len = strlen("playlistinfo")+2+INTLEN+3;
1344 char *string = malloc(len);
1345 snprintf(string, len, "playlistinfo \"%i\"\n", songPos);
1346 mpd_sendInfoCommand(connection,string);
1347 free(string);
1350 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1351 int len = strlen("playlistid")+2+INTLEN+3;
1352 char *string = malloc(len);
1353 snprintf(string, len, "playlistid \"%i\"\n", id);
1354 mpd_sendInfoCommand(connection, string);
1355 free(string);
1358 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1359 int len = strlen("plchanges")+2+LONGLONGLEN+3;
1360 char *string = malloc(len);
1361 snprintf(string, len, "plchanges \"%lld\"\n", playlist);
1362 mpd_sendInfoCommand(connection,string);
1363 free(string);
1366 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1367 int len = strlen("plchangesposid")+2+LONGLONGLEN+3;
1368 char *string = malloc(len);
1369 snprintf(string, len, "plchangesposid \"%lld\"\n", playlist);
1370 mpd_sendInfoCommand(connection,string);
1371 free(string);
1374 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1375 char * sDir = mpd_sanitizeArg(dir);
1376 int len = strlen("listall")+2+strlen(sDir)+3;
1377 char *string = malloc(len);
1378 snprintf(string, len, "listall \"%s\"\n", sDir);
1379 mpd_sendInfoCommand(connection,string);
1380 free(string);
1381 free(sDir);
1384 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1385 char * sDir = mpd_sanitizeArg(dir);
1386 int len = strlen("listallinfo")+2+strlen(sDir)+3;
1387 char *string = malloc(len);
1388 snprintf(string, len, "listallinfo \"%s\"\n", sDir);
1389 mpd_sendInfoCommand(connection,string);
1390 free(string);
1391 free(sDir);
1394 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1395 char * sDir = mpd_sanitizeArg(dir);
1396 int len = strlen("lsinfo")+2+strlen(sDir)+3;
1397 char *string = malloc(len);
1398 snprintf(string, len, "lsinfo \"%s\"\n", sDir);
1399 mpd_sendInfoCommand(connection,string);
1400 free(string);
1401 free(sDir);
1404 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1405 mpd_executeCommand(connection,"currentsong\n");
1408 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1409 const char * str)
1411 mpd_startSearch(connection, 0);
1412 mpd_addConstraintSearch(connection, table, str);
1413 mpd_commitSearch(connection);
1416 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1417 const char * str)
1419 mpd_startSearch(connection, 1);
1420 mpd_addConstraintSearch(connection, table, str);
1421 mpd_commitSearch(connection);
1424 void mpd_sendListCommand(mpd_Connection * connection, int table,
1425 const char * arg1)
1427 char st[10];
1428 int len;
1429 char *string;
1430 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1431 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1432 else {
1433 connection->error = 1;
1434 strcpy(connection->errorStr,"unknown table for list");
1435 return;
1437 if(arg1) {
1438 char * sanitArg1 = mpd_sanitizeArg(arg1);
1439 len = strlen("list")+1+strlen(sanitArg1)+2+strlen(st)+3;
1440 string = malloc(len);
1441 snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1);
1442 free(sanitArg1);
1444 else {
1445 len = strlen("list")+1+strlen(st)+2;
1446 string = malloc(len);
1447 snprintf(string, len, "list %s\n", st);
1449 mpd_sendInfoCommand(connection,string);
1450 free(string);
1453 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1454 char * sFile = mpd_sanitizeArg(file);
1455 int len = strlen("add")+2+strlen(sFile)+3;
1456 char *string = malloc(len);
1457 snprintf(string, len, "add \"%s\"\n", sFile);
1458 mpd_executeCommand(connection,string);
1459 free(string);
1460 free(sFile);
1463 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1465 int retval = -1;
1466 char *sFile = mpd_sanitizeArg(file);
1467 int len = strlen("addid")+2+strlen(sFile)+3;
1468 char *string = malloc(len);
1470 snprintf(string, len, "addid \"%s\"\n", sFile);
1471 mpd_sendInfoCommand(connection, string);
1472 free(string);
1473 free(sFile);
1475 string = mpd_getNextReturnElementNamed(connection, "Id");
1476 if (string) {
1477 retval = atoi(string);
1478 free(string);
1481 return retval;
1484 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1485 int len = strlen("delete")+2+INTLEN+3;
1486 char *string = malloc(len);
1487 snprintf(string, len, "delete \"%i\"\n", songPos);
1488 mpd_sendInfoCommand(connection,string);
1489 free(string);
1492 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1493 int len = strlen("deleteid")+2+INTLEN+3;
1494 char *string = malloc(len);
1495 snprintf(string, len, "deleteid \"%i\"\n", id);
1496 mpd_sendInfoCommand(connection,string);
1497 free(string);
1500 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1501 char * sName = mpd_sanitizeArg(name);
1502 int len = strlen("save")+2+strlen(sName)+3;
1503 char *string = malloc(len);
1504 snprintf(string, len, "save \"%s\"\n", sName);
1505 mpd_executeCommand(connection,string);
1506 free(string);
1507 free(sName);
1510 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1511 char * sName = mpd_sanitizeArg(name);
1512 int len = strlen("load")+2+strlen(sName)+3;
1513 char *string = malloc(len);
1514 snprintf(string, len, "load \"%s\"\n", sName);
1515 mpd_executeCommand(connection,string);
1516 free(string);
1517 free(sName);
1520 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1521 char * sName = mpd_sanitizeArg(name);
1522 int len = strlen("rm")+2+strlen(sName)+3;
1523 char *string = malloc(len);
1524 snprintf(string, len, "rm \"%s\"\n", sName);
1525 mpd_executeCommand(connection,string);
1526 free(string);
1527 free(sName);
1530 void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
1531 const char *to)
1533 char *sFrom = mpd_sanitizeArg(from);
1534 char *sTo = mpd_sanitizeArg(to);
1535 int len = strlen("rename")+2+strlen(sFrom)+3+strlen(sTo)+3;
1536 char *string = malloc(len);
1537 snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo);
1538 mpd_executeCommand(connection, string);
1539 free(string);
1540 free(sFrom);
1541 free(sTo);
1544 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1545 mpd_executeCommand(connection,"shuffle\n");
1548 void mpd_sendClearCommand(mpd_Connection * connection) {
1549 mpd_executeCommand(connection,"clear\n");
1552 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1553 int len = strlen("play")+2+INTLEN+3;
1554 char *string = malloc(len);
1555 snprintf(string, len, "play \"%i\"\n", songPos);
1556 mpd_sendInfoCommand(connection,string);
1557 free(string);
1560 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1561 int len = strlen("playid")+2+INTLEN+3;
1562 char *string = malloc(len);
1563 snprintf(string, len, "playid \"%i\"\n", id);
1564 mpd_sendInfoCommand(connection,string);
1565 free(string);
1568 void mpd_sendStopCommand(mpd_Connection * connection) {
1569 mpd_executeCommand(connection,"stop\n");
1572 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1573 int len = strlen("pause")+2+INTLEN+3;
1574 char *string = malloc(len);
1575 snprintf(string, len, "pause \"%i\"\n", pauseMode);
1576 mpd_executeCommand(connection,string);
1577 free(string);
1580 void mpd_sendNextCommand(mpd_Connection * connection) {
1581 mpd_executeCommand(connection,"next\n");
1584 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1585 int len = strlen("move")+2+INTLEN+3+INTLEN+3;
1586 char *string = malloc(len);
1587 snprintf(string, len, "move \"%i\" \"%i\"\n", from, to);
1588 mpd_sendInfoCommand(connection,string);
1589 free(string);
1592 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1593 int len = strlen("moveid")+2+INTLEN+3+INTLEN+3;
1594 char *string = malloc(len);
1595 snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to);
1596 mpd_sendInfoCommand(connection,string);
1597 free(string);
1600 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1601 int len = strlen("swap")+2+INTLEN+3+INTLEN+3;
1602 char *string = malloc(len);
1603 snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2);
1604 mpd_sendInfoCommand(connection,string);
1605 free(string);
1608 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1609 int len = strlen("swapid")+2+INTLEN+3+INTLEN+3;
1610 char *string = malloc(len);
1611 snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2);
1612 mpd_sendInfoCommand(connection,string);
1613 free(string);
1616 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int seek_time) {
1617 int len = strlen("seek")+2+INTLEN+3+INTLEN+3;
1618 char *string = malloc(len);
1619 snprintf(string, len, "seek \"%i\" \"%i\"\n", song, seek_time);
1620 mpd_sendInfoCommand(connection,string);
1621 free(string);
1624 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int seek_time) {
1625 int len = strlen("seekid")+2+INTLEN+3+INTLEN+3;
1626 char *string = malloc(len);
1627 snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, seek_time);
1628 mpd_sendInfoCommand(connection,string);
1629 free(string);
1632 void mpd_sendUpdateCommand(mpd_Connection * connection,const char * path) {
1633 char * sPath = mpd_sanitizeArg(path);
1634 int len = strlen("update")+2+strlen(sPath)+3;
1635 char *string = malloc(len);
1636 snprintf(string, len, "update \"%s\"\n", sPath);
1637 mpd_sendInfoCommand(connection,string);
1638 free(string);
1639 free(sPath);
1642 int mpd_getUpdateId(mpd_Connection * connection) {
1643 char * jobid;
1644 int ret = 0;
1646 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1647 if(jobid) {
1648 ret = atoi(jobid);
1649 free(jobid);
1652 return ret;
1655 void mpd_sendPrevCommand(mpd_Connection * connection) {
1656 mpd_executeCommand(connection,"previous\n");
1659 void mpd_sendSingleCommand(mpd_Connection * connection, int singleMode) {
1660 int len = strlen("repeat")+2+INTLEN+3;
1661 char *string = malloc(len);
1662 snprintf(string, len, "single \"%i\"\n", singleMode);
1663 mpd_executeCommand(connection,string);
1664 free(string);
1667 void mpd_sendConsumeCommand(mpd_Connection * connection, int consumeMode) {
1668 int len = strlen("repeat")+2+INTLEN+3;
1669 char *string = malloc(len);
1670 snprintf(string, len, "consume \"%i\"\n", consumeMode);
1671 mpd_executeCommand(connection,string);
1672 free(string);
1675 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1676 int len = strlen("repeat")+2+INTLEN+3;
1677 char *string = malloc(len);
1678 snprintf(string, len, "repeat \"%i\"\n", repeatMode);
1679 mpd_executeCommand(connection,string);
1680 free(string);
1683 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1684 int len = strlen("random")+2+INTLEN+3;
1685 char *string = malloc(len);
1686 snprintf(string, len, "random \"%i\"\n", randomMode);
1687 mpd_executeCommand(connection,string);
1688 free(string);
1691 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1692 int len = strlen("setvol")+2+INTLEN+3;
1693 char *string = malloc(len);
1694 snprintf(string, len, "setvol \"%i\"\n", volumeChange);
1695 mpd_executeCommand(connection,string);
1696 free(string);
1699 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1700 int len = strlen("crossfade")+2+INTLEN+3;
1701 char *string = malloc(len);
1702 snprintf(string, len, "crossfade \"%i\"\n", seconds);
1703 mpd_executeCommand(connection,string);
1704 free(string);
1707 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1708 char * sPass = mpd_sanitizeArg(pass);
1709 int len = strlen("password")+2+strlen(sPass)+3;
1710 char *string = malloc(len);
1711 snprintf(string, len, "password \"%s\"\n", sPass);
1712 mpd_executeCommand(connection,string);
1713 free(string);
1714 free(sPass);
1717 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1718 if(connection->commandList) {
1719 strcpy(connection->errorStr,"already in command list mode");
1720 connection->error = 1;
1721 return;
1723 connection->commandList = COMMAND_LIST;
1724 mpd_executeCommand(connection,"command_list_begin\n");
1727 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1728 if(connection->commandList) {
1729 strcpy(connection->errorStr,"already in command list mode");
1730 connection->error = 1;
1731 return;
1733 connection->commandList = COMMAND_LIST_OK;
1734 mpd_executeCommand(connection,"command_list_ok_begin\n");
1735 connection->listOks = 0;
1738 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1739 if(!connection->commandList) {
1740 strcpy(connection->errorStr,"not in command list mode");
1741 connection->error = 1;
1742 return;
1744 connection->commandList = 0;
1745 mpd_executeCommand(connection,"command_list_end\n");
1748 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1749 mpd_executeCommand(connection,"outputs\n");
1752 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1753 if(connection->doneProcessing || (connection->listOks &&
1754 connection->doneListOk))
1756 return NULL;
1759 if(connection->error) return NULL;
1761 mpd_OutputEntity* output = g_slice_new0(mpd_OutputEntity);
1762 output->id = -10;
1764 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1766 while(connection->returnElement) {
1767 mpd_ReturnElement * re = connection->returnElement;
1768 if(strcmp(re->name,"outputid")==0) {
1769 if(output!=NULL && output->id>=0) return output;
1770 output->id = atoi(re->value);
1772 else if(strcmp(re->name,"outputname")==0) {
1773 output->name = strdup(re->value);
1775 else if(strcmp(re->name,"outputenabled")==0) {
1776 output->enabled = atoi(re->value);
1779 mpd_getNextReturnElement(connection);
1780 if(connection->error) {
1781 mpd_freeOutputElement(output);
1782 return NULL;
1786 return output;
1789 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1790 int len = strlen("enableoutput")+2+INTLEN+3;
1791 char *string = malloc(len);
1792 snprintf(string, len, "enableoutput \"%i\"\n", outputId);
1793 mpd_executeCommand(connection,string);
1794 free(string);
1797 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1798 int len = strlen("disableoutput")+2+INTLEN+3;
1799 char *string = malloc(len);
1800 snprintf(string, len, "disableoutput \"%i\"\n", outputId);
1801 mpd_executeCommand(connection,string);
1802 free(string);
1805 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1806 if(output->name)
1807 free(output->name);
1808 g_slice_free(mpd_OutputEntity, output);
1812 * mpd_sendNotCommandsCommand
1813 * odd naming, but it gets the not allowed commands
1816 void mpd_sendNotCommandsCommand(mpd_Connection * connection)
1818 mpd_executeCommand(connection, "notcommands\n");
1822 * mpd_sendCommandsCommand
1823 * odd naming, but it gets the allowed commands
1825 void mpd_sendCommandsCommand(mpd_Connection * connection)
1827 mpd_executeCommand(connection, "commands\n");
1831 * Get the next returned command
1833 char * mpd_getNextCommand(mpd_Connection * connection)
1835 return mpd_getNextReturnElementNamed(connection, "command");
1838 void mpd_sendUrlHandlersCommand(mpd_Connection * connection)
1840 mpd_executeCommand(connection, "urlhandlers\n");
1843 char * mpd_getNextHandler(mpd_Connection * connection)
1845 return mpd_getNextReturnElementNamed(connection, "handler");
1848 void mpd_sendTagTypesCommand(mpd_Connection * connection)
1850 mpd_executeCommand(connection, "tagtypes\n");
1853 char * mpd_getNextTagType(mpd_Connection * connection)
1855 return mpd_getNextReturnElementNamed(connection, "tagtype");
1858 void mpd_startSearch(mpd_Connection *connection, int exact)
1860 if (connection->request) {
1861 strcpy(connection->errorStr, "search already in progress");
1862 connection->error = 1;
1863 return;
1866 if (exact) connection->request = strdup("find");
1867 else connection->request = strdup("search");
1870 void mpd_startStatsSearch(mpd_Connection *connection)
1872 if (connection->request) {
1873 strcpy(connection->errorStr, "search already in progress");
1874 connection->error = 1;
1875 return;
1878 connection->request = strdup("count");
1881 void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
1883 if (connection->request) {
1884 strcpy(connection->errorStr, "search already in progress");
1885 connection->error = 1;
1886 return;
1889 if (exact) connection->request = strdup("playlistfind");
1890 else connection->request = strdup("playlistsearch");
1893 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1895 char *strtype;
1896 int len;
1898 if (connection->request) {
1899 strcpy(connection->errorStr, "search already in progress");
1900 connection->error = 1;
1901 return;
1904 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1905 strcpy(connection->errorStr, "invalid type specified");
1906 connection->error = 1;
1907 return;
1910 strtype = mpdTagItemKeys[type];
1912 len = 5+strlen(strtype)+1;
1913 connection->request = malloc(len);
1915 snprintf(connection->request, len, "list %c%s",
1916 tolower(strtype[0]), strtype+1);
1919 void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name)
1921 char *strtype;
1922 char *arg;
1923 int len;
1924 char *string;
1926 if (!connection->request) {
1927 strcpy(connection->errorStr, "no search in progress");
1928 connection->error = 1;
1929 return;
1932 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1933 strcpy(connection->errorStr, "invalid type specified");
1934 connection->error = 1;
1935 return;
1938 if (name == NULL) {
1939 strcpy(connection->errorStr, "no name specified");
1940 connection->error = 1;
1941 return;
1944 string = strdup(connection->request);
1945 strtype = mpdTagItemKeys[type];
1946 arg = mpd_sanitizeArg(name);
1948 len = strlen(string)+1+strlen(strtype)+2+strlen(arg)+2;
1949 connection->request = realloc(connection->request, len);
1950 snprintf(connection->request, len, "%s %c%s \"%s\"",
1951 string, tolower(strtype[0]), strtype+1, arg);
1953 free(string);
1954 free(arg);
1957 void mpd_commitSearch(mpd_Connection *connection)
1959 int len;
1961 if (!connection->request) {
1962 strcpy(connection->errorStr, "no search in progress");
1963 connection->error = 1;
1964 return;
1967 len = strlen(connection->request)+2;
1968 connection->request = realloc(connection->request, len);
1969 connection->request[len-2] = '\n';
1970 connection->request[len-1] = '\0';
1971 mpd_sendInfoCommand(connection, connection->request);
1973 free(connection->request);
1974 connection->request = NULL;
1978 * @param connection a MpdConnection
1979 * @param path the path to the playlist.
1981 * List the content, with full metadata, of a stored playlist.
1984 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection,const char *path)
1986 char *arg = mpd_sanitizeArg(path);
1987 int len = strlen("listplaylistinfo")+2+strlen(arg)+3;
1988 char *query = malloc(len);
1989 snprintf(query, len, "listplaylistinfo \"%s\"\n", arg);
1990 mpd_sendInfoCommand(connection, query);
1991 free(arg);
1992 free(query);
1996 * @param connection a MpdConnection
1997 * @param path the path to the playlist.
1999 * List the content of a stored playlist.
2002 void mpd_sendListPlaylistCommand(mpd_Connection *connection,const char *path)
2004 char *arg = mpd_sanitizeArg(path);
2005 int len = strlen("listplaylist")+2+strlen(arg)+3;
2006 char *query = malloc(len);
2007 snprintf(query, len, "listplaylist \"%s\"\n", arg);
2008 mpd_sendInfoCommand(connection, query);
2009 free(arg);
2010 free(query);
2013 void mpd_sendPlaylistClearCommand(mpd_Connection *connection,const char *path)
2015 char *sPath = mpd_sanitizeArg(path);
2016 int len = strlen("playlistclear")+2+strlen(sPath)+3;
2017 char *string = malloc(len);
2018 snprintf(string, len, "playlistclear \"%s\"\n", sPath);
2019 mpd_executeCommand(connection, string);
2020 free(sPath);
2021 free(string);
2024 void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
2025 const char *playlist,const char *path)
2027 char *sPlaylist = mpd_sanitizeArg(playlist);
2028 char *sPath = mpd_sanitizeArg(path);
2029 int len = strlen("playlistadd")+2+strlen(sPlaylist)+3+strlen(sPath)+3;
2030 char *string = malloc(len);
2031 snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath);
2032 mpd_executeCommand(connection, string);
2033 free(sPlaylist);
2034 free(sPath);
2035 free(string);
2038 void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
2039 const char *playlist, int from, int to)
2041 char *sPlaylist = mpd_sanitizeArg(playlist);
2042 int len = strlen("playlistmove")+
2043 2+strlen(sPlaylist)+3+INTLEN+3+INTLEN+3;
2044 char *string = malloc(len);
2045 snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2046 sPlaylist, from, to);
2047 mpd_executeCommand(connection, string);
2048 free(sPlaylist);
2049 free(string);
2052 void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
2053 const char *playlist, int pos)
2055 char *sPlaylist = mpd_sanitizeArg(playlist);
2056 int len = strlen("playlistdelete")+2+strlen(sPlaylist)+3+INTLEN+3;
2057 char *string = malloc(len);
2058 snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos);
2059 mpd_executeCommand(connection, string);
2060 free(sPlaylist);
2061 free(string);
2063 void mpd_sendClearErrorCommand(mpd_Connection * connection) {
2064 mpd_executeCommand(connection,"clearerror\n");
2068 void mpd_sendGetEventsCommand(mpd_Connection *connection) {
2069 mpd_executeCommand(connection, "idle\nnoidle\n");
2070 // mpd_executeCommand(connection, "noidle\n");
2073 char * mpd_getNextEvent(mpd_Connection *connection)
2075 return mpd_getNextReturnElementNamed(connection, "changed");
2078 void mpd_sendListPlaylistsCommand(mpd_Connection * connection) {
2079 mpd_sendInfoCommand(connection, "listplaylists\n");
2082 char * mpd_getNextSticker (mpd_Connection * connection)
2084 return mpd_getNextReturnElementNamed(connection, "sticker");
2086 void mpd_sendGetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker)
2088 char *sSong = mpd_sanitizeArg(song_path);
2089 char *sSticker = mpd_sanitizeArg(sticker);
2090 int len = strlen("sticker get song ")+strlen(sSong)+3+strlen(sSticker)+4;
2091 char *string = malloc(len);
2092 snprintf(string, len, "sticker get song \"%s\" \"%s\"\n", sSong, sSticker);
2093 mpd_executeCommand(connection, string);
2094 free(string);
2095 free(sSong);
2096 free(sSticker);
2099 void mpd_sendSetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker, const char *value)
2102 char *sSong = mpd_sanitizeArg(song_path);
2103 char *sSticker = mpd_sanitizeArg(sticker);
2104 char *sValue = mpd_sanitizeArg(value);
2105 int len = strlen("sticker set song ")+strlen(sSong)+3+strlen(sSticker)+3+strlen(sValue)+4;
2106 char *string = malloc(len);
2107 snprintf(string, len, "sticker set song \"%s\" \"%s\" \"%s\"\n", sSong, sSticker,sValue);
2108 mpd_sendInfoCommand(connection, string);
2109 free(string);
2110 free(sSong);
2111 free(sSticker);
2112 free(sValue);
2115 void mpd_sendSetReplayGainMode(mpd_Connection *connection, const char *mode)
2117 char *smode= mpd_sanitizeArg(mode);
2118 int len = strlen("replay_gain_mode ")+strlen(smode)+4;
2119 char *string = malloc(len);
2120 snprintf(string, len, "replay_gain_mode \"%s\"\n", smode);
2121 mpd_sendInfoCommand(connection, string);
2122 free(smode);
2123 free(string);
2125 void mpd_sendReplayGainModeCommand(mpd_Connection *connection)
2127 mpd_executeCommand(connection, "replay_gain_status\n");
2129 char *mpd_getReplayGainMode(mpd_Connection *connection)
2131 return mpd_getNextReturnElementNamed(connection, "replay_gain_mode");
2134 void mpd_sendSetPrioId(mpd_Connection *connection, int id, int priority)
2136 int len = strlen("prioid ")+1+INTLEN+3+INTLEN+3;
2137 char *str = malloc(len);
2138 snprintf(str, len, "prioid \"%d\" \"%d\"\n", priority, id);
2139 mpd_sendInfoCommand(connection, str);
2140 free(str);
2142 void mpd_sendSetPrio(mpd_Connection *connection, int pos, int priority)
2144 int len = strlen("prioid ")+1+INTLEN+3+INTLEN+3;
2145 char *str = malloc(len);
2146 snprintf(str, len, "prio \"%d\" \"%d\"\n", priority, pos);
2147 mpd_sendInfoCommand(connection, str);
2148 free(str);