Modify version string to post-release version 0.17.97-git
[libmpd.git] / src / libmpdclient.c
blob4418d1c0ee8d986690d5a8942aa4fd8e41842838
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 "AlbumArtist",
274 "Any"
277 static char * mpd_sanitizeArg(const char * arg) {
278 size_t i;
279 char * ret;
280 register const char *c;
281 register char *rc;
283 /* instead of counting in that loop above, just
284 * use a bit more memory and half running time
286 ret = malloc(strlen(arg) * 2 + 1);
288 c = arg;
289 rc = ret;
290 for(i = strlen(arg)+1; i != 0; --i) {
291 if(*c=='"' || *c=='\\')
292 *rc++ = '\\';
293 *(rc++) = *(c++);
296 return ret;
299 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
301 mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
303 ret->name = strdup(name);
304 ret->value = strdup(value);
306 return ret;
309 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
310 free(re->name);
311 free(re->value);
312 free(re);
315 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
316 connection->timeout.tv_sec = (int)timeout;
317 connection->timeout.tv_usec = (int)(timeout*1e6 -
318 connection->timeout.tv_sec*1000000 +
319 0.5);
322 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
323 char * output) {
324 char * tmp;
325 char * test;
326 int i;
328 if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
329 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
330 "mpd not running on port %i on host \"%s\"",
331 port,host);
332 connection->error = MPD_ERROR_NOTMPD;
333 return 1;
336 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
338 for(i=0;i<3;i++) {
339 if(tmp) connection->version[i] = strtol(tmp,&test,10);
341 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
342 snprintf(connection->errorStr,
343 MPD_ERRORSTR_MAX_LENGTH,
344 "error parsing version number at "
345 "\"%s\"",
346 &output[strlen(MPD_WELCOME_MESSAGE)]);
347 connection->error = MPD_ERROR_NOTMPD;
348 return 1;
350 tmp = ++test;
353 return 0;
356 #ifndef WIN32
357 static int mpd_connect_un(mpd_Connection * connection,
358 const char * host, float timeout)
360 int error, flags;
361 size_t path_length;
362 struct sockaddr_un saun;
364 path_length = strlen(host);
365 if (path_length >= sizeof(saun.sun_path)) {
366 strcpy(connection->errorStr, "unix socket path is too long");
367 connection->error = MPD_ERROR_UNKHOST;
368 return -1;
371 saun.sun_family = AF_UNIX;
372 memcpy(saun.sun_path, host, path_length + 1);
374 connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
375 if (connection->sock < 0) {
376 strcpy(connection->errorStr, "problems creating socket");
377 connection->error = MPD_ERROR_SYSTEM;
378 return -1;
381 mpd_setConnectionTimeout(connection, timeout);
383 flags = fcntl(connection->sock, F_GETFL, 0);
384 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
386 error = connect(connection->sock, (struct sockaddr*)&saun, sizeof(saun));
387 if (error < 0) {
388 /* try the next address family */
389 close(connection->sock);
390 connection->sock = 0;
392 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
393 "problems connecting to \"%s\": %s",
394 host, strerror(errno));
395 connection->error = MPD_ERROR_CONNPORT;
396 return -1;
399 return 0;
401 #endif /* WIN32 */
403 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
404 int err;
405 char * rt;
406 char * output = NULL;
407 mpd_Connection * connection = malloc(sizeof(mpd_Connection));
408 struct timeval tv;
409 fd_set fds;
410 strcpy(connection->buffer,"");
411 connection->sock = -1;
412 connection->buflen = 0;
413 connection->bufstart = 0;
414 strcpy(connection->errorStr,"");
415 connection->error = 0;
416 connection->doneProcessing = 0;
417 connection->commandList = 0;
418 connection->listOks = 0;
419 connection->doneListOk = 0;
420 connection->returnElement = NULL;
421 connection->request = NULL;
423 if (winsock_dll_error(connection))
424 return connection;
426 #ifndef WIN32
427 if (host[0] == '/')
428 err = mpd_connect_un(connection, host, timeout);
429 else
430 #endif
431 err = mpd_connect(connection, host, port, timeout);
432 if (err < 0)
433 return connection;
435 while(!(rt = strstr(connection->buffer,"\n"))) {
436 tv.tv_sec = connection->timeout.tv_sec;
437 tv.tv_usec = connection->timeout.tv_usec;
438 FD_ZERO(&fds);
439 FD_SET(connection->sock,&fds);
440 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
441 int readed;
442 readed = recv(connection->sock,
443 &(connection->buffer[connection->buflen]),
444 MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
445 if(readed<=0) {
446 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
447 "problems getting a response from"
448 " \"%s\" on port %i : %s",host,
449 port, strerror(errno));
450 connection->error = MPD_ERROR_NORESPONSE;
451 return connection;
453 connection->buflen+=readed;
454 connection->buffer[connection->buflen] = '\0';
456 else if(err<0) {
457 if (SELECT_ERRNO_IGNORE)
458 continue;
459 snprintf(connection->errorStr,
460 MPD_ERRORSTR_MAX_LENGTH,
461 "problems connecting to \"%s\" on port"
462 " %i",host,port);
463 connection->error = MPD_ERROR_CONNPORT;
464 return connection;
466 else {
467 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
468 "timeout in attempting to get a response from"
469 " \"%s\" on port %i",host,port);
470 connection->error = MPD_ERROR_NORESPONSE;
471 return connection;
475 *rt = '\0';
476 output = strdup(connection->buffer);
477 strcpy(connection->buffer,rt+1);
478 connection->buflen = strlen(connection->buffer);
480 if(mpd_parseWelcome(connection,host,port,output) == 0) connection->doneProcessing = 1;
482 free(output);
484 return connection;
487 void mpd_clearError(mpd_Connection * connection) {
488 connection->error = 0;
489 connection->errorStr[0] = '\0';
492 void mpd_closeConnection(mpd_Connection * connection) {
493 closesocket(connection->sock);
494 if(connection->returnElement) free(connection->returnElement);
495 if(connection->request) free(connection->request);
496 free(connection);
497 WSACleanup();
500 static void mpd_executeCommand(mpd_Connection * connection,const char * command) {
501 int ret;
502 struct timeval tv;
503 fd_set fds;
504 const char * commandPtr = command;
505 int commandLen = strlen(command);
507 if(!connection->doneProcessing && !connection->commandList) {
508 strcpy(connection->errorStr,"not done processing current command");
509 connection->error = 1;
510 return;
513 mpd_clearError(connection);
515 FD_ZERO(&fds);
516 FD_SET(connection->sock,&fds);
517 tv.tv_sec = connection->timeout.tv_sec;
518 tv.tv_usec = connection->timeout.tv_usec;
519 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
520 (ret==-1 && SELECT_ERRNO_IGNORE)) {
521 fflush(NULL);
522 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
523 if(ret<=0)
525 if (SENDRECV_ERRNO_IGNORE) continue;
526 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
527 "problems giving command \"%s\"",command);
528 connection->error = MPD_ERROR_SENDING;
529 return;
531 else {
532 commandPtr+=ret;
533 commandLen-=ret;
536 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->storedplaylist = -1;
744 status->playlistLength = -1;
745 status->state = -1;
746 status->song = 0;
747 status->songid = 0;
748 status->elapsedTime = 0;
749 status->totalTime = 0;
750 status->bitRate = 0;
751 status->sampleRate = 0;
752 status->bits = 0;
753 status->channels = 0;
754 status->crossfade = -1;
755 status->error = NULL;
756 status->updatingDb = 0;
758 if(connection->error) {
759 free(status);
760 return NULL;
762 while(connection->returnElement) {
763 mpd_ReturnElement * re = connection->returnElement;
764 if(strcmp(re->name,"volume")==0) {
765 status->volume = atoi(re->value);
767 else if(strcmp(re->name,"repeat")==0) {
768 status->repeat = atoi(re->value);
770 else if(strcmp(re->name,"random")==0) {
771 status->random = atoi(re->value);
773 else if(strcmp(re->name,"playlist")==0) {
774 status->playlist = strtol(re->value,NULL,10);
776 else if(strcmp(re->name,"playlistlength")==0) {
777 status->playlistLength = atoi(re->value);
779 else if(strcmp(re->name,"bitrate")==0) {
780 status->bitRate = atoi(re->value);
782 else if(strcmp(re->name,"state")==0) {
783 if(strcmp(re->value,"play")==0) {
784 status->state = MPD_STATUS_STATE_PLAY;
786 else if(strcmp(re->value,"stop")==0) {
787 status->state = MPD_STATUS_STATE_STOP;
789 else if(strcmp(re->value,"pause")==0) {
790 status->state = MPD_STATUS_STATE_PAUSE;
792 else {
793 status->state = MPD_STATUS_STATE_UNKNOWN;
796 else if(strcmp(re->name,"song")==0) {
797 status->song = atoi(re->value);
799 else if(strcmp(re->name,"songid")==0) {
800 status->songid = atoi(re->value);
802 else if(strcmp(re->name,"time")==0) {
803 char * tok = strchr(re->value,':');
804 /* the second strchr below is a safety check */
805 if (tok && (strchr(tok,0) > (tok+1))) {
806 /* atoi stops at the first non-[0-9] char: */
807 status->elapsedTime = atoi(re->value);
808 status->totalTime = atoi(tok+1);
811 else if(strcmp(re->name,"error")==0) {
812 status->error = strdup(re->value);
814 else if(strcmp(re->name,"xfade")==0) {
815 status->crossfade = atoi(re->value);
817 else if(strcmp(re->name,"updating_db")==0) {
818 status->updatingDb = atoi(re->value);
820 else if(strcmp(re->name,"audio")==0) {
821 char * tok = strchr(re->value,':');
822 if (tok && (strchr(tok,0) > (tok+1))) {
823 status->sampleRate = atoi(re->value);
824 status->bits = atoi(++tok);
825 tok = strchr(tok,':');
826 if (tok && (strchr(tok,0) > (tok+1)))
827 status->channels = atoi(tok+1);
831 mpd_getNextReturnElement(connection);
832 if(connection->error) {
833 free(status);
834 return NULL;
838 if(connection->error) {
839 free(status);
840 return NULL;
842 else if(status->state<0) {
843 strcpy(connection->errorStr,"state not found");
844 connection->error = 1;
845 free(status);
846 return NULL;
849 return status;
852 void mpd_freeStatus(mpd_Status * status) {
853 if(status->error) free(status->error);
854 free(status);
857 void mpd_sendStatsCommand(mpd_Connection * connection) {
858 mpd_executeCommand(connection,"stats\n");
861 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
862 mpd_Stats * stats;
864 /*mpd_executeCommand(connection,"stats\n");
866 if(connection->error) return NULL;*/
868 if(connection->doneProcessing || (connection->listOks &&
869 connection->doneListOk))
871 return NULL;
874 if(!connection->returnElement) mpd_getNextReturnElement(connection);
876 stats = malloc(sizeof(mpd_Stats));
877 stats->numberOfArtists = 0;
878 stats->numberOfAlbums = 0;
879 stats->numberOfSongs = 0;
880 stats->uptime = 0;
881 stats->dbUpdateTime = 0;
882 stats->playTime = 0;
883 stats->dbPlayTime = 0;
885 if(connection->error) {
886 free(stats);
887 return NULL;
889 while(connection->returnElement) {
890 mpd_ReturnElement * re = connection->returnElement;
891 if(strcmp(re->name,"artists")==0) {
892 stats->numberOfArtists = atoi(re->value);
894 else if(strcmp(re->name,"albums")==0) {
895 stats->numberOfAlbums = atoi(re->value);
897 else if(strcmp(re->name,"songs")==0) {
898 stats->numberOfSongs = atoi(re->value);
900 else if(strcmp(re->name,"uptime")==0) {
901 stats->uptime = strtol(re->value,NULL,10);
903 else if(strcmp(re->name,"db_update")==0) {
904 stats->dbUpdateTime = strtol(re->value,NULL,10);
906 else if(strcmp(re->name,"playtime")==0) {
907 stats->playTime = strtol(re->value,NULL,10);
909 else if(strcmp(re->name,"db_playtime")==0) {
910 stats->dbPlayTime = strtol(re->value,NULL,10);
913 mpd_getNextReturnElement(connection);
914 if(connection->error) {
915 free(stats);
916 return NULL;
920 if(connection->error) {
921 free(stats);
922 return NULL;
925 return stats;
928 void mpd_freeStats(mpd_Stats * stats) {
929 free(stats);
932 mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection)
934 mpd_SearchStats * stats;
935 mpd_ReturnElement * re;
937 if (connection->doneProcessing ||
938 (connection->listOks && connection->doneListOk)) {
939 return NULL;
942 if (!connection->returnElement) mpd_getNextReturnElement(connection);
944 if (connection->error)
945 return NULL;
947 stats = malloc(sizeof(mpd_SearchStats));
948 stats->numberOfSongs = 0;
949 stats->playTime = 0;
951 while (connection->returnElement) {
952 re = connection->returnElement;
954 if (strcmp(re->name, "songs") == 0) {
955 stats->numberOfSongs = atoi(re->value);
956 } else if (strcmp(re->name, "playtime") == 0) {
957 stats->playTime = strtol(re->value, NULL, 10);
960 mpd_getNextReturnElement(connection);
961 if (connection->error) {
962 free(stats);
963 return NULL;
967 if (connection->error) {
968 free(stats);
969 return NULL;
972 return stats;
975 void mpd_freeSearchStats(mpd_SearchStats * stats)
977 free(stats);
980 static void mpd_initSong(mpd_Song * song) {
981 song->file = NULL;
982 song->artist = NULL;
983 song->album = NULL;
984 song->track = NULL;
985 song->title = NULL;
986 song->name = NULL;
987 song->date = NULL;
988 /* added by Qball */
989 song->genre = NULL;
990 song->composer = NULL;
991 song->performer = NULL;
992 song->disc = NULL;
993 song->comment = NULL;
994 song->albumartist = NULL;
996 song->time = MPD_SONG_NO_TIME;
997 song->pos = MPD_SONG_NO_NUM;
998 song->id = MPD_SONG_NO_ID;
1001 static void mpd_finishSong(mpd_Song * song) {
1002 if(song->file) free(song->file);
1003 if(song->artist) free(song->artist);
1004 if(song->album) free(song->album);
1005 if(song->title) free(song->title);
1006 if(song->track) free(song->track);
1007 if(song->name) free(song->name);
1008 if(song->date) free(song->date);
1009 if(song->genre) free(song->genre);
1010 if(song->composer) free(song->composer);
1011 if(song->performer) free(song->performer);
1012 if(song->disc) free(song->disc);
1013 if(song->comment) free(song->comment);
1014 if(song->albumartist) free(song->albumartist);
1017 mpd_Song * mpd_newSong(void) {
1018 mpd_Song * ret = malloc(sizeof(mpd_Song));
1020 mpd_initSong(ret);
1022 return ret;
1025 void mpd_freeSong(mpd_Song * song) {
1026 mpd_finishSong(song);
1027 free(song);
1030 mpd_Song * mpd_songDup(const mpd_Song * song) {
1031 mpd_Song * ret = mpd_newSong();
1033 if(song->file) ret->file = strdup(song->file);
1034 if(song->artist) ret->artist = strdup(song->artist);
1035 if(song->album) ret->album = strdup(song->album);
1036 if(song->title) ret->title = strdup(song->title);
1037 if(song->track) ret->track = strdup(song->track);
1038 if(song->name) ret->name = strdup(song->name);
1039 if(song->date) ret->date = strdup(song->date);
1040 if(song->genre) ret->genre= strdup(song->genre);
1041 if(song->composer) ret->composer= strdup(song->composer);
1042 if(song->performer) ret->performer = strdup(song->performer);
1043 if(song->disc) ret->disc = strdup(song->disc);
1044 if(song->comment) ret->comment = strdup(song->comment);
1045 if(song->albumartist) ret->albumartist = strdup(song->albumartist);
1046 ret->time = song->time;
1047 ret->pos = song->pos;
1048 ret->id = song->id;
1050 return ret;
1053 static void mpd_initDirectory(mpd_Directory * directory) {
1054 directory->path = NULL;
1057 static void mpd_finishDirectory(mpd_Directory * directory) {
1058 if(directory->path) free(directory->path);
1061 mpd_Directory * mpd_newDirectory(void) {
1062 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
1064 mpd_initDirectory(directory);
1066 return directory;
1069 void mpd_freeDirectory(mpd_Directory * directory) {
1070 mpd_finishDirectory(directory);
1072 free(directory);
1075 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
1076 mpd_Directory * ret = mpd_newDirectory();
1078 if(directory->path) ret->path = strdup(directory->path);
1080 return ret;
1083 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
1084 playlist->path = NULL;
1085 playlist->mtime = NULL;
1088 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
1089 if(playlist->path) free(playlist->path);
1090 if(playlist->mtime) free(playlist->mtime);
1093 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
1094 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
1096 mpd_initPlaylistFile(playlist);
1098 return playlist;
1101 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
1102 mpd_finishPlaylistFile(playlist);
1103 free(playlist);
1106 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1107 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1109 if(playlist->path) ret->path = strdup(playlist->path);
1110 if(playlist->mtime) ret->mtime = strdup(playlist->mtime);
1112 return ret;
1115 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
1116 entity->info.directory = NULL;
1119 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1120 if(entity->info.directory) {
1121 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1122 mpd_freeDirectory(entity->info.directory);
1124 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1125 mpd_freeSong(entity->info.song);
1127 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1128 mpd_freePlaylistFile(entity->info.playlistFile);
1133 mpd_InfoEntity * mpd_newInfoEntity(void) {
1134 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
1136 mpd_initInfoEntity(entity);
1138 return entity;
1141 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1142 mpd_finishInfoEntity(entity);
1143 free(entity);
1146 static void mpd_sendInfoCommand(mpd_Connection * connection,const char * command) {
1147 mpd_executeCommand(connection,command);
1150 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1151 mpd_InfoEntity * entity = NULL;
1153 if(connection->doneProcessing || (connection->listOks &&
1154 connection->doneListOk))
1156 return NULL;
1159 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1161 if(connection->returnElement) {
1162 if(strcmp(connection->returnElement->name,"file")==0) {
1163 entity = mpd_newInfoEntity();
1164 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1165 entity->info.song = mpd_newSong();
1166 entity->info.song->file =
1167 strdup(connection->returnElement->value);
1169 else if(strcmp(connection->returnElement->name,
1170 "directory")==0) {
1171 entity = mpd_newInfoEntity();
1172 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1173 entity->info.directory = mpd_newDirectory();
1174 entity->info.directory->path =
1175 strdup(connection->returnElement->value);
1177 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1178 entity = mpd_newInfoEntity();
1179 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1180 entity->info.playlistFile = mpd_newPlaylistFile();
1181 entity->info.playlistFile->path =
1182 strdup(connection->returnElement->value);
1184 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1185 entity = mpd_newInfoEntity();
1186 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1187 entity->info.song = mpd_newSong();
1188 entity->info.song->pos = atoi(connection->returnElement->value);
1190 else {
1191 connection->error = 1;
1192 strcpy(connection->errorStr,"problem parsing song info");
1193 return NULL;
1196 else return NULL;
1198 mpd_getNextReturnElement(connection);
1199 while(connection->returnElement) {
1200 mpd_ReturnElement * re = connection->returnElement;
1202 if(strcmp(re->name,"file")==0) return entity;
1203 else if(strcmp(re->name,"directory")==0) return entity;
1204 else if(strcmp(re->name,"playlist")==0) return entity;
1205 else if(strcmp(re->name,"cpos")==0) return entity;
1207 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1208 strlen(re->value)) {
1209 if(strcmp(re->name,"Artist")==0) {
1210 if(entity->info.song->artist) {
1211 int length = strlen(entity->info.song->artist);
1212 entity->info.song->artist = realloc(entity->info.song->artist,
1213 length + strlen(re->value) + 3);
1214 strcpy(&((entity->info.song->artist)[length]), ", ");
1215 strcpy(&((entity->info.song->artist)[length + 2]), re->value);
1217 else {
1218 entity->info.song->artist = strdup(re->value);
1221 else if(!entity->info.song->album &&
1222 strcmp(re->name,"Album")==0) {
1223 entity->info.song->album = strdup(re->value);
1225 else if(!entity->info.song->title &&
1226 strcmp(re->name,"Title")==0) {
1227 entity->info.song->title = strdup(re->value);
1229 else if(!entity->info.song->track &&
1230 strcmp(re->name,"Track")==0) {
1231 entity->info.song->track = strdup(re->value);
1233 else if(!entity->info.song->name &&
1234 strcmp(re->name,"Name")==0) {
1235 entity->info.song->name = strdup(re->value);
1237 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1238 strcmp(re->name,"Time")==0) {
1239 entity->info.song->time = atoi(re->value);
1241 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1242 strcmp(re->name,"Pos")==0) {
1243 entity->info.song->pos = atoi(re->value);
1245 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1246 strcmp(re->name,"Id")==0) {
1247 entity->info.song->id = atoi(re->value);
1249 else if(!entity->info.song->date &&
1250 strcmp(re->name, "Date") == 0) {
1251 entity->info.song->date = strdup(re->value);
1253 else if(!entity->info.song->genre &&
1254 strcmp(re->name, "Genre") == 0) {
1255 if(entity->info.song->genre) {
1256 int length = strlen(entity->info.song->genre);
1257 entity->info.song->genre = realloc(entity->info.song->genre,
1258 length + strlen(re->value) + 4);
1259 strcpy(&((entity->info.song->genre)[length]), ", ");
1260 strcpy(&((entity->info.song->genre)[length + 3]), re->value);
1262 else {
1263 entity->info.song->genre = strdup(re->value);
1266 else if(strcmp(re->name, "Composer") == 0) {
1267 if(entity->info.song->composer) {
1268 int length = strlen(entity->info.song->composer);
1269 entity->info.song->composer = realloc(entity->info.song->composer,
1270 length + strlen(re->value) + 3);
1271 strcpy(&((entity->info.song->composer)[length]), ", ");
1272 strcpy(&((entity->info.song->composer)[length + 2]), re->value);
1274 else {
1275 entity->info.song->composer = strdup(re->value);
1278 else if(strcmp(re->name, "Performer") == 0) {
1279 if(entity->info.song->performer) {
1280 int length = strlen(entity->info.song->performer);
1281 entity->info.song->performer = realloc(entity->info.song->performer,
1282 length + strlen(re->value) + 3);
1283 strcpy(&((entity->info.song->performer)[length]), ", ");
1284 strcpy(&((entity->info.song->performer)[length + 2]), re->value);
1286 else {
1287 entity->info.song->performer = strdup(re->value);
1290 else if(!entity->info.song->disc &&
1291 strcmp(re->name, "Disc") == 0) {
1292 entity->info.song->disc = strdup(re->value);
1294 else if(!entity->info.song->comment &&
1295 strcmp(re->name, "Comment") == 0) {
1296 entity->info.song->comment = strdup(re->value);
1299 else if(!entity->info.song->albumartist &&
1300 strcmp(re->name, "AlbumArtist") == 0) {
1301 entity->info.song->albumartist = strdup(re->value);
1304 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1306 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1307 if(!entity->info.playlistFile->mtime &&
1308 strcmp(re->name, "Last-Modified") == 0) {
1309 entity->info.playlistFile->mtime = strdup(re->value);
1313 mpd_getNextReturnElement(connection);
1316 return entity;
1319 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1320 const char * name)
1322 if(connection->doneProcessing || (connection->listOks &&
1323 connection->doneListOk))
1325 return NULL;
1328 mpd_getNextReturnElement(connection);
1329 while(connection->returnElement) {
1330 mpd_ReturnElement * re = connection->returnElement;
1332 if(strcmp(re->name,name)==0) return strdup(re->value);
1333 mpd_getNextReturnElement(connection);
1336 return NULL;
1339 char *mpd_getNextTag(mpd_Connection *connection, int type)
1341 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES ||
1342 type == MPD_TAG_ITEM_ANY)
1343 return NULL;
1344 if (type == MPD_TAG_ITEM_FILENAME)
1345 return mpd_getNextReturnElementNamed(connection, "file");
1346 return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
1349 char * mpd_getNextArtist(mpd_Connection * connection) {
1350 return mpd_getNextReturnElementNamed(connection,"Artist");
1353 char * mpd_getNextAlbum(mpd_Connection * connection) {
1354 return mpd_getNextReturnElementNamed(connection,"Album");
1357 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1358 int len = strlen("playlistinfo")+2+INTLEN+3;
1359 char *string = malloc(len);
1360 snprintf(string, len, "playlistinfo \"%i\"\n", songPos);
1361 mpd_sendInfoCommand(connection,string);
1362 free(string);
1365 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1366 int len = strlen("playlistid")+2+INTLEN+3;
1367 char *string = malloc(len);
1368 snprintf(string, len, "playlistid \"%i\"\n", id);
1369 mpd_sendInfoCommand(connection, string);
1370 free(string);
1373 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1374 int len = strlen("plchanges")+2+LONGLONGLEN+3;
1375 char *string = malloc(len);
1376 snprintf(string, len, "plchanges \"%lld\"\n", playlist);
1377 mpd_sendInfoCommand(connection,string);
1378 free(string);
1381 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1382 int len = strlen("plchangesposid")+2+LONGLONGLEN+3;
1383 char *string = malloc(len);
1384 snprintf(string, len, "plchangesposid \"%lld\"\n", playlist);
1385 mpd_sendInfoCommand(connection,string);
1386 free(string);
1389 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1390 char * sDir = mpd_sanitizeArg(dir);
1391 int len = strlen("listall")+2+strlen(sDir)+3;
1392 char *string = malloc(len);
1393 snprintf(string, len, "listall \"%s\"\n", sDir);
1394 mpd_sendInfoCommand(connection,string);
1395 free(string);
1396 free(sDir);
1399 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1400 char * sDir = mpd_sanitizeArg(dir);
1401 int len = strlen("listallinfo")+2+strlen(sDir)+3;
1402 char *string = malloc(len);
1403 snprintf(string, len, "listallinfo \"%s\"\n", sDir);
1404 mpd_sendInfoCommand(connection,string);
1405 free(string);
1406 free(sDir);
1409 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1410 char * sDir = mpd_sanitizeArg(dir);
1411 int len = strlen("lsinfo")+2+strlen(sDir)+3;
1412 char *string = malloc(len);
1413 snprintf(string, len, "lsinfo \"%s\"\n", sDir);
1414 mpd_sendInfoCommand(connection,string);
1415 free(string);
1416 free(sDir);
1419 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1420 mpd_executeCommand(connection,"currentsong\n");
1423 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1424 const char * str)
1426 mpd_startSearch(connection, 0);
1427 mpd_addConstraintSearch(connection, table, str);
1428 mpd_commitSearch(connection);
1431 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1432 const char * str)
1434 mpd_startSearch(connection, 1);
1435 mpd_addConstraintSearch(connection, table, str);
1436 mpd_commitSearch(connection);
1439 void mpd_sendListCommand(mpd_Connection * connection, int table,
1440 const char * arg1)
1442 char st[10];
1443 int len;
1444 char *string;
1445 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1446 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1447 else {
1448 connection->error = 1;
1449 strcpy(connection->errorStr,"unknown table for list");
1450 return;
1452 if(arg1) {
1453 char * sanitArg1 = mpd_sanitizeArg(arg1);
1454 len = strlen("list")+1+strlen(sanitArg1)+2+strlen(st)+3;
1455 string = malloc(len);
1456 snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1);
1457 free(sanitArg1);
1459 else {
1460 len = strlen("list")+1+strlen(st)+2;
1461 string = malloc(len);
1462 snprintf(string, len, "list %s\n", st);
1464 mpd_sendInfoCommand(connection,string);
1465 free(string);
1468 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1469 char * sFile = mpd_sanitizeArg(file);
1470 int len = strlen("add")+2+strlen(sFile)+3;
1471 char *string = malloc(len);
1472 snprintf(string, len, "add \"%s\"\n", sFile);
1473 mpd_executeCommand(connection,string);
1474 free(string);
1475 free(sFile);
1478 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1480 int retval = -1;
1481 char *sFile = mpd_sanitizeArg(file);
1482 int len = strlen("addid")+2+strlen(sFile)+3;
1483 char *string = malloc(len);
1485 snprintf(string, len, "addid \"%s\"\n", sFile);
1486 mpd_sendInfoCommand(connection, string);
1487 free(string);
1488 free(sFile);
1490 string = mpd_getNextReturnElementNamed(connection, "Id");
1491 if (string) {
1492 retval = atoi(string);
1493 free(string);
1496 return retval;
1499 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1500 int len = strlen("delete")+2+INTLEN+3;
1501 char *string = malloc(len);
1502 snprintf(string, len, "delete \"%i\"\n", songPos);
1503 mpd_sendInfoCommand(connection,string);
1504 free(string);
1507 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1508 int len = strlen("deleteid")+2+INTLEN+3;
1509 char *string = malloc(len);
1510 snprintf(string, len, "deleteid \"%i\"\n", id);
1511 mpd_sendInfoCommand(connection,string);
1512 free(string);
1515 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1516 char * sName = mpd_sanitizeArg(name);
1517 int len = strlen("save")+2+strlen(sName)+3;
1518 char *string = malloc(len);
1519 snprintf(string, len, "save \"%s\"\n", sName);
1520 mpd_executeCommand(connection,string);
1521 free(string);
1522 free(sName);
1525 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1526 char * sName = mpd_sanitizeArg(name);
1527 int len = strlen("load")+2+strlen(sName)+3;
1528 char *string = malloc(len);
1529 snprintf(string, len, "load \"%s\"\n", sName);
1530 mpd_executeCommand(connection,string);
1531 free(string);
1532 free(sName);
1535 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1536 char * sName = mpd_sanitizeArg(name);
1537 int len = strlen("rm")+2+strlen(sName)+3;
1538 char *string = malloc(len);
1539 snprintf(string, len, "rm \"%s\"\n", sName);
1540 mpd_executeCommand(connection,string);
1541 free(string);
1542 free(sName);
1545 void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
1546 const char *to)
1548 char *sFrom = mpd_sanitizeArg(from);
1549 char *sTo = mpd_sanitizeArg(to);
1550 int len = strlen("rename")+2+strlen(sFrom)+3+strlen(sTo)+3;
1551 char *string = malloc(len);
1552 snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo);
1553 mpd_executeCommand(connection, string);
1554 free(string);
1555 free(sFrom);
1556 free(sTo);
1559 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1560 mpd_executeCommand(connection,"shuffle\n");
1563 void mpd_sendClearCommand(mpd_Connection * connection) {
1564 mpd_executeCommand(connection,"clear\n");
1567 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1568 int len = strlen("play")+2+INTLEN+3;
1569 char *string = malloc(len);
1570 snprintf(string, len, "play \"%i\"\n", songPos);
1571 mpd_sendInfoCommand(connection,string);
1572 free(string);
1575 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1576 int len = strlen("playid")+2+INTLEN+3;
1577 char *string = malloc(len);
1578 snprintf(string, len, "playid \"%i\"\n", id);
1579 mpd_sendInfoCommand(connection,string);
1580 free(string);
1583 void mpd_sendStopCommand(mpd_Connection * connection) {
1584 mpd_executeCommand(connection,"stop\n");
1587 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1588 int len = strlen("pause")+2+INTLEN+3;
1589 char *string = malloc(len);
1590 snprintf(string, len, "pause \"%i\"\n", pauseMode);
1591 mpd_executeCommand(connection,string);
1592 free(string);
1595 void mpd_sendNextCommand(mpd_Connection * connection) {
1596 mpd_executeCommand(connection,"next\n");
1599 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1600 int len = strlen("move")+2+INTLEN+3+INTLEN+3;
1601 char *string = malloc(len);
1602 snprintf(string, len, "move \"%i\" \"%i\"\n", from, to);
1603 mpd_sendInfoCommand(connection,string);
1604 free(string);
1607 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1608 int len = strlen("moveid")+2+INTLEN+3+INTLEN+3;
1609 char *string = malloc(len);
1610 snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to);
1611 mpd_sendInfoCommand(connection,string);
1612 free(string);
1615 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1616 int len = strlen("swap")+2+INTLEN+3+INTLEN+3;
1617 char *string = malloc(len);
1618 snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2);
1619 mpd_sendInfoCommand(connection,string);
1620 free(string);
1623 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1624 int len = strlen("swapid")+2+INTLEN+3+INTLEN+3;
1625 char *string = malloc(len);
1626 snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2);
1627 mpd_sendInfoCommand(connection,string);
1628 free(string);
1631 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1632 int len = strlen("seek")+2+INTLEN+3+INTLEN+3;
1633 char *string = malloc(len);
1634 snprintf(string, len, "seek \"%i\" \"%i\"\n", song, time);
1635 mpd_sendInfoCommand(connection,string);
1636 free(string);
1639 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1640 int len = strlen("seekid")+2+INTLEN+3+INTLEN+3;
1641 char *string = malloc(len);
1642 snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, time);
1643 mpd_sendInfoCommand(connection,string);
1644 free(string);
1647 void mpd_sendUpdateCommand(mpd_Connection * connection,const char * path) {
1648 char * sPath = mpd_sanitizeArg(path);
1649 int len = strlen("update")+2+strlen(sPath)+3;
1650 char *string = malloc(len);
1651 snprintf(string, len, "update \"%s\"\n", sPath);
1652 mpd_sendInfoCommand(connection,string);
1653 free(string);
1654 free(sPath);
1657 int mpd_getUpdateId(mpd_Connection * connection) {
1658 char * jobid;
1659 int ret = 0;
1661 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1662 if(jobid) {
1663 ret = atoi(jobid);
1664 free(jobid);
1667 return ret;
1670 void mpd_sendPrevCommand(mpd_Connection * connection) {
1671 mpd_executeCommand(connection,"previous\n");
1674 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1675 int len = strlen("repeat")+2+INTLEN+3;
1676 char *string = malloc(len);
1677 snprintf(string, len, "repeat \"%i\"\n", repeatMode);
1678 mpd_executeCommand(connection,string);
1679 free(string);
1682 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1683 int len = strlen("random")+2+INTLEN+3;
1684 char *string = malloc(len);
1685 snprintf(string, len, "random \"%i\"\n", randomMode);
1686 mpd_executeCommand(connection,string);
1687 free(string);
1690 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1691 int len = strlen("setvol")+2+INTLEN+3;
1692 char *string = malloc(len);
1693 snprintf(string, len, "setvol \"%i\"\n", volumeChange);
1694 mpd_executeCommand(connection,string);
1695 free(string);
1698 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1699 int len = strlen("volume")+2+INTLEN+3;
1700 char *string = malloc(len);
1701 snprintf(string, len, "volume \"%i\"\n", volumeChange);
1702 mpd_executeCommand(connection,string);
1703 free(string);
1706 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1707 int len = strlen("crossfade")+2+INTLEN+3;
1708 char *string = malloc(len);
1709 snprintf(string, len, "crossfade \"%i\"\n", seconds);
1710 mpd_executeCommand(connection,string);
1711 free(string);
1714 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1715 char * sPass = mpd_sanitizeArg(pass);
1716 int len = strlen("password")+2+strlen(sPass)+3;
1717 char *string = malloc(len);
1718 snprintf(string, len, "password \"%s\"\n", sPass);
1719 mpd_executeCommand(connection,string);
1720 free(string);
1721 free(sPass);
1724 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1725 if(connection->commandList) {
1726 strcpy(connection->errorStr,"already in command list mode");
1727 connection->error = 1;
1728 return;
1730 connection->commandList = COMMAND_LIST;
1731 mpd_executeCommand(connection,"command_list_begin\n");
1734 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1735 if(connection->commandList) {
1736 strcpy(connection->errorStr,"already in command list mode");
1737 connection->error = 1;
1738 return;
1740 connection->commandList = COMMAND_LIST_OK;
1741 mpd_executeCommand(connection,"command_list_ok_begin\n");
1742 connection->listOks = 0;
1745 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1746 if(!connection->commandList) {
1747 strcpy(connection->errorStr,"not in command list mode");
1748 connection->error = 1;
1749 return;
1751 connection->commandList = 0;
1752 mpd_executeCommand(connection,"command_list_end\n");
1755 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1756 mpd_executeCommand(connection,"outputs\n");
1759 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1760 mpd_OutputEntity * output = NULL;
1762 if(connection->doneProcessing || (connection->listOks &&
1763 connection->doneListOk))
1765 return NULL;
1768 if(connection->error) return NULL;
1770 output = malloc(sizeof(mpd_OutputEntity));
1771 output->id = -10;
1772 output->name = NULL;
1773 output->enabled = 0;
1775 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1777 while(connection->returnElement) {
1778 mpd_ReturnElement * re = connection->returnElement;
1779 if(strcmp(re->name,"outputid")==0) {
1780 if(output!=NULL && output->id>=0) return output;
1781 output->id = atoi(re->value);
1783 else if(strcmp(re->name,"outputname")==0) {
1784 output->name = strdup(re->value);
1786 else if(strcmp(re->name,"outputenabled")==0) {
1787 output->enabled = atoi(re->value);
1790 mpd_getNextReturnElement(connection);
1791 if(connection->error) {
1792 free(output);
1793 return NULL;
1798 return output;
1801 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1802 int len = strlen("enableoutput")+2+INTLEN+3;
1803 char *string = malloc(len);
1804 snprintf(string, len, "enableoutput \"%i\"\n", outputId);
1805 mpd_executeCommand(connection,string);
1806 free(string);
1809 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1810 int len = strlen("disableoutput")+2+INTLEN+3;
1811 char *string = malloc(len);
1812 snprintf(string, len, "disableoutput \"%i\"\n", outputId);
1813 mpd_executeCommand(connection,string);
1814 free(string);
1817 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1818 free(output->name);
1819 free(output);
1823 * mpd_sendNotCommandsCommand
1824 * odd naming, but it gets the not allowed commands
1827 void mpd_sendNotCommandsCommand(mpd_Connection * connection)
1829 mpd_executeCommand(connection, "notcommands\n");
1833 * mpd_sendCommandsCommand
1834 * odd naming, but it gets the allowed commands
1836 void mpd_sendCommandsCommand(mpd_Connection * connection)
1838 mpd_executeCommand(connection, "commands\n");
1842 * Get the next returned command
1844 char * mpd_getNextCommand(mpd_Connection * connection)
1846 return mpd_getNextReturnElementNamed(connection, "command");
1849 void mpd_sendUrlHandlersCommand(mpd_Connection * connection)
1851 mpd_executeCommand(connection, "urlhandlers\n");
1854 char * mpd_getNextHandler(mpd_Connection * connection)
1856 return mpd_getNextReturnElementNamed(connection, "handler");
1859 void mpd_sendTagTypesCommand(mpd_Connection * connection)
1861 mpd_executeCommand(connection, "tagtypes\n");
1864 char * mpd_getNextTagType(mpd_Connection * connection)
1866 return mpd_getNextReturnElementNamed(connection, "tagtype");
1869 void mpd_startSearch(mpd_Connection *connection, int exact)
1871 if (connection->request) {
1872 strcpy(connection->errorStr, "search already in progress");
1873 connection->error = 1;
1874 return;
1877 if (exact) connection->request = strdup("find");
1878 else connection->request = strdup("search");
1881 void mpd_startStatsSearch(mpd_Connection *connection)
1883 if (connection->request) {
1884 strcpy(connection->errorStr, "search already in progress");
1885 connection->error = 1;
1886 return;
1889 connection->request = strdup("count");
1892 void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
1894 if (connection->request) {
1895 strcpy(connection->errorStr, "search already in progress");
1896 connection->error = 1;
1897 return;
1900 if (exact) connection->request = strdup("playlistfind");
1901 else connection->request = strdup("playlistsearch");
1904 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1906 char *strtype;
1907 int len;
1909 if (connection->request) {
1910 strcpy(connection->errorStr, "search already in progress");
1911 connection->error = 1;
1912 return;
1915 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1916 strcpy(connection->errorStr, "invalid type specified");
1917 connection->error = 1;
1918 return;
1921 strtype = mpdTagItemKeys[type];
1923 len = 5+strlen(strtype)+1;
1924 connection->request = malloc(len);
1926 snprintf(connection->request, len, "list %c%s",
1927 tolower(strtype[0]), strtype+1);
1930 void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name)
1932 char *strtype;
1933 char *arg;
1934 int len;
1935 char *string;
1937 if (!connection->request) {
1938 strcpy(connection->errorStr, "no search in progress");
1939 connection->error = 1;
1940 return;
1943 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1944 strcpy(connection->errorStr, "invalid type specified");
1945 connection->error = 1;
1946 return;
1949 if (name == NULL) {
1950 strcpy(connection->errorStr, "no name specified");
1951 connection->error = 1;
1952 return;
1955 string = strdup(connection->request);
1956 strtype = mpdTagItemKeys[type];
1957 arg = mpd_sanitizeArg(name);
1959 len = strlen(string)+1+strlen(strtype)+2+strlen(arg)+2;
1960 connection->request = realloc(connection->request, len);
1961 snprintf(connection->request, len, "%s %c%s \"%s\"",
1962 string, tolower(strtype[0]), strtype+1, arg);
1964 free(string);
1965 free(arg);
1968 void mpd_commitSearch(mpd_Connection *connection)
1970 int len;
1972 if (!connection->request) {
1973 strcpy(connection->errorStr, "no search in progress");
1974 connection->error = 1;
1975 return;
1978 len = strlen(connection->request)+2;
1979 connection->request = realloc(connection->request, len);
1980 connection->request[len-2] = '\n';
1981 connection->request[len-1] = '\0';
1982 mpd_sendInfoCommand(connection, connection->request);
1984 free(connection->request);
1985 connection->request = NULL;
1989 * @param connection a MpdConnection
1990 * @param path the path to the playlist.
1992 * List the content, with full metadata, of a stored playlist.
1995 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection,const char *path)
1997 char *arg = mpd_sanitizeArg(path);
1998 int len = strlen("listplaylistinfo")+2+strlen(arg)+3;
1999 char *query = malloc(len);
2000 snprintf(query, len, "listplaylistinfo \"%s\"\n", arg);
2001 mpd_sendInfoCommand(connection, query);
2002 free(arg);
2003 free(query);
2007 * @param connection a MpdConnection
2008 * @param path the path to the playlist.
2010 * List the content of a stored playlist.
2013 void mpd_sendListPlaylistCommand(mpd_Connection *connection,const char *path)
2015 char *arg = mpd_sanitizeArg(path);
2016 int len = strlen("listplaylist")+2+strlen(arg)+3;
2017 char *query = malloc(len);
2018 snprintf(query, len, "listplaylist \"%s\"\n", arg);
2019 mpd_sendInfoCommand(connection, query);
2020 free(arg);
2021 free(query);
2024 void mpd_sendPlaylistClearCommand(mpd_Connection *connection,const char *path)
2026 char *sPath = mpd_sanitizeArg(path);
2027 int len = strlen("playlistclear")+2+strlen(sPath)+3;
2028 char *string = malloc(len);
2029 snprintf(string, len, "playlistclear \"%s\"\n", sPath);
2030 mpd_executeCommand(connection, string);
2031 free(sPath);
2032 free(string);
2035 void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
2036 const char *playlist,const char *path)
2038 char *sPlaylist = mpd_sanitizeArg(playlist);
2039 char *sPath = mpd_sanitizeArg(path);
2040 int len = strlen("playlistadd")+2+strlen(sPlaylist)+3+strlen(sPath)+3;
2041 char *string = malloc(len);
2042 snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath);
2043 mpd_executeCommand(connection, string);
2044 free(sPlaylist);
2045 free(sPath);
2046 free(string);
2049 void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
2050 const char *playlist, int from, int to)
2052 char *sPlaylist = mpd_sanitizeArg(playlist);
2053 int len = strlen("playlistmove")+
2054 2+strlen(sPlaylist)+3+INTLEN+3+INTLEN+3;
2055 char *string = malloc(len);
2056 snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2057 sPlaylist, from, to);
2058 mpd_executeCommand(connection, string);
2059 free(sPlaylist);
2060 free(string);
2063 void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
2064 const char *playlist, int pos)
2066 char *sPlaylist = mpd_sanitizeArg(playlist);
2067 int len = strlen("playlistdelete")+2+strlen(sPlaylist)+3+INTLEN+3;
2068 char *string = malloc(len);
2069 snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos);
2070 mpd_executeCommand(connection, string);
2071 free(sPlaylist);
2072 free(string);
2074 void mpd_sendClearErrorCommand(mpd_Connection * connection) {
2075 mpd_executeCommand(connection,"clearerror\n");
2079 void mpd_sendGetEventsCommand(mpd_Connection *connection) {
2080 mpd_executeCommand(connection, "idle\nnoidle\n");
2081 // mpd_executeCommand(connection, "noidle\n");
2084 char * mpd_getNextEvent(mpd_Connection *connection)
2086 return mpd_getNextReturnElementNamed(connection, "changed");
2089 void mpd_sendListPlaylistsCommand(mpd_Connection * connection) {
2090 mpd_sendInfoCommand(connection, "listplaylists\n");
2093 char * mpd_getNextSticker (mpd_Connection * connection)
2095 return mpd_getNextReturnElementNamed(connection, "sticker");
2097 void mpd_sendGetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker)
2099 char *sSong = mpd_sanitizeArg(song_path);
2100 char *sSticker = mpd_sanitizeArg(sticker);
2101 int len = strlen("sticker get song ")+strlen(sSong)+3+strlen(sSticker)+4;
2102 char *string = malloc(len);
2103 snprintf(string, len, "sticker get song \"%s\" \"%s\"\n", sSong, sSticker);
2104 mpd_executeCommand(connection, string);
2105 free(string);
2106 free(sSong);
2107 free(sSticker);
2110 void mpd_sendSetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker, const char *value)
2113 char *sSong = mpd_sanitizeArg(song_path);
2114 char *sSticker = mpd_sanitizeArg(sticker);
2115 char *sValue = mpd_sanitizeArg(value);
2116 int len = strlen("sticker set song ")+strlen(sSong)+3+strlen(sSticker)+3+strlen(sValue)+4;
2117 char *string = malloc(len);
2118 snprintf(string, len, "sticker set song \"%s\" \"%s\" \"%s\"\n", sSong, sSticker,sValue);
2119 mpd_sendInfoCommand(connection, string);
2120 free(string);
2121 free(sSong);
2122 free(sSticker);
2123 free(sValue);