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
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"
37 #include <sys/types.h>
39 #include <sys/param.h>
50 # include <ws2tcpip.h>
53 # include <netinet/in.h>
54 # include <arpa/inet.h>
55 # include <sys/socket.h>
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
77 # define MSG_DONTWAIT 0
81 # define SELECT_ERRNO_IGNORE (errno == WSAEINTR || errno == WSAEINPROGRESS)
82 # define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
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)
92 static int winsock_dll_error(mpd_Connection
*connection
)
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
;
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
)
112 ioctlsocket(connection
->sock
, FIONBIO
, (u_long FAR
*) &iMode
);
115 #elif defined(__solaris__) || defined(__sun__)
116 static int do_connect_fail(mpd_Connection
*connection
,
117 const struct sockaddr
*serv_addr
, int addrlen
)
120 if (connect(connection
->sock
, serv_addr
, addrlen
) < 0)
122 flags
= fcntl(connection
->sock
, F_GETFL
, 0);
123 fcntl(connection
->sock
, F_SETFL
, flags
| O_NONBLOCK
);
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
;
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)
137 /* Do a non blocking connect */
138 res
= connect(connection
->sock
, serv_addr
, addrlen
);
140 if (errno
== EINPROGRESS
) {
141 tv
.tv_sec
= connection
->timeout
.tv_sec
;
142 tv
.tv_usec
= connection
->timeout
.tv_usec
;
144 FD_SET(connection
->sock
, &myset
);
145 if (select(connection
->sock
+1, NULL
, &myset
, NULL
, &tv
) > 0) {
148 /* Check for errors */
149 getsockopt(connection
->sock
, SOL_SOCKET
, SO_ERROR
, (void*)(&valopt
), &lon
);
151 fprintf(stderr
, "Error in connection() %d - %s\n", valopt
, strerror(valopt
));
156 /* Connecting timed out. */
157 fprintf(stderr
, "Timeout or error() %d - %s\n", valopt
, strerror(valopt
));
162 /* Failed to connect */
163 fprintf(stderr
, "Error connecting %d - %s\n", errno
, strerror(errno
));
173 static int mpd_connect(mpd_Connection
* connection
, const char * host
, int port
,
177 char service
[INTLEN
+1];
178 struct addrinfo hints
;
179 struct addrinfo
*res
= NULL
;
180 struct addrinfo
*addrinfo
= NULL
;
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
);
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
;
206 for (res
= addrinfo
; res
; res
= res
->ai_next
) {
208 if (connection
->sock
>= 0)
209 closesocket(connection
->sock
);
210 connection
->sock
= socket(res
->ai_family
, SOCK_STREAM
,
212 if (connection
->sock
< 0) {
213 snprintf(connection
->errorStr
, MPD_ERRORSTR_MAX_LENGTH
,
214 "problems creating socket: %s",
216 connection
->error
= MPD_ERROR_SYSTEM
;
217 freeaddrinfo(addrinfo
);
221 mpd_setConnectionTimeout(connection
, timeout
);
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;
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
;
248 #else /* !MPD_HAVE_GAI */
249 static int mpd_connect(mpd_Connection
* connection
, const char * host
, int port
,
253 struct sockaddr
* dest
;
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
;
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
) {
271 memcpy((char *)&sin
.sin_addr
.s_addr
,(char *)he
->h_addr
,
273 dest
= (struct sockaddr
*)&sin
;
274 destlen
= sizeof(struct sockaddr_in
);
277 strcpy(connection
->errorStr
,"address type is not IPv4");
278 connection
->error
= MPD_ERROR_SYSTEM
;
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
;
291 mpd_setConnectionTimeout(connection
,timeout
);
294 if (do_connect_fail(connection
, dest
, destlen
)) {
295 snprintf(connection
->errorStr
,MPD_ERRORSTR_MAX_LENGTH
,
296 "problems connecting to \"%s\" on port"
298 connection
->error
= MPD_ERROR_CONNPORT
;
304 #endif /* !MPD_HAVE_GAI */
306 char * mpdTagItemKeys
[MPD_TAG_NUM_OF_ITEM_TYPES
] =
324 static char * mpd_sanitizeArg(const char * arg
) {
327 register const char *c
;
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);
337 for(i
= strlen(arg
)+1; i
!= 0; --i
) {
338 if(*c
=='"' || *c
=='\\')
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
);
356 static void mpd_freeReturnElement(mpd_ReturnElement
* re
) {
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 +
369 static int mpd_parseWelcome(mpd_Connection
* connection
, const char * host
, int port
,
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\"",
379 connection
->error
= MPD_ERROR_NOTMPD
;
383 tmp
= &output
[strlen(MPD_WELCOME_MESSAGE
)];
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 "
393 &output
[strlen(MPD_WELCOME_MESSAGE
)]);
394 connection
->error
= MPD_ERROR_NOTMPD
;
404 static int mpd_connect_un(mpd_Connection
* connection
,
405 const char * host
, float timeout
)
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
;
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
;
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
));
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
;
450 mpd_Connection
* mpd_newConnection(const char * host
, int port
, float timeout
) {
453 char * output
= NULL
;
454 mpd_Connection
* connection
= g_slice_new0(mpd_Connection
);
457 strcpy(connection
->buffer
,"");
458 connection
->sock
= -1;
459 strcpy(connection
->errorStr
,"");
461 if (winsock_dll_error(connection
))
466 err
= mpd_connect_un(connection
, host
, timeout
);
469 err
= mpd_connect(connection
, host
, port
, timeout
);
473 while(!(rt
= strstr(connection
->buffer
,"\n"))) {
474 tv
.tv_sec
= connection
->timeout
.tv_sec
;
475 tv
.tv_usec
= connection
->timeout
.tv_usec
;
477 FD_SET(connection
->sock
,&fds
);
478 if((err
= select(connection
->sock
+1,&fds
,NULL
,NULL
,&tv
)) == 1) {
480 readed
= recv(connection
->sock
,
481 &(connection
->buffer
[connection
->buflen
]),
482 MPD_BUFFER_MAX_LENGTH
-connection
->buflen
,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
;
491 connection
->buflen
+=readed
;
492 connection
->buffer
[connection
->buflen
] = '\0';
495 if (SELECT_ERRNO_IGNORE
)
497 snprintf(connection
->errorStr
,
498 MPD_ERRORSTR_MAX_LENGTH
,
499 "problems connecting to \"%s\" on port"
501 connection
->error
= MPD_ERROR_CONNPORT
;
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
;
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;
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
);
538 static void mpd_executeCommand(mpd_Connection
* connection
,const char * command
) {
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;
551 mpd_clearError(connection
);
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
)) {
560 ret
= send(connection
->sock
,commandPtr
,commandLen
,MSG_DONTWAIT
);
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
;
574 if(commandLen
<=0) break;
578 snprintf(connection
->errorStr
,MPD_ERRORSTR_MAX_LENGTH
,
579 "timeout sending command \"%s\"",command
);
580 connection
->error
= MPD_ERROR_TIMEOUT
;
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
;
599 char * bufferCheck
= NULL
;
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;
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
,
620 connection
->bufstart
,
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;
633 bufferCheck
= connection
->buffer
+connection
->buflen
;
634 tv
.tv_sec
= connection
->timeout
.tv_sec
;
635 tv
.tv_usec
= connection
->timeout
.tv_usec
;
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
,
643 if(readed
<0 && SENDRECV_ERRNO_IGNORE
) {
647 strcpy(connection
->errorStr
,"connection"
649 connection
->error
= MPD_ERROR_CONNCLOSED
;
650 connection
->doneProcessing
= 1;
651 connection
->doneListOk
= 0;
654 connection
->buflen
+=readed
;
655 connection
->buffer
[connection
->buflen
] = '\0';
657 else if(err
<0 && SELECT_ERRNO_IGNORE
) continue;
659 strcpy(connection
->errorStr
,"connection timeout");
660 connection
->error
= MPD_ERROR_TIMEOUT
;
661 connection
->doneProcessing
= 1;
662 connection
->doneListOk
= 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;
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;
689 connection
->doneListOk
= 1;
690 connection
->listOks
--;
695 if(strncmp(output
,"ACK",strlen("ACK"))==0) {
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
, '[');
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
;
718 tok
= strchr(output
, ':');
726 connection
->returnElement
= mpd_newReturnElement(name
,&(value
[1]));
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;
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
))
772 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
774 mpd_Status
* status
= g_slice_new0(mpd_Status
);
776 status
->playlist
= -1;
777 status
->storedplaylist
= -1;
778 status
->playlistLength
= -1;
780 status
->nextsong
= -1;
781 status
->nextsongid
= -1;
782 status
->crossfade
= -1;
784 if(connection
->error
) {
785 g_slice_free(mpd_Status
, status
);
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
;
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
);
876 if(connection
->error
) {
877 g_slice_free(mpd_Status
, status
);
880 else if(status
->state
<0) {
881 strcpy(connection
->errorStr
,"state not found");
882 connection
->error
= 1;
883 g_slice_free(mpd_Status
, 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
))
910 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
912 mpd_Stats
* stats
= g_slice_new0(mpd_Stats
);
914 if(connection
->error
) {
915 mpd_freeStats(stats
);
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
);
949 if(connection
->error
) {
950 mpd_freeStats(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
)) {
968 if (!connection
->returnElement
) mpd_getNextReturnElement(connection
);
970 if (connection
->error
)
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
);
992 if (connection
->error
) {
993 mpd_freeSearchStats(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
;
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
;
1055 ret
->priority
= song
->priority
;
1056 ret
->rating
= song
->rating
;
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
);
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
);
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
))
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
,
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
);
1174 connection
->error
= 1;
1175 strcpy(connection
->errorStr
,"problem parsing song info");
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
);
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
);
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
);
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
);
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
);
1305 static char * mpd_getNextReturnElementNamed(mpd_Connection
* connection
,
1308 if(connection
->doneProcessing
|| (connection
->listOks
&&
1309 connection
->doneListOk
))
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
);
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
)
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
);
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
);
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
);
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
);
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
);
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
);
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
);
1405 void mpd_sendCurrentSongCommand(mpd_Connection
* connection
) {
1406 mpd_executeCommand(connection
,"currentsong\n");
1409 void mpd_sendSearchCommand(mpd_Connection
* connection
, int table
,
1412 mpd_startSearch(connection
, 0);
1413 mpd_addConstraintSearch(connection
, table
, str
);
1414 mpd_commitSearch(connection
);
1417 void mpd_sendFindCommand(mpd_Connection
* connection
, int table
,
1420 mpd_startSearch(connection
, 1);
1421 mpd_addConstraintSearch(connection
, table
, str
);
1422 mpd_commitSearch(connection
);
1425 void mpd_sendListCommand(mpd_Connection
* connection
, int table
,
1431 if(table
== MPD_TABLE_ARTIST
) strcpy(st
,"artist");
1432 else if(table
== MPD_TABLE_ALBUM
) strcpy(st
,"album");
1434 connection
->error
= 1;
1435 strcpy(connection
->errorStr
,"unknown table for list");
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
);
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
);
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
);
1464 int mpd_sendAddIdCommand(mpd_Connection
*connection
, const char *file
)
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
);
1476 string
= mpd_getNextReturnElementNamed(connection
, "Id");
1478 retval
= atoi(string
);
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
);
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
);
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
);
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
);
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
);
1531 void mpd_sendRenameCommand(mpd_Connection
*connection
, const char *from
,
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
1643 int mpd_getUpdateId(mpd_Connection
* connection
) {
1647 jobid
= mpd_getNextReturnElementNamed(connection
,"updating_db");
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
);
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
);
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
);
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
);
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
);
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
);
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
);
1718 void mpd_sendCommandListBegin(mpd_Connection
* connection
) {
1719 if(connection
->commandList
) {
1720 strcpy(connection
->errorStr
,"already in command list mode");
1721 connection
->error
= 1;
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;
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;
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
))
1760 if(connection
->error
) return NULL
;
1762 mpd_OutputEntity
* output
= g_slice_new0(mpd_OutputEntity
);
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
);
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
);
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
);
1806 void mpd_freeOutputElement(mpd_OutputEntity
* output
) {
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;
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;
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;
1890 if (exact
) connection
->request
= strdup("playlistfind");
1891 else connection
->request
= strdup("playlistsearch");
1894 void mpd_startFieldSearch(mpd_Connection
*connection
, int type
)
1899 if (connection
->request
) {
1900 strcpy(connection
->errorStr
, "search already in progress");
1901 connection
->error
= 1;
1905 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
) {
1906 strcpy(connection
->errorStr
, "invalid type specified");
1907 connection
->error
= 1;
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
)
1927 if (!connection
->request
) {
1928 strcpy(connection
->errorStr
, "no search in progress");
1929 connection
->error
= 1;
1933 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
) {
1934 strcpy(connection
->errorStr
, "invalid type specified");
1935 connection
->error
= 1;
1940 strcpy(connection
->errorStr
, "no name specified");
1941 connection
->error
= 1;
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
);
1958 void mpd_commitSearch(mpd_Connection
*connection
)
1962 if (!connection
->request
) {
1963 strcpy(connection
->errorStr
, "no search in progress");
1964 connection
->error
= 1;
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);