improve logging
[scrobby.git] / src / libmpdclient.c
blob576f3dc72a51b8799ce4b4294be53d7228dcd7c6
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 sun;
363 path_length = strlen(host);
364 if (path_length >= sizeof(sun.sun_path)) {
365 strcpy(connection->errorStr, "unix socket path is too long");
366 connection->error = MPD_ERROR_UNKHOST;
367 return -1;
370 sun.sun_family = AF_UNIX;
371 memcpy(sun.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*)&sun, sizeof(sun));
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;
519 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
520 (ret==-1 && SELECT_ERRNO_IGNORE)) {
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;
538 if(commandLen>0) {
539 perror("");
540 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
541 "timeout sending command \"%s\"",command);
542 connection->error = MPD_ERROR_TIMEOUT;
543 return;
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;
554 char * rt = NULL;
555 char * name = NULL;
556 char * value = NULL;
557 fd_set fds;
558 struct timeval tv;
559 char * tok = NULL;
560 int readed;
561 char * bufferCheck = NULL;
562 int err;
563 int pos;
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;
573 return;
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,
581 connection->buffer+
582 connection->bufstart,
583 connection->buflen-
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;
593 return;
595 bufferCheck = connection->buffer+connection->buflen;
596 tv.tv_sec = connection->timeout.tv_sec;
597 tv.tv_usec = connection->timeout.tv_usec;
598 FD_ZERO(&fds);
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,
604 MSG_DONTWAIT);
605 if(readed<0 && SENDRECV_ERRNO_IGNORE) {
606 continue;
608 if(readed<=0) {
609 strcpy(connection->errorStr,"connection"
610 " closed");
611 connection->error = MPD_ERROR_CONNCLOSED;
612 connection->doneProcessing = 1;
613 connection->doneListOk = 0;
614 return;
616 connection->buflen+=readed;
617 connection->buffer[connection->buflen] = '\0';
619 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
620 else {
621 strcpy(connection->errorStr,"connection timeout");
622 connection->error = MPD_ERROR_TIMEOUT;
623 connection->doneProcessing = 1;
624 connection->doneListOk = 0;
625 return;
629 *rt = '\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;
641 return;
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;
650 else {
651 connection->doneListOk = 1;
652 connection->listOks--;
654 return;
657 if(strncmp(output,"ACK",strlen("ACK"))==0) {
658 char * test;
659 char * needle;
660 int val;
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, '[');
670 if(!needle) return;
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;
677 return;
680 tok = strchr(output, ':');
681 if (!tok) return;
682 pos = tok - output;
683 value = ++tok;
684 name = output;
685 name[pos] = '\0';
687 if(value[0]==' ') {
688 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
690 else {
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;
716 return 0;
719 void mpd_sendStatusCommand(mpd_Connection * connection) {
720 mpd_executeCommand(connection,"status\n");
723 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
724 mpd_Status * status;
726 /*mpd_executeCommand(connection,"status\n");
728 if(connection->error) return NULL;*/
730 if(connection->doneProcessing || (connection->listOks &&
731 connection->doneListOk))
733 return NULL;
736 if(!connection->returnElement) mpd_getNextReturnElement(connection);
738 status = malloc(sizeof(mpd_Status));
739 status->volume = -1;
740 status->repeat = 0;
741 status->random = 0;
742 status->playlist = -1;
743 status->playlistLength = -1;
744 status->state = -1;
745 status->song = 0;
746 status->songid = -1;
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 if (song)
1024 mpd_finishSong(song);
1025 free(song);
1028 mpd_Song * mpd_songDup(mpd_Song * song) {
1029 mpd_Song * ret = mpd_newSong();
1031 if(song->file) ret->file = strdup(song->file);
1032 if(song->artist) ret->artist = strdup(song->artist);
1033 if(song->album) ret->album = strdup(song->album);
1034 if(song->title) ret->title = strdup(song->title);
1035 if(song->track) ret->track = strdup(song->track);
1036 if(song->name) ret->name = strdup(song->name);
1037 if(song->date) ret->date = strdup(song->date);
1038 if(song->genre) ret->genre= strdup(song->genre);
1039 if(song->composer) ret->composer= strdup(song->composer);
1040 if(song->performer) ret->performer = strdup(song->performer);
1041 if(song->disc) ret->disc = strdup(song->disc);
1042 if(song->comment) ret->comment = strdup(song->comment);
1043 ret->time = song->time;
1044 ret->pos = song->pos;
1045 ret->id = song->id;
1047 return ret;
1050 static void mpd_initDirectory(mpd_Directory * directory) {
1051 directory->path = NULL;
1054 static void mpd_finishDirectory(mpd_Directory * directory) {
1055 if(directory->path) free(directory->path);
1058 mpd_Directory * mpd_newDirectory(void) {
1059 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
1061 mpd_initDirectory(directory);
1063 return directory;
1066 void mpd_freeDirectory(mpd_Directory * directory) {
1067 mpd_finishDirectory(directory);
1069 free(directory);
1072 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
1073 mpd_Directory * ret = mpd_newDirectory();
1075 if(directory->path) ret->path = strdup(directory->path);
1077 return ret;
1080 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
1081 playlist->path = NULL;
1084 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
1085 if(playlist->path) free(playlist->path);
1088 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
1089 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
1091 mpd_initPlaylistFile(playlist);
1093 return playlist;
1096 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
1097 mpd_finishPlaylistFile(playlist);
1098 free(playlist);
1101 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1102 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1104 if(playlist->path) ret->path = strdup(playlist->path);
1106 return ret;
1109 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
1110 entity->info.directory = NULL;
1113 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1114 if(entity->info.directory) {
1115 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1116 mpd_freeDirectory(entity->info.directory);
1118 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1119 mpd_freeSong(entity->info.song);
1121 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1122 mpd_freePlaylistFile(entity->info.playlistFile);
1127 mpd_InfoEntity * mpd_newInfoEntity(void) {
1128 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
1130 mpd_initInfoEntity(entity);
1132 return entity;
1135 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1136 mpd_finishInfoEntity(entity);
1137 free(entity);
1140 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1141 mpd_executeCommand(connection,command);
1144 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1145 mpd_InfoEntity * entity = NULL;
1147 if(connection->doneProcessing || (connection->listOks &&
1148 connection->doneListOk))
1150 return NULL;
1153 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1155 if(connection->returnElement) {
1156 if(strcmp(connection->returnElement->name,"file")==0) {
1157 entity = mpd_newInfoEntity();
1158 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1159 entity->info.song = mpd_newSong();
1160 entity->info.song->file =
1161 strdup(connection->returnElement->value);
1163 else if(strcmp(connection->returnElement->name,
1164 "directory")==0) {
1165 entity = mpd_newInfoEntity();
1166 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1167 entity->info.directory = mpd_newDirectory();
1168 entity->info.directory->path =
1169 strdup(connection->returnElement->value);
1171 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1172 entity = mpd_newInfoEntity();
1173 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1174 entity->info.playlistFile = mpd_newPlaylistFile();
1175 entity->info.playlistFile->path =
1176 strdup(connection->returnElement->value);
1178 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1179 entity = mpd_newInfoEntity();
1180 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1181 entity->info.song = mpd_newSong();
1182 entity->info.song->pos = atoi(connection->returnElement->value);
1184 else {
1185 connection->error = 1;
1186 strcpy(connection->errorStr,"problem parsing song info");
1187 return NULL;
1190 else return NULL;
1192 mpd_getNextReturnElement(connection);
1193 while(connection->returnElement) {
1194 mpd_ReturnElement * re = connection->returnElement;
1196 if(strcmp(re->name,"file")==0) return entity;
1197 else if(strcmp(re->name,"directory")==0) return entity;
1198 else if(strcmp(re->name,"playlist")==0) return entity;
1199 else if(strcmp(re->name,"cpos")==0) return entity;
1201 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1202 strlen(re->value)) {
1203 if(!entity->info.song->artist &&
1204 strcmp(re->name,"Artist")==0) {
1205 entity->info.song->artist = strdup(re->value);
1207 else if(!entity->info.song->album &&
1208 strcmp(re->name,"Album")==0) {
1209 entity->info.song->album = strdup(re->value);
1211 else if(!entity->info.song->title &&
1212 strcmp(re->name,"Title")==0) {
1213 entity->info.song->title = strdup(re->value);
1215 else if(!entity->info.song->track &&
1216 strcmp(re->name,"Track")==0) {
1217 entity->info.song->track = strdup(re->value);
1219 else if(!entity->info.song->name &&
1220 strcmp(re->name,"Name")==0) {
1221 entity->info.song->name = strdup(re->value);
1223 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1224 strcmp(re->name,"Time")==0) {
1225 entity->info.song->time = atoi(re->value);
1227 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1228 strcmp(re->name,"Pos")==0) {
1229 entity->info.song->pos = atoi(re->value);
1231 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1232 strcmp(re->name,"Id")==0) {
1233 entity->info.song->id = atoi(re->value);
1235 else if(!entity->info.song->date &&
1236 strcmp(re->name, "Date") == 0) {
1237 entity->info.song->date = strdup(re->value);
1239 else if(!entity->info.song->genre &&
1240 strcmp(re->name, "Genre") == 0) {
1241 entity->info.song->genre = strdup(re->value);
1243 else if(!entity->info.song->composer &&
1244 strcmp(re->name, "Composer") == 0) {
1245 entity->info.song->composer = strdup(re->value);
1247 else if(!entity->info.song->performer &&
1248 strcmp(re->name, "Performer") == 0) {
1249 entity->info.song->performer = strdup(re->value);
1251 else if(!entity->info.song->disc &&
1252 strcmp(re->name, "Disc") == 0) {
1253 entity->info.song->disc = strdup(re->value);
1255 else if(!entity->info.song->comment &&
1256 strcmp(re->name, "Comment") == 0) {
1257 entity->info.song->comment = strdup(re->value);
1260 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1262 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1265 mpd_getNextReturnElement(connection);
1268 return entity;
1271 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1272 const char * name)
1274 if(connection->doneProcessing || (connection->listOks &&
1275 connection->doneListOk))
1277 return NULL;
1280 mpd_getNextReturnElement(connection);
1281 while(connection->returnElement) {
1282 mpd_ReturnElement * re = connection->returnElement;
1284 if(strcmp(re->name,name)==0) return strdup(re->value);
1285 mpd_getNextReturnElement(connection);
1288 return NULL;
1291 char *mpd_getNextTag(mpd_Connection *connection, int type)
1293 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES ||
1294 type == MPD_TAG_ITEM_ANY)
1295 return NULL;
1296 if (type == MPD_TAG_ITEM_FILENAME)
1297 return mpd_getNextReturnElementNamed(connection, "file");
1298 return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
1301 char * mpd_getNextArtist(mpd_Connection * connection) {
1302 return mpd_getNextReturnElementNamed(connection,"Artist");
1305 char * mpd_getNextAlbum(mpd_Connection * connection) {
1306 return mpd_getNextReturnElementNamed(connection,"Album");
1309 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1310 int len = strlen("playlistinfo")+2+INTLEN+3;
1311 char *string = malloc(len);
1312 snprintf(string, len, "playlistinfo \"%i\"\n", songPos);
1313 mpd_sendInfoCommand(connection,string);
1314 free(string);
1317 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1318 int len = strlen("playlistid")+2+INTLEN+3;
1319 char *string = malloc(len);
1320 snprintf(string, len, "playlistid \"%i\"\n", id);
1321 mpd_sendInfoCommand(connection, string);
1322 free(string);
1325 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1326 int len = strlen("plchanges")+2+LONGLONGLEN+3;
1327 char *string = malloc(len);
1328 snprintf(string, len, "plchanges \"%lld\"\n", playlist);
1329 mpd_sendInfoCommand(connection,string);
1330 free(string);
1333 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1334 int len = strlen("plchangesposid")+2+LONGLONGLEN+3;
1335 char *string = malloc(len);
1336 snprintf(string, len, "plchangesposid \"%lld\"\n", playlist);
1337 mpd_sendInfoCommand(connection,string);
1338 free(string);
1341 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1342 char * sDir = mpd_sanitizeArg(dir);
1343 int len = strlen("listall")+2+strlen(sDir)+3;
1344 char *string = malloc(len);
1345 snprintf(string, len, "listall \"%s\"\n", sDir);
1346 mpd_sendInfoCommand(connection,string);
1347 free(string);
1348 free(sDir);
1351 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1352 char * sDir = mpd_sanitizeArg(dir);
1353 int len = strlen("listallinfo")+2+strlen(sDir)+3;
1354 char *string = malloc(len);
1355 snprintf(string, len, "listallinfo \"%s\"\n", sDir);
1356 mpd_sendInfoCommand(connection,string);
1357 free(string);
1358 free(sDir);
1361 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1362 char * sDir = mpd_sanitizeArg(dir);
1363 int len = strlen("lsinfo")+2+strlen(sDir)+3;
1364 char *string = malloc(len);
1365 snprintf(string, len, "lsinfo \"%s\"\n", sDir);
1366 mpd_sendInfoCommand(connection,string);
1367 free(string);
1368 free(sDir);
1371 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1372 mpd_executeCommand(connection,"currentsong\n");
1375 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1376 const char * str)
1378 mpd_startSearch(connection, 0);
1379 mpd_addConstraintSearch(connection, table, str);
1380 mpd_commitSearch(connection);
1383 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1384 const char * str)
1386 mpd_startSearch(connection, 1);
1387 mpd_addConstraintSearch(connection, table, str);
1388 mpd_commitSearch(connection);
1391 void mpd_sendListCommand(mpd_Connection * connection, int table,
1392 const char * arg1)
1394 char st[10];
1395 int len;
1396 char *string;
1397 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1398 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1399 else {
1400 connection->error = 1;
1401 strcpy(connection->errorStr,"unknown table for list");
1402 return;
1404 if(arg1) {
1405 char * sanitArg1 = mpd_sanitizeArg(arg1);
1406 len = strlen("list")+1+strlen(sanitArg1)+2+strlen(st)+3;
1407 string = malloc(len);
1408 snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1);
1409 free(sanitArg1);
1411 else {
1412 len = strlen("list")+1+strlen(st)+2;
1413 string = malloc(len);
1414 snprintf(string, len, "list %s\n", st);
1416 mpd_sendInfoCommand(connection,string);
1417 free(string);
1420 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1421 char * sFile = mpd_sanitizeArg(file);
1422 int len = strlen("add")+2+strlen(sFile)+3;
1423 char *string = malloc(len);
1424 snprintf(string, len, "add \"%s\"\n", sFile);
1425 mpd_executeCommand(connection,string);
1426 free(string);
1427 free(sFile);
1430 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1432 int retval = -1;
1433 char *sFile = mpd_sanitizeArg(file);
1434 int len = strlen("addid")+2+strlen(sFile)+3;
1435 char *string = malloc(len);
1437 snprintf(string, len, "addid \"%s\"\n", sFile);
1438 mpd_sendInfoCommand(connection, string);
1439 free(string);
1440 free(sFile);
1442 string = mpd_getNextReturnElementNamed(connection, "Id");
1443 if (string) {
1444 retval = atoi(string);
1445 free(string);
1448 return retval;
1451 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1452 int len = strlen("delete")+2+INTLEN+3;
1453 char *string = malloc(len);
1454 snprintf(string, len, "delete \"%i\"\n", songPos);
1455 mpd_sendInfoCommand(connection,string);
1456 free(string);
1459 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1460 int len = strlen("deleteid")+2+INTLEN+3;
1461 char *string = malloc(len);
1462 snprintf(string, len, "deleteid \"%i\"\n", id);
1463 mpd_sendInfoCommand(connection,string);
1464 free(string);
1467 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1468 char * sName = mpd_sanitizeArg(name);
1469 int len = strlen("save")+2+strlen(sName)+3;
1470 char *string = malloc(len);
1471 snprintf(string, len, "save \"%s\"\n", sName);
1472 mpd_executeCommand(connection,string);
1473 free(string);
1474 free(sName);
1477 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1478 char * sName = mpd_sanitizeArg(name);
1479 int len = strlen("load")+2+strlen(sName)+3;
1480 char *string = malloc(len);
1481 snprintf(string, len, "load \"%s\"\n", sName);
1482 mpd_executeCommand(connection,string);
1483 free(string);
1484 free(sName);
1487 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1488 char * sName = mpd_sanitizeArg(name);
1489 int len = strlen("rm")+2+strlen(sName)+3;
1490 char *string = malloc(len);
1491 snprintf(string, len, "rm \"%s\"\n", sName);
1492 mpd_executeCommand(connection,string);
1493 free(string);
1494 free(sName);
1497 void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
1498 const char *to)
1500 char *sFrom = mpd_sanitizeArg(from);
1501 char *sTo = mpd_sanitizeArg(to);
1502 int len = strlen("rename")+2+strlen(sFrom)+3+strlen(sTo)+3;
1503 char *string = malloc(len);
1504 snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo);
1505 mpd_executeCommand(connection, string);
1506 free(string);
1507 free(sFrom);
1508 free(sTo);
1511 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1512 mpd_executeCommand(connection,"shuffle\n");
1515 void mpd_sendClearCommand(mpd_Connection * connection) {
1516 mpd_executeCommand(connection,"clear\n");
1519 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1520 int len = strlen("play")+2+INTLEN+3;
1521 char *string = malloc(len);
1522 snprintf(string, len, "play \"%i\"\n", songPos);
1523 mpd_sendInfoCommand(connection,string);
1524 free(string);
1527 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1528 int len = strlen("playid")+2+INTLEN+3;
1529 char *string = malloc(len);
1530 snprintf(string, len, "playid \"%i\"\n", id);
1531 mpd_sendInfoCommand(connection,string);
1532 free(string);
1535 void mpd_sendStopCommand(mpd_Connection * connection) {
1536 mpd_executeCommand(connection,"stop\n");
1539 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1540 int len = strlen("pause")+2+INTLEN+3;
1541 char *string = malloc(len);
1542 snprintf(string, len, "pause \"%i\"\n", pauseMode);
1543 mpd_executeCommand(connection,string);
1544 free(string);
1547 void mpd_sendNextCommand(mpd_Connection * connection) {
1548 mpd_executeCommand(connection,"next\n");
1551 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1552 int len = strlen("move")+2+INTLEN+3+INTLEN+3;
1553 char *string = malloc(len);
1554 snprintf(string, len, "move \"%i\" \"%i\"\n", from, to);
1555 mpd_sendInfoCommand(connection,string);
1556 free(string);
1559 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1560 int len = strlen("moveid")+2+INTLEN+3+INTLEN+3;
1561 char *string = malloc(len);
1562 snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to);
1563 mpd_sendInfoCommand(connection,string);
1564 free(string);
1567 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1568 int len = strlen("swap")+2+INTLEN+3+INTLEN+3;
1569 char *string = malloc(len);
1570 snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2);
1571 mpd_sendInfoCommand(connection,string);
1572 free(string);
1575 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1576 int len = strlen("swapid")+2+INTLEN+3+INTLEN+3;
1577 char *string = malloc(len);
1578 snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2);
1579 mpd_sendInfoCommand(connection,string);
1580 free(string);
1583 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1584 int len = strlen("seek")+2+INTLEN+3+INTLEN+3;
1585 char *string = malloc(len);
1586 snprintf(string, len, "seek \"%i\" \"%i\"\n", song, time);
1587 mpd_sendInfoCommand(connection,string);
1588 free(string);
1591 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1592 int len = strlen("seekid")+2+INTLEN+3+INTLEN+3;
1593 char *string = malloc(len);
1594 snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, time);
1595 mpd_sendInfoCommand(connection,string);
1596 free(string);
1599 void mpd_sendUpdateCommand(mpd_Connection * connection, char * path) {
1600 char * sPath = mpd_sanitizeArg(path);
1601 int len = strlen("update")+2+strlen(sPath)+3;
1602 char *string = malloc(len);
1603 snprintf(string, len, "update \"%s\"\n", sPath);
1604 mpd_sendInfoCommand(connection,string);
1605 free(string);
1606 free(sPath);
1609 int mpd_getUpdateId(mpd_Connection * connection) {
1610 char * jobid;
1611 int ret = 0;
1613 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1614 if(jobid) {
1615 ret = atoi(jobid);
1616 free(jobid);
1619 return ret;
1622 void mpd_sendPrevCommand(mpd_Connection * connection) {
1623 mpd_executeCommand(connection,"previous\n");
1626 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1627 int len = strlen("repeat")+2+INTLEN+3;
1628 char *string = malloc(len);
1629 snprintf(string, len, "repeat \"%i\"\n", repeatMode);
1630 mpd_executeCommand(connection,string);
1631 free(string);
1634 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1635 int len = strlen("random")+2+INTLEN+3;
1636 char *string = malloc(len);
1637 snprintf(string, len, "random \"%i\"\n", randomMode);
1638 mpd_executeCommand(connection,string);
1639 free(string);
1642 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1643 int len = strlen("setvol")+2+INTLEN+3;
1644 char *string = malloc(len);
1645 snprintf(string, len, "setvol \"%i\"\n", volumeChange);
1646 mpd_executeCommand(connection,string);
1647 free(string);
1650 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1651 int len = strlen("volume")+2+INTLEN+3;
1652 char *string = malloc(len);
1653 snprintf(string, len, "volume \"%i\"\n", volumeChange);
1654 mpd_executeCommand(connection,string);
1655 free(string);
1658 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1659 int len = strlen("crossfade")+2+INTLEN+3;
1660 char *string = malloc(len);
1661 snprintf(string, len, "crossfade \"%i\"\n", seconds);
1662 mpd_executeCommand(connection,string);
1663 free(string);
1666 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1667 char * sPass = mpd_sanitizeArg(pass);
1668 int len = strlen("password")+2+strlen(sPass)+3;
1669 char *string = malloc(len);
1670 snprintf(string, len, "password \"%s\"\n", sPass);
1671 mpd_executeCommand(connection,string);
1672 free(string);
1673 free(sPass);
1676 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1677 if(connection->commandList) {
1678 strcpy(connection->errorStr,"already in command list mode");
1679 connection->error = 1;
1680 return;
1682 connection->commandList = COMMAND_LIST;
1683 mpd_executeCommand(connection,"command_list_begin\n");
1686 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1687 if(connection->commandList) {
1688 strcpy(connection->errorStr,"already in command list mode");
1689 connection->error = 1;
1690 return;
1692 connection->commandList = COMMAND_LIST_OK;
1693 mpd_executeCommand(connection,"command_list_ok_begin\n");
1694 connection->listOks = 0;
1697 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1698 if(!connection->commandList) {
1699 strcpy(connection->errorStr,"not in command list mode");
1700 connection->error = 1;
1701 return;
1703 connection->commandList = 0;
1704 mpd_executeCommand(connection,"command_list_end\n");
1707 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1708 mpd_executeCommand(connection,"outputs\n");
1711 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1712 mpd_OutputEntity * output = NULL;
1714 if(connection->doneProcessing || (connection->listOks &&
1715 connection->doneListOk))
1717 return NULL;
1720 if(connection->error) return NULL;
1722 output = malloc(sizeof(mpd_OutputEntity));
1723 output->id = -10;
1724 output->name = NULL;
1725 output->enabled = 0;
1727 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1729 while(connection->returnElement) {
1730 mpd_ReturnElement * re = connection->returnElement;
1731 if(strcmp(re->name,"outputid")==0) {
1732 if(output!=NULL && output->id>=0) return output;
1733 output->id = atoi(re->value);
1735 else if(strcmp(re->name,"outputname")==0) {
1736 output->name = strdup(re->value);
1738 else if(strcmp(re->name,"outputenabled")==0) {
1739 output->enabled = atoi(re->value);
1742 mpd_getNextReturnElement(connection);
1743 if(connection->error) {
1744 free(output);
1745 return NULL;
1750 return output;
1753 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1754 int len = strlen("enableoutput")+2+INTLEN+3;
1755 char *string = malloc(len);
1756 snprintf(string, len, "enableoutput \"%i\"\n", outputId);
1757 mpd_executeCommand(connection,string);
1758 free(string);
1761 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1762 int len = strlen("disableoutput")+2+INTLEN+3;
1763 char *string = malloc(len);
1764 snprintf(string, len, "disableoutput \"%i\"\n", outputId);
1765 mpd_executeCommand(connection,string);
1766 free(string);
1769 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1770 free(output->name);
1771 free(output);
1775 * mpd_sendNotCommandsCommand
1776 * odd naming, but it gets the not allowed commands
1779 void mpd_sendNotCommandsCommand(mpd_Connection * connection)
1781 mpd_executeCommand(connection, "notcommands\n");
1785 * mpd_sendCommandsCommand
1786 * odd naming, but it gets the allowed commands
1788 void mpd_sendCommandsCommand(mpd_Connection * connection)
1790 mpd_executeCommand(connection, "commands\n");
1794 * Get the next returned command
1796 char * mpd_getNextCommand(mpd_Connection * connection)
1798 return mpd_getNextReturnElementNamed(connection, "command");
1801 void mpd_sendUrlHandlersCommand(mpd_Connection * connection)
1803 mpd_executeCommand(connection, "urlhandlers\n");
1806 char * mpd_getNextHandler(mpd_Connection * connection)
1808 return mpd_getNextReturnElementNamed(connection, "handler");
1811 void mpd_sendTagTypesCommand(mpd_Connection * connection)
1813 mpd_executeCommand(connection, "tagtypes\n");
1816 char * mpd_getNextTagType(mpd_Connection * connection)
1818 return mpd_getNextReturnElementNamed(connection, "tagtype");
1821 void mpd_startSearch(mpd_Connection *connection, int exact)
1823 if (connection->request) {
1824 strcpy(connection->errorStr, "search already in progress");
1825 connection->error = 1;
1826 return;
1829 if (exact) connection->request = strdup("find");
1830 else connection->request = strdup("search");
1833 void mpd_startStatsSearch(mpd_Connection *connection)
1835 if (connection->request) {
1836 strcpy(connection->errorStr, "search already in progress");
1837 connection->error = 1;
1838 return;
1841 connection->request = strdup("count");
1844 void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
1846 if (connection->request) {
1847 strcpy(connection->errorStr, "search already in progress");
1848 connection->error = 1;
1849 return;
1852 if (exact) connection->request = strdup("playlistfind");
1853 else connection->request = strdup("playlistsearch");
1856 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1858 char *strtype;
1859 int len;
1861 if (connection->request) {
1862 strcpy(connection->errorStr, "search already in progress");
1863 connection->error = 1;
1864 return;
1867 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1868 strcpy(connection->errorStr, "invalid type specified");
1869 connection->error = 1;
1870 return;
1873 strtype = mpdTagItemKeys[type];
1875 len = 5+strlen(strtype)+1;
1876 connection->request = malloc(len);
1878 snprintf(connection->request, len, "list %c%s",
1879 tolower(strtype[0]), strtype+1);
1882 void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name)
1884 char *strtype;
1885 char *arg;
1886 int len;
1887 char *string;
1889 if (!connection->request) {
1890 strcpy(connection->errorStr, "no search in progress");
1891 connection->error = 1;
1892 return;
1895 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1896 strcpy(connection->errorStr, "invalid type specified");
1897 connection->error = 1;
1898 return;
1901 if (name == NULL) {
1902 strcpy(connection->errorStr, "no name specified");
1903 connection->error = 1;
1904 return;
1907 string = strdup(connection->request);
1908 strtype = mpdTagItemKeys[type];
1909 arg = mpd_sanitizeArg(name);
1911 len = strlen(string)+1+strlen(strtype)+2+strlen(arg)+2;
1912 connection->request = realloc(connection->request, len);
1913 snprintf(connection->request, len, "%s %c%s \"%s\"",
1914 string, tolower(strtype[0]), strtype+1, arg);
1916 free(string);
1917 free(arg);
1920 void mpd_commitSearch(mpd_Connection *connection)
1922 int len;
1924 if (!connection->request) {
1925 strcpy(connection->errorStr, "no search in progress");
1926 connection->error = 1;
1927 return;
1930 len = strlen(connection->request)+2;
1931 connection->request = realloc(connection->request, len);
1932 connection->request[len-2] = '\n';
1933 connection->request[len-1] = '\0';
1934 mpd_sendInfoCommand(connection, connection->request);
1936 free(connection->request);
1937 connection->request = NULL;
1941 * @param connection a MpdConnection
1942 * @param path the path to the playlist.
1944 * List the content, with full metadata, of a stored playlist.
1947 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1949 char *arg = mpd_sanitizeArg(path);
1950 int len = strlen("listplaylistinfo")+2+strlen(arg)+3;
1951 char *query = malloc(len);
1952 snprintf(query, len, "listplaylistinfo \"%s\"\n", arg);
1953 mpd_sendInfoCommand(connection, query);
1954 free(arg);
1955 free(query);
1959 * @param connection a MpdConnection
1960 * @param path the path to the playlist.
1962 * List the content of a stored playlist.
1965 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1967 char *arg = mpd_sanitizeArg(path);
1968 int len = strlen("listplaylist")+2+strlen(arg)+3;
1969 char *query = malloc(len);
1970 snprintf(query, len, "listplaylist \"%s\"\n", arg);
1971 mpd_sendInfoCommand(connection, query);
1972 free(arg);
1973 free(query);
1976 void mpd_sendPlaylistClearCommand(mpd_Connection *connection, char *path)
1978 char *sPath = mpd_sanitizeArg(path);
1979 int len = strlen("playlistclear")+2+strlen(sPath)+3;
1980 char *string = malloc(len);
1981 snprintf(string, len, "playlistclear \"%s\"\n", sPath);
1982 mpd_executeCommand(connection, string);
1983 free(sPath);
1984 free(string);
1987 void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
1988 char *playlist, char *path)
1990 char *sPlaylist = mpd_sanitizeArg(playlist);
1991 char *sPath = mpd_sanitizeArg(path);
1992 int len = strlen("playlistadd")+2+strlen(sPlaylist)+3+strlen(sPath)+3;
1993 char *string = malloc(len);
1994 snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath);
1995 mpd_executeCommand(connection, string);
1996 free(sPlaylist);
1997 free(sPath);
1998 free(string);
2001 void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
2002 char *playlist, int from, int to)
2004 char *sPlaylist = mpd_sanitizeArg(playlist);
2005 int len = strlen("playlistmove")+
2006 2+strlen(sPlaylist)+3+INTLEN+3+INTLEN+3;
2007 char *string = malloc(len);
2008 snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2009 sPlaylist, from, to);
2010 mpd_executeCommand(connection, string);
2011 free(sPlaylist);
2012 free(string);
2015 void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
2016 char *playlist, int pos)
2018 char *sPlaylist = mpd_sanitizeArg(playlist);
2019 int len = strlen("playlistdelete")+2+strlen(sPlaylist)+3+INTLEN+3;
2020 char *string = malloc(len);
2021 snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos);
2022 mpd_executeCommand(connection, string);
2023 free(sPlaylist);
2024 free(string);