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
;
1029 void mpd_freeSong(mpd_Song
* song
) {
1030 mpd_finishSong(song
);
1031 g_slice_free(mpd_Song
, song
);
1034 mpd_Song
* mpd_songDup(const mpd_Song
* song
) {
1035 mpd_Song
* ret
= mpd_newSong();
1037 if(song
->file
) ret
->file
= strdup(song
->file
);
1038 if(song
->artist
) ret
->artist
= strdup(song
->artist
);
1039 if(song
->album
) ret
->album
= strdup(song
->album
);
1040 if(song
->title
) ret
->title
= strdup(song
->title
);
1041 if(song
->track
) ret
->track
= strdup(song
->track
);
1042 if(song
->name
) ret
->name
= strdup(song
->name
);
1043 if(song
->date
) ret
->date
= strdup(song
->date
);
1044 if(song
->genre
) ret
->genre
= strdup(song
->genre
);
1045 if(song
->composer
) ret
->composer
= strdup(song
->composer
);
1046 if(song
->performer
) ret
->performer
= strdup(song
->performer
);
1047 if(song
->disc
) ret
->disc
= strdup(song
->disc
);
1048 if(song
->comment
) ret
->comment
= strdup(song
->comment
);
1049 if(song
->albumartist
) ret
->albumartist
= strdup(song
->albumartist
);
1050 ret
->time
= song
->time
;
1051 ret
->pos
= song
->pos
;
1058 static void mpd_finishDirectory(mpd_Directory
* directory
) {
1059 if(directory
->path
) free(directory
->path
);
1062 mpd_Directory
* mpd_newDirectory(void) {
1063 return g_slice_new0(mpd_Directory
);
1066 void mpd_freeDirectory(mpd_Directory
* directory
) {
1067 mpd_finishDirectory(directory
);
1068 g_slice_free(mpd_Directory
, directory
);
1071 mpd_Directory
* mpd_directoryDup(mpd_Directory
* directory
) {
1072 mpd_Directory
* ret
= mpd_newDirectory();
1074 if(directory
->path
) ret
->path
= strdup(directory
->path
);
1079 static void mpd_finishPlaylistFile(mpd_PlaylistFile
* playlist
) {
1080 if(playlist
->path
) free(playlist
->path
);
1081 if(playlist
->mtime
) free(playlist
->mtime
);
1084 mpd_PlaylistFile
* mpd_newPlaylistFile(void) {
1085 return g_slice_new0(mpd_PlaylistFile
);
1088 void mpd_freePlaylistFile(mpd_PlaylistFile
* playlist
) {
1089 mpd_finishPlaylistFile(playlist
);
1090 g_slice_free(mpd_PlaylistFile
, playlist
);
1093 mpd_PlaylistFile
* mpd_playlistFileDup(mpd_PlaylistFile
* playlist
) {
1094 mpd_PlaylistFile
* ret
= mpd_newPlaylistFile();
1096 if(playlist
->path
) ret
->path
= strdup(playlist
->path
);
1097 if(playlist
->mtime
) ret
->mtime
= strdup(playlist
->mtime
);
1102 static void mpd_finishInfoEntity(mpd_InfoEntity
* entity
) {
1103 if(entity
->info
.directory
) {
1104 if(entity
->type
== MPD_INFO_ENTITY_TYPE_DIRECTORY
) {
1105 mpd_freeDirectory(entity
->info
.directory
);
1107 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_SONG
) {
1108 mpd_freeSong(entity
->info
.song
);
1110 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_PLAYLISTFILE
) {
1111 mpd_freePlaylistFile(entity
->info
.playlistFile
);
1116 mpd_InfoEntity
* mpd_newInfoEntity(void) {
1117 return g_slice_new0(mpd_InfoEntity
);
1120 void mpd_freeInfoEntity(mpd_InfoEntity
* entity
) {
1121 mpd_finishInfoEntity(entity
);
1122 g_slice_free(mpd_InfoEntity
, entity
);
1125 static void mpd_sendInfoCommand(mpd_Connection
* connection
,const char * command
) {
1126 mpd_executeCommand(connection
,command
);
1129 mpd_InfoEntity
* mpd_getNextInfoEntity(mpd_Connection
* connection
) {
1130 mpd_InfoEntity
* entity
= NULL
;
1132 if(connection
->doneProcessing
|| (connection
->listOks
&&
1133 connection
->doneListOk
))
1138 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
1140 if(connection
->returnElement
) {
1141 if(strcmp(connection
->returnElement
->name
,"file")==0) {
1142 entity
= mpd_newInfoEntity();
1143 entity
->type
= MPD_INFO_ENTITY_TYPE_SONG
;
1144 entity
->info
.song
= mpd_newSong();
1145 entity
->info
.song
->file
=
1146 strdup(connection
->returnElement
->value
);
1148 else if(strcmp(connection
->returnElement
->name
,
1150 entity
= mpd_newInfoEntity();
1151 entity
->type
= MPD_INFO_ENTITY_TYPE_DIRECTORY
;
1152 entity
->info
.directory
= mpd_newDirectory();
1153 entity
->info
.directory
->path
=
1154 strdup(connection
->returnElement
->value
);
1156 else if(strcmp(connection
->returnElement
->name
,"playlist")==0) {
1157 entity
= mpd_newInfoEntity();
1158 entity
->type
= MPD_INFO_ENTITY_TYPE_PLAYLISTFILE
;
1159 entity
->info
.playlistFile
= mpd_newPlaylistFile();
1160 entity
->info
.playlistFile
->path
=
1161 strdup(connection
->returnElement
->value
);
1163 else if(strcmp(connection
->returnElement
->name
, "cpos") == 0){
1164 entity
= mpd_newInfoEntity();
1165 entity
->type
= MPD_INFO_ENTITY_TYPE_SONG
;
1166 entity
->info
.song
= mpd_newSong();
1167 entity
->info
.song
->pos
= atoi(connection
->returnElement
->value
);
1170 connection
->error
= 1;
1171 strcpy(connection
->errorStr
,"problem parsing song info");
1177 mpd_getNextReturnElement(connection
);
1178 while(connection
->returnElement
) {
1179 mpd_ReturnElement
* re
= connection
->returnElement
;
1181 if(strcmp(re
->name
,"file")==0) return entity
;
1182 else if(strcmp(re
->name
,"directory")==0) return entity
;
1183 else if(strcmp(re
->name
,"playlist")==0) return entity
;
1184 else if(strcmp(re
->name
,"cpos")==0) return entity
;
1186 if(entity
->type
== MPD_INFO_ENTITY_TYPE_SONG
&&
1187 strlen(re
->value
)) {
1188 if(strcmp(re
->name
,"Artist")==0) {
1189 if(entity
->info
.song
->artist
) {
1190 int length
= strlen(entity
->info
.song
->artist
);
1191 entity
->info
.song
->artist
= realloc(entity
->info
.song
->artist
,
1192 length
+ strlen(re
->value
) + 3);
1193 strcpy(&((entity
->info
.song
->artist
)[length
]), ", ");
1194 strcpy(&((entity
->info
.song
->artist
)[length
+ 2]), re
->value
);
1197 entity
->info
.song
->artist
= strdup(re
->value
);
1200 else if(!entity
->info
.song
->album
&&
1201 strcmp(re
->name
,"Album")==0) {
1202 entity
->info
.song
->album
= strdup(re
->value
);
1204 else if(!entity
->info
.song
->title
&&
1205 strcmp(re
->name
,"Title")==0) {
1206 entity
->info
.song
->title
= strdup(re
->value
);
1208 else if(!entity
->info
.song
->track
&&
1209 strcmp(re
->name
,"Track")==0) {
1210 entity
->info
.song
->track
= strdup(re
->value
);
1212 else if(!entity
->info
.song
->name
&&
1213 strcmp(re
->name
,"Name")==0) {
1214 entity
->info
.song
->name
= strdup(re
->value
);
1216 else if(entity
->info
.song
->time
==MPD_SONG_NO_TIME
&&
1217 strcmp(re
->name
,"Time")==0) {
1218 entity
->info
.song
->time
= atoi(re
->value
);
1220 else if(entity
->info
.song
->pos
==MPD_SONG_NO_NUM
&&
1221 strcmp(re
->name
,"Pos")==0) {
1222 entity
->info
.song
->pos
= atoi(re
->value
);
1224 else if(entity
->info
.song
->id
==MPD_SONG_NO_ID
&&
1225 strcmp(re
->name
,"Id")==0) {
1226 entity
->info
.song
->id
= atoi(re
->value
);
1228 else if(!entity
->info
.song
->date
&&
1229 strcmp(re
->name
, "Date") == 0) {
1230 entity
->info
.song
->date
= strdup(re
->value
);
1232 else if(!entity
->info
.song
->genre
&&
1233 strcmp(re
->name
, "Genre") == 0) {
1234 if(entity
->info
.song
->genre
) {
1235 int length
= strlen(entity
->info
.song
->genre
);
1236 entity
->info
.song
->genre
= realloc(entity
->info
.song
->genre
,
1237 length
+ strlen(re
->value
) + 4);
1238 strcpy(&((entity
->info
.song
->genre
)[length
]), ", ");
1239 strcpy(&((entity
->info
.song
->genre
)[length
+ 3]), re
->value
);
1242 entity
->info
.song
->genre
= strdup(re
->value
);
1245 else if(strcmp(re
->name
, "Composer") == 0) {
1246 if(entity
->info
.song
->composer
) {
1247 int length
= strlen(entity
->info
.song
->composer
);
1248 entity
->info
.song
->composer
= realloc(entity
->info
.song
->composer
,
1249 length
+ strlen(re
->value
) + 3);
1250 strcpy(&((entity
->info
.song
->composer
)[length
]), ", ");
1251 strcpy(&((entity
->info
.song
->composer
)[length
+ 2]), re
->value
);
1254 entity
->info
.song
->composer
= strdup(re
->value
);
1257 else if(strcmp(re
->name
, "Performer") == 0) {
1258 if(entity
->info
.song
->performer
) {
1259 int length
= strlen(entity
->info
.song
->performer
);
1260 entity
->info
.song
->performer
= realloc(entity
->info
.song
->performer
,
1261 length
+ strlen(re
->value
) + 3);
1262 strcpy(&((entity
->info
.song
->performer
)[length
]), ", ");
1263 strcpy(&((entity
->info
.song
->performer
)[length
+ 2]), re
->value
);
1266 entity
->info
.song
->performer
= strdup(re
->value
);
1269 else if(!entity
->info
.song
->disc
&&
1270 strcmp(re
->name
, "Disc") == 0) {
1271 entity
->info
.song
->disc
= strdup(re
->value
);
1273 else if(!entity
->info
.song
->comment
&&
1274 strcmp(re
->name
, "Comment") == 0) {
1275 entity
->info
.song
->comment
= strdup(re
->value
);
1278 else if(!entity
->info
.song
->albumartist
&&
1279 strcmp(re
->name
, "AlbumArtist") == 0) {
1280 entity
->info
.song
->albumartist
= strdup(re
->value
);
1283 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_DIRECTORY
) {
1285 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_PLAYLISTFILE
) {
1286 if(!entity
->info
.playlistFile
->mtime
&&
1287 strcmp(re
->name
, "Last-Modified") == 0) {
1288 entity
->info
.playlistFile
->mtime
= strdup(re
->value
);
1292 mpd_getNextReturnElement(connection
);
1298 static char * mpd_getNextReturnElementNamed(mpd_Connection
* connection
,
1301 if(connection
->doneProcessing
|| (connection
->listOks
&&
1302 connection
->doneListOk
))
1307 mpd_getNextReturnElement(connection
);
1308 while(connection
->returnElement
) {
1309 mpd_ReturnElement
* re
= connection
->returnElement
;
1311 if(strcmp(re
->name
,name
)==0) return strdup(re
->value
);
1312 mpd_getNextReturnElement(connection
);
1318 char *mpd_getNextTag(mpd_Connection
*connection
, int type
)
1320 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
||
1321 type
== MPD_TAG_ITEM_ANY
)
1323 if (type
== MPD_TAG_ITEM_FILENAME
)
1324 return mpd_getNextReturnElementNamed(connection
, "file");
1325 return mpd_getNextReturnElementNamed(connection
, mpdTagItemKeys
[type
]);
1328 char * mpd_getNextArtist(mpd_Connection
* connection
) {
1329 return mpd_getNextReturnElementNamed(connection
,"Artist");
1332 char * mpd_getNextAlbum(mpd_Connection
* connection
) {
1333 return mpd_getNextReturnElementNamed(connection
,"Album");
1336 void mpd_sendPlaylistInfoCommand(mpd_Connection
* connection
, int songPos
) {
1337 int len
= strlen("playlistinfo")+2+INTLEN
+3;
1338 char *string
= malloc(len
);
1339 snprintf(string
, len
, "playlistinfo \"%i\"\n", songPos
);
1340 mpd_sendInfoCommand(connection
,string
);
1344 void mpd_sendPlaylistIdCommand(mpd_Connection
* connection
, int id
) {
1345 int len
= strlen("playlistid")+2+INTLEN
+3;
1346 char *string
= malloc(len
);
1347 snprintf(string
, len
, "playlistid \"%i\"\n", id
);
1348 mpd_sendInfoCommand(connection
, string
);
1352 void mpd_sendPlChangesCommand(mpd_Connection
* connection
, long long playlist
) {
1353 int len
= strlen("plchanges")+2+LONGLONGLEN
+3;
1354 char *string
= malloc(len
);
1355 snprintf(string
, len
, "plchanges \"%lld\"\n", playlist
);
1356 mpd_sendInfoCommand(connection
,string
);
1360 void mpd_sendPlChangesPosIdCommand(mpd_Connection
* connection
, long long playlist
) {
1361 int len
= strlen("plchangesposid")+2+LONGLONGLEN
+3;
1362 char *string
= malloc(len
);
1363 snprintf(string
, len
, "plchangesposid \"%lld\"\n", playlist
);
1364 mpd_sendInfoCommand(connection
,string
);
1368 void mpd_sendListallCommand(mpd_Connection
* connection
, const char * dir
) {
1369 char * sDir
= mpd_sanitizeArg(dir
);
1370 int len
= strlen("listall")+2+strlen(sDir
)+3;
1371 char *string
= malloc(len
);
1372 snprintf(string
, len
, "listall \"%s\"\n", sDir
);
1373 mpd_sendInfoCommand(connection
,string
);
1378 void mpd_sendListallInfoCommand(mpd_Connection
* connection
, const char * dir
) {
1379 char * sDir
= mpd_sanitizeArg(dir
);
1380 int len
= strlen("listallinfo")+2+strlen(sDir
)+3;
1381 char *string
= malloc(len
);
1382 snprintf(string
, len
, "listallinfo \"%s\"\n", sDir
);
1383 mpd_sendInfoCommand(connection
,string
);
1388 void mpd_sendLsInfoCommand(mpd_Connection
* connection
, const char * dir
) {
1389 char * sDir
= mpd_sanitizeArg(dir
);
1390 int len
= strlen("lsinfo")+2+strlen(sDir
)+3;
1391 char *string
= malloc(len
);
1392 snprintf(string
, len
, "lsinfo \"%s\"\n", sDir
);
1393 mpd_sendInfoCommand(connection
,string
);
1398 void mpd_sendCurrentSongCommand(mpd_Connection
* connection
) {
1399 mpd_executeCommand(connection
,"currentsong\n");
1402 void mpd_sendSearchCommand(mpd_Connection
* connection
, int table
,
1405 mpd_startSearch(connection
, 0);
1406 mpd_addConstraintSearch(connection
, table
, str
);
1407 mpd_commitSearch(connection
);
1410 void mpd_sendFindCommand(mpd_Connection
* connection
, int table
,
1413 mpd_startSearch(connection
, 1);
1414 mpd_addConstraintSearch(connection
, table
, str
);
1415 mpd_commitSearch(connection
);
1418 void mpd_sendListCommand(mpd_Connection
* connection
, int table
,
1424 if(table
== MPD_TABLE_ARTIST
) strcpy(st
,"artist");
1425 else if(table
== MPD_TABLE_ALBUM
) strcpy(st
,"album");
1427 connection
->error
= 1;
1428 strcpy(connection
->errorStr
,"unknown table for list");
1432 char * sanitArg1
= mpd_sanitizeArg(arg1
);
1433 len
= strlen("list")+1+strlen(sanitArg1
)+2+strlen(st
)+3;
1434 string
= malloc(len
);
1435 snprintf(string
, len
, "list %s \"%s\"\n", st
, sanitArg1
);
1439 len
= strlen("list")+1+strlen(st
)+2;
1440 string
= malloc(len
);
1441 snprintf(string
, len
, "list %s\n", st
);
1443 mpd_sendInfoCommand(connection
,string
);
1447 void mpd_sendAddCommand(mpd_Connection
* connection
, const char * file
) {
1448 char * sFile
= mpd_sanitizeArg(file
);
1449 int len
= strlen("add")+2+strlen(sFile
)+3;
1450 char *string
= malloc(len
);
1451 snprintf(string
, len
, "add \"%s\"\n", sFile
);
1452 mpd_executeCommand(connection
,string
);
1457 int mpd_sendAddIdCommand(mpd_Connection
*connection
, const char *file
)
1460 char *sFile
= mpd_sanitizeArg(file
);
1461 int len
= strlen("addid")+2+strlen(sFile
)+3;
1462 char *string
= malloc(len
);
1464 snprintf(string
, len
, "addid \"%s\"\n", sFile
);
1465 mpd_sendInfoCommand(connection
, string
);
1469 string
= mpd_getNextReturnElementNamed(connection
, "Id");
1471 retval
= atoi(string
);
1478 void mpd_sendDeleteCommand(mpd_Connection
* connection
, int songPos
) {
1479 int len
= strlen("delete")+2+INTLEN
+3;
1480 char *string
= malloc(len
);
1481 snprintf(string
, len
, "delete \"%i\"\n", songPos
);
1482 mpd_sendInfoCommand(connection
,string
);
1486 void mpd_sendDeleteIdCommand(mpd_Connection
* connection
, int id
) {
1487 int len
= strlen("deleteid")+2+INTLEN
+3;
1488 char *string
= malloc(len
);
1489 snprintf(string
, len
, "deleteid \"%i\"\n", id
);
1490 mpd_sendInfoCommand(connection
,string
);
1494 void mpd_sendSaveCommand(mpd_Connection
* connection
, const char * name
) {
1495 char * sName
= mpd_sanitizeArg(name
);
1496 int len
= strlen("save")+2+strlen(sName
)+3;
1497 char *string
= malloc(len
);
1498 snprintf(string
, len
, "save \"%s\"\n", sName
);
1499 mpd_executeCommand(connection
,string
);
1504 void mpd_sendLoadCommand(mpd_Connection
* connection
, const char * name
) {
1505 char * sName
= mpd_sanitizeArg(name
);
1506 int len
= strlen("load")+2+strlen(sName
)+3;
1507 char *string
= malloc(len
);
1508 snprintf(string
, len
, "load \"%s\"\n", sName
);
1509 mpd_executeCommand(connection
,string
);
1514 void mpd_sendRmCommand(mpd_Connection
* connection
, const char * name
) {
1515 char * sName
= mpd_sanitizeArg(name
);
1516 int len
= strlen("rm")+2+strlen(sName
)+3;
1517 char *string
= malloc(len
);
1518 snprintf(string
, len
, "rm \"%s\"\n", sName
);
1519 mpd_executeCommand(connection
,string
);
1524 void mpd_sendRenameCommand(mpd_Connection
*connection
, const char *from
,
1527 char *sFrom
= mpd_sanitizeArg(from
);
1528 char *sTo
= mpd_sanitizeArg(to
);
1529 int len
= strlen("rename")+2+strlen(sFrom
)+3+strlen(sTo
)+3;
1530 char *string
= malloc(len
);
1531 snprintf(string
, len
, "rename \"%s\" \"%s\"\n", sFrom
, sTo
);
1532 mpd_executeCommand(connection
, string
);
1538 void mpd_sendShuffleCommand(mpd_Connection
* connection
) {
1539 mpd_executeCommand(connection
,"shuffle\n");
1542 void mpd_sendClearCommand(mpd_Connection
* connection
) {
1543 mpd_executeCommand(connection
,"clear\n");
1546 void mpd_sendPlayCommand(mpd_Connection
* connection
, int songPos
) {
1547 int len
= strlen("play")+2+INTLEN
+3;
1548 char *string
= malloc(len
);
1549 snprintf(string
, len
, "play \"%i\"\n", songPos
);
1550 mpd_sendInfoCommand(connection
,string
);
1554 void mpd_sendPlayIdCommand(mpd_Connection
* connection
, int id
) {
1555 int len
= strlen("playid")+2+INTLEN
+3;
1556 char *string
= malloc(len
);
1557 snprintf(string
, len
, "playid \"%i\"\n", id
);
1558 mpd_sendInfoCommand(connection
,string
);
1562 void mpd_sendStopCommand(mpd_Connection
* connection
) {
1563 mpd_executeCommand(connection
,"stop\n");
1566 void mpd_sendPauseCommand(mpd_Connection
* connection
, int pauseMode
) {
1567 int len
= strlen("pause")+2+INTLEN
+3;
1568 char *string
= malloc(len
);
1569 snprintf(string
, len
, "pause \"%i\"\n", pauseMode
);
1570 mpd_executeCommand(connection
,string
);
1574 void mpd_sendNextCommand(mpd_Connection
* connection
) {
1575 mpd_executeCommand(connection
,"next\n");
1578 void mpd_sendMoveCommand(mpd_Connection
* connection
, int from
, int to
) {
1579 int len
= strlen("move")+2+INTLEN
+3+INTLEN
+3;
1580 char *string
= malloc(len
);
1581 snprintf(string
, len
, "move \"%i\" \"%i\"\n", from
, to
);
1582 mpd_sendInfoCommand(connection
,string
);
1586 void mpd_sendMoveIdCommand(mpd_Connection
* connection
, int id
, int to
) {
1587 int len
= strlen("moveid")+2+INTLEN
+3+INTLEN
+3;
1588 char *string
= malloc(len
);
1589 snprintf(string
, len
, "moveid \"%i\" \"%i\"\n", id
, to
);
1590 mpd_sendInfoCommand(connection
,string
);
1594 void mpd_sendSwapCommand(mpd_Connection
* connection
, int song1
, int song2
) {
1595 int len
= strlen("swap")+2+INTLEN
+3+INTLEN
+3;
1596 char *string
= malloc(len
);
1597 snprintf(string
, len
, "swap \"%i\" \"%i\"\n", song1
, song2
);
1598 mpd_sendInfoCommand(connection
,string
);
1602 void mpd_sendSwapIdCommand(mpd_Connection
* connection
, int id1
, int id2
) {
1603 int len
= strlen("swapid")+2+INTLEN
+3+INTLEN
+3;
1604 char *string
= malloc(len
);
1605 snprintf(string
, len
, "swapid \"%i\" \"%i\"\n", id1
, id2
);
1606 mpd_sendInfoCommand(connection
,string
);
1610 void mpd_sendSeekCommand(mpd_Connection
* connection
, int song
, int seek_time
) {
1611 int len
= strlen("seek")+2+INTLEN
+3+INTLEN
+3;
1612 char *string
= malloc(len
);
1613 snprintf(string
, len
, "seek \"%i\" \"%i\"\n", song
, seek_time
);
1614 mpd_sendInfoCommand(connection
,string
);
1618 void mpd_sendSeekIdCommand(mpd_Connection
* connection
, int id
, int seek_time
) {
1619 int len
= strlen("seekid")+2+INTLEN
+3+INTLEN
+3;
1620 char *string
= malloc(len
);
1621 snprintf(string
, len
, "seekid \"%i\" \"%i\"\n", id
, seek_time
);
1622 mpd_sendInfoCommand(connection
,string
);
1626 void mpd_sendUpdateCommand(mpd_Connection
* connection
,const char * path
) {
1627 char * sPath
= mpd_sanitizeArg(path
);
1628 int len
= strlen("update")+2+strlen(sPath
)+3;
1629 char *string
= malloc(len
);
1630 snprintf(string
, len
, "update \"%s\"\n", sPath
);
1631 mpd_sendInfoCommand(connection
,string
);
1636 int mpd_getUpdateId(mpd_Connection
* connection
) {
1640 jobid
= mpd_getNextReturnElementNamed(connection
,"updating_db");
1649 void mpd_sendPrevCommand(mpd_Connection
* connection
) {
1650 mpd_executeCommand(connection
,"previous\n");
1653 void mpd_sendSingleCommand(mpd_Connection
* connection
, int singleMode
) {
1654 int len
= strlen("repeat")+2+INTLEN
+3;
1655 char *string
= malloc(len
);
1656 snprintf(string
, len
, "single \"%i\"\n", singleMode
);
1657 mpd_executeCommand(connection
,string
);
1661 void mpd_sendConsumeCommand(mpd_Connection
* connection
, int consumeMode
) {
1662 int len
= strlen("repeat")+2+INTLEN
+3;
1663 char *string
= malloc(len
);
1664 snprintf(string
, len
, "consume \"%i\"\n", consumeMode
);
1665 mpd_executeCommand(connection
,string
);
1669 void mpd_sendRepeatCommand(mpd_Connection
* connection
, int repeatMode
) {
1670 int len
= strlen("repeat")+2+INTLEN
+3;
1671 char *string
= malloc(len
);
1672 snprintf(string
, len
, "repeat \"%i\"\n", repeatMode
);
1673 mpd_executeCommand(connection
,string
);
1677 void mpd_sendRandomCommand(mpd_Connection
* connection
, int randomMode
) {
1678 int len
= strlen("random")+2+INTLEN
+3;
1679 char *string
= malloc(len
);
1680 snprintf(string
, len
, "random \"%i\"\n", randomMode
);
1681 mpd_executeCommand(connection
,string
);
1685 void mpd_sendSetvolCommand(mpd_Connection
* connection
, int volumeChange
) {
1686 int len
= strlen("setvol")+2+INTLEN
+3;
1687 char *string
= malloc(len
);
1688 snprintf(string
, len
, "setvol \"%i\"\n", volumeChange
);
1689 mpd_executeCommand(connection
,string
);
1693 void mpd_sendCrossfadeCommand(mpd_Connection
* connection
, int seconds
) {
1694 int len
= strlen("crossfade")+2+INTLEN
+3;
1695 char *string
= malloc(len
);
1696 snprintf(string
, len
, "crossfade \"%i\"\n", seconds
);
1697 mpd_executeCommand(connection
,string
);
1701 void mpd_sendPasswordCommand(mpd_Connection
* connection
, const char * pass
) {
1702 char * sPass
= mpd_sanitizeArg(pass
);
1703 int len
= strlen("password")+2+strlen(sPass
)+3;
1704 char *string
= malloc(len
);
1705 snprintf(string
, len
, "password \"%s\"\n", sPass
);
1706 mpd_executeCommand(connection
,string
);
1711 void mpd_sendCommandListBegin(mpd_Connection
* connection
) {
1712 if(connection
->commandList
) {
1713 strcpy(connection
->errorStr
,"already in command list mode");
1714 connection
->error
= 1;
1717 connection
->commandList
= COMMAND_LIST
;
1718 mpd_executeCommand(connection
,"command_list_begin\n");
1721 void mpd_sendCommandListOkBegin(mpd_Connection
* connection
) {
1722 if(connection
->commandList
) {
1723 strcpy(connection
->errorStr
,"already in command list mode");
1724 connection
->error
= 1;
1727 connection
->commandList
= COMMAND_LIST_OK
;
1728 mpd_executeCommand(connection
,"command_list_ok_begin\n");
1729 connection
->listOks
= 0;
1732 void mpd_sendCommandListEnd(mpd_Connection
* connection
) {
1733 if(!connection
->commandList
) {
1734 strcpy(connection
->errorStr
,"not in command list mode");
1735 connection
->error
= 1;
1738 connection
->commandList
= 0;
1739 mpd_executeCommand(connection
,"command_list_end\n");
1742 void mpd_sendOutputsCommand(mpd_Connection
* connection
) {
1743 mpd_executeCommand(connection
,"outputs\n");
1746 mpd_OutputEntity
* mpd_getNextOutput(mpd_Connection
* connection
) {
1747 if(connection
->doneProcessing
|| (connection
->listOks
&&
1748 connection
->doneListOk
))
1753 if(connection
->error
) return NULL
;
1755 mpd_OutputEntity
* output
= g_slice_new0(mpd_OutputEntity
);
1758 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
1760 while(connection
->returnElement
) {
1761 mpd_ReturnElement
* re
= connection
->returnElement
;
1762 if(strcmp(re
->name
,"outputid")==0) {
1763 if(output
!=NULL
&& output
->id
>=0) return output
;
1764 output
->id
= atoi(re
->value
);
1766 else if(strcmp(re
->name
,"outputname")==0) {
1767 output
->name
= strdup(re
->value
);
1769 else if(strcmp(re
->name
,"outputenabled")==0) {
1770 output
->enabled
= atoi(re
->value
);
1773 mpd_getNextReturnElement(connection
);
1774 if(connection
->error
) {
1775 mpd_freeOutputElement(output
);
1783 void mpd_sendEnableOutputCommand(mpd_Connection
* connection
, int outputId
) {
1784 int len
= strlen("enableoutput")+2+INTLEN
+3;
1785 char *string
= malloc(len
);
1786 snprintf(string
, len
, "enableoutput \"%i\"\n", outputId
);
1787 mpd_executeCommand(connection
,string
);
1791 void mpd_sendDisableOutputCommand(mpd_Connection
* connection
, int outputId
) {
1792 int len
= strlen("disableoutput")+2+INTLEN
+3;
1793 char *string
= malloc(len
);
1794 snprintf(string
, len
, "disableoutput \"%i\"\n", outputId
);
1795 mpd_executeCommand(connection
,string
);
1799 void mpd_freeOutputElement(mpd_OutputEntity
* output
) {
1802 g_slice_free(mpd_OutputEntity
, output
);
1806 * mpd_sendNotCommandsCommand
1807 * odd naming, but it gets the not allowed commands
1810 void mpd_sendNotCommandsCommand(mpd_Connection
* connection
)
1812 mpd_executeCommand(connection
, "notcommands\n");
1816 * mpd_sendCommandsCommand
1817 * odd naming, but it gets the allowed commands
1819 void mpd_sendCommandsCommand(mpd_Connection
* connection
)
1821 mpd_executeCommand(connection
, "commands\n");
1825 * Get the next returned command
1827 char * mpd_getNextCommand(mpd_Connection
* connection
)
1829 return mpd_getNextReturnElementNamed(connection
, "command");
1832 void mpd_sendUrlHandlersCommand(mpd_Connection
* connection
)
1834 mpd_executeCommand(connection
, "urlhandlers\n");
1837 char * mpd_getNextHandler(mpd_Connection
* connection
)
1839 return mpd_getNextReturnElementNamed(connection
, "handler");
1842 void mpd_sendTagTypesCommand(mpd_Connection
* connection
)
1844 mpd_executeCommand(connection
, "tagtypes\n");
1847 char * mpd_getNextTagType(mpd_Connection
* connection
)
1849 return mpd_getNextReturnElementNamed(connection
, "tagtype");
1852 void mpd_startSearch(mpd_Connection
*connection
, int exact
)
1854 if (connection
->request
) {
1855 strcpy(connection
->errorStr
, "search already in progress");
1856 connection
->error
= 1;
1860 if (exact
) connection
->request
= strdup("find");
1861 else connection
->request
= strdup("search");
1864 void mpd_startStatsSearch(mpd_Connection
*connection
)
1866 if (connection
->request
) {
1867 strcpy(connection
->errorStr
, "search already in progress");
1868 connection
->error
= 1;
1872 connection
->request
= strdup("count");
1875 void mpd_startPlaylistSearch(mpd_Connection
*connection
, int exact
)
1877 if (connection
->request
) {
1878 strcpy(connection
->errorStr
, "search already in progress");
1879 connection
->error
= 1;
1883 if (exact
) connection
->request
= strdup("playlistfind");
1884 else connection
->request
= strdup("playlistsearch");
1887 void mpd_startFieldSearch(mpd_Connection
*connection
, int type
)
1892 if (connection
->request
) {
1893 strcpy(connection
->errorStr
, "search already in progress");
1894 connection
->error
= 1;
1898 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
) {
1899 strcpy(connection
->errorStr
, "invalid type specified");
1900 connection
->error
= 1;
1904 strtype
= mpdTagItemKeys
[type
];
1906 len
= 5+strlen(strtype
)+1;
1907 connection
->request
= malloc(len
);
1909 snprintf(connection
->request
, len
, "list %c%s",
1910 tolower(strtype
[0]), strtype
+1);
1913 void mpd_addConstraintSearch(mpd_Connection
*connection
, int type
, const char *name
)
1920 if (!connection
->request
) {
1921 strcpy(connection
->errorStr
, "no search in progress");
1922 connection
->error
= 1;
1926 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
) {
1927 strcpy(connection
->errorStr
, "invalid type specified");
1928 connection
->error
= 1;
1933 strcpy(connection
->errorStr
, "no name specified");
1934 connection
->error
= 1;
1938 string
= strdup(connection
->request
);
1939 strtype
= mpdTagItemKeys
[type
];
1940 arg
= mpd_sanitizeArg(name
);
1942 len
= strlen(string
)+1+strlen(strtype
)+2+strlen(arg
)+2;
1943 connection
->request
= realloc(connection
->request
, len
);
1944 snprintf(connection
->request
, len
, "%s %c%s \"%s\"",
1945 string
, tolower(strtype
[0]), strtype
+1, arg
);
1951 void mpd_commitSearch(mpd_Connection
*connection
)
1955 if (!connection
->request
) {
1956 strcpy(connection
->errorStr
, "no search in progress");
1957 connection
->error
= 1;
1961 len
= strlen(connection
->request
)+2;
1962 connection
->request
= realloc(connection
->request
, len
);
1963 connection
->request
[len
-2] = '\n';
1964 connection
->request
[len
-1] = '\0';
1965 mpd_sendInfoCommand(connection
, connection
->request
);
1967 free(connection
->request
);
1968 connection
->request
= NULL
;
1972 * @param connection a MpdConnection
1973 * @param path the path to the playlist.
1975 * List the content, with full metadata, of a stored playlist.
1978 void mpd_sendListPlaylistInfoCommand(mpd_Connection
*connection
,const char *path
)
1980 char *arg
= mpd_sanitizeArg(path
);
1981 int len
= strlen("listplaylistinfo")+2+strlen(arg
)+3;
1982 char *query
= malloc(len
);
1983 snprintf(query
, len
, "listplaylistinfo \"%s\"\n", arg
);
1984 mpd_sendInfoCommand(connection
, query
);
1990 * @param connection a MpdConnection
1991 * @param path the path to the playlist.
1993 * List the content of a stored playlist.
1996 void mpd_sendListPlaylistCommand(mpd_Connection
*connection
,const char *path
)
1998 char *arg
= mpd_sanitizeArg(path
);
1999 int len
= strlen("listplaylist")+2+strlen(arg
)+3;
2000 char *query
= malloc(len
);
2001 snprintf(query
, len
, "listplaylist \"%s\"\n", arg
);
2002 mpd_sendInfoCommand(connection
, query
);
2007 void mpd_sendPlaylistClearCommand(mpd_Connection
*connection
,const char *path
)
2009 char *sPath
= mpd_sanitizeArg(path
);
2010 int len
= strlen("playlistclear")+2+strlen(sPath
)+3;
2011 char *string
= malloc(len
);
2012 snprintf(string
, len
, "playlistclear \"%s\"\n", sPath
);
2013 mpd_executeCommand(connection
, string
);
2018 void mpd_sendPlaylistAddCommand(mpd_Connection
*connection
,
2019 const char *playlist
,const char *path
)
2021 char *sPlaylist
= mpd_sanitizeArg(playlist
);
2022 char *sPath
= mpd_sanitizeArg(path
);
2023 int len
= strlen("playlistadd")+2+strlen(sPlaylist
)+3+strlen(sPath
)+3;
2024 char *string
= malloc(len
);
2025 snprintf(string
, len
, "playlistadd \"%s\" \"%s\"\n", sPlaylist
, sPath
);
2026 mpd_executeCommand(connection
, string
);
2032 void mpd_sendPlaylistMoveCommand(mpd_Connection
*connection
,
2033 const char *playlist
, int from
, int to
)
2035 char *sPlaylist
= mpd_sanitizeArg(playlist
);
2036 int len
= strlen("playlistmove")+
2037 2+strlen(sPlaylist
)+3+INTLEN
+3+INTLEN
+3;
2038 char *string
= malloc(len
);
2039 snprintf(string
, len
, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2040 sPlaylist
, from
, to
);
2041 mpd_executeCommand(connection
, string
);
2046 void mpd_sendPlaylistDeleteCommand(mpd_Connection
*connection
,
2047 const char *playlist
, int pos
)
2049 char *sPlaylist
= mpd_sanitizeArg(playlist
);
2050 int len
= strlen("playlistdelete")+2+strlen(sPlaylist
)+3+INTLEN
+3;
2051 char *string
= malloc(len
);
2052 snprintf(string
, len
, "playlistdelete \"%s\" \"%i\"\n", sPlaylist
, pos
);
2053 mpd_executeCommand(connection
, string
);
2057 void mpd_sendClearErrorCommand(mpd_Connection
* connection
) {
2058 mpd_executeCommand(connection
,"clearerror\n");
2062 void mpd_sendGetEventsCommand(mpd_Connection
*connection
) {
2063 mpd_executeCommand(connection
, "idle\nnoidle\n");
2064 // mpd_executeCommand(connection, "noidle\n");
2067 char * mpd_getNextEvent(mpd_Connection
*connection
)
2069 return mpd_getNextReturnElementNamed(connection
, "changed");
2072 void mpd_sendListPlaylistsCommand(mpd_Connection
* connection
) {
2073 mpd_sendInfoCommand(connection
, "listplaylists\n");
2076 char * mpd_getNextSticker (mpd_Connection
* connection
)
2078 return mpd_getNextReturnElementNamed(connection
, "sticker");
2080 void mpd_sendGetSongSticker(mpd_Connection
*connection
, const char *song_path
, const char *sticker
)
2082 char *sSong
= mpd_sanitizeArg(song_path
);
2083 char *sSticker
= mpd_sanitizeArg(sticker
);
2084 int len
= strlen("sticker get song ")+strlen(sSong
)+3+strlen(sSticker
)+4;
2085 char *string
= malloc(len
);
2086 snprintf(string
, len
, "sticker get song \"%s\" \"%s\"\n", sSong
, sSticker
);
2087 mpd_executeCommand(connection
, string
);
2093 void mpd_sendSetSongSticker(mpd_Connection
*connection
, const char *song_path
, const char *sticker
, const char *value
)
2096 char *sSong
= mpd_sanitizeArg(song_path
);
2097 char *sSticker
= mpd_sanitizeArg(sticker
);
2098 char *sValue
= mpd_sanitizeArg(value
);
2099 int len
= strlen("sticker set song ")+strlen(sSong
)+3+strlen(sSticker
)+3+strlen(sValue
)+4;
2100 char *string
= malloc(len
);
2101 snprintf(string
, len
, "sticker set song \"%s\" \"%s\" \"%s\"\n", sSong
, sSticker
,sValue
);
2102 mpd_sendInfoCommand(connection
, string
);
2109 void mpd_sendSetReplayGainMode(mpd_Connection
*connection
, const char *mode
)
2111 char *smode
= mpd_sanitizeArg(mode
);
2112 int len
= strlen("replay_gain_mode ")+strlen(smode
)+4;
2113 char *string
= malloc(len
);
2114 snprintf(string
, len
, "replay_gain_mode \"%s\"\n", smode
);
2115 mpd_sendInfoCommand(connection
, string
);
2119 void mpd_sendReplayGainModeCommand(mpd_Connection
*connection
)
2121 mpd_executeCommand(connection
, "replay_gain_status\n");
2123 char *mpd_getReplayGainMode(mpd_Connection
*connection
)
2125 return mpd_getNextReturnElementNamed(connection
, "replay_gain_mode");