change to const
[libmpd.git] / src / libmpdclient.c
blob7cb4def6120b06cc8ee63a55798b7e83f453e265
1 /* libmpdclient
2 (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
3 This project's homepage is: http://www.musicpd.org
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
12 - Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 - Neither the name of the Music Player Daemon nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
24 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include "libmpdclient.h"
35 #include <errno.h>
36 #include <ctype.h>
37 #include <sys/types.h>
38 #include <stdio.h>
39 #include <sys/param.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <fcntl.h>
44 #include <limits.h>
46 #ifdef WIN32
47 # include <ws2tcpip.h>
48 # include <winsock.h>
49 #else
50 # include <netinet/in.h>
51 # include <arpa/inet.h>
52 # include <sys/socket.h>
53 # include <netdb.h>
54 #endif
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
63 #ifndef MPD_NO_GAI
64 # ifdef AI_ADDRCONFIG
65 # define MPD_HAVE_GAI
66 # endif
67 #endif
69 #ifndef WIN32
70 #include <sys/un.h>
71 #endif
73 #ifndef MSG_DONTWAIT
74 # define MSG_DONTWAIT 0
75 #endif
77 #ifdef WIN32
78 # define SELECT_ERRNO_IGNORE (errno == WSAEINTR || errno == WSAEINPROGRESS)
79 # define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
80 #else
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)
86 #endif
88 #ifdef WIN32
89 static int winsock_dll_error(mpd_Connection *connection)
91 WSADATA wsaData;
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;
98 return 1;
100 return 0;
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)
108 return 1;
109 ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
110 return 0;
112 #else /* !WIN32 (sane operating systems) */
113 static int do_connect_fail(mpd_Connection *connection,
114 const struct sockaddr *serv_addr, int addrlen)
116 int flags;
117 if (connect(connection->sock, serv_addr, addrlen) < 0)
118 return 1;
119 flags = fcntl(connection->sock, F_GETFL, 0);
120 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
121 return 0;
123 #endif /* !WIN32 */
125 #ifdef MPD_HAVE_GAI
126 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
127 float timeout)
129 int error;
130 char service[INTLEN+1];
131 struct addrinfo hints;
132 struct addrinfo *res = NULL;
133 struct addrinfo *addrinfo = NULL;
136 * Setup hints
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);
151 if (error) {
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;
156 return -1;
159 for (res = addrinfo; res; res = res->ai_next) {
160 /* create socket */
161 if (connection->sock >= 0)
162 closesocket(connection->sock);
163 connection->sock = socket(res->ai_family, SOCK_STREAM,
164 res->ai_protocol);
165 if (connection->sock < 0) {
166 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
167 "problems creating socket: %s",
168 strerror(errno));
169 connection->error = MPD_ERROR_SYSTEM;
170 freeaddrinfo(addrinfo);
171 return -1;
174 mpd_setConnectionTimeout(connection, timeout);
176 /* connect stuff */
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;
182 continue;
185 break;
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;
196 return -1;
199 return 0;
201 #else /* !MPD_HAVE_GAI */
202 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
203 float timeout)
205 struct hostent * he;
206 struct sockaddr * dest;
207 int destlen;
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;
214 return -1;
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) {
223 case AF_INET:
224 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
225 he->h_length);
226 dest = (struct sockaddr *)&sin;
227 destlen = sizeof(struct sockaddr_in);
228 break;
229 default:
230 strcpy(connection->errorStr,"address type is not IPv4");
231 connection->error = MPD_ERROR_SYSTEM;
232 return -1;
233 break;
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;
241 return -1;
244 mpd_setConnectionTimeout(connection,timeout);
246 /* connect stuff */
247 if (do_connect_fail(connection, dest, destlen)) {
248 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
249 "problems connecting to \"%s\" on port"
250 " %i",host,port);
251 connection->error = MPD_ERROR_CONNPORT;
252 return -1;
255 return 0;
257 #endif /* !MPD_HAVE_GAI */
259 char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
261 "Artist",
262 "Album",
263 "Title",
264 "Track",
265 "Name",
266 "Genre",
267 "Date",
268 "Composer",
269 "Performer",
270 "Comment",
271 "Disc",
272 "Filename",
273 "Any"
276 static char * mpd_sanitizeArg(const char * arg) {
277 size_t i;
278 char * ret;
279 register const char *c;
280 register char *rc;
282 /* instead of counting in that loop above, just
283 * use a bit more memory and half running time
285 ret = malloc(strlen(arg) * 2 + 1);
287 c = arg;
288 rc = ret;
289 for(i = strlen(arg)+1; i != 0; --i) {
290 if(*c=='"' || *c=='\\')
291 *rc++ = '\\';
292 *(rc++) = *(c++);
295 return ret;
298 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
300 mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
302 ret->name = strdup(name);
303 ret->value = strdup(value);
305 return ret;
308 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
309 free(re->name);
310 free(re->value);
311 free(re);
314 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
315 connection->timeout.tv_sec = (int)timeout;
316 connection->timeout.tv_usec = (int)(timeout*1e6 -
317 connection->timeout.tv_sec*1000000 +
318 0.5);
321 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
322 char * output) {
323 char * tmp;
324 char * test;
325 int i;
327 if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
328 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
329 "mpd not running on port %i on host \"%s\"",
330 port,host);
331 connection->error = MPD_ERROR_NOTMPD;
332 return 1;
335 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
337 for(i=0;i<3;i++) {
338 if(tmp) connection->version[i] = strtol(tmp,&test,10);
340 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
341 snprintf(connection->errorStr,
342 MPD_ERRORSTR_MAX_LENGTH,
343 "error parsing version number at "
344 "\"%s\"",
345 &output[strlen(MPD_WELCOME_MESSAGE)]);
346 connection->error = MPD_ERROR_NOTMPD;
347 return 1;
349 tmp = ++test;
352 return 0;
355 #ifndef WIN32
356 static int mpd_connect_un(mpd_Connection * connection,
357 const char * host, float timeout)
359 int error, flags;
360 size_t path_length;
361 struct sockaddr_un saun;
363 path_length = strlen(host);
364 if (path_length >= sizeof(saun.sun_path)) {
365 strcpy(connection->errorStr, "unix socket path is too long");
366 connection->error = MPD_ERROR_UNKHOST;
367 return -1;
370 saun.sun_family = AF_UNIX;
371 memcpy(saun.sun_path, host, path_length + 1);
373 connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
374 if (connection->sock < 0) {
375 strcpy(connection->errorStr, "problems creating socket");
376 connection->error = MPD_ERROR_SYSTEM;
377 return -1;
380 mpd_setConnectionTimeout(connection, timeout);
382 flags = fcntl(connection->sock, F_GETFL, 0);
383 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
385 error = connect(connection->sock, (struct sockaddr*)&saun, sizeof(saun));
386 if (error < 0) {
387 /* try the next address family */
388 close(connection->sock);
389 connection->sock = 0;
391 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
392 "problems connecting to \"%s\": %s",
393 host, strerror(errno));
394 connection->error = MPD_ERROR_CONNPORT;
395 return -1;
398 return 0;
400 #endif /* WIN32 */
402 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
403 int err;
404 char * rt;
405 char * output = NULL;
406 mpd_Connection * connection = malloc(sizeof(mpd_Connection));
407 struct timeval tv;
408 fd_set fds;
409 strcpy(connection->buffer,"");
410 connection->sock = -1;
411 connection->buflen = 0;
412 connection->bufstart = 0;
413 strcpy(connection->errorStr,"");
414 connection->error = 0;
415 connection->doneProcessing = 0;
416 connection->commandList = 0;
417 connection->listOks = 0;
418 connection->doneListOk = 0;
419 connection->returnElement = NULL;
420 connection->request = NULL;
422 if (winsock_dll_error(connection))
423 return connection;
425 #ifndef WIN32
426 if (host[0] == '/')
427 err = mpd_connect_un(connection, host, timeout);
428 else
429 #endif
430 err = mpd_connect(connection, host, port, timeout);
431 if (err < 0)
432 return connection;
434 while(!(rt = strstr(connection->buffer,"\n"))) {
435 tv.tv_sec = connection->timeout.tv_sec;
436 tv.tv_usec = connection->timeout.tv_usec;
437 FD_ZERO(&fds);
438 FD_SET(connection->sock,&fds);
439 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
440 int readed;
441 readed = recv(connection->sock,
442 &(connection->buffer[connection->buflen]),
443 MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
444 if(readed<=0) {
445 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
446 "problems getting a response from"
447 " \"%s\" on port %i : %s",host,
448 port, strerror(errno));
449 connection->error = MPD_ERROR_NORESPONSE;
450 return connection;
452 connection->buflen+=readed;
453 connection->buffer[connection->buflen] = '\0';
455 else if(err<0) {
456 if (SELECT_ERRNO_IGNORE)
457 continue;
458 snprintf(connection->errorStr,
459 MPD_ERRORSTR_MAX_LENGTH,
460 "problems connecting to \"%s\" on port"
461 " %i",host,port);
462 connection->error = MPD_ERROR_CONNPORT;
463 return connection;
465 else {
466 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
467 "timeout in attempting to get a response from"
468 " \"%s\" on port %i",host,port);
469 connection->error = MPD_ERROR_NORESPONSE;
470 return connection;
474 *rt = '\0';
475 output = strdup(connection->buffer);
476 strcpy(connection->buffer,rt+1);
477 connection->buflen = strlen(connection->buffer);
479 if(mpd_parseWelcome(connection,host,port,output) == 0) connection->doneProcessing = 1;
481 free(output);
483 return connection;
486 void mpd_clearError(mpd_Connection * connection) {
487 connection->error = 0;
488 connection->errorStr[0] = '\0';
491 void mpd_closeConnection(mpd_Connection * connection) {
492 closesocket(connection->sock);
493 if(connection->returnElement) free(connection->returnElement);
494 if(connection->request) free(connection->request);
495 free(connection);
496 WSACleanup();
499 static void mpd_executeCommand(mpd_Connection * connection, char * command) {
500 int ret;
501 struct timeval tv;
502 fd_set fds;
503 char * commandPtr = command;
504 int commandLen = strlen(command);
506 if(!connection->doneProcessing && !connection->commandList) {
507 strcpy(connection->errorStr,"not done processing current command");
508 connection->error = 1;
509 return;
512 mpd_clearError(connection);
514 FD_ZERO(&fds);
515 FD_SET(connection->sock,&fds);
516 tv.tv_sec = connection->timeout.tv_sec;
517 tv.tv_usec = connection->timeout.tv_usec;
518 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
519 (ret==-1 && SELECT_ERRNO_IGNORE)) {
520 fflush(NULL);
521 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
522 if(ret<=0)
524 if (SENDRECV_ERRNO_IGNORE) continue;
525 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
526 "problems giving command \"%s\"",command);
527 connection->error = MPD_ERROR_SENDING;
528 return;
530 else {
531 commandPtr+=ret;
532 commandLen-=ret;
535 if(commandLen<=0) break;
537 if(commandLen>0) {
538 perror("");
539 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
540 "timeout sending command \"%s\"",command);
541 connection->error = MPD_ERROR_TIMEOUT;
542 return;
545 if(!connection->commandList) connection->doneProcessing = 0;
546 else if(connection->commandList == COMMAND_LIST_OK) {
547 connection->listOks++;
551 static void mpd_getNextReturnElement(mpd_Connection * connection) {
552 char * output = NULL;
553 char * rt = NULL;
554 char * name = NULL;
555 char * value = NULL;
556 fd_set fds;
557 struct timeval tv;
558 char * tok = NULL;
559 int readed;
560 char * bufferCheck = NULL;
561 int err;
562 int pos;
564 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
565 connection->returnElement = NULL;
567 if(connection->doneProcessing || (connection->listOks &&
568 connection->doneListOk))
570 strcpy(connection->errorStr,"already done processing current command");
571 connection->error = 1;
572 return;
575 bufferCheck = connection->buffer+connection->bufstart;
576 while(connection->bufstart>=connection->buflen ||
577 !(rt = strchr(bufferCheck,'\n'))) {
578 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
579 memmove(connection->buffer,
580 connection->buffer+
581 connection->bufstart,
582 connection->buflen-
583 connection->bufstart+1);
584 connection->buflen-=connection->bufstart;
585 connection->bufstart = 0;
587 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
588 strcpy(connection->errorStr,"buffer overrun");
589 connection->error = MPD_ERROR_BUFFEROVERRUN;
590 connection->doneProcessing = 1;
591 connection->doneListOk = 0;
592 return;
594 bufferCheck = connection->buffer+connection->buflen;
595 tv.tv_sec = connection->timeout.tv_sec;
596 tv.tv_usec = connection->timeout.tv_usec;
597 FD_ZERO(&fds);
598 FD_SET(connection->sock,&fds);
599 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
600 readed = recv(connection->sock,
601 connection->buffer+connection->buflen,
602 MPD_BUFFER_MAX_LENGTH-connection->buflen,
603 MSG_DONTWAIT);
604 if(readed<0 && SENDRECV_ERRNO_IGNORE) {
605 continue;
607 if(readed<=0) {
608 strcpy(connection->errorStr,"connection"
609 " closed");
610 connection->error = MPD_ERROR_CONNCLOSED;
611 connection->doneProcessing = 1;
612 connection->doneListOk = 0;
613 return;
615 connection->buflen+=readed;
616 connection->buffer[connection->buflen] = '\0';
618 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
619 else {
620 strcpy(connection->errorStr,"connection timeout");
621 connection->error = MPD_ERROR_TIMEOUT;
622 connection->doneProcessing = 1;
623 connection->doneListOk = 0;
624 return;
628 *rt = '\0';
629 output = connection->buffer+connection->bufstart;
630 connection->bufstart = rt - connection->buffer + 1;
632 if(strcmp(output,"OK")==0) {
633 if(connection->listOks > 0) {
634 strcpy(connection->errorStr, "expected more list_OK's");
635 connection->error = 1;
637 connection->listOks = 0;
638 connection->doneProcessing = 1;
639 connection->doneListOk = 0;
640 return;
643 if(strcmp(output, "list_OK") == 0) {
644 if(!connection->listOks) {
645 strcpy(connection->errorStr,
646 "got an unexpected list_OK");
647 connection->error = 1;
649 else {
650 connection->doneListOk = 1;
651 connection->listOks--;
653 return;
656 if(strncmp(output,"ACK",strlen("ACK"))==0) {
657 char * test;
658 char * needle;
659 int val;
661 strcpy(connection->errorStr, output);
662 connection->error = MPD_ERROR_ACK;
663 connection->errorCode = MPD_ACK_ERROR_UNK;
664 connection->errorAt = MPD_ERROR_AT_UNK;
665 connection->doneProcessing = 1;
666 connection->doneListOk = 0;
668 needle = strchr(output, '[');
669 if(!needle) return;
670 val = strtol(needle+1, &test, 10);
671 if(*test != '@') return;
672 connection->errorCode = val;
673 val = strtol(test+1, &test, 10);
674 if(*test != ']') return;
675 connection->errorAt = val;
676 return;
679 tok = strchr(output, ':');
680 if (!tok) return;
681 pos = tok - output;
682 value = ++tok;
683 name = output;
684 name[pos] = '\0';
686 if(value[0]==' ') {
687 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
689 else {
690 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
691 "error parsing: %s:%s",name,value);
692 connection->error = 1;
696 void mpd_finishCommand(mpd_Connection * connection) {
697 while(!connection->doneProcessing) {
698 if(connection->doneListOk) connection->doneListOk = 0;
699 mpd_getNextReturnElement(connection);
703 static void mpd_finishListOkCommand(mpd_Connection * connection) {
704 while(!connection->doneProcessing && connection->listOks &&
705 !connection->doneListOk)
707 mpd_getNextReturnElement(connection);
711 int mpd_nextListOkCommand(mpd_Connection * connection) {
712 mpd_finishListOkCommand(connection);
713 if(!connection->doneProcessing) connection->doneListOk = 0;
714 if(connection->listOks == 0 || connection->doneProcessing) return -1;
715 return 0;
718 void mpd_sendStatusCommand(mpd_Connection * connection) {
719 mpd_executeCommand(connection,"status\n");
722 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
723 mpd_Status * status;
725 /*mpd_executeCommand(connection,"status\n");
727 if(connection->error) return NULL;*/
729 if(connection->doneProcessing || (connection->listOks &&
730 connection->doneListOk))
732 return NULL;
735 if(!connection->returnElement) mpd_getNextReturnElement(connection);
737 status = malloc(sizeof(mpd_Status));
738 status->volume = -1;
739 status->repeat = 0;
740 status->random = 0;
741 status->playlist = -1;
742 status->storedplaylist = -1;
743 status->playlistLength = -1;
744 status->state = -1;
745 status->song = 0;
746 status->songid = 0;
747 status->elapsedTime = 0;
748 status->totalTime = 0;
749 status->bitRate = 0;
750 status->sampleRate = 0;
751 status->bits = 0;
752 status->channels = 0;
753 status->crossfade = -1;
754 status->error = NULL;
755 status->updatingDb = 0;
757 if(connection->error) {
758 free(status);
759 return NULL;
761 while(connection->returnElement) {
762 mpd_ReturnElement * re = connection->returnElement;
763 if(strcmp(re->name,"volume")==0) {
764 status->volume = atoi(re->value);
766 else if(strcmp(re->name,"repeat")==0) {
767 status->repeat = atoi(re->value);
769 else if(strcmp(re->name,"random")==0) {
770 status->random = atoi(re->value);
772 else if(strcmp(re->name,"playlist")==0) {
773 status->playlist = strtol(re->value,NULL,10);
775 else if(strcmp(re->name,"playlistlength")==0) {
776 status->playlistLength = atoi(re->value);
778 else if(strcmp(re->name,"bitrate")==0) {
779 status->bitRate = atoi(re->value);
781 else if(strcmp(re->name,"state")==0) {
782 if(strcmp(re->value,"play")==0) {
783 status->state = MPD_STATUS_STATE_PLAY;
785 else if(strcmp(re->value,"stop")==0) {
786 status->state = MPD_STATUS_STATE_STOP;
788 else if(strcmp(re->value,"pause")==0) {
789 status->state = MPD_STATUS_STATE_PAUSE;
791 else {
792 status->state = MPD_STATUS_STATE_UNKNOWN;
795 else if(strcmp(re->name,"song")==0) {
796 status->song = atoi(re->value);
798 else if(strcmp(re->name,"songid")==0) {
799 status->songid = atoi(re->value);
801 else if(strcmp(re->name,"time")==0) {
802 char * tok = strchr(re->value,':');
803 /* the second strchr below is a safety check */
804 if (tok && (strchr(tok,0) > (tok+1))) {
805 /* atoi stops at the first non-[0-9] char: */
806 status->elapsedTime = atoi(re->value);
807 status->totalTime = atoi(tok+1);
810 else if(strcmp(re->name,"error")==0) {
811 status->error = strdup(re->value);
813 else if(strcmp(re->name,"xfade")==0) {
814 status->crossfade = atoi(re->value);
816 else if(strcmp(re->name,"updating_db")==0) {
817 status->updatingDb = atoi(re->value);
819 else if(strcmp(re->name,"audio")==0) {
820 char * tok = strchr(re->value,':');
821 if (tok && (strchr(tok,0) > (tok+1))) {
822 status->sampleRate = atoi(re->value);
823 status->bits = atoi(++tok);
824 tok = strchr(tok,':');
825 if (tok && (strchr(tok,0) > (tok+1)))
826 status->channels = atoi(tok+1);
830 mpd_getNextReturnElement(connection);
831 if(connection->error) {
832 free(status);
833 return NULL;
837 if(connection->error) {
838 free(status);
839 return NULL;
841 else if(status->state<0) {
842 strcpy(connection->errorStr,"state not found");
843 connection->error = 1;
844 free(status);
845 return NULL;
848 return status;
851 void mpd_freeStatus(mpd_Status * status) {
852 if(status->error) free(status->error);
853 free(status);
856 void mpd_sendStatsCommand(mpd_Connection * connection) {
857 mpd_executeCommand(connection,"stats\n");
860 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
861 mpd_Stats * stats;
863 /*mpd_executeCommand(connection,"stats\n");
865 if(connection->error) return NULL;*/
867 if(connection->doneProcessing || (connection->listOks &&
868 connection->doneListOk))
870 return NULL;
873 if(!connection->returnElement) mpd_getNextReturnElement(connection);
875 stats = malloc(sizeof(mpd_Stats));
876 stats->numberOfArtists = 0;
877 stats->numberOfAlbums = 0;
878 stats->numberOfSongs = 0;
879 stats->uptime = 0;
880 stats->dbUpdateTime = 0;
881 stats->playTime = 0;
882 stats->dbPlayTime = 0;
884 if(connection->error) {
885 free(stats);
886 return NULL;
888 while(connection->returnElement) {
889 mpd_ReturnElement * re = connection->returnElement;
890 if(strcmp(re->name,"artists")==0) {
891 stats->numberOfArtists = atoi(re->value);
893 else if(strcmp(re->name,"albums")==0) {
894 stats->numberOfAlbums = atoi(re->value);
896 else if(strcmp(re->name,"songs")==0) {
897 stats->numberOfSongs = atoi(re->value);
899 else if(strcmp(re->name,"uptime")==0) {
900 stats->uptime = strtol(re->value,NULL,10);
902 else if(strcmp(re->name,"db_update")==0) {
903 stats->dbUpdateTime = strtol(re->value,NULL,10);
905 else if(strcmp(re->name,"playtime")==0) {
906 stats->playTime = strtol(re->value,NULL,10);
908 else if(strcmp(re->name,"db_playtime")==0) {
909 stats->dbPlayTime = strtol(re->value,NULL,10);
912 mpd_getNextReturnElement(connection);
913 if(connection->error) {
914 free(stats);
915 return NULL;
919 if(connection->error) {
920 free(stats);
921 return NULL;
924 return stats;
927 void mpd_freeStats(mpd_Stats * stats) {
928 free(stats);
931 mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection)
933 mpd_SearchStats * stats;
934 mpd_ReturnElement * re;
936 if (connection->doneProcessing ||
937 (connection->listOks && connection->doneListOk)) {
938 return NULL;
941 if (!connection->returnElement) mpd_getNextReturnElement(connection);
943 if (connection->error)
944 return NULL;
946 stats = malloc(sizeof(mpd_SearchStats));
947 stats->numberOfSongs = 0;
948 stats->playTime = 0;
950 while (connection->returnElement) {
951 re = connection->returnElement;
953 if (strcmp(re->name, "songs") == 0) {
954 stats->numberOfSongs = atoi(re->value);
955 } else if (strcmp(re->name, "playtime") == 0) {
956 stats->playTime = strtol(re->value, NULL, 10);
959 mpd_getNextReturnElement(connection);
960 if (connection->error) {
961 free(stats);
962 return NULL;
966 if (connection->error) {
967 free(stats);
968 return NULL;
971 return stats;
974 void mpd_freeSearchStats(mpd_SearchStats * stats)
976 free(stats);
979 static void mpd_initSong(mpd_Song * song) {
980 song->file = NULL;
981 song->artist = NULL;
982 song->album = NULL;
983 song->track = NULL;
984 song->title = NULL;
985 song->name = NULL;
986 song->date = NULL;
987 /* added by Qball */
988 song->genre = NULL;
989 song->composer = NULL;
990 song->performer = NULL;
991 song->disc = NULL;
992 song->comment = NULL;
994 song->time = MPD_SONG_NO_TIME;
995 song->pos = MPD_SONG_NO_NUM;
996 song->id = MPD_SONG_NO_ID;
999 static void mpd_finishSong(mpd_Song * song) {
1000 if(song->file) free(song->file);
1001 if(song->artist) free(song->artist);
1002 if(song->album) free(song->album);
1003 if(song->title) free(song->title);
1004 if(song->track) free(song->track);
1005 if(song->name) free(song->name);
1006 if(song->date) free(song->date);
1007 if(song->genre) free(song->genre);
1008 if(song->composer) free(song->composer);
1009 if(song->performer) free(song->performer);
1010 if(song->disc) free(song->disc);
1011 if(song->comment) free(song->comment);
1014 mpd_Song * mpd_newSong(void) {
1015 mpd_Song * ret = malloc(sizeof(mpd_Song));
1017 mpd_initSong(ret);
1019 return ret;
1022 void mpd_freeSong(mpd_Song * song) {
1023 mpd_finishSong(song);
1024 free(song);
1027 mpd_Song * mpd_songDup(mpd_Song * song) {
1028 mpd_Song * ret = mpd_newSong();
1030 if(song->file) ret->file = strdup(song->file);
1031 if(song->artist) ret->artist = strdup(song->artist);
1032 if(song->album) ret->album = strdup(song->album);
1033 if(song->title) ret->title = strdup(song->title);
1034 if(song->track) ret->track = strdup(song->track);
1035 if(song->name) ret->name = strdup(song->name);
1036 if(song->date) ret->date = strdup(song->date);
1037 if(song->genre) ret->genre= strdup(song->genre);
1038 if(song->composer) ret->composer= strdup(song->composer);
1039 if(song->performer) ret->performer = strdup(song->performer);
1040 if(song->disc) ret->disc = strdup(song->disc);
1041 if(song->comment) ret->comment = strdup(song->comment);
1042 ret->time = song->time;
1043 ret->pos = song->pos;
1044 ret->id = song->id;
1046 return ret;
1049 static void mpd_initDirectory(mpd_Directory * directory) {
1050 directory->path = NULL;
1053 static void mpd_finishDirectory(mpd_Directory * directory) {
1054 if(directory->path) free(directory->path);
1057 mpd_Directory * mpd_newDirectory(void) {
1058 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
1060 mpd_initDirectory(directory);
1062 return directory;
1065 void mpd_freeDirectory(mpd_Directory * directory) {
1066 mpd_finishDirectory(directory);
1068 free(directory);
1071 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
1072 mpd_Directory * ret = mpd_newDirectory();
1074 if(directory->path) ret->path = strdup(directory->path);
1076 return ret;
1079 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
1080 playlist->path = NULL;
1081 playlist->mtime = NULL;
1084 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
1085 if(playlist->path) free(playlist->path);
1086 if(playlist->mtime) free(playlist->mtime);
1089 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
1090 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
1092 mpd_initPlaylistFile(playlist);
1094 return playlist;
1097 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
1098 mpd_finishPlaylistFile(playlist);
1099 free(playlist);
1102 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1103 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1105 if(playlist->path) ret->path = strdup(playlist->path);
1106 if(playlist->mtime) ret->mtime = strdup(playlist->mtime);
1108 return ret;
1111 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
1112 entity->info.directory = NULL;
1115 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1116 if(entity->info.directory) {
1117 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1118 mpd_freeDirectory(entity->info.directory);
1120 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1121 mpd_freeSong(entity->info.song);
1123 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1124 mpd_freePlaylistFile(entity->info.playlistFile);
1129 mpd_InfoEntity * mpd_newInfoEntity(void) {
1130 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
1132 mpd_initInfoEntity(entity);
1134 return entity;
1137 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1138 mpd_finishInfoEntity(entity);
1139 free(entity);
1142 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1143 mpd_executeCommand(connection,command);
1146 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1147 mpd_InfoEntity * entity = NULL;
1149 if(connection->doneProcessing || (connection->listOks &&
1150 connection->doneListOk))
1152 return NULL;
1155 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1157 if(connection->returnElement) {
1158 if(strcmp(connection->returnElement->name,"file")==0) {
1159 entity = mpd_newInfoEntity();
1160 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1161 entity->info.song = mpd_newSong();
1162 entity->info.song->file =
1163 strdup(connection->returnElement->value);
1165 else if(strcmp(connection->returnElement->name,
1166 "directory")==0) {
1167 entity = mpd_newInfoEntity();
1168 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1169 entity->info.directory = mpd_newDirectory();
1170 entity->info.directory->path =
1171 strdup(connection->returnElement->value);
1173 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1174 entity = mpd_newInfoEntity();
1175 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1176 entity->info.playlistFile = mpd_newPlaylistFile();
1177 entity->info.playlistFile->path =
1178 strdup(connection->returnElement->value);
1180 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1181 entity = mpd_newInfoEntity();
1182 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1183 entity->info.song = mpd_newSong();
1184 entity->info.song->pos = atoi(connection->returnElement->value);
1186 else {
1187 connection->error = 1;
1188 strcpy(connection->errorStr,"problem parsing song info");
1189 return NULL;
1192 else return NULL;
1194 mpd_getNextReturnElement(connection);
1195 while(connection->returnElement) {
1196 mpd_ReturnElement * re = connection->returnElement;
1198 if(strcmp(re->name,"file")==0) return entity;
1199 else if(strcmp(re->name,"directory")==0) return entity;
1200 else if(strcmp(re->name,"playlist")==0) return entity;
1201 else if(strcmp(re->name,"cpos")==0) return entity;
1203 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1204 strlen(re->value)) {
1205 if(strcmp(re->name,"Artist")==0) {
1206 if(entity->info.song->artist) {
1207 int length = strlen(entity->info.song->artist);
1208 entity->info.song->artist = realloc(entity->info.song->artist,
1209 length + strlen(re->value) + 3);
1210 strcpy(&((entity->info.song->artist)[length]), ", ");
1211 strcpy(&((entity->info.song->artist)[length + 2]), re->value);
1213 else {
1214 entity->info.song->artist = strdup(re->value);
1217 else if(!entity->info.song->album &&
1218 strcmp(re->name,"Album")==0) {
1219 entity->info.song->album = strdup(re->value);
1221 else if(!entity->info.song->title &&
1222 strcmp(re->name,"Title")==0) {
1223 entity->info.song->title = strdup(re->value);
1225 else if(!entity->info.song->track &&
1226 strcmp(re->name,"Track")==0) {
1227 entity->info.song->track = strdup(re->value);
1229 else if(!entity->info.song->name &&
1230 strcmp(re->name,"Name")==0) {
1231 entity->info.song->name = strdup(re->value);
1233 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1234 strcmp(re->name,"Time")==0) {
1235 entity->info.song->time = atoi(re->value);
1237 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1238 strcmp(re->name,"Pos")==0) {
1239 entity->info.song->pos = atoi(re->value);
1241 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1242 strcmp(re->name,"Id")==0) {
1243 entity->info.song->id = atoi(re->value);
1245 else if(!entity->info.song->date &&
1246 strcmp(re->name, "Date") == 0) {
1247 entity->info.song->date = strdup(re->value);
1249 else if(!entity->info.song->genre &&
1250 strcmp(re->name, "Genre") == 0) {
1251 if(entity->info.song->genre) {
1252 int length = strlen(entity->info.song->genre);
1253 entity->info.song->genre = realloc(entity->info.song->genre,
1254 length + strlen(re->value) + 4);
1255 strcpy(&((entity->info.song->genre)[length]), ", ");
1256 strcpy(&((entity->info.song->genre)[length + 3]), re->value);
1258 else {
1259 entity->info.song->genre = strdup(re->value);
1262 else if(strcmp(re->name, "Composer") == 0) {
1263 if(entity->info.song->composer) {
1264 int length = strlen(entity->info.song->composer);
1265 entity->info.song->composer = realloc(entity->info.song->composer,
1266 length + strlen(re->value) + 3);
1267 strcpy(&((entity->info.song->composer)[length]), ", ");
1268 strcpy(&((entity->info.song->composer)[length + 2]), re->value);
1270 else {
1271 entity->info.song->composer = strdup(re->value);
1274 else if(strcmp(re->name, "Performer") == 0) {
1275 if(entity->info.song->performer) {
1276 int length = strlen(entity->info.song->performer);
1277 entity->info.song->performer = realloc(entity->info.song->performer,
1278 length + strlen(re->value) + 3);
1279 strcpy(&((entity->info.song->performer)[length]), ", ");
1280 strcpy(&((entity->info.song->performer)[length + 2]), re->value);
1282 else {
1283 entity->info.song->performer = strdup(re->value);
1286 else if(!entity->info.song->disc &&
1287 strcmp(re->name, "Disc") == 0) {
1288 entity->info.song->disc = strdup(re->value);
1290 else if(!entity->info.song->comment &&
1291 strcmp(re->name, "Comment") == 0) {
1292 entity->info.song->comment = strdup(re->value);
1295 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1297 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1298 if(!entity->info.playlistFile->mtime &&
1299 strcmp(re->name, "Last-Modified") == 0) {
1300 entity->info.playlistFile->mtime = strdup(re->value);
1304 mpd_getNextReturnElement(connection);
1307 return entity;
1310 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1311 const char * name)
1313 if(connection->doneProcessing || (connection->listOks &&
1314 connection->doneListOk))
1316 return NULL;
1319 mpd_getNextReturnElement(connection);
1320 while(connection->returnElement) {
1321 mpd_ReturnElement * re = connection->returnElement;
1323 if(strcmp(re->name,name)==0) return strdup(re->value);
1324 mpd_getNextReturnElement(connection);
1327 return NULL;
1330 char *mpd_getNextTag(mpd_Connection *connection, int type)
1332 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES ||
1333 type == MPD_TAG_ITEM_ANY)
1334 return NULL;
1335 if (type == MPD_TAG_ITEM_FILENAME)
1336 return mpd_getNextReturnElementNamed(connection, "file");
1337 return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
1340 char * mpd_getNextArtist(mpd_Connection * connection) {
1341 return mpd_getNextReturnElementNamed(connection,"Artist");
1344 char * mpd_getNextAlbum(mpd_Connection * connection) {
1345 return mpd_getNextReturnElementNamed(connection,"Album");
1348 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1349 int len = strlen("playlistinfo")+2+INTLEN+3;
1350 char *string = malloc(len);
1351 snprintf(string, len, "playlistinfo \"%i\"\n", songPos);
1352 mpd_sendInfoCommand(connection,string);
1353 free(string);
1356 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1357 int len = strlen("playlistid")+2+INTLEN+3;
1358 char *string = malloc(len);
1359 snprintf(string, len, "playlistid \"%i\"\n", id);
1360 mpd_sendInfoCommand(connection, string);
1361 free(string);
1364 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1365 int len = strlen("plchanges")+2+LONGLONGLEN+3;
1366 char *string = malloc(len);
1367 snprintf(string, len, "plchanges \"%lld\"\n", playlist);
1368 mpd_sendInfoCommand(connection,string);
1369 free(string);
1372 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1373 int len = strlen("plchangesposid")+2+LONGLONGLEN+3;
1374 char *string = malloc(len);
1375 snprintf(string, len, "plchangesposid \"%lld\"\n", playlist);
1376 mpd_sendInfoCommand(connection,string);
1377 free(string);
1380 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1381 char * sDir = mpd_sanitizeArg(dir);
1382 int len = strlen("listall")+2+strlen(sDir)+3;
1383 char *string = malloc(len);
1384 snprintf(string, len, "listall \"%s\"\n", sDir);
1385 mpd_sendInfoCommand(connection,string);
1386 free(string);
1387 free(sDir);
1390 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1391 char * sDir = mpd_sanitizeArg(dir);
1392 int len = strlen("listallinfo")+2+strlen(sDir)+3;
1393 char *string = malloc(len);
1394 snprintf(string, len, "listallinfo \"%s\"\n", sDir);
1395 mpd_sendInfoCommand(connection,string);
1396 free(string);
1397 free(sDir);
1400 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1401 char * sDir = mpd_sanitizeArg(dir);
1402 int len = strlen("lsinfo")+2+strlen(sDir)+3;
1403 char *string = malloc(len);
1404 snprintf(string, len, "lsinfo \"%s\"\n", sDir);
1405 mpd_sendInfoCommand(connection,string);
1406 free(string);
1407 free(sDir);
1410 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1411 mpd_executeCommand(connection,"currentsong\n");
1414 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1415 const char * str)
1417 mpd_startSearch(connection, 0);
1418 mpd_addConstraintSearch(connection, table, str);
1419 mpd_commitSearch(connection);
1422 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1423 const char * str)
1425 mpd_startSearch(connection, 1);
1426 mpd_addConstraintSearch(connection, table, str);
1427 mpd_commitSearch(connection);
1430 void mpd_sendListCommand(mpd_Connection * connection, int table,
1431 const char * arg1)
1433 char st[10];
1434 int len;
1435 char *string;
1436 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1437 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1438 else {
1439 connection->error = 1;
1440 strcpy(connection->errorStr,"unknown table for list");
1441 return;
1443 if(arg1) {
1444 char * sanitArg1 = mpd_sanitizeArg(arg1);
1445 len = strlen("list")+1+strlen(sanitArg1)+2+strlen(st)+3;
1446 string = malloc(len);
1447 snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1);
1448 free(sanitArg1);
1450 else {
1451 len = strlen("list")+1+strlen(st)+2;
1452 string = malloc(len);
1453 snprintf(string, len, "list %s\n", st);
1455 mpd_sendInfoCommand(connection,string);
1456 free(string);
1459 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1460 char * sFile = mpd_sanitizeArg(file);
1461 int len = strlen("add")+2+strlen(sFile)+3;
1462 char *string = malloc(len);
1463 snprintf(string, len, "add \"%s\"\n", sFile);
1464 mpd_executeCommand(connection,string);
1465 free(string);
1466 free(sFile);
1469 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1471 int retval = -1;
1472 char *sFile = mpd_sanitizeArg(file);
1473 int len = strlen("addid")+2+strlen(sFile)+3;
1474 char *string = malloc(len);
1476 snprintf(string, len, "addid \"%s\"\n", sFile);
1477 mpd_sendInfoCommand(connection, string);
1478 free(string);
1479 free(sFile);
1481 string = mpd_getNextReturnElementNamed(connection, "Id");
1482 if (string) {
1483 retval = atoi(string);
1484 free(string);
1487 return retval;
1490 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1491 int len = strlen("delete")+2+INTLEN+3;
1492 char *string = malloc(len);
1493 snprintf(string, len, "delete \"%i\"\n", songPos);
1494 mpd_sendInfoCommand(connection,string);
1495 free(string);
1498 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1499 int len = strlen("deleteid")+2+INTLEN+3;
1500 char *string = malloc(len);
1501 snprintf(string, len, "deleteid \"%i\"\n", id);
1502 mpd_sendInfoCommand(connection,string);
1503 free(string);
1506 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1507 char * sName = mpd_sanitizeArg(name);
1508 int len = strlen("save")+2+strlen(sName)+3;
1509 char *string = malloc(len);
1510 snprintf(string, len, "save \"%s\"\n", sName);
1511 mpd_executeCommand(connection,string);
1512 free(string);
1513 free(sName);
1516 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1517 char * sName = mpd_sanitizeArg(name);
1518 int len = strlen("load")+2+strlen(sName)+3;
1519 char *string = malloc(len);
1520 snprintf(string, len, "load \"%s\"\n", sName);
1521 mpd_executeCommand(connection,string);
1522 free(string);
1523 free(sName);
1526 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1527 char * sName = mpd_sanitizeArg(name);
1528 int len = strlen("rm")+2+strlen(sName)+3;
1529 char *string = malloc(len);
1530 snprintf(string, len, "rm \"%s\"\n", sName);
1531 mpd_executeCommand(connection,string);
1532 free(string);
1533 free(sName);
1536 void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
1537 const char *to)
1539 char *sFrom = mpd_sanitizeArg(from);
1540 char *sTo = mpd_sanitizeArg(to);
1541 int len = strlen("rename")+2+strlen(sFrom)+3+strlen(sTo)+3;
1542 char *string = malloc(len);
1543 snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo);
1544 mpd_executeCommand(connection, string);
1545 free(string);
1546 free(sFrom);
1547 free(sTo);
1550 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1551 mpd_executeCommand(connection,"shuffle\n");
1554 void mpd_sendClearCommand(mpd_Connection * connection) {
1555 mpd_executeCommand(connection,"clear\n");
1558 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1559 int len = strlen("play")+2+INTLEN+3;
1560 char *string = malloc(len);
1561 snprintf(string, len, "play \"%i\"\n", songPos);
1562 mpd_sendInfoCommand(connection,string);
1563 free(string);
1566 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1567 int len = strlen("playid")+2+INTLEN+3;
1568 char *string = malloc(len);
1569 snprintf(string, len, "playid \"%i\"\n", id);
1570 mpd_sendInfoCommand(connection,string);
1571 free(string);
1574 void mpd_sendStopCommand(mpd_Connection * connection) {
1575 mpd_executeCommand(connection,"stop\n");
1578 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1579 int len = strlen("pause")+2+INTLEN+3;
1580 char *string = malloc(len);
1581 snprintf(string, len, "pause \"%i\"\n", pauseMode);
1582 mpd_executeCommand(connection,string);
1583 free(string);
1586 void mpd_sendNextCommand(mpd_Connection * connection) {
1587 mpd_executeCommand(connection,"next\n");
1590 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1591 int len = strlen("move")+2+INTLEN+3+INTLEN+3;
1592 char *string = malloc(len);
1593 snprintf(string, len, "move \"%i\" \"%i\"\n", from, to);
1594 mpd_sendInfoCommand(connection,string);
1595 free(string);
1598 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1599 int len = strlen("moveid")+2+INTLEN+3+INTLEN+3;
1600 char *string = malloc(len);
1601 snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to);
1602 mpd_sendInfoCommand(connection,string);
1603 free(string);
1606 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1607 int len = strlen("swap")+2+INTLEN+3+INTLEN+3;
1608 char *string = malloc(len);
1609 snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2);
1610 mpd_sendInfoCommand(connection,string);
1611 free(string);
1614 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1615 int len = strlen("swapid")+2+INTLEN+3+INTLEN+3;
1616 char *string = malloc(len);
1617 snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2);
1618 mpd_sendInfoCommand(connection,string);
1619 free(string);
1622 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1623 int len = strlen("seek")+2+INTLEN+3+INTLEN+3;
1624 char *string = malloc(len);
1625 snprintf(string, len, "seek \"%i\" \"%i\"\n", song, time);
1626 mpd_sendInfoCommand(connection,string);
1627 free(string);
1630 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1631 int len = strlen("seekid")+2+INTLEN+3+INTLEN+3;
1632 char *string = malloc(len);
1633 snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, time);
1634 mpd_sendInfoCommand(connection,string);
1635 free(string);
1638 void mpd_sendUpdateCommand(mpd_Connection * connection, char * path) {
1639 char * sPath = mpd_sanitizeArg(path);
1640 int len = strlen("update")+2+strlen(sPath)+3;
1641 char *string = malloc(len);
1642 snprintf(string, len, "update \"%s\"\n", sPath);
1643 mpd_sendInfoCommand(connection,string);
1644 free(string);
1645 free(sPath);
1648 int mpd_getUpdateId(mpd_Connection * connection) {
1649 char * jobid;
1650 int ret = 0;
1652 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1653 if(jobid) {
1654 ret = atoi(jobid);
1655 free(jobid);
1658 return ret;
1661 void mpd_sendPrevCommand(mpd_Connection * connection) {
1662 mpd_executeCommand(connection,"previous\n");
1665 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1666 int len = strlen("repeat")+2+INTLEN+3;
1667 char *string = malloc(len);
1668 snprintf(string, len, "repeat \"%i\"\n", repeatMode);
1669 mpd_executeCommand(connection,string);
1670 free(string);
1673 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1674 int len = strlen("random")+2+INTLEN+3;
1675 char *string = malloc(len);
1676 snprintf(string, len, "random \"%i\"\n", randomMode);
1677 mpd_executeCommand(connection,string);
1678 free(string);
1681 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1682 int len = strlen("setvol")+2+INTLEN+3;
1683 char *string = malloc(len);
1684 snprintf(string, len, "setvol \"%i\"\n", volumeChange);
1685 mpd_executeCommand(connection,string);
1686 free(string);
1689 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1690 int len = strlen("volume")+2+INTLEN+3;
1691 char *string = malloc(len);
1692 snprintf(string, len, "volume \"%i\"\n", volumeChange);
1693 mpd_executeCommand(connection,string);
1694 free(string);
1697 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1698 int len = strlen("crossfade")+2+INTLEN+3;
1699 char *string = malloc(len);
1700 snprintf(string, len, "crossfade \"%i\"\n", seconds);
1701 mpd_executeCommand(connection,string);
1702 free(string);
1705 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1706 char * sPass = mpd_sanitizeArg(pass);
1707 int len = strlen("password")+2+strlen(sPass)+3;
1708 char *string = malloc(len);
1709 snprintf(string, len, "password \"%s\"\n", sPass);
1710 mpd_executeCommand(connection,string);
1711 free(string);
1712 free(sPass);
1715 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1716 if(connection->commandList) {
1717 strcpy(connection->errorStr,"already in command list mode");
1718 connection->error = 1;
1719 return;
1721 connection->commandList = COMMAND_LIST;
1722 mpd_executeCommand(connection,"command_list_begin\n");
1725 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1726 if(connection->commandList) {
1727 strcpy(connection->errorStr,"already in command list mode");
1728 connection->error = 1;
1729 return;
1731 connection->commandList = COMMAND_LIST_OK;
1732 mpd_executeCommand(connection,"command_list_ok_begin\n");
1733 connection->listOks = 0;
1736 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1737 if(!connection->commandList) {
1738 strcpy(connection->errorStr,"not in command list mode");
1739 connection->error = 1;
1740 return;
1742 connection->commandList = 0;
1743 mpd_executeCommand(connection,"command_list_end\n");
1746 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1747 mpd_executeCommand(connection,"outputs\n");
1750 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1751 mpd_OutputEntity * output = NULL;
1753 if(connection->doneProcessing || (connection->listOks &&
1754 connection->doneListOk))
1756 return NULL;
1759 if(connection->error) return NULL;
1761 output = malloc(sizeof(mpd_OutputEntity));
1762 output->id = -10;
1763 output->name = NULL;
1764 output->enabled = 0;
1766 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1768 while(connection->returnElement) {
1769 mpd_ReturnElement * re = connection->returnElement;
1770 if(strcmp(re->name,"outputid")==0) {
1771 if(output!=NULL && output->id>=0) return output;
1772 output->id = atoi(re->value);
1774 else if(strcmp(re->name,"outputname")==0) {
1775 output->name = strdup(re->value);
1777 else if(strcmp(re->name,"outputenabled")==0) {
1778 output->enabled = atoi(re->value);
1781 mpd_getNextReturnElement(connection);
1782 if(connection->error) {
1783 free(output);
1784 return NULL;
1789 return output;
1792 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1793 int len = strlen("enableoutput")+2+INTLEN+3;
1794 char *string = malloc(len);
1795 snprintf(string, len, "enableoutput \"%i\"\n", outputId);
1796 mpd_executeCommand(connection,string);
1797 free(string);
1800 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1801 int len = strlen("disableoutput")+2+INTLEN+3;
1802 char *string = malloc(len);
1803 snprintf(string, len, "disableoutput \"%i\"\n", outputId);
1804 mpd_executeCommand(connection,string);
1805 free(string);
1808 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1809 free(output->name);
1810 free(output);
1814 * mpd_sendNotCommandsCommand
1815 * odd naming, but it gets the not allowed commands
1818 void mpd_sendNotCommandsCommand(mpd_Connection * connection)
1820 mpd_executeCommand(connection, "notcommands\n");
1824 * mpd_sendCommandsCommand
1825 * odd naming, but it gets the allowed commands
1827 void mpd_sendCommandsCommand(mpd_Connection * connection)
1829 mpd_executeCommand(connection, "commands\n");
1833 * Get the next returned command
1835 char * mpd_getNextCommand(mpd_Connection * connection)
1837 return mpd_getNextReturnElementNamed(connection, "command");
1840 void mpd_sendUrlHandlersCommand(mpd_Connection * connection)
1842 mpd_executeCommand(connection, "urlhandlers\n");
1845 char * mpd_getNextHandler(mpd_Connection * connection)
1847 return mpd_getNextReturnElementNamed(connection, "handler");
1850 void mpd_sendTagTypesCommand(mpd_Connection * connection)
1852 mpd_executeCommand(connection, "tagtypes\n");
1855 char * mpd_getNextTagType(mpd_Connection * connection)
1857 return mpd_getNextReturnElementNamed(connection, "tagtype");
1860 void mpd_startSearch(mpd_Connection *connection, int exact)
1862 if (connection->request) {
1863 strcpy(connection->errorStr, "search already in progress");
1864 connection->error = 1;
1865 return;
1868 if (exact) connection->request = strdup("find");
1869 else connection->request = strdup("search");
1872 void mpd_startStatsSearch(mpd_Connection *connection)
1874 if (connection->request) {
1875 strcpy(connection->errorStr, "search already in progress");
1876 connection->error = 1;
1877 return;
1880 connection->request = strdup("count");
1883 void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
1885 if (connection->request) {
1886 strcpy(connection->errorStr, "search already in progress");
1887 connection->error = 1;
1888 return;
1891 if (exact) connection->request = strdup("playlistfind");
1892 else connection->request = strdup("playlistsearch");
1895 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1897 char *strtype;
1898 int len;
1900 if (connection->request) {
1901 strcpy(connection->errorStr, "search already in progress");
1902 connection->error = 1;
1903 return;
1906 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1907 strcpy(connection->errorStr, "invalid type specified");
1908 connection->error = 1;
1909 return;
1912 strtype = mpdTagItemKeys[type];
1914 len = 5+strlen(strtype)+1;
1915 connection->request = malloc(len);
1917 snprintf(connection->request, len, "list %c%s",
1918 tolower(strtype[0]), strtype+1);
1921 void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name)
1923 char *strtype;
1924 char *arg;
1925 int len;
1926 char *string;
1928 if (!connection->request) {
1929 strcpy(connection->errorStr, "no search in progress");
1930 connection->error = 1;
1931 return;
1934 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1935 strcpy(connection->errorStr, "invalid type specified");
1936 connection->error = 1;
1937 return;
1940 if (name == NULL) {
1941 strcpy(connection->errorStr, "no name specified");
1942 connection->error = 1;
1943 return;
1946 string = strdup(connection->request);
1947 strtype = mpdTagItemKeys[type];
1948 arg = mpd_sanitizeArg(name);
1950 len = strlen(string)+1+strlen(strtype)+2+strlen(arg)+2;
1951 connection->request = realloc(connection->request, len);
1952 snprintf(connection->request, len, "%s %c%s \"%s\"",
1953 string, tolower(strtype[0]), strtype+1, arg);
1955 free(string);
1956 free(arg);
1959 void mpd_commitSearch(mpd_Connection *connection)
1961 int len;
1963 if (!connection->request) {
1964 strcpy(connection->errorStr, "no search in progress");
1965 connection->error = 1;
1966 return;
1969 len = strlen(connection->request)+2;
1970 connection->request = realloc(connection->request, len);
1971 connection->request[len-2] = '\n';
1972 connection->request[len-1] = '\0';
1973 mpd_sendInfoCommand(connection, connection->request);
1975 free(connection->request);
1976 connection->request = NULL;
1980 * @param connection a MpdConnection
1981 * @param path the path to the playlist.
1983 * List the content, with full metadata, of a stored playlist.
1986 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1988 char *arg = mpd_sanitizeArg(path);
1989 int len = strlen("listplaylistinfo")+2+strlen(arg)+3;
1990 char *query = malloc(len);
1991 snprintf(query, len, "listplaylistinfo \"%s\"\n", arg);
1992 mpd_sendInfoCommand(connection, query);
1993 free(arg);
1994 free(query);
1998 * @param connection a MpdConnection
1999 * @param path the path to the playlist.
2001 * List the content of a stored playlist.
2004 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
2006 char *arg = mpd_sanitizeArg(path);
2007 int len = strlen("listplaylist")+2+strlen(arg)+3;
2008 char *query = malloc(len);
2009 snprintf(query, len, "listplaylist \"%s\"\n", arg);
2010 mpd_sendInfoCommand(connection, query);
2011 free(arg);
2012 free(query);
2015 void mpd_sendPlaylistClearCommand(mpd_Connection *connection, char *path)
2017 char *sPath = mpd_sanitizeArg(path);
2018 int len = strlen("playlistclear")+2+strlen(sPath)+3;
2019 char *string = malloc(len);
2020 snprintf(string, len, "playlistclear \"%s\"\n", sPath);
2021 mpd_executeCommand(connection, string);
2022 free(sPath);
2023 free(string);
2026 void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
2027 char *playlist, char *path)
2029 char *sPlaylist = mpd_sanitizeArg(playlist);
2030 char *sPath = mpd_sanitizeArg(path);
2031 int len = strlen("playlistadd")+2+strlen(sPlaylist)+3+strlen(sPath)+3;
2032 char *string = malloc(len);
2033 snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath);
2034 mpd_executeCommand(connection, string);
2035 free(sPlaylist);
2036 free(sPath);
2037 free(string);
2040 void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
2041 char *playlist, int from, int to)
2043 char *sPlaylist = mpd_sanitizeArg(playlist);
2044 int len = strlen("playlistmove")+
2045 2+strlen(sPlaylist)+3+INTLEN+3+INTLEN+3;
2046 char *string = malloc(len);
2047 snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2048 sPlaylist, from, to);
2049 mpd_executeCommand(connection, string);
2050 free(sPlaylist);
2051 free(string);
2054 void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
2055 char *playlist, int pos)
2057 char *sPlaylist = mpd_sanitizeArg(playlist);
2058 int len = strlen("playlistdelete")+2+strlen(sPlaylist)+3+INTLEN+3;
2059 char *string = malloc(len);
2060 snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos);
2061 mpd_executeCommand(connection, string);
2062 free(sPlaylist);
2063 free(string);
2065 void mpd_sendClearErrorCommand(mpd_Connection * connection) {
2066 mpd_executeCommand(connection,"clearerror\n");
2070 void mpd_sendGetEventsCommand(mpd_Connection *connection) {
2071 mpd_executeCommand(connection, "idle\nnoidle\n");
2072 // mpd_executeCommand(connection, "noidle\n");
2075 char * mpd_getNextEvent(mpd_Connection *connection)
2077 return mpd_getNextReturnElementNamed(connection, "changed");
2080 void mpd_sendListPlaylistsCommand(mpd_Connection * connection) {
2081 mpd_sendInfoCommand(connection, "listplaylists\n");