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
));
744 status
->playlist
= -1;
745 status
->storedplaylist
= -1;
746 status
->playlistLength
= -1;
750 status
->nextsong
= -1;
751 status
->nextsongid
= -1;
752 status
->elapsedTime
= 0;
753 status
->totalTime
= 0;
755 status
->sampleRate
= 0;
757 status
->channels
= 0;
758 status
->crossfade
= -1;
759 status
->error
= NULL
;
760 status
->updatingDb
= 0;
762 if(connection
->error
) {
766 while(connection
->returnElement
) {
767 mpd_ReturnElement
* re
= connection
->returnElement
;
768 if(strcmp(re
->name
,"volume")==0) {
769 status
->volume
= atoi(re
->value
);
771 else if(strcmp(re
->name
,"repeat")==0) {
772 status
->repeat
= atoi(re
->value
);
774 else if(strcmp(re
->name
,"single")==0) {
775 status
->single
= atoi(re
->value
);
777 else if(strcmp(re
->name
,"consume")==0) {
778 status
->consume
= atoi(re
->value
);
780 else if(strcmp(re
->name
,"random")==0) {
781 status
->random
= atoi(re
->value
);
783 else if(strcmp(re
->name
,"playlist")==0) {
784 status
->playlist
= strtol(re
->value
,NULL
,10);
786 else if(strcmp(re
->name
,"playlistlength")==0) {
787 status
->playlistLength
= atoi(re
->value
);
789 else if(strcmp(re
->name
,"bitrate")==0) {
790 status
->bitRate
= atoi(re
->value
);
792 else if(strcmp(re
->name
,"state")==0) {
793 if(strcmp(re
->value
,"play")==0) {
794 status
->state
= MPD_STATUS_STATE_PLAY
;
796 else if(strcmp(re
->value
,"stop")==0) {
797 status
->state
= MPD_STATUS_STATE_STOP
;
799 else if(strcmp(re
->value
,"pause")==0) {
800 status
->state
= MPD_STATUS_STATE_PAUSE
;
803 status
->state
= MPD_STATUS_STATE_UNKNOWN
;
806 else if(strcmp(re
->name
,"song")==0) {
807 status
->song
= atoi(re
->value
);
809 else if(strcmp(re
->name
,"songid")==0) {
810 status
->songid
= atoi(re
->value
);
812 else if(strcmp(re
->name
,"nextsong")==0) {
813 status
->nextsong
= atoi(re
->value
);
815 else if(strcmp(re
->name
,"nextsongid")==0) {
816 status
->nextsongid
= atoi(re
->value
);
818 else if(strcmp(re
->name
,"time")==0) {
819 char * tok
= strchr(re
->value
,':');
820 /* the second strchr below is a safety check */
821 if (tok
&& (strchr(tok
,0) > (tok
+1))) {
822 /* atoi stops at the first non-[0-9] char: */
823 status
->elapsedTime
= atoi(re
->value
);
824 status
->totalTime
= atoi(tok
+1);
827 else if(strcmp(re
->name
,"error")==0) {
828 status
->error
= strdup(re
->value
);
830 else if(strcmp(re
->name
,"xfade")==0) {
831 status
->crossfade
= atoi(re
->value
);
833 else if(strcmp(re
->name
,"updating_db")==0) {
834 status
->updatingDb
= atoi(re
->value
);
836 else if(strcmp(re
->name
,"audio")==0) {
837 char * tok
= strchr(re
->value
,':');
838 if (tok
&& (strchr(tok
,0) > (tok
+1))) {
839 status
->sampleRate
= atoi(re
->value
);
840 status
->bits
= atoi(++tok
);
841 tok
= strchr(tok
,':');
842 if (tok
&& (strchr(tok
,0) > (tok
+1)))
843 status
->channels
= atoi(tok
+1);
847 mpd_getNextReturnElement(connection
);
848 if(connection
->error
) {
854 if(connection
->error
) {
858 else if(status
->state
<0) {
859 strcpy(connection
->errorStr
,"state not found");
860 connection
->error
= 1;
868 void mpd_freeStatus(mpd_Status
* status
) {
869 if(status
->error
) free(status
->error
);
873 void mpd_sendStatsCommand(mpd_Connection
* connection
) {
874 mpd_executeCommand(connection
,"stats\n");
877 mpd_Stats
* mpd_getStats(mpd_Connection
* connection
) {
880 /*mpd_executeCommand(connection,"stats\n");
882 if(connection->error) return NULL;*/
884 if(connection
->doneProcessing
|| (connection
->listOks
&&
885 connection
->doneListOk
))
890 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
892 stats
= malloc(sizeof(mpd_Stats
));
893 stats
->numberOfArtists
= 0;
894 stats
->numberOfAlbums
= 0;
895 stats
->numberOfSongs
= 0;
897 stats
->dbUpdateTime
= 0;
899 stats
->dbPlayTime
= 0;
901 if(connection
->error
) {
905 while(connection
->returnElement
) {
906 mpd_ReturnElement
* re
= connection
->returnElement
;
907 if(strcmp(re
->name
,"artists")==0) {
908 stats
->numberOfArtists
= atoi(re
->value
);
910 else if(strcmp(re
->name
,"albums")==0) {
911 stats
->numberOfAlbums
= atoi(re
->value
);
913 else if(strcmp(re
->name
,"songs")==0) {
914 stats
->numberOfSongs
= atoi(re
->value
);
916 else if(strcmp(re
->name
,"uptime")==0) {
917 stats
->uptime
= strtol(re
->value
,NULL
,10);
919 else if(strcmp(re
->name
,"db_update")==0) {
920 stats
->dbUpdateTime
= strtol(re
->value
,NULL
,10);
922 else if(strcmp(re
->name
,"playtime")==0) {
923 stats
->playTime
= strtol(re
->value
,NULL
,10);
925 else if(strcmp(re
->name
,"db_playtime")==0) {
926 stats
->dbPlayTime
= strtol(re
->value
,NULL
,10);
929 mpd_getNextReturnElement(connection
);
930 if(connection
->error
) {
936 if(connection
->error
) {
944 void mpd_freeStats(mpd_Stats
* stats
) {
948 mpd_SearchStats
* mpd_getSearchStats(mpd_Connection
* connection
)
950 mpd_SearchStats
* stats
;
951 mpd_ReturnElement
* re
;
953 if (connection
->doneProcessing
||
954 (connection
->listOks
&& connection
->doneListOk
)) {
958 if (!connection
->returnElement
) mpd_getNextReturnElement(connection
);
960 if (connection
->error
)
963 stats
= malloc(sizeof(mpd_SearchStats
));
964 stats
->numberOfSongs
= 0;
967 while (connection
->returnElement
) {
968 re
= connection
->returnElement
;
970 if (strcmp(re
->name
, "songs") == 0) {
971 stats
->numberOfSongs
= atoi(re
->value
);
972 } else if (strcmp(re
->name
, "playtime") == 0) {
973 stats
->playTime
= strtol(re
->value
, NULL
, 10);
976 mpd_getNextReturnElement(connection
);
977 if (connection
->error
) {
983 if (connection
->error
) {
991 void mpd_freeSearchStats(mpd_SearchStats
* stats
)
996 static void mpd_initSong(mpd_Song
* song
) {
1004 /* added by Qball */
1006 song
->composer
= NULL
;
1007 song
->performer
= NULL
;
1009 song
->comment
= NULL
;
1010 song
->albumartist
= NULL
;
1012 song
->time
= MPD_SONG_NO_TIME
;
1013 song
->pos
= MPD_SONG_NO_NUM
;
1014 song
->id
= MPD_SONG_NO_ID
;
1017 static void mpd_finishSong(mpd_Song
* song
) {
1018 if(song
->file
) free(song
->file
);
1019 if(song
->artist
) free(song
->artist
);
1020 if(song
->album
) free(song
->album
);
1021 if(song
->title
) free(song
->title
);
1022 if(song
->track
) free(song
->track
);
1023 if(song
->name
) free(song
->name
);
1024 if(song
->date
) free(song
->date
);
1025 if(song
->genre
) free(song
->genre
);
1026 if(song
->composer
) free(song
->composer
);
1027 if(song
->performer
) free(song
->performer
);
1028 if(song
->disc
) free(song
->disc
);
1029 if(song
->comment
) free(song
->comment
);
1030 if(song
->albumartist
) free(song
->albumartist
);
1033 mpd_Song
* mpd_newSong(void) {
1034 mpd_Song
* ret
= malloc(sizeof(mpd_Song
));
1041 void mpd_freeSong(mpd_Song
* song
) {
1042 mpd_finishSong(song
);
1046 mpd_Song
* mpd_songDup(const mpd_Song
* song
) {
1047 mpd_Song
* ret
= mpd_newSong();
1049 if(song
->file
) ret
->file
= strdup(song
->file
);
1050 if(song
->artist
) ret
->artist
= strdup(song
->artist
);
1051 if(song
->album
) ret
->album
= strdup(song
->album
);
1052 if(song
->title
) ret
->title
= strdup(song
->title
);
1053 if(song
->track
) ret
->track
= strdup(song
->track
);
1054 if(song
->name
) ret
->name
= strdup(song
->name
);
1055 if(song
->date
) ret
->date
= strdup(song
->date
);
1056 if(song
->genre
) ret
->genre
= strdup(song
->genre
);
1057 if(song
->composer
) ret
->composer
= strdup(song
->composer
);
1058 if(song
->performer
) ret
->performer
= strdup(song
->performer
);
1059 if(song
->disc
) ret
->disc
= strdup(song
->disc
);
1060 if(song
->comment
) ret
->comment
= strdup(song
->comment
);
1061 if(song
->albumartist
) ret
->albumartist
= strdup(song
->albumartist
);
1062 ret
->time
= song
->time
;
1063 ret
->pos
= song
->pos
;
1069 static void mpd_initDirectory(mpd_Directory
* directory
) {
1070 directory
->path
= NULL
;
1073 static void mpd_finishDirectory(mpd_Directory
* directory
) {
1074 if(directory
->path
) free(directory
->path
);
1077 mpd_Directory
* mpd_newDirectory(void) {
1078 mpd_Directory
* directory
= malloc(sizeof(mpd_Directory
));;
1080 mpd_initDirectory(directory
);
1085 void mpd_freeDirectory(mpd_Directory
* directory
) {
1086 mpd_finishDirectory(directory
);
1091 mpd_Directory
* mpd_directoryDup(mpd_Directory
* directory
) {
1092 mpd_Directory
* ret
= mpd_newDirectory();
1094 if(directory
->path
) ret
->path
= strdup(directory
->path
);
1099 static void mpd_initPlaylistFile(mpd_PlaylistFile
* playlist
) {
1100 playlist
->path
= NULL
;
1101 playlist
->mtime
= NULL
;
1104 static void mpd_finishPlaylistFile(mpd_PlaylistFile
* playlist
) {
1105 if(playlist
->path
) free(playlist
->path
);
1106 if(playlist
->mtime
) free(playlist
->mtime
);
1109 mpd_PlaylistFile
* mpd_newPlaylistFile(void) {
1110 mpd_PlaylistFile
* playlist
= malloc(sizeof(mpd_PlaylistFile
));
1112 mpd_initPlaylistFile(playlist
);
1117 void mpd_freePlaylistFile(mpd_PlaylistFile
* playlist
) {
1118 mpd_finishPlaylistFile(playlist
);
1122 mpd_PlaylistFile
* mpd_playlistFileDup(mpd_PlaylistFile
* playlist
) {
1123 mpd_PlaylistFile
* ret
= mpd_newPlaylistFile();
1125 if(playlist
->path
) ret
->path
= strdup(playlist
->path
);
1126 if(playlist
->mtime
) ret
->mtime
= strdup(playlist
->mtime
);
1131 static void mpd_initInfoEntity(mpd_InfoEntity
* entity
) {
1132 entity
->info
.directory
= NULL
;
1135 static void mpd_finishInfoEntity(mpd_InfoEntity
* entity
) {
1136 if(entity
->info
.directory
) {
1137 if(entity
->type
== MPD_INFO_ENTITY_TYPE_DIRECTORY
) {
1138 mpd_freeDirectory(entity
->info
.directory
);
1140 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_SONG
) {
1141 mpd_freeSong(entity
->info
.song
);
1143 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_PLAYLISTFILE
) {
1144 mpd_freePlaylistFile(entity
->info
.playlistFile
);
1149 mpd_InfoEntity
* mpd_newInfoEntity(void) {
1150 mpd_InfoEntity
* entity
= malloc(sizeof(mpd_InfoEntity
));
1152 mpd_initInfoEntity(entity
);
1157 void mpd_freeInfoEntity(mpd_InfoEntity
* entity
) {
1158 mpd_finishInfoEntity(entity
);
1162 static void mpd_sendInfoCommand(mpd_Connection
* connection
,const char * command
) {
1163 mpd_executeCommand(connection
,command
);
1166 mpd_InfoEntity
* mpd_getNextInfoEntity(mpd_Connection
* connection
) {
1167 mpd_InfoEntity
* entity
= NULL
;
1169 if(connection
->doneProcessing
|| (connection
->listOks
&&
1170 connection
->doneListOk
))
1175 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
1177 if(connection
->returnElement
) {
1178 if(strcmp(connection
->returnElement
->name
,"file")==0) {
1179 entity
= mpd_newInfoEntity();
1180 entity
->type
= MPD_INFO_ENTITY_TYPE_SONG
;
1181 entity
->info
.song
= mpd_newSong();
1182 entity
->info
.song
->file
=
1183 strdup(connection
->returnElement
->value
);
1185 else if(strcmp(connection
->returnElement
->name
,
1187 entity
= mpd_newInfoEntity();
1188 entity
->type
= MPD_INFO_ENTITY_TYPE_DIRECTORY
;
1189 entity
->info
.directory
= mpd_newDirectory();
1190 entity
->info
.directory
->path
=
1191 strdup(connection
->returnElement
->value
);
1193 else if(strcmp(connection
->returnElement
->name
,"playlist")==0) {
1194 entity
= mpd_newInfoEntity();
1195 entity
->type
= MPD_INFO_ENTITY_TYPE_PLAYLISTFILE
;
1196 entity
->info
.playlistFile
= mpd_newPlaylistFile();
1197 entity
->info
.playlistFile
->path
=
1198 strdup(connection
->returnElement
->value
);
1200 else if(strcmp(connection
->returnElement
->name
, "cpos") == 0){
1201 entity
= mpd_newInfoEntity();
1202 entity
->type
= MPD_INFO_ENTITY_TYPE_SONG
;
1203 entity
->info
.song
= mpd_newSong();
1204 entity
->info
.song
->pos
= atoi(connection
->returnElement
->value
);
1207 connection
->error
= 1;
1208 strcpy(connection
->errorStr
,"problem parsing song info");
1214 mpd_getNextReturnElement(connection
);
1215 while(connection
->returnElement
) {
1216 mpd_ReturnElement
* re
= connection
->returnElement
;
1218 if(strcmp(re
->name
,"file")==0) return entity
;
1219 else if(strcmp(re
->name
,"directory")==0) return entity
;
1220 else if(strcmp(re
->name
,"playlist")==0) return entity
;
1221 else if(strcmp(re
->name
,"cpos")==0) return entity
;
1223 if(entity
->type
== MPD_INFO_ENTITY_TYPE_SONG
&&
1224 strlen(re
->value
)) {
1225 if(strcmp(re
->name
,"Artist")==0) {
1226 if(entity
->info
.song
->artist
) {
1227 int length
= strlen(entity
->info
.song
->artist
);
1228 entity
->info
.song
->artist
= realloc(entity
->info
.song
->artist
,
1229 length
+ strlen(re
->value
) + 3);
1230 strcpy(&((entity
->info
.song
->artist
)[length
]), ", ");
1231 strcpy(&((entity
->info
.song
->artist
)[length
+ 2]), re
->value
);
1234 entity
->info
.song
->artist
= strdup(re
->value
);
1237 else if(!entity
->info
.song
->album
&&
1238 strcmp(re
->name
,"Album")==0) {
1239 entity
->info
.song
->album
= strdup(re
->value
);
1241 else if(!entity
->info
.song
->title
&&
1242 strcmp(re
->name
,"Title")==0) {
1243 entity
->info
.song
->title
= strdup(re
->value
);
1245 else if(!entity
->info
.song
->track
&&
1246 strcmp(re
->name
,"Track")==0) {
1247 entity
->info
.song
->track
= strdup(re
->value
);
1249 else if(!entity
->info
.song
->name
&&
1250 strcmp(re
->name
,"Name")==0) {
1251 entity
->info
.song
->name
= strdup(re
->value
);
1253 else if(entity
->info
.song
->time
==MPD_SONG_NO_TIME
&&
1254 strcmp(re
->name
,"Time")==0) {
1255 entity
->info
.song
->time
= atoi(re
->value
);
1257 else if(entity
->info
.song
->pos
==MPD_SONG_NO_NUM
&&
1258 strcmp(re
->name
,"Pos")==0) {
1259 entity
->info
.song
->pos
= atoi(re
->value
);
1261 else if(entity
->info
.song
->id
==MPD_SONG_NO_ID
&&
1262 strcmp(re
->name
,"Id")==0) {
1263 entity
->info
.song
->id
= atoi(re
->value
);
1265 else if(!entity
->info
.song
->date
&&
1266 strcmp(re
->name
, "Date") == 0) {
1267 entity
->info
.song
->date
= strdup(re
->value
);
1269 else if(!entity
->info
.song
->genre
&&
1270 strcmp(re
->name
, "Genre") == 0) {
1271 if(entity
->info
.song
->genre
) {
1272 int length
= strlen(entity
->info
.song
->genre
);
1273 entity
->info
.song
->genre
= realloc(entity
->info
.song
->genre
,
1274 length
+ strlen(re
->value
) + 4);
1275 strcpy(&((entity
->info
.song
->genre
)[length
]), ", ");
1276 strcpy(&((entity
->info
.song
->genre
)[length
+ 3]), re
->value
);
1279 entity
->info
.song
->genre
= strdup(re
->value
);
1282 else if(strcmp(re
->name
, "Composer") == 0) {
1283 if(entity
->info
.song
->composer
) {
1284 int length
= strlen(entity
->info
.song
->composer
);
1285 entity
->info
.song
->composer
= realloc(entity
->info
.song
->composer
,
1286 length
+ strlen(re
->value
) + 3);
1287 strcpy(&((entity
->info
.song
->composer
)[length
]), ", ");
1288 strcpy(&((entity
->info
.song
->composer
)[length
+ 2]), re
->value
);
1291 entity
->info
.song
->composer
= strdup(re
->value
);
1294 else if(strcmp(re
->name
, "Performer") == 0) {
1295 if(entity
->info
.song
->performer
) {
1296 int length
= strlen(entity
->info
.song
->performer
);
1297 entity
->info
.song
->performer
= realloc(entity
->info
.song
->performer
,
1298 length
+ strlen(re
->value
) + 3);
1299 strcpy(&((entity
->info
.song
->performer
)[length
]), ", ");
1300 strcpy(&((entity
->info
.song
->performer
)[length
+ 2]), re
->value
);
1303 entity
->info
.song
->performer
= strdup(re
->value
);
1306 else if(!entity
->info
.song
->disc
&&
1307 strcmp(re
->name
, "Disc") == 0) {
1308 entity
->info
.song
->disc
= strdup(re
->value
);
1310 else if(!entity
->info
.song
->comment
&&
1311 strcmp(re
->name
, "Comment") == 0) {
1312 entity
->info
.song
->comment
= strdup(re
->value
);
1315 else if(!entity
->info
.song
->albumartist
&&
1316 strcmp(re
->name
, "AlbumArtist") == 0) {
1317 entity
->info
.song
->albumartist
= strdup(re
->value
);
1320 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_DIRECTORY
) {
1322 else if(entity
->type
== MPD_INFO_ENTITY_TYPE_PLAYLISTFILE
) {
1323 if(!entity
->info
.playlistFile
->mtime
&&
1324 strcmp(re
->name
, "Last-Modified") == 0) {
1325 entity
->info
.playlistFile
->mtime
= strdup(re
->value
);
1329 mpd_getNextReturnElement(connection
);
1335 static char * mpd_getNextReturnElementNamed(mpd_Connection
* connection
,
1338 if(connection
->doneProcessing
|| (connection
->listOks
&&
1339 connection
->doneListOk
))
1344 mpd_getNextReturnElement(connection
);
1345 while(connection
->returnElement
) {
1346 mpd_ReturnElement
* re
= connection
->returnElement
;
1348 if(strcmp(re
->name
,name
)==0) return strdup(re
->value
);
1349 mpd_getNextReturnElement(connection
);
1355 char *mpd_getNextTag(mpd_Connection
*connection
, int type
)
1357 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
||
1358 type
== MPD_TAG_ITEM_ANY
)
1360 if (type
== MPD_TAG_ITEM_FILENAME
)
1361 return mpd_getNextReturnElementNamed(connection
, "file");
1362 return mpd_getNextReturnElementNamed(connection
, mpdTagItemKeys
[type
]);
1365 char * mpd_getNextArtist(mpd_Connection
* connection
) {
1366 return mpd_getNextReturnElementNamed(connection
,"Artist");
1369 char * mpd_getNextAlbum(mpd_Connection
* connection
) {
1370 return mpd_getNextReturnElementNamed(connection
,"Album");
1373 void mpd_sendPlaylistInfoCommand(mpd_Connection
* connection
, int songPos
) {
1374 int len
= strlen("playlistinfo")+2+INTLEN
+3;
1375 char *string
= malloc(len
);
1376 snprintf(string
, len
, "playlistinfo \"%i\"\n", songPos
);
1377 mpd_sendInfoCommand(connection
,string
);
1381 void mpd_sendPlaylistIdCommand(mpd_Connection
* connection
, int id
) {
1382 int len
= strlen("playlistid")+2+INTLEN
+3;
1383 char *string
= malloc(len
);
1384 snprintf(string
, len
, "playlistid \"%i\"\n", id
);
1385 mpd_sendInfoCommand(connection
, string
);
1389 void mpd_sendPlChangesCommand(mpd_Connection
* connection
, long long playlist
) {
1390 int len
= strlen("plchanges")+2+LONGLONGLEN
+3;
1391 char *string
= malloc(len
);
1392 snprintf(string
, len
, "plchanges \"%lld\"\n", playlist
);
1393 mpd_sendInfoCommand(connection
,string
);
1397 void mpd_sendPlChangesPosIdCommand(mpd_Connection
* connection
, long long playlist
) {
1398 int len
= strlen("plchangesposid")+2+LONGLONGLEN
+3;
1399 char *string
= malloc(len
);
1400 snprintf(string
, len
, "plchangesposid \"%lld\"\n", playlist
);
1401 mpd_sendInfoCommand(connection
,string
);
1405 void mpd_sendListallCommand(mpd_Connection
* connection
, const char * dir
) {
1406 char * sDir
= mpd_sanitizeArg(dir
);
1407 int len
= strlen("listall")+2+strlen(sDir
)+3;
1408 char *string
= malloc(len
);
1409 snprintf(string
, len
, "listall \"%s\"\n", sDir
);
1410 mpd_sendInfoCommand(connection
,string
);
1415 void mpd_sendListallInfoCommand(mpd_Connection
* connection
, const char * dir
) {
1416 char * sDir
= mpd_sanitizeArg(dir
);
1417 int len
= strlen("listallinfo")+2+strlen(sDir
)+3;
1418 char *string
= malloc(len
);
1419 snprintf(string
, len
, "listallinfo \"%s\"\n", sDir
);
1420 mpd_sendInfoCommand(connection
,string
);
1425 void mpd_sendLsInfoCommand(mpd_Connection
* connection
, const char * dir
) {
1426 char * sDir
= mpd_sanitizeArg(dir
);
1427 int len
= strlen("lsinfo")+2+strlen(sDir
)+3;
1428 char *string
= malloc(len
);
1429 snprintf(string
, len
, "lsinfo \"%s\"\n", sDir
);
1430 mpd_sendInfoCommand(connection
,string
);
1435 void mpd_sendCurrentSongCommand(mpd_Connection
* connection
) {
1436 mpd_executeCommand(connection
,"currentsong\n");
1439 void mpd_sendSearchCommand(mpd_Connection
* connection
, int table
,
1442 mpd_startSearch(connection
, 0);
1443 mpd_addConstraintSearch(connection
, table
, str
);
1444 mpd_commitSearch(connection
);
1447 void mpd_sendFindCommand(mpd_Connection
* connection
, int table
,
1450 mpd_startSearch(connection
, 1);
1451 mpd_addConstraintSearch(connection
, table
, str
);
1452 mpd_commitSearch(connection
);
1455 void mpd_sendListCommand(mpd_Connection
* connection
, int table
,
1461 if(table
== MPD_TABLE_ARTIST
) strcpy(st
,"artist");
1462 else if(table
== MPD_TABLE_ALBUM
) strcpy(st
,"album");
1464 connection
->error
= 1;
1465 strcpy(connection
->errorStr
,"unknown table for list");
1469 char * sanitArg1
= mpd_sanitizeArg(arg1
);
1470 len
= strlen("list")+1+strlen(sanitArg1
)+2+strlen(st
)+3;
1471 string
= malloc(len
);
1472 snprintf(string
, len
, "list %s \"%s\"\n", st
, sanitArg1
);
1476 len
= strlen("list")+1+strlen(st
)+2;
1477 string
= malloc(len
);
1478 snprintf(string
, len
, "list %s\n", st
);
1480 mpd_sendInfoCommand(connection
,string
);
1484 void mpd_sendAddCommand(mpd_Connection
* connection
, const char * file
) {
1485 char * sFile
= mpd_sanitizeArg(file
);
1486 int len
= strlen("add")+2+strlen(sFile
)+3;
1487 char *string
= malloc(len
);
1488 snprintf(string
, len
, "add \"%s\"\n", sFile
);
1489 mpd_executeCommand(connection
,string
);
1494 int mpd_sendAddIdCommand(mpd_Connection
*connection
, const char *file
)
1497 char *sFile
= mpd_sanitizeArg(file
);
1498 int len
= strlen("addid")+2+strlen(sFile
)+3;
1499 char *string
= malloc(len
);
1501 snprintf(string
, len
, "addid \"%s\"\n", sFile
);
1502 mpd_sendInfoCommand(connection
, string
);
1506 string
= mpd_getNextReturnElementNamed(connection
, "Id");
1508 retval
= atoi(string
);
1515 void mpd_sendDeleteCommand(mpd_Connection
* connection
, int songPos
) {
1516 int len
= strlen("delete")+2+INTLEN
+3;
1517 char *string
= malloc(len
);
1518 snprintf(string
, len
, "delete \"%i\"\n", songPos
);
1519 mpd_sendInfoCommand(connection
,string
);
1523 void mpd_sendDeleteIdCommand(mpd_Connection
* connection
, int id
) {
1524 int len
= strlen("deleteid")+2+INTLEN
+3;
1525 char *string
= malloc(len
);
1526 snprintf(string
, len
, "deleteid \"%i\"\n", id
);
1527 mpd_sendInfoCommand(connection
,string
);
1531 void mpd_sendSaveCommand(mpd_Connection
* connection
, const char * name
) {
1532 char * sName
= mpd_sanitizeArg(name
);
1533 int len
= strlen("save")+2+strlen(sName
)+3;
1534 char *string
= malloc(len
);
1535 snprintf(string
, len
, "save \"%s\"\n", sName
);
1536 mpd_executeCommand(connection
,string
);
1541 void mpd_sendLoadCommand(mpd_Connection
* connection
, const char * name
) {
1542 char * sName
= mpd_sanitizeArg(name
);
1543 int len
= strlen("load")+2+strlen(sName
)+3;
1544 char *string
= malloc(len
);
1545 snprintf(string
, len
, "load \"%s\"\n", sName
);
1546 mpd_executeCommand(connection
,string
);
1551 void mpd_sendRmCommand(mpd_Connection
* connection
, const char * name
) {
1552 char * sName
= mpd_sanitizeArg(name
);
1553 int len
= strlen("rm")+2+strlen(sName
)+3;
1554 char *string
= malloc(len
);
1555 snprintf(string
, len
, "rm \"%s\"\n", sName
);
1556 mpd_executeCommand(connection
,string
);
1561 void mpd_sendRenameCommand(mpd_Connection
*connection
, const char *from
,
1564 char *sFrom
= mpd_sanitizeArg(from
);
1565 char *sTo
= mpd_sanitizeArg(to
);
1566 int len
= strlen("rename")+2+strlen(sFrom
)+3+strlen(sTo
)+3;
1567 char *string
= malloc(len
);
1568 snprintf(string
, len
, "rename \"%s\" \"%s\"\n", sFrom
, sTo
);
1569 mpd_executeCommand(connection
, string
);
1575 void mpd_sendShuffleCommand(mpd_Connection
* connection
) {
1576 mpd_executeCommand(connection
,"shuffle\n");
1579 void mpd_sendClearCommand(mpd_Connection
* connection
) {
1580 mpd_executeCommand(connection
,"clear\n");
1583 void mpd_sendPlayCommand(mpd_Connection
* connection
, int songPos
) {
1584 int len
= strlen("play")+2+INTLEN
+3;
1585 char *string
= malloc(len
);
1586 snprintf(string
, len
, "play \"%i\"\n", songPos
);
1587 mpd_sendInfoCommand(connection
,string
);
1591 void mpd_sendPlayIdCommand(mpd_Connection
* connection
, int id
) {
1592 int len
= strlen("playid")+2+INTLEN
+3;
1593 char *string
= malloc(len
);
1594 snprintf(string
, len
, "playid \"%i\"\n", id
);
1595 mpd_sendInfoCommand(connection
,string
);
1599 void mpd_sendStopCommand(mpd_Connection
* connection
) {
1600 mpd_executeCommand(connection
,"stop\n");
1603 void mpd_sendPauseCommand(mpd_Connection
* connection
, int pauseMode
) {
1604 int len
= strlen("pause")+2+INTLEN
+3;
1605 char *string
= malloc(len
);
1606 snprintf(string
, len
, "pause \"%i\"\n", pauseMode
);
1607 mpd_executeCommand(connection
,string
);
1611 void mpd_sendNextCommand(mpd_Connection
* connection
) {
1612 mpd_executeCommand(connection
,"next\n");
1615 void mpd_sendMoveCommand(mpd_Connection
* connection
, int from
, int to
) {
1616 int len
= strlen("move")+2+INTLEN
+3+INTLEN
+3;
1617 char *string
= malloc(len
);
1618 snprintf(string
, len
, "move \"%i\" \"%i\"\n", from
, to
);
1619 mpd_sendInfoCommand(connection
,string
);
1623 void mpd_sendMoveIdCommand(mpd_Connection
* connection
, int id
, int to
) {
1624 int len
= strlen("moveid")+2+INTLEN
+3+INTLEN
+3;
1625 char *string
= malloc(len
);
1626 snprintf(string
, len
, "moveid \"%i\" \"%i\"\n", id
, to
);
1627 mpd_sendInfoCommand(connection
,string
);
1631 void mpd_sendSwapCommand(mpd_Connection
* connection
, int song1
, int song2
) {
1632 int len
= strlen("swap")+2+INTLEN
+3+INTLEN
+3;
1633 char *string
= malloc(len
);
1634 snprintf(string
, len
, "swap \"%i\" \"%i\"\n", song1
, song2
);
1635 mpd_sendInfoCommand(connection
,string
);
1639 void mpd_sendSwapIdCommand(mpd_Connection
* connection
, int id1
, int id2
) {
1640 int len
= strlen("swapid")+2+INTLEN
+3+INTLEN
+3;
1641 char *string
= malloc(len
);
1642 snprintf(string
, len
, "swapid \"%i\" \"%i\"\n", id1
, id2
);
1643 mpd_sendInfoCommand(connection
,string
);
1647 void mpd_sendSeekCommand(mpd_Connection
* connection
, int song
, int time
) {
1648 int len
= strlen("seek")+2+INTLEN
+3+INTLEN
+3;
1649 char *string
= malloc(len
);
1650 snprintf(string
, len
, "seek \"%i\" \"%i\"\n", song
, time
);
1651 mpd_sendInfoCommand(connection
,string
);
1655 void mpd_sendSeekIdCommand(mpd_Connection
* connection
, int id
, int time
) {
1656 int len
= strlen("seekid")+2+INTLEN
+3+INTLEN
+3;
1657 char *string
= malloc(len
);
1658 snprintf(string
, len
, "seekid \"%i\" \"%i\"\n", id
, time
);
1659 mpd_sendInfoCommand(connection
,string
);
1663 void mpd_sendUpdateCommand(mpd_Connection
* connection
,const char * path
) {
1664 char * sPath
= mpd_sanitizeArg(path
);
1665 int len
= strlen("update")+2+strlen(sPath
)+3;
1666 char *string
= malloc(len
);
1667 snprintf(string
, len
, "update \"%s\"\n", sPath
);
1668 mpd_sendInfoCommand(connection
,string
);
1673 int mpd_getUpdateId(mpd_Connection
* connection
) {
1677 jobid
= mpd_getNextReturnElementNamed(connection
,"updating_db");
1686 void mpd_sendPrevCommand(mpd_Connection
* connection
) {
1687 mpd_executeCommand(connection
,"previous\n");
1690 void mpd_sendSingleCommand(mpd_Connection
* connection
, int singleMode
) {
1691 int len
= strlen("repeat")+2+INTLEN
+3;
1692 char *string
= malloc(len
);
1693 snprintf(string
, len
, "single \"%i\"\n", singleMode
);
1694 mpd_executeCommand(connection
,string
);
1698 void mpd_sendConsumeCommand(mpd_Connection
* connection
, int consumeMode
) {
1699 int len
= strlen("repeat")+2+INTLEN
+3;
1700 char *string
= malloc(len
);
1701 snprintf(string
, len
, "consume \"%i\"\n", consumeMode
);
1702 mpd_executeCommand(connection
,string
);
1706 void mpd_sendRepeatCommand(mpd_Connection
* connection
, int repeatMode
) {
1707 int len
= strlen("repeat")+2+INTLEN
+3;
1708 char *string
= malloc(len
);
1709 snprintf(string
, len
, "repeat \"%i\"\n", repeatMode
);
1710 mpd_executeCommand(connection
,string
);
1714 void mpd_sendRandomCommand(mpd_Connection
* connection
, int randomMode
) {
1715 int len
= strlen("random")+2+INTLEN
+3;
1716 char *string
= malloc(len
);
1717 snprintf(string
, len
, "random \"%i\"\n", randomMode
);
1718 mpd_executeCommand(connection
,string
);
1722 void mpd_sendSetvolCommand(mpd_Connection
* connection
, int volumeChange
) {
1723 int len
= strlen("setvol")+2+INTLEN
+3;
1724 char *string
= malloc(len
);
1725 snprintf(string
, len
, "setvol \"%i\"\n", volumeChange
);
1726 mpd_executeCommand(connection
,string
);
1730 void mpd_sendVolumeCommand(mpd_Connection
* connection
, int volumeChange
) {
1731 int len
= strlen("volume")+2+INTLEN
+3;
1732 char *string
= malloc(len
);
1733 snprintf(string
, len
, "volume \"%i\"\n", volumeChange
);
1734 mpd_executeCommand(connection
,string
);
1738 void mpd_sendCrossfadeCommand(mpd_Connection
* connection
, int seconds
) {
1739 int len
= strlen("crossfade")+2+INTLEN
+3;
1740 char *string
= malloc(len
);
1741 snprintf(string
, len
, "crossfade \"%i\"\n", seconds
);
1742 mpd_executeCommand(connection
,string
);
1746 void mpd_sendPasswordCommand(mpd_Connection
* connection
, const char * pass
) {
1747 char * sPass
= mpd_sanitizeArg(pass
);
1748 int len
= strlen("password")+2+strlen(sPass
)+3;
1749 char *string
= malloc(len
);
1750 snprintf(string
, len
, "password \"%s\"\n", sPass
);
1751 mpd_executeCommand(connection
,string
);
1756 void mpd_sendCommandListBegin(mpd_Connection
* connection
) {
1757 if(connection
->commandList
) {
1758 strcpy(connection
->errorStr
,"already in command list mode");
1759 connection
->error
= 1;
1762 connection
->commandList
= COMMAND_LIST
;
1763 mpd_executeCommand(connection
,"command_list_begin\n");
1766 void mpd_sendCommandListOkBegin(mpd_Connection
* connection
) {
1767 if(connection
->commandList
) {
1768 strcpy(connection
->errorStr
,"already in command list mode");
1769 connection
->error
= 1;
1772 connection
->commandList
= COMMAND_LIST_OK
;
1773 mpd_executeCommand(connection
,"command_list_ok_begin\n");
1774 connection
->listOks
= 0;
1777 void mpd_sendCommandListEnd(mpd_Connection
* connection
) {
1778 if(!connection
->commandList
) {
1779 strcpy(connection
->errorStr
,"not in command list mode");
1780 connection
->error
= 1;
1783 connection
->commandList
= 0;
1784 mpd_executeCommand(connection
,"command_list_end\n");
1787 void mpd_sendOutputsCommand(mpd_Connection
* connection
) {
1788 mpd_executeCommand(connection
,"outputs\n");
1791 mpd_OutputEntity
* mpd_getNextOutput(mpd_Connection
* connection
) {
1792 mpd_OutputEntity
* output
= NULL
;
1794 if(connection
->doneProcessing
|| (connection
->listOks
&&
1795 connection
->doneListOk
))
1800 if(connection
->error
) return NULL
;
1802 output
= malloc(sizeof(mpd_OutputEntity
));
1804 output
->name
= NULL
;
1805 output
->enabled
= 0;
1807 if(!connection
->returnElement
) mpd_getNextReturnElement(connection
);
1809 while(connection
->returnElement
) {
1810 mpd_ReturnElement
* re
= connection
->returnElement
;
1811 if(strcmp(re
->name
,"outputid")==0) {
1812 if(output
!=NULL
&& output
->id
>=0) return output
;
1813 output
->id
= atoi(re
->value
);
1815 else if(strcmp(re
->name
,"outputname")==0) {
1816 output
->name
= strdup(re
->value
);
1818 else if(strcmp(re
->name
,"outputenabled")==0) {
1819 output
->enabled
= atoi(re
->value
);
1822 mpd_getNextReturnElement(connection
);
1823 if(connection
->error
) {
1833 void mpd_sendEnableOutputCommand(mpd_Connection
* connection
, int outputId
) {
1834 int len
= strlen("enableoutput")+2+INTLEN
+3;
1835 char *string
= malloc(len
);
1836 snprintf(string
, len
, "enableoutput \"%i\"\n", outputId
);
1837 mpd_executeCommand(connection
,string
);
1841 void mpd_sendDisableOutputCommand(mpd_Connection
* connection
, int outputId
) {
1842 int len
= strlen("disableoutput")+2+INTLEN
+3;
1843 char *string
= malloc(len
);
1844 snprintf(string
, len
, "disableoutput \"%i\"\n", outputId
);
1845 mpd_executeCommand(connection
,string
);
1849 void mpd_freeOutputElement(mpd_OutputEntity
* output
) {
1855 * mpd_sendNotCommandsCommand
1856 * odd naming, but it gets the not allowed commands
1859 void mpd_sendNotCommandsCommand(mpd_Connection
* connection
)
1861 mpd_executeCommand(connection
, "notcommands\n");
1865 * mpd_sendCommandsCommand
1866 * odd naming, but it gets the allowed commands
1868 void mpd_sendCommandsCommand(mpd_Connection
* connection
)
1870 mpd_executeCommand(connection
, "commands\n");
1874 * Get the next returned command
1876 char * mpd_getNextCommand(mpd_Connection
* connection
)
1878 return mpd_getNextReturnElementNamed(connection
, "command");
1881 void mpd_sendUrlHandlersCommand(mpd_Connection
* connection
)
1883 mpd_executeCommand(connection
, "urlhandlers\n");
1886 char * mpd_getNextHandler(mpd_Connection
* connection
)
1888 return mpd_getNextReturnElementNamed(connection
, "handler");
1891 void mpd_sendTagTypesCommand(mpd_Connection
* connection
)
1893 mpd_executeCommand(connection
, "tagtypes\n");
1896 char * mpd_getNextTagType(mpd_Connection
* connection
)
1898 return mpd_getNextReturnElementNamed(connection
, "tagtype");
1901 void mpd_startSearch(mpd_Connection
*connection
, int exact
)
1903 if (connection
->request
) {
1904 strcpy(connection
->errorStr
, "search already in progress");
1905 connection
->error
= 1;
1909 if (exact
) connection
->request
= strdup("find");
1910 else connection
->request
= strdup("search");
1913 void mpd_startStatsSearch(mpd_Connection
*connection
)
1915 if (connection
->request
) {
1916 strcpy(connection
->errorStr
, "search already in progress");
1917 connection
->error
= 1;
1921 connection
->request
= strdup("count");
1924 void mpd_startPlaylistSearch(mpd_Connection
*connection
, int exact
)
1926 if (connection
->request
) {
1927 strcpy(connection
->errorStr
, "search already in progress");
1928 connection
->error
= 1;
1932 if (exact
) connection
->request
= strdup("playlistfind");
1933 else connection
->request
= strdup("playlistsearch");
1936 void mpd_startFieldSearch(mpd_Connection
*connection
, int type
)
1941 if (connection
->request
) {
1942 strcpy(connection
->errorStr
, "search already in progress");
1943 connection
->error
= 1;
1947 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
) {
1948 strcpy(connection
->errorStr
, "invalid type specified");
1949 connection
->error
= 1;
1953 strtype
= mpdTagItemKeys
[type
];
1955 len
= 5+strlen(strtype
)+1;
1956 connection
->request
= malloc(len
);
1958 snprintf(connection
->request
, len
, "list %c%s",
1959 tolower(strtype
[0]), strtype
+1);
1962 void mpd_addConstraintSearch(mpd_Connection
*connection
, int type
, const char *name
)
1969 if (!connection
->request
) {
1970 strcpy(connection
->errorStr
, "no search in progress");
1971 connection
->error
= 1;
1975 if (type
< 0 || type
>= MPD_TAG_NUM_OF_ITEM_TYPES
) {
1976 strcpy(connection
->errorStr
, "invalid type specified");
1977 connection
->error
= 1;
1982 strcpy(connection
->errorStr
, "no name specified");
1983 connection
->error
= 1;
1987 string
= strdup(connection
->request
);
1988 strtype
= mpdTagItemKeys
[type
];
1989 arg
= mpd_sanitizeArg(name
);
1991 len
= strlen(string
)+1+strlen(strtype
)+2+strlen(arg
)+2;
1992 connection
->request
= realloc(connection
->request
, len
);
1993 snprintf(connection
->request
, len
, "%s %c%s \"%s\"",
1994 string
, tolower(strtype
[0]), strtype
+1, arg
);
2000 void mpd_commitSearch(mpd_Connection
*connection
)
2004 if (!connection
->request
) {
2005 strcpy(connection
->errorStr
, "no search in progress");
2006 connection
->error
= 1;
2010 len
= strlen(connection
->request
)+2;
2011 connection
->request
= realloc(connection
->request
, len
);
2012 connection
->request
[len
-2] = '\n';
2013 connection
->request
[len
-1] = '\0';
2014 mpd_sendInfoCommand(connection
, connection
->request
);
2016 free(connection
->request
);
2017 connection
->request
= NULL
;
2021 * @param connection a MpdConnection
2022 * @param path the path to the playlist.
2024 * List the content, with full metadata, of a stored playlist.
2027 void mpd_sendListPlaylistInfoCommand(mpd_Connection
*connection
,const char *path
)
2029 char *arg
= mpd_sanitizeArg(path
);
2030 int len
= strlen("listplaylistinfo")+2+strlen(arg
)+3;
2031 char *query
= malloc(len
);
2032 snprintf(query
, len
, "listplaylistinfo \"%s\"\n", arg
);
2033 mpd_sendInfoCommand(connection
, query
);
2039 * @param connection a MpdConnection
2040 * @param path the path to the playlist.
2042 * List the content of a stored playlist.
2045 void mpd_sendListPlaylistCommand(mpd_Connection
*connection
,const char *path
)
2047 char *arg
= mpd_sanitizeArg(path
);
2048 int len
= strlen("listplaylist")+2+strlen(arg
)+3;
2049 char *query
= malloc(len
);
2050 snprintf(query
, len
, "listplaylist \"%s\"\n", arg
);
2051 mpd_sendInfoCommand(connection
, query
);
2056 void mpd_sendPlaylistClearCommand(mpd_Connection
*connection
,const char *path
)
2058 char *sPath
= mpd_sanitizeArg(path
);
2059 int len
= strlen("playlistclear")+2+strlen(sPath
)+3;
2060 char *string
= malloc(len
);
2061 snprintf(string
, len
, "playlistclear \"%s\"\n", sPath
);
2062 mpd_executeCommand(connection
, string
);
2067 void mpd_sendPlaylistAddCommand(mpd_Connection
*connection
,
2068 const char *playlist
,const char *path
)
2070 char *sPlaylist
= mpd_sanitizeArg(playlist
);
2071 char *sPath
= mpd_sanitizeArg(path
);
2072 int len
= strlen("playlistadd")+2+strlen(sPlaylist
)+3+strlen(sPath
)+3;
2073 char *string
= malloc(len
);
2074 snprintf(string
, len
, "playlistadd \"%s\" \"%s\"\n", sPlaylist
, sPath
);
2075 mpd_executeCommand(connection
, string
);
2081 void mpd_sendPlaylistMoveCommand(mpd_Connection
*connection
,
2082 const char *playlist
, int from
, int to
)
2084 char *sPlaylist
= mpd_sanitizeArg(playlist
);
2085 int len
= strlen("playlistmove")+
2086 2+strlen(sPlaylist
)+3+INTLEN
+3+INTLEN
+3;
2087 char *string
= malloc(len
);
2088 snprintf(string
, len
, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2089 sPlaylist
, from
, to
);
2090 mpd_executeCommand(connection
, string
);
2095 void mpd_sendPlaylistDeleteCommand(mpd_Connection
*connection
,
2096 const char *playlist
, int pos
)
2098 char *sPlaylist
= mpd_sanitizeArg(playlist
);
2099 int len
= strlen("playlistdelete")+2+strlen(sPlaylist
)+3+INTLEN
+3;
2100 char *string
= malloc(len
);
2101 snprintf(string
, len
, "playlistdelete \"%s\" \"%i\"\n", sPlaylist
, pos
);
2102 mpd_executeCommand(connection
, string
);
2106 void mpd_sendClearErrorCommand(mpd_Connection
* connection
) {
2107 mpd_executeCommand(connection
,"clearerror\n");
2111 void mpd_sendGetEventsCommand(mpd_Connection
*connection
) {
2112 mpd_executeCommand(connection
, "idle\nnoidle\n");
2113 // mpd_executeCommand(connection, "noidle\n");
2116 char * mpd_getNextEvent(mpd_Connection
*connection
)
2118 return mpd_getNextReturnElementNamed(connection
, "changed");
2121 void mpd_sendListPlaylistsCommand(mpd_Connection
* connection
) {
2122 mpd_sendInfoCommand(connection
, "listplaylists\n");
2125 char * mpd_getNextSticker (mpd_Connection
* connection
)
2127 return mpd_getNextReturnElementNamed(connection
, "sticker");
2129 void mpd_sendGetSongSticker(mpd_Connection
*connection
, const char *song_path
, const char *sticker
)
2131 char *sSong
= mpd_sanitizeArg(song_path
);
2132 char *sSticker
= mpd_sanitizeArg(sticker
);
2133 int len
= strlen("sticker get song ")+strlen(sSong
)+3+strlen(sSticker
)+4;
2134 char *string
= malloc(len
);
2135 snprintf(string
, len
, "sticker get song \"%s\" \"%s\"\n", sSong
, sSticker
);
2136 mpd_executeCommand(connection
, string
);
2142 void mpd_sendSetSongSticker(mpd_Connection
*connection
, const char *song_path
, const char *sticker
, const char *value
)
2145 char *sSong
= mpd_sanitizeArg(song_path
);
2146 char *sSticker
= mpd_sanitizeArg(sticker
);
2147 char *sValue
= mpd_sanitizeArg(value
);
2148 int len
= strlen("sticker set song ")+strlen(sSong
)+3+strlen(sSticker
)+3+strlen(sValue
)+4;
2149 char *string
= malloc(len
);
2150 snprintf(string
, len
, "sticker set song \"%s\" \"%s\" \"%s\"\n", sSong
, sSticker
,sValue
);
2151 mpd_sendInfoCommand(connection
, string
);