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.
33 #include "libmpdclient.h"
37 #include <sys/types.h>
39 #include <sys/param.h>
47 # include <ws2tcpip.h>
50 # include <netinet/in.h>
51 # include <arpa/inet.h>
52 # include <sys/socket.h>
56 /* (bits+1)/3 (plus the sign character) */
57 #define INTLEN ((sizeof(int) * CHAR_BIT + 1) / 3 + 1)
58 #define LONGLONGLEN ((sizeof(long long) * CHAR_BIT + 1) / 3 + 1)
60 #define COMMAND_LIST 1
61 #define COMMAND_LIST_OK 2
74 # define MSG_DONTWAIT 0
78 # define SELECT_ERRNO_IGNORE (errno == WSAEINTR || errno == WSAEINPROGRESS)
79 # define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
81 # define SELECT_ERRNO_IGNORE (errno == EINTR)
82 # define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
83 # define winsock_dll_error(c) 0
84 # define closesocket(s) close(s)
85 # define WSACleanup() do { /* nothing */ } while (0)
89 static int winsock_dll_error(mpd_Connection
*connection
)
92 if ((WSAStartup(MAKEWORD(2, 2), &wsaData
)) != 0 ||
93 LOBYTE(wsaData
.wVersion
) != 2 ||
94 HIBYTE(wsaData
.wVersion
) != 2 ) {
95 strcpy(connection
->errorStr
,
96 "Could not find usable WinSock DLL.");
97 connection
->error
= MPD_ERROR_SYSTEM
;
103 static int do_connect_fail(mpd_Connection
*connection
,
104 const struct sockaddr
*serv_addr
, int addrlen
)
106 int iMode
= 1; /* 0 = blocking, else non-blocking */
107 if (connect(connection
->sock
, serv_addr
, addrlen
) == SOCKET_ERROR
)
109 ioctlsocket(connection
->sock
, FIONBIO
, (u_long FAR
*) &iMode
);
112 #else /* !WIN32 (sane operating systems) */
113 static int do_connect_fail(mpd_Connection
*connection
,
114 const struct sockaddr
*serv_addr
, int addrlen
)
117 if (connect(connection
->sock
, serv_addr
, addrlen
) < 0)
119 flags
= fcntl(connection
->sock
, F_GETFL
, 0);
120 fcntl(connection
->sock
, F_SETFL
, flags
| O_NONBLOCK
);
126 static int mpd_connect(mpd_Connection
* connection
, const char * host
, int port
,
130 char service
[INTLEN
+1];
131 struct addrinfo hints
;
132 struct addrinfo
*res
= NULL
;
133 struct addrinfo
*addrinfo
= NULL
;
138 hints
.ai_flags
= AI_ADDRCONFIG
;
139 hints
.ai_family
= AF_UNSPEC
;
140 hints
.ai_socktype
= SOCK_STREAM
;
141 hints
.ai_protocol
= IPPROTO_TCP
;
142 hints
.ai_addrlen
= 0;
143 hints
.ai_addr
= NULL
;
144 hints
.ai_canonname
= NULL
;
145 hints
.ai_next
= NULL
;
147 snprintf(service
, sizeof(service
), "%i", port
);
149 error
= getaddrinfo(host
, service
, &hints
, &addrinfo
);
152 snprintf(connection
->errorStr
, MPD_ERRORSTR_MAX_LENGTH
,
153 "host \"%s\" not found: %s",
154 host
, gai_strerror(error
));
155 connection
->error
= MPD_ERROR_UNKHOST
;
159 for (res
= addrinfo
; res
; res
= res
->ai_next
) {
161 if (connection
->sock
>= 0)
162 closesocket(connection
->sock
);
163 connection
->sock
= socket(res
->ai_family
, SOCK_STREAM
,
165 if (connection
->sock
< 0) {
166 snprintf(connection
->errorStr
, MPD_ERRORSTR_MAX_LENGTH
,
167 "problems creating socket: %s",
169 connection
->error
= MPD_ERROR_SYSTEM
;
170 freeaddrinfo(addrinfo
);
174 mpd_setConnectionTimeout(connection
, timeout
);
177 if (do_connect_fail(connection
,
178 res
->ai_addr
, res
->ai_addrlen
)) {
179 /* try the next address */
180 closesocket(connection
->sock
);
181 connection
->sock
= -1;
188 freeaddrinfo(addrinfo
);
190 if (connection
->sock
< 0) {
191 snprintf(connection
->errorStr
, MPD_ERRORSTR_MAX_LENGTH
,
192 "problems connecting to \"%s\" on port %i: %s",
193 host
, port
, strerror(errno
));
194 connection
->error
= MPD_ERROR_CONNPORT
;
201 #else /* !MPD_HAVE_GAI */
202 static int mpd_connect(mpd_Connection
* connection
, const char * host
, int port
,
206 struct sockaddr
* dest
;
208 struct sockaddr_in sin
;
210 if(!(he
=gethostbyname(host
))) {
211 snprintf(connection
->errorStr
,MPD_ERRORSTR_MAX_LENGTH
,
212 "host \"%s\" not found",host
);
213 connection
->error
= MPD_ERROR_UNKHOST
;
217 memset(&sin
,0,sizeof(struct sockaddr_in
));
218 /*dest.sin_family = he->h_addrtype;*/
219 sin
.sin_family
= AF_INET
;
220 sin
.sin_port
= htons(port
);
222 switch(he
->h_addrtype
) {
224 memcpy((char *)&sin
.sin_addr
.s_addr
,(char *)he
->h_addr
,
226 dest
= (struct sockaddr
*)&sin
;
227 destlen
= sizeof(struct sockaddr_in
);
230 strcpy(connection
->errorStr
,"address type is not IPv4");
231 connection
->error
= MPD_ERROR_SYSTEM
;
236 if (connection
->sock
>= 0)
237 closesocket(connection
->sock
);
238 if((connection
->sock
= socket(dest
->sa_family
,SOCK_STREAM
,0))<0) {
239 strcpy(connection
->errorStr
,"problems creating socket");
240 connection
->error
= MPD_ERROR_SYSTEM
;
244 mpd_setConnectionTimeout(connection
,timeout
);
247 if (do_connect_fail(connection
, dest
, destlen
)) {
248 snprintf(connection
->errorStr
,MPD_ERRORSTR_MAX_LENGTH
,
249 "problems connecting to \"%s\" on port"
251 connection
->error
= MPD_ERROR_CONNPORT
;
257 #endif /* !MPD_HAVE_GAI */
259 char * mpdTagItemKeys
[MPD_TAG_NUM_OF_ITEM_TYPES
] =
277 static char * mpd_sanitizeArg(const char * arg
) {
280 register const char *c
;
283 /* instead of counting in that loop above, just
284 * use a bit more memory and half running time
286 ret
= malloc(strlen(arg
) * 2 + 1);
290 for(i
= strlen(arg
)+1; i
!= 0; --i
) {
291 if(*c
=='"' || *c
=='\\')
299 static mpd_ReturnElement
* mpd_newReturnElement(const char * name
, const char * value
)
301 mpd_ReturnElement
* ret
= malloc(sizeof(mpd_ReturnElement
));
303 ret
->name
= strdup(name
);
304 ret
->value
= strdup(value
);
309 static void mpd_freeReturnElement(mpd_ReturnElement
* re
) {
315 void mpd_setConnectionTimeout(mpd_Connection
* connection
, float timeout
) {
316 connection
->timeout
.tv_sec
= (int)timeout
;
317 connection
->timeout
.tv_usec
= (int)(timeout
*1e6
-
318 connection
->timeout
.tv_sec
*1000000 +
322 static int mpd_parseWelcome(mpd_Connection
* connection
, const char * host
, int port
,
328 if(strncmp(output
,MPD_WELCOME_MESSAGE
,strlen(MPD_WELCOME_MESSAGE
))) {
329 snprintf(connection
->errorStr
,MPD_ERRORSTR_MAX_LENGTH
,
330 "mpd not running on port %i on host \"%s\"",
332 connection
->error
= MPD_ERROR_NOTMPD
;
336 tmp
= &output
[strlen(MPD_WELCOME_MESSAGE
)];
339 if(tmp
) connection
->version
[i
] = strtol(tmp
,&test
,10);
341 if (!tmp
|| (test
[0] != '.' && test
[0] != '\0')) {
342 snprintf(connection
->errorStr
,
343 MPD_ERRORSTR_MAX_LENGTH
,
344 "error parsing version number at "
346 &output
[strlen(MPD_WELCOME_MESSAGE
)]);
347 connection
->error
= MPD_ERROR_NOTMPD
;
357 static int mpd_connect_un(mpd_Connection
* connection
,
358 const char * host
, float timeout
)
362 struct sockaddr_un saun
;
364 path_length
= strlen(host
);
365 if (path_length
>= sizeof(saun
.sun_path
)) {
366 strcpy(connection
->errorStr
, "unix socket path is too long");
367 connection
->error
= MPD_ERROR_UNKHOST
;
371 saun
.sun_family
= AF_UNIX
;
372 memcpy(saun
.sun_path
, host
, path_length
+ 1);
374 connection
->sock
= socket(AF_UNIX
, SOCK_STREAM
, 0);
375 if (connection
->sock
< 0) {
376 strcpy(connection
->errorStr
, "problems creating socket");
377 connection
->error
= MPD_ERROR_SYSTEM
;
381 mpd_setConnectionTimeout(connection
, timeout
);
383 flags
= fcntl(connection
->sock
, F_GETFL
, 0);
384 fcntl(connection
->sock
, F_SETFL
, flags
| O_NONBLOCK
);
386 error
= connect(connection
->sock
, (struct sockaddr
*)&saun
, sizeof(saun
));
388 /* try the next address family */
389 close(connection
->sock
);
390 connection
->sock
= 0;
392 snprintf(connection
->errorStr
,MPD_BUFFER_MAX_LENGTH
,
393 "problems connecting to \"%s\": %s",
394 host
, strerror(errno
));
395 connection
->error
= MPD_ERROR_CONNPORT
;
403 mpd_Connection
* mpd_newConnection(const char * host
, int port
, float timeout
) {
406 char * output
= NULL
;
407 mpd_Connection
* connection
= malloc(sizeof(mpd_Connection
));
410 strcpy(connection
->buffer
,"");
411 connection
->sock
= -1;
412 connection
->buflen
= 0;
413 connection
->bufstart
= 0;
414 strcpy(connection
->errorStr
,"");
415 connection
->error
= 0;
416 connection
->doneProcessing
= 0;
417 connection
->commandList
= 0;
418 connection
->listOks
= 0;
419 connection
->doneListOk
= 0;
420 connection
->returnElement
= NULL
;
421 connection
->request
= NULL
;
423 if (winsock_dll_error(connection
))
428 err
= mpd_connect_un(connection
, host
, timeout
);
431 err
= mpd_connect(connection
, host
, port
, timeout
);
435 while(!(rt
= strstr(connection
->buffer
,"\n"))) {
436 tv
.tv_sec
= connection
->timeout
.tv_sec
;
437 tv
.tv_usec
= connection
->timeout
.tv_usec
;
439 FD_SET(connection
->sock
,&fds
);
440 if((err
= select(connection
->sock
+1,&fds
,NULL
,NULL
,&tv
)) == 1) {
442 readed
= recv(connection
->sock
,
443 &(connection
->buffer
[connection
->buflen
]),
444 MPD_BUFFER_MAX_LENGTH
-connection
->buflen
,0);
446 snprintf(connection
->errorStr
,MPD_ERRORSTR_MAX_LENGTH
,
447 "problems getting a response from"
448 " \"%s\" on port %i : %s",host
,
449 port
, strerror(errno
));
450 connection
->error
= MPD_ERROR_NORESPONSE
;
453 connection
->buflen
+=readed
;
454 connection
->buffer
[connection
->buflen
] = '\0';
457 if (SELECT_ERRNO_IGNORE
)
459 snprintf(connection
->errorStr
,
460 MPD_ERRORSTR_MAX_LENGTH
,
461 "problems connecting to \"%s\" on port"
463 connection
->error
= MPD_ERROR_CONNPORT
;
467 snprintf(connection
->errorStr
,MPD_ERRORSTR_MAX_LENGTH
,
468 "timeout in attempting to get a response from"
469 " \"%s\" on port %i",host
,port
);
470 connection
->error
= MPD_ERROR_NORESPONSE
;
476 output
= strdup(connection
->buffer
);
477 strcpy(connection
->buffer
,rt
+1);
478 connection
->buflen
= strlen(connection
->buffer
);
480 if(mpd_parseWelcome(connection
,host
,port
,output
) == 0) connection
->doneProcessing
= 1;
487 void mpd_clearError(mpd_Connection
* connection
) {
488 connection
->error
= 0;
489 connection
->errorStr
[0] = '\0';
492 void mpd_closeConnection(mpd_Connection
* connection
) {
493 closesocket(connection
->sock
);
494 if(connection
->returnElement
) free(connection
->returnElement
);
495 if(connection
->request
) free(connection
->request
);
500 static void mpd_executeCommand(mpd_Connection
* connection
,const char * command
) {
504 const char * commandPtr
= command
;
505 int commandLen
= strlen(command
);
507 if(!connection
->doneProcessing
&& !connection
->commandList
) {
508 strcpy(connection
->errorStr
,"not done processing current command");
509 connection
->error
= 1;
513 mpd_clearError(connection
);
516 FD_SET(connection
->sock
,&fds
);
517 tv
.tv_sec
= connection
->timeout
.tv_sec
;
518 tv
.tv_usec
= connection
->timeout
.tv_usec
;
519 while((ret
= select(connection
->sock
+1,NULL
,&fds
,NULL
,&tv
)==1) ||
520 (ret
==-1 && SELECT_ERRNO_IGNORE
)) {
522 ret
= send(connection
->sock
,commandPtr
,commandLen
,MSG_DONTWAIT
);
525 if (SENDRECV_ERRNO_IGNORE
) continue;
526 snprintf(connection
->errorStr
,MPD_ERRORSTR_MAX_LENGTH
,
527 "problems giving command \"%s\"",command
);
528 connection
->error
= MPD_ERROR_SENDING
;
536 if(commandLen
<=0) break;
540 snprintf(connection
->errorStr
,MPD_ERRORSTR_MAX_LENGTH
,
541 "timeout sending command \"%s\"",command
);
542 connection
->error
= MPD_ERROR_TIMEOUT
;
546 if(!connection
->commandList
) connection
->doneProcessing
= 0;
547 else if(connection
->commandList
== COMMAND_LIST_OK
) {
548 connection
->listOks
++;
552 static void mpd_getNextReturnElement(mpd_Connection
* connection
) {
553 char * output
= NULL
;
561 char * bufferCheck
= NULL
;
565 if(connection
->returnElement
) mpd_freeReturnElement(connection
->returnElement
);
566 connection
->returnElement
= NULL
;
568 if(connection
->doneProcessing
|| (connection
->listOks
&&
569 connection
->doneListOk
))
571 strcpy(connection
->errorStr
,"already done processing current command");
572 connection
->error
= 1;
576 bufferCheck
= connection
->buffer
+connection
->bufstart
;
577 while(connection
->bufstart
>=connection
->buflen
||
578 !(rt
= strchr(bufferCheck
,'\n'))) {
579 if(connection
->buflen
>=MPD_BUFFER_MAX_LENGTH
) {
580 memmove(connection
->buffer
,
582 connection
->bufstart
,
584 connection
->bufstart
+1);
585 connection
->buflen
-=connection
->bufstart
;
586 connection
->bufstart
= 0;
588 if(connection
->buflen
>=MPD_BUFFER_MAX_LENGTH
) {
589 strcpy(connection
->errorStr
,"buffer overrun");
590 connection
->error
= MPD_ERROR_BUFFEROVERRUN
;
591 connection
->doneProcessing
= 1;
592 connection
->doneListOk
= 0;
595 bufferCheck
= connection
->buffer
+connection
->buflen
;
596 tv
.tv_sec
= connection
->timeout
.tv_sec
;
597 tv
.tv_usec
= connection
->timeout
.tv_usec
;
599 FD_SET(connection
->sock
,&fds
);
600 if((err
= select(connection
->sock
+1,&fds
,NULL
,NULL
,&tv
) == 1)) {
601 readed
= recv(connection
->sock
,
602 connection
->buffer
+connection
->buflen
,
603 MPD_BUFFER_MAX_LENGTH
-connection
->buflen
,
605 if(readed
<0 && SENDRECV_ERRNO_IGNORE
) {
609 strcpy(connection
->errorStr
,"connection"
611 connection
->error
= MPD_ERROR_CONNCLOSED
;
612 connection
->doneProcessing
= 1;
613 connection
->doneListOk
= 0;
616 connection
->buflen
+=readed
;
617 connection
->buffer
[connection
->buflen
] = '\0';
619 else if(err
<0 && SELECT_ERRNO_IGNORE
) continue;
621 strcpy(connection
->errorStr
,"connection timeout");
622 connection
->error
= MPD_ERROR_TIMEOUT
;
623 connection
->doneProcessing
= 1;
624 connection
->doneListOk
= 0;
630 output
= connection
->buffer
+connection
->bufstart
;
631 connection
->bufstart
= rt
- connection
->buffer
+ 1;
633 if(strcmp(output
,"OK")==0) {
634 if(connection
->listOks
> 0) {
635 strcpy(connection
->errorStr
, "expected more list_OK's");
636 connection
->error
= 1;
638 connection
->listOks
= 0;
639 connection
->doneProcessing
= 1;
640 connection
->doneListOk
= 0;
644 if(strcmp(output
, "list_OK") == 0) {
645 if(!connection
->listOks
) {
646 strcpy(connection
->errorStr
,
647 "got an unexpected list_OK");
648 connection
->error
= 1;
651 connection
->doneListOk
= 1;
652 connection
->listOks
--;
657 if(strncmp(output
,"ACK",strlen("ACK"))==0) {
662 strcpy(connection
->errorStr
, output
);
663 connection
->error
= MPD_ERROR_ACK
;
664 connection
->errorCode
= MPD_ACK_ERROR_UNK
;
665 connection
->errorAt
= MPD_ERROR_AT_UNK
;
666 connection
->doneProcessing
= 1;
667 connection
->doneListOk
= 0;
669 needle
= strchr(output
, '[');
671 val
= strtol(needle
+1, &test
, 10);
672 if(*test
!= '@') return;
673 connection
->errorCode
= val
;
674 val
= strtol(test
+1, &test
, 10);
675 if(*test
!= ']') return;
676 connection
->errorAt
= val
;
680 tok
= strchr(output
, ':');
688 connection
->returnElement
= mpd_newReturnElement(name
,&(value
[1]));
691 snprintf(connection
->errorStr
,MPD_ERRORSTR_MAX_LENGTH
,
692 "error parsing: %s:%s",name
,value
);
693 connection
->error
= 1;
697 void mpd_finishCommand(mpd_Connection
* connection
) {
698 while(!connection
->doneProcessing
) {
699 if(connection
->doneListOk
) connection
->doneListOk
= 0;
700 mpd_getNextReturnElement(connection
);
704 static void mpd_finishListOkCommand(mpd_Connection
* connection
) {
705 while(!connection
->doneProcessing
&& connection
->listOks
&&
706 !connection
->doneListOk
)
708 mpd_getNextReturnElement(connection
);
712 int mpd_nextListOkCommand(mpd_Connection
* connection
) {
713 mpd_finishListOkCommand(connection
);
714 if(!connection
->doneProcessing
) connection
->doneListOk
= 0;
715 if(connection
->listOks
== 0 || connection
->doneProcessing
) return -1;
719 void mpd_sendStatusCommand(mpd_Connection
* connection
) {
720 mpd_executeCommand(connection
,"status\n");
723 mpd_Status
* mpd_getStatus(mpd_Connection
* connection
) {
726 /*mpd_executeCommand(connection,"status\n");
728 if(connection->error) return NULL;*/
730 if(connection
->doneProcessing
|| (connection
->listOks
&&
731 connection
->doneListOk
))
736 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
738 status
= malloc(sizeof(mpd_Status
));
742 status
->playlist
= -1;
743 status
->storedplaylist
= -1;
744 status
->playlistLength
= -1;
748 status
->elapsedTime
= 0;
749 status
->totalTime
= 0;
751 status
->sampleRate
= 0;
753 status
->channels
= 0;
754 status
->crossfade
= -1;
755 status
->error
= NULL
;
756 status
->updatingDb
= 0;
758 if(connection
->error
) {
762 while(connection
->returnElement
) {
763 mpd_ReturnElement
* re
= connection
->returnElement
;
764 if(strcmp(re
->name
,"volume")==0) {
765 status
->volume
= atoi(re
->value
);
767 else if(strcmp(re
->name
,"repeat")==0) {
768 status
->repeat
= atoi(re
->value
);
770 else if(strcmp(re
->name
,"random")==0) {
771 status
->random
= atoi(re
->value
);
773 else if(strcmp(re
->name
,"playlist")==0) {
774 status
->playlist
= strtol(re
->value
,NULL
,10);
776 else if(strcmp(re
->name
,"playlistlength")==0) {
777 status
->playlistLength
= atoi(re
->value
);
779 else if(strcmp(re
->name
,"bitrate")==0) {
780 status
->bitRate
= atoi(re
->value
);
782 else if(strcmp(re
->name
,"state")==0) {
783 if(strcmp(re
->value
,"play")==0) {
784 status
->state
= MPD_STATUS_STATE_PLAY
;
786 else if(strcmp(re
->value
,"stop")==0) {
787 status
->state
= MPD_STATUS_STATE_STOP
;
789 else if(strcmp(re
->value
,"pause")==0) {
790 status
->state
= MPD_STATUS_STATE_PAUSE
;
793 status
->state
= MPD_STATUS_STATE_UNKNOWN
;
796 else if(strcmp(re
->name
,"song")==0) {
797 status
->song
= atoi(re
->value
);
799 else if(strcmp(re
->name
,"songid")==0) {
800 status
->songid
= atoi(re
->value
);
802 else if(strcmp(re
->name
,"time")==0) {
803 char * tok
= strchr(re
->value
,':');
804 /* the second strchr below is a safety check */
805 if (tok
&& (strchr(tok
,0) > (tok
+1))) {
806 /* atoi stops at the first non-[0-9] char: */
807 status
->elapsedTime
= atoi(re
->value
);
808 status
->totalTime
= atoi(tok
+1);
811 else if(strcmp(re
->name
,"error")==0) {
812 status
->error
= strdup(re
->value
);
814 else if(strcmp(re
->name
,"xfade")==0) {
815 status
->crossfade
= atoi(re
->value
);
817 else if(strcmp(re
->name
,"updating_db")==0) {
818 status
->updatingDb
= atoi(re
->value
);
820 else if(strcmp(re
->name
,"audio")==0) {
821 char * tok
= strchr(re
->value
,':');
822 if (tok
&& (strchr(tok
,0) > (tok
+1))) {
823 status
->sampleRate
= atoi(re
->value
);
824 status
->bits
= atoi(++tok
);
825 tok
= strchr(tok
,':');
826 if (tok
&& (strchr(tok
,0) > (tok
+1)))
827 status
->channels
= atoi(tok
+1);
831 mpd_getNextReturnElement(connection
);
832 if(connection
->error
) {
838 if(connection
->error
) {
842 else if(status
->state
<0) {
843 strcpy(connection
->errorStr
,"state not found");
844 connection
->error
= 1;
852 void mpd_freeStatus(mpd_Status
* status
) {
853 if(status
->error
) free(status
->error
);
857 void mpd_sendStatsCommand(mpd_Connection
* connection
) {
858 mpd_executeCommand(connection
,"stats\n");
861 mpd_Stats
* mpd_getStats(mpd_Connection
* connection
) {
864 /*mpd_executeCommand(connection,"stats\n");
866 if(connection->error) return NULL;*/
868 if(connection
->doneProcessing
|| (connection
->listOks
&&
869 connection
->doneListOk
))
874 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
876 stats
= malloc(sizeof(mpd_Stats
));
877 stats
->numberOfArtists
= 0;
878 stats
->numberOfAlbums
= 0;
879 stats
->numberOfSongs
= 0;
881 stats
->dbUpdateTime
= 0;
883 stats
->dbPlayTime
= 0;
885 if(connection
->error
) {
889 while(connection
->returnElement
) {
890 mpd_ReturnElement
* re
= connection
->returnElement
;
891 if(strcmp(re
->name
,"artists")==0) {
892 stats
->numberOfArtists
= atoi(re
->value
);
894 else if(strcmp(re
->name
,"albums")==0) {
895 stats
->numberOfAlbums
= atoi(re
->value
);
897 else if(strcmp(re
->name
,"songs")==0) {
898 stats
->numberOfSongs
= atoi(re
->value
);
900 else if(strcmp(re
->name
,"uptime")==0) {
901 stats
->uptime
= strtol(re
->value
,NULL
,10);
903 else if(strcmp(re
->name
,"db_update")==0) {
904 stats
->dbUpdateTime
= strtol(re
->value
,NULL
,10);
906 else if(strcmp(re
->name
,"playtime")==0) {
907 stats
->playTime
= strtol(re
->value
,NULL
,10);
909 else if(strcmp(re
->name
,"db_playtime")==0) {
910 stats
->dbPlayTime
= strtol(re
->value
,NULL
,10);
913 mpd_getNextReturnElement(connection
);
914 if(connection
->error
) {
920 if(connection
->error
) {
928 void mpd_freeStats(mpd_Stats
* stats
) {
932 mpd_SearchStats
* mpd_getSearchStats(mpd_Connection
* connection
)
934 mpd_SearchStats
* stats
;
935 mpd_ReturnElement
* re
;
937 if (connection
->doneProcessing
||
938 (connection
->listOks
&& connection
->doneListOk
)) {
942 if (!connection
->returnElement
) mpd_getNextReturnElement(connection
);
944 if (connection
->error
)
947 stats
= malloc(sizeof(mpd_SearchStats
));
948 stats
->numberOfSongs
= 0;
951 while (connection
->returnElement
) {
952 re
= connection
->returnElement
;
954 if (strcmp(re
->name
, "songs") == 0) {
955 stats
->numberOfSongs
= atoi(re
->value
);
956 } else if (strcmp(re
->name
, "playtime") == 0) {
957 stats
->playTime
= strtol(re
->value
, NULL
, 10);
960 mpd_getNextReturnElement(connection
);
961 if (connection
->error
) {
967 if (connection
->error
) {
975 void mpd_freeSearchStats(mpd_SearchStats
* stats
)
980 static void mpd_initSong(mpd_Song
* song
) {
990 song
->composer
= NULL
;
991 song
->performer
= NULL
;
993 song
->comment
= NULL
;
994 song
->albumartist
= NULL
;
996 song
->time
= MPD_SONG_NO_TIME
;
997 song
->pos
= MPD_SONG_NO_NUM
;
998 song
->id
= MPD_SONG_NO_ID
;
1001 static void mpd_finishSong(mpd_Song
* song
) {
1002 if(song
->file
) free(song
->file
);
1003 if(song
->artist
) free(song
->artist
);
1004 if(song
->album
) free(song
->album
);
1005 if(song
->title
) free(song
->title
);
1006 if(song
->track
) free(song
->track
);
1007 if(song
->name
) free(song
->name
);
1008 if(song
->date
) free(song
->date
);
1009 if(song
->genre
) free(song
->genre
);
1010 if(song
->composer
) free(song
->composer
);
1011 if(song
->performer
) free(song
->performer
);
1012 if(song
->disc
) free(song
->disc
);
1013 if(song
->comment
) free(song
->comment
);
1014 if(song
->albumartist
) free(song
->albumartist
);
1017 mpd_Song
* mpd_newSong(void) {
1018 mpd_Song
* ret
= malloc(sizeof(mpd_Song
));
1025 void mpd_freeSong(mpd_Song
* song
) {
1026 mpd_finishSong(song
);
1030 mpd_Song
* mpd_songDup(const mpd_Song
* song
) {
1031 mpd_Song
* ret
= mpd_newSong();
1033 if(song
->file
) ret
->file
= strdup(song
->file
);
1034 if(song
->artist
) ret
->artist
= strdup(song
->artist
);
1035 if(song
->album
) ret
->album
= strdup(song
->album
);
1036 if(song
->title
) ret
->title
= strdup(song
->title
);
1037 if(song
->track
) ret
->track
= strdup(song
->track
);
1038 if(song
->name
) ret
->name
= strdup(song
->name
);
1039 if(song
->date
) ret
->date
= strdup(song
->date
);
1040 if(song
->genre
) ret
->genre
= strdup(song
->genre
);
1041 if(song
->composer
) ret
->composer
= strdup(song
->composer
);
1042 if(song
->performer
) ret
->performer
= strdup(song
->performer
);
1043 if(song
->disc
) ret
->disc
= strdup(song
->disc
);
1044 if(song
->comment
) ret
->comment
= strdup(song
->comment
);
1045 if(song
->albumartist
) ret
->albumartist
= strdup(song
->albumartist
);
1046 ret
->time
= song
->time
;
1047 ret
->pos
= song
->pos
;
1053 static void mpd_initDirectory(mpd_Directory
* directory
) {
1054 directory
->path
= NULL
;
1057 static void mpd_finishDirectory(mpd_Directory
* directory
) {
1058 if(directory
->path
) free(directory
->path
);
1061 mpd_Directory
* mpd_newDirectory(void) {
1062 mpd_Directory
* directory
= malloc(sizeof(mpd_Directory
));;
1064 mpd_initDirectory(directory
);
1069 void mpd_freeDirectory(mpd_Directory
* directory
) {
1070 mpd_finishDirectory(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_initPlaylistFile(mpd_PlaylistFile
* playlist
) {
1084 playlist
->path
= NULL
;
1085 playlist
->mtime
= NULL
;
1088 static void mpd_finishPlaylistFile(mpd_PlaylistFile
* playlist
) {
1089 if(playlist
->path
) free(playlist
->path
);
1090 if(playlist
->mtime
) free(playlist
->mtime
);
1093 mpd_PlaylistFile
* mpd_newPlaylistFile(void) {
1094 mpd_PlaylistFile
* playlist
= malloc(sizeof(mpd_PlaylistFile
));
1096 mpd_initPlaylistFile(playlist
);
1101 void mpd_freePlaylistFile(mpd_PlaylistFile
* playlist
) {
1102 mpd_finishPlaylistFile(playlist
);
1106 mpd_PlaylistFile
* mpd_playlistFileDup(mpd_PlaylistFile
* playlist
) {
1107 mpd_PlaylistFile
* ret
= mpd_newPlaylistFile();
1109 if(playlist
->path
) ret
->path
= strdup(playlist
->path
);
1110 if(playlist
->mtime
) ret
->mtime
= strdup(playlist
->mtime
);
1115 static void mpd_initInfoEntity(mpd_InfoEntity
* entity
) {
1116 entity
->info
.directory
= NULL
;
1119 static void mpd_finishInfoEntity(mpd_InfoEntity
* entity
) {
1120 if(entity
->info
.directory
) {
1121 if(entity
->type
== MPD_INFO_ENTITY_TYPE_DIRECTORY
) {
1122 mpd_freeDirectory(entity
->info
.directory
);
1124 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_SONG
) {
1125 mpd_freeSong(entity
->info
.song
);
1127 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_PLAYLISTFILE
) {
1128 mpd_freePlaylistFile(entity
->info
.playlistFile
);
1133 mpd_InfoEntity
* mpd_newInfoEntity(void) {
1134 mpd_InfoEntity
* entity
= malloc(sizeof(mpd_InfoEntity
));
1136 mpd_initInfoEntity(entity
);
1141 void mpd_freeInfoEntity(mpd_InfoEntity
* entity
) {
1142 mpd_finishInfoEntity(entity
);
1146 static void mpd_sendInfoCommand(mpd_Connection
* connection
,const char * command
) {
1147 mpd_executeCommand(connection
,command
);
1150 mpd_InfoEntity
* mpd_getNextInfoEntity(mpd_Connection
* connection
) {
1151 mpd_InfoEntity
* entity
= NULL
;
1153 if(connection
->doneProcessing
|| (connection
->listOks
&&
1154 connection
->doneListOk
))
1159 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
1161 if(connection
->returnElement
) {
1162 if(strcmp(connection
->returnElement
->name
,"file")==0) {
1163 entity
= mpd_newInfoEntity();
1164 entity
->type
= MPD_INFO_ENTITY_TYPE_SONG
;
1165 entity
->info
.song
= mpd_newSong();
1166 entity
->info
.song
->file
=
1167 strdup(connection
->returnElement
->value
);
1169 else if(strcmp(connection
->returnElement
->name
,
1171 entity
= mpd_newInfoEntity();
1172 entity
->type
= MPD_INFO_ENTITY_TYPE_DIRECTORY
;
1173 entity
->info
.directory
= mpd_newDirectory();
1174 entity
->info
.directory
->path
=
1175 strdup(connection
->returnElement
->value
);
1177 else if(strcmp(connection
->returnElement
->name
,"playlist")==0) {
1178 entity
= mpd_newInfoEntity();
1179 entity
->type
= MPD_INFO_ENTITY_TYPE_PLAYLISTFILE
;
1180 entity
->info
.playlistFile
= mpd_newPlaylistFile();
1181 entity
->info
.playlistFile
->path
=
1182 strdup(connection
->returnElement
->value
);
1184 else if(strcmp(connection
->returnElement
->name
, "cpos") == 0){
1185 entity
= mpd_newInfoEntity();
1186 entity
->type
= MPD_INFO_ENTITY_TYPE_SONG
;
1187 entity
->info
.song
= mpd_newSong();
1188 entity
->info
.song
->pos
= atoi(connection
->returnElement
->value
);
1191 connection
->error
= 1;
1192 strcpy(connection
->errorStr
,"problem parsing song info");
1198 mpd_getNextReturnElement(connection
);
1199 while(connection
->returnElement
) {
1200 mpd_ReturnElement
* re
= connection
->returnElement
;
1202 if(strcmp(re
->name
,"file")==0) return entity
;
1203 else if(strcmp(re
->name
,"directory")==0) return entity
;
1204 else if(strcmp(re
->name
,"playlist")==0) return entity
;
1205 else if(strcmp(re
->name
,"cpos")==0) return entity
;
1207 if(entity
->type
== MPD_INFO_ENTITY_TYPE_SONG
&&
1208 strlen(re
->value
)) {
1209 if(strcmp(re
->name
,"Artist")==0) {
1210 if(entity
->info
.song
->artist
) {
1211 int length
= strlen(entity
->info
.song
->artist
);
1212 entity
->info
.song
->artist
= realloc(entity
->info
.song
->artist
,
1213 length
+ strlen(re
->value
) + 3);
1214 strcpy(&((entity
->info
.song
->artist
)[length
]), ", ");
1215 strcpy(&((entity
->info
.song
->artist
)[length
+ 2]), re
->value
);
1218 entity
->info
.song
->artist
= strdup(re
->value
);
1221 else if(!entity
->info
.song
->album
&&
1222 strcmp(re
->name
,"Album")==0) {
1223 entity
->info
.song
->album
= strdup(re
->value
);
1225 else if(!entity
->info
.song
->title
&&
1226 strcmp(re
->name
,"Title")==0) {
1227 entity
->info
.song
->title
= strdup(re
->value
);
1229 else if(!entity
->info
.song
->track
&&
1230 strcmp(re
->name
,"Track")==0) {
1231 entity
->info
.song
->track
= strdup(re
->value
);
1233 else if(!entity
->info
.song
->name
&&
1234 strcmp(re
->name
,"Name")==0) {
1235 entity
->info
.song
->name
= strdup(re
->value
);
1237 else if(entity
->info
.song
->time
==MPD_SONG_NO_TIME
&&
1238 strcmp(re
->name
,"Time")==0) {
1239 entity
->info
.song
->time
= atoi(re
->value
);
1241 else if(entity
->info
.song
->pos
==MPD_SONG_NO_NUM
&&
1242 strcmp(re
->name
,"Pos")==0) {
1243 entity
->info
.song
->pos
= atoi(re
->value
);
1245 else if(entity
->info
.song
->id
==MPD_SONG_NO_ID
&&
1246 strcmp(re
->name
,"Id")==0) {
1247 entity
->info
.song
->id
= atoi(re
->value
);
1249 else if(!entity
->info
.song
->date
&&
1250 strcmp(re
->name
, "Date") == 0) {
1251 entity
->info
.song
->date
= strdup(re
->value
);
1253 else if(!entity
->info
.song
->genre
&&
1254 strcmp(re
->name
, "Genre") == 0) {
1255 if(entity
->info
.song
->genre
) {
1256 int length
= strlen(entity
->info
.song
->genre
);
1257 entity
->info
.song
->genre
= realloc(entity
->info
.song
->genre
,
1258 length
+ strlen(re
->value
) + 4);
1259 strcpy(&((entity
->info
.song
->genre
)[length
]), ", ");
1260 strcpy(&((entity
->info
.song
->genre
)[length
+ 3]), re
->value
);
1263 entity
->info
.song
->genre
= strdup(re
->value
);
1266 else if(strcmp(re
->name
, "Composer") == 0) {
1267 if(entity
->info
.song
->composer
) {
1268 int length
= strlen(entity
->info
.song
->composer
);
1269 entity
->info
.song
->composer
= realloc(entity
->info
.song
->composer
,
1270 length
+ strlen(re
->value
) + 3);
1271 strcpy(&((entity
->info
.song
->composer
)[length
]), ", ");
1272 strcpy(&((entity
->info
.song
->composer
)[length
+ 2]), re
->value
);
1275 entity
->info
.song
->composer
= strdup(re
->value
);
1278 else if(strcmp(re
->name
, "Performer") == 0) {
1279 if(entity
->info
.song
->performer
) {
1280 int length
= strlen(entity
->info
.song
->performer
);
1281 entity
->info
.song
->performer
= realloc(entity
->info
.song
->performer
,
1282 length
+ strlen(re
->value
) + 3);
1283 strcpy(&((entity
->info
.song
->performer
)[length
]), ", ");
1284 strcpy(&((entity
->info
.song
->performer
)[length
+ 2]), re
->value
);
1287 entity
->info
.song
->performer
= strdup(re
->value
);
1290 else if(!entity
->info
.song
->disc
&&
1291 strcmp(re
->name
, "Disc") == 0) {
1292 entity
->info
.song
->disc
= strdup(re
->value
);
1294 else if(!entity
->info
.song
->comment
&&
1295 strcmp(re
->name
, "Comment") == 0) {
1296 entity
->info
.song
->comment
= strdup(re
->value
);
1299 else if(!entity
->info
.song
->albumartist
&&
1300 strcmp(re
->name
, "AlbumArtist") == 0) {
1301 entity
->info
.song
->albumartist
= strdup(re
->value
);
1304 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_DIRECTORY
) {
1306 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_PLAYLISTFILE
) {
1307 if(!entity
->info
.playlistFile
->mtime
&&
1308 strcmp(re
->name
, "Last-Modified") == 0) {
1309 entity
->info
.playlistFile
->mtime
= strdup(re
->value
);
1313 mpd_getNextReturnElement(connection
);
1319 static char * mpd_getNextReturnElementNamed(mpd_Connection
* connection
,
1322 if(connection
->doneProcessing
|| (connection
->listOks
&&
1323 connection
->doneListOk
))
1328 mpd_getNextReturnElement(connection
);
1329 while(connection
->returnElement
) {
1330 mpd_ReturnElement
* re
= connection
->returnElement
;
1332 if(strcmp(re
->name
,name
)==0) return strdup(re
->value
);
1333 mpd_getNextReturnElement(connection
);
1339 char *mpd_getNextTag(mpd_Connection
*connection
, int type
)
1341 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
||
1342 type
== MPD_TAG_ITEM_ANY
)
1344 if (type
== MPD_TAG_ITEM_FILENAME
)
1345 return mpd_getNextReturnElementNamed(connection
, "file");
1346 return mpd_getNextReturnElementNamed(connection
, mpdTagItemKeys
[type
]);
1349 char * mpd_getNextArtist(mpd_Connection
* connection
) {
1350 return mpd_getNextReturnElementNamed(connection
,"Artist");
1353 char * mpd_getNextAlbum(mpd_Connection
* connection
) {
1354 return mpd_getNextReturnElementNamed(connection
,"Album");
1357 void mpd_sendPlaylistInfoCommand(mpd_Connection
* connection
, int songPos
) {
1358 int len
= strlen("playlistinfo")+2+INTLEN
+3;
1359 char *string
= malloc(len
);
1360 snprintf(string
, len
, "playlistinfo \"%i\"\n", songPos
);
1361 mpd_sendInfoCommand(connection
,string
);
1365 void mpd_sendPlaylistIdCommand(mpd_Connection
* connection
, int id
) {
1366 int len
= strlen("playlistid")+2+INTLEN
+3;
1367 char *string
= malloc(len
);
1368 snprintf(string
, len
, "playlistid \"%i\"\n", id
);
1369 mpd_sendInfoCommand(connection
, string
);
1373 void mpd_sendPlChangesCommand(mpd_Connection
* connection
, long long playlist
) {
1374 int len
= strlen("plchanges")+2+LONGLONGLEN
+3;
1375 char *string
= malloc(len
);
1376 snprintf(string
, len
, "plchanges \"%lld\"\n", playlist
);
1377 mpd_sendInfoCommand(connection
,string
);
1381 void mpd_sendPlChangesPosIdCommand(mpd_Connection
* connection
, long long playlist
) {
1382 int len
= strlen("plchangesposid")+2+LONGLONGLEN
+3;
1383 char *string
= malloc(len
);
1384 snprintf(string
, len
, "plchangesposid \"%lld\"\n", playlist
);
1385 mpd_sendInfoCommand(connection
,string
);
1389 void mpd_sendListallCommand(mpd_Connection
* connection
, const char * dir
) {
1390 char * sDir
= mpd_sanitizeArg(dir
);
1391 int len
= strlen("listall")+2+strlen(sDir
)+3;
1392 char *string
= malloc(len
);
1393 snprintf(string
, len
, "listall \"%s\"\n", sDir
);
1394 mpd_sendInfoCommand(connection
,string
);
1399 void mpd_sendListallInfoCommand(mpd_Connection
* connection
, const char * dir
) {
1400 char * sDir
= mpd_sanitizeArg(dir
);
1401 int len
= strlen("listallinfo")+2+strlen(sDir
)+3;
1402 char *string
= malloc(len
);
1403 snprintf(string
, len
, "listallinfo \"%s\"\n", sDir
);
1404 mpd_sendInfoCommand(connection
,string
);
1409 void mpd_sendLsInfoCommand(mpd_Connection
* connection
, const char * dir
) {
1410 char * sDir
= mpd_sanitizeArg(dir
);
1411 int len
= strlen("lsinfo")+2+strlen(sDir
)+3;
1412 char *string
= malloc(len
);
1413 snprintf(string
, len
, "lsinfo \"%s\"\n", sDir
);
1414 mpd_sendInfoCommand(connection
,string
);
1419 void mpd_sendCurrentSongCommand(mpd_Connection
* connection
) {
1420 mpd_executeCommand(connection
,"currentsong\n");
1423 void mpd_sendSearchCommand(mpd_Connection
* connection
, int table
,
1426 mpd_startSearch(connection
, 0);
1427 mpd_addConstraintSearch(connection
, table
, str
);
1428 mpd_commitSearch(connection
);
1431 void mpd_sendFindCommand(mpd_Connection
* connection
, int table
,
1434 mpd_startSearch(connection
, 1);
1435 mpd_addConstraintSearch(connection
, table
, str
);
1436 mpd_commitSearch(connection
);
1439 void mpd_sendListCommand(mpd_Connection
* connection
, int table
,
1445 if(table
== MPD_TABLE_ARTIST
) strcpy(st
,"artist");
1446 else if(table
== MPD_TABLE_ALBUM
) strcpy(st
,"album");
1448 connection
->error
= 1;
1449 strcpy(connection
->errorStr
,"unknown table for list");
1453 char * sanitArg1
= mpd_sanitizeArg(arg1
);
1454 len
= strlen("list")+1+strlen(sanitArg1
)+2+strlen(st
)+3;
1455 string
= malloc(len
);
1456 snprintf(string
, len
, "list %s \"%s\"\n", st
, sanitArg1
);
1460 len
= strlen("list")+1+strlen(st
)+2;
1461 string
= malloc(len
);
1462 snprintf(string
, len
, "list %s\n", st
);
1464 mpd_sendInfoCommand(connection
,string
);
1468 void mpd_sendAddCommand(mpd_Connection
* connection
, const char * file
) {
1469 char * sFile
= mpd_sanitizeArg(file
);
1470 int len
= strlen("add")+2+strlen(sFile
)+3;
1471 char *string
= malloc(len
);
1472 snprintf(string
, len
, "add \"%s\"\n", sFile
);
1473 mpd_executeCommand(connection
,string
);
1478 int mpd_sendAddIdCommand(mpd_Connection
*connection
, const char *file
)
1481 char *sFile
= mpd_sanitizeArg(file
);
1482 int len
= strlen("addid")+2+strlen(sFile
)+3;
1483 char *string
= malloc(len
);
1485 snprintf(string
, len
, "addid \"%s\"\n", sFile
);
1486 mpd_sendInfoCommand(connection
, string
);
1490 string
= mpd_getNextReturnElementNamed(connection
, "Id");
1492 retval
= atoi(string
);
1499 void mpd_sendDeleteCommand(mpd_Connection
* connection
, int songPos
) {
1500 int len
= strlen("delete")+2+INTLEN
+3;
1501 char *string
= malloc(len
);
1502 snprintf(string
, len
, "delete \"%i\"\n", songPos
);
1503 mpd_sendInfoCommand(connection
,string
);
1507 void mpd_sendDeleteIdCommand(mpd_Connection
* connection
, int id
) {
1508 int len
= strlen("deleteid")+2+INTLEN
+3;
1509 char *string
= malloc(len
);
1510 snprintf(string
, len
, "deleteid \"%i\"\n", id
);
1511 mpd_sendInfoCommand(connection
,string
);
1515 void mpd_sendSaveCommand(mpd_Connection
* connection
, const char * name
) {
1516 char * sName
= mpd_sanitizeArg(name
);
1517 int len
= strlen("save")+2+strlen(sName
)+3;
1518 char *string
= malloc(len
);
1519 snprintf(string
, len
, "save \"%s\"\n", sName
);
1520 mpd_executeCommand(connection
,string
);
1525 void mpd_sendLoadCommand(mpd_Connection
* connection
, const char * name
) {
1526 char * sName
= mpd_sanitizeArg(name
);
1527 int len
= strlen("load")+2+strlen(sName
)+3;
1528 char *string
= malloc(len
);
1529 snprintf(string
, len
, "load \"%s\"\n", sName
);
1530 mpd_executeCommand(connection
,string
);
1535 void mpd_sendRmCommand(mpd_Connection
* connection
, const char * name
) {
1536 char * sName
= mpd_sanitizeArg(name
);
1537 int len
= strlen("rm")+2+strlen(sName
)+3;
1538 char *string
= malloc(len
);
1539 snprintf(string
, len
, "rm \"%s\"\n", sName
);
1540 mpd_executeCommand(connection
,string
);
1545 void mpd_sendRenameCommand(mpd_Connection
*connection
, const char *from
,
1548 char *sFrom
= mpd_sanitizeArg(from
);
1549 char *sTo
= mpd_sanitizeArg(to
);
1550 int len
= strlen("rename")+2+strlen(sFrom
)+3+strlen(sTo
)+3;
1551 char *string
= malloc(len
);
1552 snprintf(string
, len
, "rename \"%s\" \"%s\"\n", sFrom
, sTo
);
1553 mpd_executeCommand(connection
, string
);
1559 void mpd_sendShuffleCommand(mpd_Connection
* connection
) {
1560 mpd_executeCommand(connection
,"shuffle\n");
1563 void mpd_sendClearCommand(mpd_Connection
* connection
) {
1564 mpd_executeCommand(connection
,"clear\n");
1567 void mpd_sendPlayCommand(mpd_Connection
* connection
, int songPos
) {
1568 int len
= strlen("play")+2+INTLEN
+3;
1569 char *string
= malloc(len
);
1570 snprintf(string
, len
, "play \"%i\"\n", songPos
);
1571 mpd_sendInfoCommand(connection
,string
);
1575 void mpd_sendPlayIdCommand(mpd_Connection
* connection
, int id
) {
1576 int len
= strlen("playid")+2+INTLEN
+3;
1577 char *string
= malloc(len
);
1578 snprintf(string
, len
, "playid \"%i\"\n", id
);
1579 mpd_sendInfoCommand(connection
,string
);
1583 void mpd_sendStopCommand(mpd_Connection
* connection
) {
1584 mpd_executeCommand(connection
,"stop\n");
1587 void mpd_sendPauseCommand(mpd_Connection
* connection
, int pauseMode
) {
1588 int len
= strlen("pause")+2+INTLEN
+3;
1589 char *string
= malloc(len
);
1590 snprintf(string
, len
, "pause \"%i\"\n", pauseMode
);
1591 mpd_executeCommand(connection
,string
);
1595 void mpd_sendNextCommand(mpd_Connection
* connection
) {
1596 mpd_executeCommand(connection
,"next\n");
1599 void mpd_sendMoveCommand(mpd_Connection
* connection
, int from
, int to
) {
1600 int len
= strlen("move")+2+INTLEN
+3+INTLEN
+3;
1601 char *string
= malloc(len
);
1602 snprintf(string
, len
, "move \"%i\" \"%i\"\n", from
, to
);
1603 mpd_sendInfoCommand(connection
,string
);
1607 void mpd_sendMoveIdCommand(mpd_Connection
* connection
, int id
, int to
) {
1608 int len
= strlen("moveid")+2+INTLEN
+3+INTLEN
+3;
1609 char *string
= malloc(len
);
1610 snprintf(string
, len
, "moveid \"%i\" \"%i\"\n", id
, to
);
1611 mpd_sendInfoCommand(connection
,string
);
1615 void mpd_sendSwapCommand(mpd_Connection
* connection
, int song1
, int song2
) {
1616 int len
= strlen("swap")+2+INTLEN
+3+INTLEN
+3;
1617 char *string
= malloc(len
);
1618 snprintf(string
, len
, "swap \"%i\" \"%i\"\n", song1
, song2
);
1619 mpd_sendInfoCommand(connection
,string
);
1623 void mpd_sendSwapIdCommand(mpd_Connection
* connection
, int id1
, int id2
) {
1624 int len
= strlen("swapid")+2+INTLEN
+3+INTLEN
+3;
1625 char *string
= malloc(len
);
1626 snprintf(string
, len
, "swapid \"%i\" \"%i\"\n", id1
, id2
);
1627 mpd_sendInfoCommand(connection
,string
);
1631 void mpd_sendSeekCommand(mpd_Connection
* connection
, int song
, int time
) {
1632 int len
= strlen("seek")+2+INTLEN
+3+INTLEN
+3;
1633 char *string
= malloc(len
);
1634 snprintf(string
, len
, "seek \"%i\" \"%i\"\n", song
, time
);
1635 mpd_sendInfoCommand(connection
,string
);
1639 void mpd_sendSeekIdCommand(mpd_Connection
* connection
, int id
, int time
) {
1640 int len
= strlen("seekid")+2+INTLEN
+3+INTLEN
+3;
1641 char *string
= malloc(len
);
1642 snprintf(string
, len
, "seekid \"%i\" \"%i\"\n", id
, time
);
1643 mpd_sendInfoCommand(connection
,string
);
1647 void mpd_sendUpdateCommand(mpd_Connection
* connection
,const char * path
) {
1648 char * sPath
= mpd_sanitizeArg(path
);
1649 int len
= strlen("update")+2+strlen(sPath
)+3;
1650 char *string
= malloc(len
);
1651 snprintf(string
, len
, "update \"%s\"\n", sPath
);
1652 mpd_sendInfoCommand(connection
,string
);
1657 int mpd_getUpdateId(mpd_Connection
* connection
) {
1661 jobid
= mpd_getNextReturnElementNamed(connection
,"updating_db");
1670 void mpd_sendPrevCommand(mpd_Connection
* connection
) {
1671 mpd_executeCommand(connection
,"previous\n");
1674 void mpd_sendRepeatCommand(mpd_Connection
* connection
, int repeatMode
) {
1675 int len
= strlen("repeat")+2+INTLEN
+3;
1676 char *string
= malloc(len
);
1677 snprintf(string
, len
, "repeat \"%i\"\n", repeatMode
);
1678 mpd_executeCommand(connection
,string
);
1682 void mpd_sendRandomCommand(mpd_Connection
* connection
, int randomMode
) {
1683 int len
= strlen("random")+2+INTLEN
+3;
1684 char *string
= malloc(len
);
1685 snprintf(string
, len
, "random \"%i\"\n", randomMode
);
1686 mpd_executeCommand(connection
,string
);
1690 void mpd_sendSetvolCommand(mpd_Connection
* connection
, int volumeChange
) {
1691 int len
= strlen("setvol")+2+INTLEN
+3;
1692 char *string
= malloc(len
);
1693 snprintf(string
, len
, "setvol \"%i\"\n", volumeChange
);
1694 mpd_executeCommand(connection
,string
);
1698 void mpd_sendVolumeCommand(mpd_Connection
* connection
, int volumeChange
) {
1699 int len
= strlen("volume")+2+INTLEN
+3;
1700 char *string
= malloc(len
);
1701 snprintf(string
, len
, "volume \"%i\"\n", volumeChange
);
1702 mpd_executeCommand(connection
,string
);
1706 void mpd_sendCrossfadeCommand(mpd_Connection
* connection
, int seconds
) {
1707 int len
= strlen("crossfade")+2+INTLEN
+3;
1708 char *string
= malloc(len
);
1709 snprintf(string
, len
, "crossfade \"%i\"\n", seconds
);
1710 mpd_executeCommand(connection
,string
);
1714 void mpd_sendPasswordCommand(mpd_Connection
* connection
, const char * pass
) {
1715 char * sPass
= mpd_sanitizeArg(pass
);
1716 int len
= strlen("password")+2+strlen(sPass
)+3;
1717 char *string
= malloc(len
);
1718 snprintf(string
, len
, "password \"%s\"\n", sPass
);
1719 mpd_executeCommand(connection
,string
);
1724 void mpd_sendCommandListBegin(mpd_Connection
* connection
) {
1725 if(connection
->commandList
) {
1726 strcpy(connection
->errorStr
,"already in command list mode");
1727 connection
->error
= 1;
1730 connection
->commandList
= COMMAND_LIST
;
1731 mpd_executeCommand(connection
,"command_list_begin\n");
1734 void mpd_sendCommandListOkBegin(mpd_Connection
* connection
) {
1735 if(connection
->commandList
) {
1736 strcpy(connection
->errorStr
,"already in command list mode");
1737 connection
->error
= 1;
1740 connection
->commandList
= COMMAND_LIST_OK
;
1741 mpd_executeCommand(connection
,"command_list_ok_begin\n");
1742 connection
->listOks
= 0;
1745 void mpd_sendCommandListEnd(mpd_Connection
* connection
) {
1746 if(!connection
->commandList
) {
1747 strcpy(connection
->errorStr
,"not in command list mode");
1748 connection
->error
= 1;
1751 connection
->commandList
= 0;
1752 mpd_executeCommand(connection
,"command_list_end\n");
1755 void mpd_sendOutputsCommand(mpd_Connection
* connection
) {
1756 mpd_executeCommand(connection
,"outputs\n");
1759 mpd_OutputEntity
* mpd_getNextOutput(mpd_Connection
* connection
) {
1760 mpd_OutputEntity
* output
= NULL
;
1762 if(connection
->doneProcessing
|| (connection
->listOks
&&
1763 connection
->doneListOk
))
1768 if(connection
->error
) return NULL
;
1770 output
= malloc(sizeof(mpd_OutputEntity
));
1772 output
->name
= NULL
;
1773 output
->enabled
= 0;
1775 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
1777 while(connection
->returnElement
) {
1778 mpd_ReturnElement
* re
= connection
->returnElement
;
1779 if(strcmp(re
->name
,"outputid")==0) {
1780 if(output
!=NULL
&& output
->id
>=0) return output
;
1781 output
->id
= atoi(re
->value
);
1783 else if(strcmp(re
->name
,"outputname")==0) {
1784 output
->name
= strdup(re
->value
);
1786 else if(strcmp(re
->name
,"outputenabled")==0) {
1787 output
->enabled
= atoi(re
->value
);
1790 mpd_getNextReturnElement(connection
);
1791 if(connection
->error
) {
1801 void mpd_sendEnableOutputCommand(mpd_Connection
* connection
, int outputId
) {
1802 int len
= strlen("enableoutput")+2+INTLEN
+3;
1803 char *string
= malloc(len
);
1804 snprintf(string
, len
, "enableoutput \"%i\"\n", outputId
);
1805 mpd_executeCommand(connection
,string
);
1809 void mpd_sendDisableOutputCommand(mpd_Connection
* connection
, int outputId
) {
1810 int len
= strlen("disableoutput")+2+INTLEN
+3;
1811 char *string
= malloc(len
);
1812 snprintf(string
, len
, "disableoutput \"%i\"\n", outputId
);
1813 mpd_executeCommand(connection
,string
);
1817 void mpd_freeOutputElement(mpd_OutputEntity
* output
) {
1823 * mpd_sendNotCommandsCommand
1824 * odd naming, but it gets the not allowed commands
1827 void mpd_sendNotCommandsCommand(mpd_Connection
* connection
)
1829 mpd_executeCommand(connection
, "notcommands\n");
1833 * mpd_sendCommandsCommand
1834 * odd naming, but it gets the allowed commands
1836 void mpd_sendCommandsCommand(mpd_Connection
* connection
)
1838 mpd_executeCommand(connection
, "commands\n");
1842 * Get the next returned command
1844 char * mpd_getNextCommand(mpd_Connection
* connection
)
1846 return mpd_getNextReturnElementNamed(connection
, "command");
1849 void mpd_sendUrlHandlersCommand(mpd_Connection
* connection
)
1851 mpd_executeCommand(connection
, "urlhandlers\n");
1854 char * mpd_getNextHandler(mpd_Connection
* connection
)
1856 return mpd_getNextReturnElementNamed(connection
, "handler");
1859 void mpd_sendTagTypesCommand(mpd_Connection
* connection
)
1861 mpd_executeCommand(connection
, "tagtypes\n");
1864 char * mpd_getNextTagType(mpd_Connection
* connection
)
1866 return mpd_getNextReturnElementNamed(connection
, "tagtype");
1869 void mpd_startSearch(mpd_Connection
*connection
, int exact
)
1871 if (connection
->request
) {
1872 strcpy(connection
->errorStr
, "search already in progress");
1873 connection
->error
= 1;
1877 if (exact
) connection
->request
= strdup("find");
1878 else connection
->request
= strdup("search");
1881 void mpd_startStatsSearch(mpd_Connection
*connection
)
1883 if (connection
->request
) {
1884 strcpy(connection
->errorStr
, "search already in progress");
1885 connection
->error
= 1;
1889 connection
->request
= strdup("count");
1892 void mpd_startPlaylistSearch(mpd_Connection
*connection
, int exact
)
1894 if (connection
->request
) {
1895 strcpy(connection
->errorStr
, "search already in progress");
1896 connection
->error
= 1;
1900 if (exact
) connection
->request
= strdup("playlistfind");
1901 else connection
->request
= strdup("playlistsearch");
1904 void mpd_startFieldSearch(mpd_Connection
*connection
, int type
)
1909 if (connection
->request
) {
1910 strcpy(connection
->errorStr
, "search already in progress");
1911 connection
->error
= 1;
1915 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
) {
1916 strcpy(connection
->errorStr
, "invalid type specified");
1917 connection
->error
= 1;
1921 strtype
= mpdTagItemKeys
[type
];
1923 len
= 5+strlen(strtype
)+1;
1924 connection
->request
= malloc(len
);
1926 snprintf(connection
->request
, len
, "list %c%s",
1927 tolower(strtype
[0]), strtype
+1);
1930 void mpd_addConstraintSearch(mpd_Connection
*connection
, int type
, const char *name
)
1937 if (!connection
->request
) {
1938 strcpy(connection
->errorStr
, "no search in progress");
1939 connection
->error
= 1;
1943 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
) {
1944 strcpy(connection
->errorStr
, "invalid type specified");
1945 connection
->error
= 1;
1950 strcpy(connection
->errorStr
, "no name specified");
1951 connection
->error
= 1;
1955 string
= strdup(connection
->request
);
1956 strtype
= mpdTagItemKeys
[type
];
1957 arg
= mpd_sanitizeArg(name
);
1959 len
= strlen(string
)+1+strlen(strtype
)+2+strlen(arg
)+2;
1960 connection
->request
= realloc(connection
->request
, len
);
1961 snprintf(connection
->request
, len
, "%s %c%s \"%s\"",
1962 string
, tolower(strtype
[0]), strtype
+1, arg
);
1968 void mpd_commitSearch(mpd_Connection
*connection
)
1972 if (!connection
->request
) {
1973 strcpy(connection
->errorStr
, "no search in progress");
1974 connection
->error
= 1;
1978 len
= strlen(connection
->request
)+2;
1979 connection
->request
= realloc(connection
->request
, len
);
1980 connection
->request
[len
-2] = '\n';
1981 connection
->request
[len
-1] = '\0';
1982 mpd_sendInfoCommand(connection
, connection
->request
);
1984 free(connection
->request
);
1985 connection
->request
= NULL
;
1989 * @param connection a MpdConnection
1990 * @param path the path to the playlist.
1992 * List the content, with full metadata, of a stored playlist.
1995 void mpd_sendListPlaylistInfoCommand(mpd_Connection
*connection
,const char *path
)
1997 char *arg
= mpd_sanitizeArg(path
);
1998 int len
= strlen("listplaylistinfo")+2+strlen(arg
)+3;
1999 char *query
= malloc(len
);
2000 snprintf(query
, len
, "listplaylistinfo \"%s\"\n", arg
);
2001 mpd_sendInfoCommand(connection
, query
);
2007 * @param connection a MpdConnection
2008 * @param path the path to the playlist.
2010 * List the content of a stored playlist.
2013 void mpd_sendListPlaylistCommand(mpd_Connection
*connection
,const char *path
)
2015 char *arg
= mpd_sanitizeArg(path
);
2016 int len
= strlen("listplaylist")+2+strlen(arg
)+3;
2017 char *query
= malloc(len
);
2018 snprintf(query
, len
, "listplaylist \"%s\"\n", arg
);
2019 mpd_sendInfoCommand(connection
, query
);
2024 void mpd_sendPlaylistClearCommand(mpd_Connection
*connection
,const char *path
)
2026 char *sPath
= mpd_sanitizeArg(path
);
2027 int len
= strlen("playlistclear")+2+strlen(sPath
)+3;
2028 char *string
= malloc(len
);
2029 snprintf(string
, len
, "playlistclear \"%s\"\n", sPath
);
2030 mpd_executeCommand(connection
, string
);
2035 void mpd_sendPlaylistAddCommand(mpd_Connection
*connection
,
2036 const char *playlist
,const char *path
)
2038 char *sPlaylist
= mpd_sanitizeArg(playlist
);
2039 char *sPath
= mpd_sanitizeArg(path
);
2040 int len
= strlen("playlistadd")+2+strlen(sPlaylist
)+3+strlen(sPath
)+3;
2041 char *string
= malloc(len
);
2042 snprintf(string
, len
, "playlistadd \"%s\" \"%s\"\n", sPlaylist
, sPath
);
2043 mpd_executeCommand(connection
, string
);
2049 void mpd_sendPlaylistMoveCommand(mpd_Connection
*connection
,
2050 const char *playlist
, int from
, int to
)
2052 char *sPlaylist
= mpd_sanitizeArg(playlist
);
2053 int len
= strlen("playlistmove")+
2054 2+strlen(sPlaylist
)+3+INTLEN
+3+INTLEN
+3;
2055 char *string
= malloc(len
);
2056 snprintf(string
, len
, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2057 sPlaylist
, from
, to
);
2058 mpd_executeCommand(connection
, string
);
2063 void mpd_sendPlaylistDeleteCommand(mpd_Connection
*connection
,
2064 const char *playlist
, int pos
)
2066 char *sPlaylist
= mpd_sanitizeArg(playlist
);
2067 int len
= strlen("playlistdelete")+2+strlen(sPlaylist
)+3+INTLEN
+3;
2068 char *string
= malloc(len
);
2069 snprintf(string
, len
, "playlistdelete \"%s\" \"%i\"\n", sPlaylist
, pos
);
2070 mpd_executeCommand(connection
, string
);
2074 void mpd_sendClearErrorCommand(mpd_Connection
* connection
) {
2075 mpd_executeCommand(connection
,"clearerror\n");
2079 void mpd_sendGetEventsCommand(mpd_Connection
*connection
) {
2080 mpd_executeCommand(connection
, "idle\nnoidle\n");
2081 // mpd_executeCommand(connection, "noidle\n");
2084 char * mpd_getNextEvent(mpd_Connection
*connection
)
2086 return mpd_getNextReturnElementNamed(connection
, "changed");
2089 void mpd_sendListPlaylistsCommand(mpd_Connection
* connection
) {
2090 mpd_sendInfoCommand(connection
, "listplaylists\n");
2093 char * mpd_getNextSticker (mpd_Connection
* connection
)
2095 return mpd_getNextReturnElementNamed(connection
, "sticker");
2097 void mpd_sendGetSongSticker(mpd_Connection
*connection
, const char *song_path
, const char *sticker
)
2099 char *sSong
= mpd_sanitizeArg(song_path
);
2100 char *sSticker
= mpd_sanitizeArg(sticker
);
2101 int len
= strlen("sticker get song ")+strlen(sSong
)+3+strlen(sSticker
)+4;
2102 char *string
= malloc(len
);
2103 snprintf(string
, len
, "sticker get song \"%s\" \"%s\"\n", sSong
, sSticker
);
2104 mpd_executeCommand(connection
, string
);
2110 void mpd_sendSetSongSticker(mpd_Connection
*connection
, const char *song_path
, const char *sticker
, const char *value
)
2113 char *sSong
= mpd_sanitizeArg(song_path
);
2114 char *sSticker
= mpd_sanitizeArg(sticker
);
2115 char *sValue
= mpd_sanitizeArg(value
);
2116 int len
= strlen("sticker set song ")+strlen(sSong
)+3+strlen(sSticker
)+3+strlen(sValue
)+4;
2117 char *string
= malloc(len
);
2118 snprintf(string
, len
, "sticker set song \"%s\" \"%s\" \"%s\"\n", sSong
, sSticker
,sValue
);
2119 mpd_sendInfoCommand(connection
, string
);