Add rating field to mpd_Song
[libmpd.git] / src / libmpdclient.c
blob404fe2ba09ff5f8f3db9117d9c2df4c0f4dd2907
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 ret->rating = MPD_SONG_NO_RATING;
1028 return ret;
1031 void mpd_freeSong(mpd_Song * song) {
1032 mpd_finishSong(song);
1033 g_slice_free(mpd_Song, song);
1036 mpd_Song * mpd_songDup(const mpd_Song * song) {
1037 mpd_Song * ret = mpd_newSong();
1039 if(song->file) ret->file = strdup(song->file);
1040 if(song->artist) ret->artist = strdup(song->artist);
1041 if(song->album) ret->album = strdup(song->album);
1042 if(song->title) ret->title = strdup(song->title);
1043 if(song->track) ret->track = strdup(song->track);
1044 if(song->name) ret->name = strdup(song->name);
1045 if(song->date) ret->date = strdup(song->date);
1046 if(song->genre) ret->genre= strdup(song->genre);
1047 if(song->composer) ret->composer= strdup(song->composer);
1048 if(song->performer) ret->performer = strdup(song->performer);
1049 if(song->disc) ret->disc = strdup(song->disc);
1050 if(song->comment) ret->comment = strdup(song->comment);
1051 if(song->albumartist) ret->albumartist = strdup(song->albumartist);
1052 ret->time = song->time;
1053 ret->pos = song->pos;
1054 ret->id = song->id;
1055 ret->priority = song->priority;
1056 ret->rating = song->rating;
1058 return ret;
1062 static void mpd_finishDirectory(mpd_Directory * directory) {
1063 if(directory->path) free(directory->path);
1066 mpd_Directory * mpd_newDirectory(void) {
1067 return g_slice_new0(mpd_Directory);
1070 void mpd_freeDirectory(mpd_Directory * directory) {
1071 mpd_finishDirectory(directory);
1072 g_slice_free(mpd_Directory, directory);
1075 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
1076 mpd_Directory * ret = mpd_newDirectory();
1078 if(directory->path) ret->path = strdup(directory->path);
1080 return ret;
1083 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
1084 if(playlist->path) free(playlist->path);
1085 if(playlist->mtime) free(playlist->mtime);
1088 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
1089 return g_slice_new0(mpd_PlaylistFile);
1092 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
1093 mpd_finishPlaylistFile(playlist);
1094 g_slice_free(mpd_PlaylistFile, playlist);
1097 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1098 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1100 if(playlist->path) ret->path = strdup(playlist->path);
1101 if(playlist->mtime) ret->mtime = strdup(playlist->mtime);
1103 return ret;
1106 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1107 if(entity->info.directory) {
1108 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1109 mpd_freeDirectory(entity->info.directory);
1111 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1112 mpd_freeSong(entity->info.song);
1114 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1115 mpd_freePlaylistFile(entity->info.playlistFile);
1120 mpd_InfoEntity * mpd_newInfoEntity(void) {
1121 return g_slice_new0(mpd_InfoEntity);
1124 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1125 mpd_finishInfoEntity(entity);
1126 g_slice_free(mpd_InfoEntity, entity);
1129 static void mpd_sendInfoCommand(mpd_Connection * connection,const char * command) {
1130 mpd_executeCommand(connection,command);
1133 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1134 mpd_InfoEntity * entity = NULL;
1136 if(connection->doneProcessing || (connection->listOks &&
1137 connection->doneListOk))
1139 return NULL;
1142 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1144 if(connection->returnElement) {
1145 if(strcmp(connection->returnElement->name,"file")==0) {
1146 entity = mpd_newInfoEntity();
1147 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1148 entity->info.song = mpd_newSong();
1149 entity->info.song->file =
1150 strdup(connection->returnElement->value);
1152 else if(strcmp(connection->returnElement->name,
1153 "directory")==0) {
1154 entity = mpd_newInfoEntity();
1155 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1156 entity->info.directory = mpd_newDirectory();
1157 entity->info.directory->path =
1158 strdup(connection->returnElement->value);
1160 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1161 entity = mpd_newInfoEntity();
1162 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1163 entity->info.playlistFile = mpd_newPlaylistFile();
1164 entity->info.playlistFile->path =
1165 strdup(connection->returnElement->value);
1167 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1168 entity = mpd_newInfoEntity();
1169 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1170 entity->info.song = mpd_newSong();
1171 entity->info.song->pos = atoi(connection->returnElement->value);
1173 else {
1174 connection->error = 1;
1175 strcpy(connection->errorStr,"problem parsing song info");
1176 return NULL;
1179 else return NULL;
1181 mpd_getNextReturnElement(connection);
1182 while(connection->returnElement) {
1183 mpd_ReturnElement * re = connection->returnElement;
1185 if(strcmp(re->name,"file")==0) return entity;
1186 else if(strcmp(re->name,"directory")==0) return entity;
1187 else if(strcmp(re->name,"playlist")==0) return entity;
1188 else if(strcmp(re->name,"cpos")==0) return entity;
1190 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1191 strlen(re->value)) {
1192 if(strcmp(re->name,"Artist")==0) {
1193 if(entity->info.song->artist) {
1194 int length = strlen(entity->info.song->artist);
1195 entity->info.song->artist = realloc(entity->info.song->artist,
1196 length + strlen(re->value) + 3);
1197 strcpy(&((entity->info.song->artist)[length]), ", ");
1198 strcpy(&((entity->info.song->artist)[length + 2]), re->value);
1200 else {
1201 entity->info.song->artist = strdup(re->value);
1204 else if(!entity->info.song->album &&
1205 strcmp(re->name,"Album")==0) {
1206 entity->info.song->album = strdup(re->value);
1208 else if(!entity->info.song->title &&
1209 strcmp(re->name,"Title")==0) {
1210 entity->info.song->title = strdup(re->value);
1212 else if(!entity->info.song->track &&
1213 strcmp(re->name,"Track")==0) {
1214 entity->info.song->track = strdup(re->value);
1216 else if(!entity->info.song->name &&
1217 strcmp(re->name,"Name")==0) {
1218 entity->info.song->name = strdup(re->value);
1220 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1221 strcmp(re->name,"Time")==0) {
1222 entity->info.song->time = atoi(re->value);
1224 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1225 strcmp(re->name,"Pos")==0) {
1226 entity->info.song->pos = atoi(re->value);
1228 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1229 strcmp(re->name,"Id")==0) {
1230 entity->info.song->id = atoi(re->value);
1232 else if (strcmp(re->name, "Prio") == 0) {
1233 entity->info.song->priority = atoi(re->value);
1235 else if(!entity->info.song->date &&
1236 strcmp(re->name, "Date") == 0) {
1237 entity->info.song->date = strdup(re->value);
1239 else if(!entity->info.song->genre &&
1240 strcmp(re->name, "Genre") == 0) {
1241 if(entity->info.song->genre) {
1242 int length = strlen(entity->info.song->genre);
1243 entity->info.song->genre = realloc(entity->info.song->genre,
1244 length + strlen(re->value) + 4);
1245 strcpy(&((entity->info.song->genre)[length]), ", ");
1246 strcpy(&((entity->info.song->genre)[length + 3]), re->value);
1248 else {
1249 entity->info.song->genre = strdup(re->value);
1252 else if(strcmp(re->name, "Composer") == 0) {
1253 if(entity->info.song->composer) {
1254 int length = strlen(entity->info.song->composer);
1255 entity->info.song->composer = realloc(entity->info.song->composer,
1256 length + strlen(re->value) + 3);
1257 strcpy(&((entity->info.song->composer)[length]), ", ");
1258 strcpy(&((entity->info.song->composer)[length + 2]), re->value);
1260 else {
1261 entity->info.song->composer = strdup(re->value);
1264 else if(strcmp(re->name, "Performer") == 0) {
1265 if(entity->info.song->performer) {
1266 int length = strlen(entity->info.song->performer);
1267 entity->info.song->performer = realloc(entity->info.song->performer,
1268 length + strlen(re->value) + 3);
1269 strcpy(&((entity->info.song->performer)[length]), ", ");
1270 strcpy(&((entity->info.song->performer)[length + 2]), re->value);
1272 else {
1273 entity->info.song->performer = strdup(re->value);
1276 else if(!entity->info.song->disc &&
1277 strcmp(re->name, "Disc") == 0) {
1278 entity->info.song->disc = strdup(re->value);
1280 else if(!entity->info.song->comment &&
1281 strcmp(re->name, "Comment") == 0) {
1282 entity->info.song->comment = strdup(re->value);
1285 else if(!entity->info.song->albumartist &&
1286 strcmp(re->name, "AlbumArtist") == 0) {
1287 entity->info.song->albumartist = strdup(re->value);
1290 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1292 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1293 if(!entity->info.playlistFile->mtime &&
1294 strcmp(re->name, "Last-Modified") == 0) {
1295 entity->info.playlistFile->mtime = strdup(re->value);
1299 mpd_getNextReturnElement(connection);
1302 return entity;
1305 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1306 const char * name)
1308 if(connection->doneProcessing || (connection->listOks &&
1309 connection->doneListOk))
1311 return NULL;
1314 mpd_getNextReturnElement(connection);
1315 while(connection->returnElement) {
1316 mpd_ReturnElement * re = connection->returnElement;
1318 if(strcmp(re->name,name)==0) return strdup(re->value);
1319 mpd_getNextReturnElement(connection);
1322 return NULL;
1325 char *mpd_getNextTag(mpd_Connection *connection, int type)
1327 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES ||
1328 type == MPD_TAG_ITEM_ANY)
1329 return NULL;
1330 if (type == MPD_TAG_ITEM_FILENAME)
1331 return mpd_getNextReturnElementNamed(connection, "file");
1332 return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
1335 char * mpd_getNextArtist(mpd_Connection * connection) {
1336 return mpd_getNextReturnElementNamed(connection,"Artist");
1339 char * mpd_getNextAlbum(mpd_Connection * connection) {
1340 return mpd_getNextReturnElementNamed(connection,"Album");
1343 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1344 int len = strlen("playlistinfo")+2+INTLEN+3;
1345 char *string = malloc(len);
1346 snprintf(string, len, "playlistinfo \"%i\"\n", songPos);
1347 mpd_sendInfoCommand(connection,string);
1348 free(string);
1351 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1352 int len = strlen("playlistid")+2+INTLEN+3;
1353 char *string = malloc(len);
1354 snprintf(string, len, "playlistid \"%i\"\n", id);
1355 mpd_sendInfoCommand(connection, string);
1356 free(string);
1359 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1360 int len = strlen("plchanges")+2+LONGLONGLEN+3;
1361 char *string = malloc(len);
1362 snprintf(string, len, "plchanges \"%lld\"\n", playlist);
1363 mpd_sendInfoCommand(connection,string);
1364 free(string);
1367 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1368 int len = strlen("plchangesposid")+2+LONGLONGLEN+3;
1369 char *string = malloc(len);
1370 snprintf(string, len, "plchangesposid \"%lld\"\n", playlist);
1371 mpd_sendInfoCommand(connection,string);
1372 free(string);
1375 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1376 char * sDir = mpd_sanitizeArg(dir);
1377 int len = strlen("listall")+2+strlen(sDir)+3;
1378 char *string = malloc(len);
1379 snprintf(string, len, "listall \"%s\"\n", sDir);
1380 mpd_sendInfoCommand(connection,string);
1381 free(string);
1382 free(sDir);
1385 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1386 char * sDir = mpd_sanitizeArg(dir);
1387 int len = strlen("listallinfo")+2+strlen(sDir)+3;
1388 char *string = malloc(len);
1389 snprintf(string, len, "listallinfo \"%s\"\n", sDir);
1390 mpd_sendInfoCommand(connection,string);
1391 free(string);
1392 free(sDir);
1395 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1396 char * sDir = mpd_sanitizeArg(dir);
1397 int len = strlen("lsinfo")+2+strlen(sDir)+3;
1398 char *string = malloc(len);
1399 snprintf(string, len, "lsinfo \"%s\"\n", sDir);
1400 mpd_sendInfoCommand(connection,string);
1401 free(string);
1402 free(sDir);
1405 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1406 mpd_executeCommand(connection,"currentsong\n");
1409 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1410 const char * str)
1412 mpd_startSearch(connection, 0);
1413 mpd_addConstraintSearch(connection, table, str);
1414 mpd_commitSearch(connection);
1417 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1418 const char * str)
1420 mpd_startSearch(connection, 1);
1421 mpd_addConstraintSearch(connection, table, str);
1422 mpd_commitSearch(connection);
1425 void mpd_sendListCommand(mpd_Connection * connection, int table,
1426 const char * arg1)
1428 char st[10];
1429 int len;
1430 char *string;
1431 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1432 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1433 else {
1434 connection->error = 1;
1435 strcpy(connection->errorStr,"unknown table for list");
1436 return;
1438 if(arg1) {
1439 char * sanitArg1 = mpd_sanitizeArg(arg1);
1440 len = strlen("list")+1+strlen(sanitArg1)+2+strlen(st)+3;
1441 string = malloc(len);
1442 snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1);
1443 free(sanitArg1);
1445 else {
1446 len = strlen("list")+1+strlen(st)+2;
1447 string = malloc(len);
1448 snprintf(string, len, "list %s\n", st);
1450 mpd_sendInfoCommand(connection,string);
1451 free(string);
1454 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1455 char * sFile = mpd_sanitizeArg(file);
1456 int len = strlen("add")+2+strlen(sFile)+3;
1457 char *string = malloc(len);
1458 snprintf(string, len, "add \"%s\"\n", sFile);
1459 mpd_executeCommand(connection,string);
1460 free(string);
1461 free(sFile);
1464 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1466 int retval = -1;
1467 char *sFile = mpd_sanitizeArg(file);
1468 int len = strlen("addid")+2+strlen(sFile)+3;
1469 char *string = malloc(len);
1471 snprintf(string, len, "addid \"%s\"\n", sFile);
1472 mpd_sendInfoCommand(connection, string);
1473 free(string);
1474 free(sFile);
1476 string = mpd_getNextReturnElementNamed(connection, "Id");
1477 if (string) {
1478 retval = atoi(string);
1479 free(string);
1482 return retval;
1485 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1486 int len = strlen("delete")+2+INTLEN+3;
1487 char *string = malloc(len);
1488 snprintf(string, len, "delete \"%i\"\n", songPos);
1489 mpd_sendInfoCommand(connection,string);
1490 free(string);
1493 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1494 int len = strlen("deleteid")+2+INTLEN+3;
1495 char *string = malloc(len);
1496 snprintf(string, len, "deleteid \"%i\"\n", id);
1497 mpd_sendInfoCommand(connection,string);
1498 free(string);
1501 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1502 char * sName = mpd_sanitizeArg(name);
1503 int len = strlen("save")+2+strlen(sName)+3;
1504 char *string = malloc(len);
1505 snprintf(string, len, "save \"%s\"\n", sName);
1506 mpd_executeCommand(connection,string);
1507 free(string);
1508 free(sName);
1511 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1512 char * sName = mpd_sanitizeArg(name);
1513 int len = strlen("load")+2+strlen(sName)+3;
1514 char *string = malloc(len);
1515 snprintf(string, len, "load \"%s\"\n", sName);
1516 mpd_executeCommand(connection,string);
1517 free(string);
1518 free(sName);
1521 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1522 char * sName = mpd_sanitizeArg(name);
1523 int len = strlen("rm")+2+strlen(sName)+3;
1524 char *string = malloc(len);
1525 snprintf(string, len, "rm \"%s\"\n", sName);
1526 mpd_executeCommand(connection,string);
1527 free(string);
1528 free(sName);
1531 void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
1532 const char *to)
1534 char *sFrom = mpd_sanitizeArg(from);
1535 char *sTo = mpd_sanitizeArg(to);
1536 int len = strlen("rename")+2+strlen(sFrom)+3+strlen(sTo)+3;
1537 char *string = malloc(len);
1538 snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo);
1539 mpd_executeCommand(connection, string);
1540 free(string);
1541 free(sFrom);
1542 free(sTo);
1545 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1546 mpd_executeCommand(connection,"shuffle\n");
1549 void mpd_sendClearCommand(mpd_Connection * connection) {
1550 mpd_executeCommand(connection,"clear\n");
1553 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1554 int len = strlen("play")+2+INTLEN+3;
1555 char *string = malloc(len);
1556 snprintf(string, len, "play \"%i\"\n", songPos);
1557 mpd_sendInfoCommand(connection,string);
1558 free(string);
1561 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1562 int len = strlen("playid")+2+INTLEN+3;
1563 char *string = malloc(len);
1564 snprintf(string, len, "playid \"%i\"\n", id);
1565 mpd_sendInfoCommand(connection,string);
1566 free(string);
1569 void mpd_sendStopCommand(mpd_Connection * connection) {
1570 mpd_executeCommand(connection,"stop\n");
1573 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1574 int len = strlen("pause")+2+INTLEN+3;
1575 char *string = malloc(len);
1576 snprintf(string, len, "pause \"%i\"\n", pauseMode);
1577 mpd_executeCommand(connection,string);
1578 free(string);
1581 void mpd_sendNextCommand(mpd_Connection * connection) {
1582 mpd_executeCommand(connection,"next\n");
1585 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1586 int len = strlen("move")+2+INTLEN+3+INTLEN+3;
1587 char *string = malloc(len);
1588 snprintf(string, len, "move \"%i\" \"%i\"\n", from, to);
1589 mpd_sendInfoCommand(connection,string);
1590 free(string);
1593 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1594 int len = strlen("moveid")+2+INTLEN+3+INTLEN+3;
1595 char *string = malloc(len);
1596 snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to);
1597 mpd_sendInfoCommand(connection,string);
1598 free(string);
1601 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1602 int len = strlen("swap")+2+INTLEN+3+INTLEN+3;
1603 char *string = malloc(len);
1604 snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2);
1605 mpd_sendInfoCommand(connection,string);
1606 free(string);
1609 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1610 int len = strlen("swapid")+2+INTLEN+3+INTLEN+3;
1611 char *string = malloc(len);
1612 snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2);
1613 mpd_sendInfoCommand(connection,string);
1614 free(string);
1617 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int seek_time) {
1618 int len = strlen("seek")+2+INTLEN+3+INTLEN+3;
1619 char *string = malloc(len);
1620 snprintf(string, len, "seek \"%i\" \"%i\"\n", song, seek_time);
1621 mpd_sendInfoCommand(connection,string);
1622 free(string);
1625 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int seek_time) {
1626 int len = strlen("seekid")+2+INTLEN+3+INTLEN+3;
1627 char *string = malloc(len);
1628 snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, seek_time);
1629 mpd_sendInfoCommand(connection,string);
1630 free(string);
1633 void mpd_sendUpdateCommand(mpd_Connection * connection,const char * path) {
1634 char * sPath = mpd_sanitizeArg(path);
1635 int len = strlen("update")+2+strlen(sPath)+3;
1636 char *string = malloc(len);
1637 snprintf(string, len, "update \"%s\"\n", sPath);
1638 mpd_sendInfoCommand(connection,string);
1639 free(string);
1640 free(sPath);
1643 int mpd_getUpdateId(mpd_Connection * connection) {
1644 char * jobid;
1645 int ret = 0;
1647 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1648 if(jobid) {
1649 ret = atoi(jobid);
1650 free(jobid);
1653 return ret;
1656 void mpd_sendPrevCommand(mpd_Connection * connection) {
1657 mpd_executeCommand(connection,"previous\n");
1660 void mpd_sendSingleCommand(mpd_Connection * connection, int singleMode) {
1661 int len = strlen("repeat")+2+INTLEN+3;
1662 char *string = malloc(len);
1663 snprintf(string, len, "single \"%i\"\n", singleMode);
1664 mpd_executeCommand(connection,string);
1665 free(string);
1668 void mpd_sendConsumeCommand(mpd_Connection * connection, int consumeMode) {
1669 int len = strlen("repeat")+2+INTLEN+3;
1670 char *string = malloc(len);
1671 snprintf(string, len, "consume \"%i\"\n", consumeMode);
1672 mpd_executeCommand(connection,string);
1673 free(string);
1676 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1677 int len = strlen("repeat")+2+INTLEN+3;
1678 char *string = malloc(len);
1679 snprintf(string, len, "repeat \"%i\"\n", repeatMode);
1680 mpd_executeCommand(connection,string);
1681 free(string);
1684 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1685 int len = strlen("random")+2+INTLEN+3;
1686 char *string = malloc(len);
1687 snprintf(string, len, "random \"%i\"\n", randomMode);
1688 mpd_executeCommand(connection,string);
1689 free(string);
1692 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1693 int len = strlen("setvol")+2+INTLEN+3;
1694 char *string = malloc(len);
1695 snprintf(string, len, "setvol \"%i\"\n", volumeChange);
1696 mpd_executeCommand(connection,string);
1697 free(string);
1700 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1701 int len = strlen("crossfade")+2+INTLEN+3;
1702 char *string = malloc(len);
1703 snprintf(string, len, "crossfade \"%i\"\n", seconds);
1704 mpd_executeCommand(connection,string);
1705 free(string);
1708 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1709 char * sPass = mpd_sanitizeArg(pass);
1710 int len = strlen("password")+2+strlen(sPass)+3;
1711 char *string = malloc(len);
1712 snprintf(string, len, "password \"%s\"\n", sPass);
1713 mpd_executeCommand(connection,string);
1714 free(string);
1715 free(sPass);
1718 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1719 if(connection->commandList) {
1720 strcpy(connection->errorStr,"already in command list mode");
1721 connection->error = 1;
1722 return;
1724 connection->commandList = COMMAND_LIST;
1725 mpd_executeCommand(connection,"command_list_begin\n");
1728 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1729 if(connection->commandList) {
1730 strcpy(connection->errorStr,"already in command list mode");
1731 connection->error = 1;
1732 return;
1734 connection->commandList = COMMAND_LIST_OK;
1735 mpd_executeCommand(connection,"command_list_ok_begin\n");
1736 connection->listOks = 0;
1739 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1740 if(!connection->commandList) {
1741 strcpy(connection->errorStr,"not in command list mode");
1742 connection->error = 1;
1743 return;
1745 connection->commandList = 0;
1746 mpd_executeCommand(connection,"command_list_end\n");
1749 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1750 mpd_executeCommand(connection,"outputs\n");
1753 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1754 if(connection->doneProcessing || (connection->listOks &&
1755 connection->doneListOk))
1757 return NULL;
1760 if(connection->error) return NULL;
1762 mpd_OutputEntity* output = g_slice_new0(mpd_OutputEntity);
1763 output->id = -10;
1765 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1767 while(connection->returnElement) {
1768 mpd_ReturnElement * re = connection->returnElement;
1769 if(strcmp(re->name,"outputid")==0) {
1770 if(output!=NULL && output->id>=0) return output;
1771 output->id = atoi(re->value);
1773 else if(strcmp(re->name,"outputname")==0) {
1774 output->name = strdup(re->value);
1776 else if(strcmp(re->name,"outputenabled")==0) {
1777 output->enabled = atoi(re->value);
1780 mpd_getNextReturnElement(connection);
1781 if(connection->error) {
1782 mpd_freeOutputElement(output);
1783 return NULL;
1787 return output;
1790 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1791 int len = strlen("enableoutput")+2+INTLEN+3;
1792 char *string = malloc(len);
1793 snprintf(string, len, "enableoutput \"%i\"\n", outputId);
1794 mpd_executeCommand(connection,string);
1795 free(string);
1798 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1799 int len = strlen("disableoutput")+2+INTLEN+3;
1800 char *string = malloc(len);
1801 snprintf(string, len, "disableoutput \"%i\"\n", outputId);
1802 mpd_executeCommand(connection,string);
1803 free(string);
1806 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1807 if(output->name)
1808 free(output->name);
1809 g_slice_free(mpd_OutputEntity, output);
1813 * mpd_sendNotCommandsCommand
1814 * odd naming, but it gets the not allowed commands
1817 void mpd_sendNotCommandsCommand(mpd_Connection * connection)
1819 mpd_executeCommand(connection, "notcommands\n");
1823 * mpd_sendCommandsCommand
1824 * odd naming, but it gets the allowed commands
1826 void mpd_sendCommandsCommand(mpd_Connection * connection)
1828 mpd_executeCommand(connection, "commands\n");
1832 * Get the next returned command
1834 char * mpd_getNextCommand(mpd_Connection * connection)
1836 return mpd_getNextReturnElementNamed(connection, "command");
1839 void mpd_sendUrlHandlersCommand(mpd_Connection * connection)
1841 mpd_executeCommand(connection, "urlhandlers\n");
1844 char * mpd_getNextHandler(mpd_Connection * connection)
1846 return mpd_getNextReturnElementNamed(connection, "handler");
1849 void mpd_sendTagTypesCommand(mpd_Connection * connection)
1851 mpd_executeCommand(connection, "tagtypes\n");
1854 char * mpd_getNextTagType(mpd_Connection * connection)
1856 return mpd_getNextReturnElementNamed(connection, "tagtype");
1859 void mpd_startSearch(mpd_Connection *connection, int exact)
1861 if (connection->request) {
1862 strcpy(connection->errorStr, "search already in progress");
1863 connection->error = 1;
1864 return;
1867 if (exact) connection->request = strdup("find");
1868 else connection->request = strdup("search");
1871 void mpd_startStatsSearch(mpd_Connection *connection)
1873 if (connection->request) {
1874 strcpy(connection->errorStr, "search already in progress");
1875 connection->error = 1;
1876 return;
1879 connection->request = strdup("count");
1882 void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
1884 if (connection->request) {
1885 strcpy(connection->errorStr, "search already in progress");
1886 connection->error = 1;
1887 return;
1890 if (exact) connection->request = strdup("playlistfind");
1891 else connection->request = strdup("playlistsearch");
1894 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1896 char *strtype;
1897 int len;
1899 if (connection->request) {
1900 strcpy(connection->errorStr, "search already in progress");
1901 connection->error = 1;
1902 return;
1905 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1906 strcpy(connection->errorStr, "invalid type specified");
1907 connection->error = 1;
1908 return;
1911 strtype = mpdTagItemKeys[type];
1913 len = 5+strlen(strtype)+1;
1914 connection->request = malloc(len);
1916 snprintf(connection->request, len, "list %c%s",
1917 tolower(strtype[0]), strtype+1);
1920 void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name)
1922 char *strtype;
1923 char *arg;
1924 int len;
1925 char *string;
1927 if (!connection->request) {
1928 strcpy(connection->errorStr, "no search in progress");
1929 connection->error = 1;
1930 return;
1933 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1934 strcpy(connection->errorStr, "invalid type specified");
1935 connection->error = 1;
1936 return;
1939 if (name == NULL) {
1940 strcpy(connection->errorStr, "no name specified");
1941 connection->error = 1;
1942 return;
1945 string = strdup(connection->request);
1946 strtype = mpdTagItemKeys[type];
1947 arg = mpd_sanitizeArg(name);
1949 len = strlen(string)+1+strlen(strtype)+2+strlen(arg)+2;
1950 connection->request = realloc(connection->request, len);
1951 snprintf(connection->request, len, "%s %c%s \"%s\"",
1952 string, tolower(strtype[0]), strtype+1, arg);
1954 free(string);
1955 free(arg);
1958 void mpd_commitSearch(mpd_Connection *connection)
1960 int len;
1962 if (!connection->request) {
1963 strcpy(connection->errorStr, "no search in progress");
1964 connection->error = 1;
1965 return;
1968 len = strlen(connection->request)+2;
1969 connection->request = realloc(connection->request, len);
1970 connection->request[len-2] = '\n';
1971 connection->request[len-1] = '\0';
1972 mpd_sendInfoCommand(connection, connection->request);
1974 free(connection->request);
1975 connection->request = NULL;
1979 * @param connection a MpdConnection
1980 * @param path the path to the playlist.
1982 * List the content, with full metadata, of a stored playlist.
1985 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection,const char *path)
1987 char *arg = mpd_sanitizeArg(path);
1988 int len = strlen("listplaylistinfo")+2+strlen(arg)+3;
1989 char *query = malloc(len);
1990 snprintf(query, len, "listplaylistinfo \"%s\"\n", arg);
1991 mpd_sendInfoCommand(connection, query);
1992 free(arg);
1993 free(query);
1997 * @param connection a MpdConnection
1998 * @param path the path to the playlist.
2000 * List the content of a stored playlist.
2003 void mpd_sendListPlaylistCommand(mpd_Connection *connection,const char *path)
2005 char *arg = mpd_sanitizeArg(path);
2006 int len = strlen("listplaylist")+2+strlen(arg)+3;
2007 char *query = malloc(len);
2008 snprintf(query, len, "listplaylist \"%s\"\n", arg);
2009 mpd_sendInfoCommand(connection, query);
2010 free(arg);
2011 free(query);
2014 void mpd_sendPlaylistClearCommand(mpd_Connection *connection,const char *path)
2016 char *sPath = mpd_sanitizeArg(path);
2017 int len = strlen("playlistclear")+2+strlen(sPath)+3;
2018 char *string = malloc(len);
2019 snprintf(string, len, "playlistclear \"%s\"\n", sPath);
2020 mpd_executeCommand(connection, string);
2021 free(sPath);
2022 free(string);
2025 void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
2026 const char *playlist,const char *path)
2028 char *sPlaylist = mpd_sanitizeArg(playlist);
2029 char *sPath = mpd_sanitizeArg(path);
2030 int len = strlen("playlistadd")+2+strlen(sPlaylist)+3+strlen(sPath)+3;
2031 char *string = malloc(len);
2032 snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath);
2033 mpd_executeCommand(connection, string);
2034 free(sPlaylist);
2035 free(sPath);
2036 free(string);
2039 void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
2040 const char *playlist, int from, int to)
2042 char *sPlaylist = mpd_sanitizeArg(playlist);
2043 int len = strlen("playlistmove")+
2044 2+strlen(sPlaylist)+3+INTLEN+3+INTLEN+3;
2045 char *string = malloc(len);
2046 snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2047 sPlaylist, from, to);
2048 mpd_executeCommand(connection, string);
2049 free(sPlaylist);
2050 free(string);
2053 void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
2054 const char *playlist, int pos)
2056 char *sPlaylist = mpd_sanitizeArg(playlist);
2057 int len = strlen("playlistdelete")+2+strlen(sPlaylist)+3+INTLEN+3;
2058 char *string = malloc(len);
2059 snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos);
2060 mpd_executeCommand(connection, string);
2061 free(sPlaylist);
2062 free(string);
2064 void mpd_sendClearErrorCommand(mpd_Connection * connection) {
2065 mpd_executeCommand(connection,"clearerror\n");
2069 void mpd_sendGetEventsCommand(mpd_Connection *connection) {
2070 mpd_executeCommand(connection, "idle\nnoidle\n");
2071 // mpd_executeCommand(connection, "noidle\n");
2074 char * mpd_getNextEvent(mpd_Connection *connection)
2076 return mpd_getNextReturnElementNamed(connection, "changed");
2079 void mpd_sendListPlaylistsCommand(mpd_Connection * connection) {
2080 mpd_sendInfoCommand(connection, "listplaylists\n");
2083 char * mpd_getNextSticker (mpd_Connection * connection)
2085 return mpd_getNextReturnElementNamed(connection, "sticker");
2087 void mpd_sendGetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker)
2089 char *sSong = mpd_sanitizeArg(song_path);
2090 char *sSticker = mpd_sanitizeArg(sticker);
2091 int len = strlen("sticker get song ")+strlen(sSong)+3+strlen(sSticker)+4;
2092 char *string = malloc(len);
2093 snprintf(string, len, "sticker get song \"%s\" \"%s\"\n", sSong, sSticker);
2094 mpd_executeCommand(connection, string);
2095 free(string);
2096 free(sSong);
2097 free(sSticker);
2100 void mpd_sendSetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker, const char *value)
2103 char *sSong = mpd_sanitizeArg(song_path);
2104 char *sSticker = mpd_sanitizeArg(sticker);
2105 char *sValue = mpd_sanitizeArg(value);
2106 int len = strlen("sticker set song ")+strlen(sSong)+3+strlen(sSticker)+3+strlen(sValue)+4;
2107 char *string = malloc(len);
2108 snprintf(string, len, "sticker set song \"%s\" \"%s\" \"%s\"\n", sSong, sSticker,sValue);
2109 mpd_sendInfoCommand(connection, string);
2110 free(string);
2111 free(sSong);
2112 free(sSticker);
2113 free(sValue);
2116 void mpd_sendSetReplayGainMode(mpd_Connection *connection, const char *mode)
2118 char *smode= mpd_sanitizeArg(mode);
2119 int len = strlen("replay_gain_mode ")+strlen(smode)+4;
2120 char *string = malloc(len);
2121 snprintf(string, len, "replay_gain_mode \"%s\"\n", smode);
2122 mpd_sendInfoCommand(connection, string);
2123 free(smode);
2124 free(string);
2126 void mpd_sendReplayGainModeCommand(mpd_Connection *connection)
2128 mpd_executeCommand(connection, "replay_gain_status\n");
2130 char *mpd_getReplayGainMode(mpd_Connection *connection)
2132 return mpd_getNextReturnElementNamed(connection, "replay_gain_mode");
2135 void mpd_sendSetPrioId(mpd_Connection *connection, int id, int priority)
2137 int len = strlen("prioid ")+1+INTLEN+3+INTLEN+3;
2138 char *str = malloc(len);
2139 snprintf(str, len, "prioid \"%d\" \"%d\"\n", id, priority);
2140 mpd_sendInfoCommand(connection, str);
2141 free(str);
2143 void mpd_sendSetPrio(mpd_Connection *connection, int pos, int priority)
2145 int len = strlen("prioid ")+1+INTLEN+3+INTLEN+3;
2146 char *str = malloc(len);
2147 snprintf(str, len, "prio \"%d\" \"%d\"\n", pos, priority);
2148 mpd_sendInfoCommand(connection, str);
2149 free(str);