Update libtool version by 1 age (added functions)
[libmpd.git] / src / libmpdclient.c
blob697f8a205978e0adb46eb78f93d1712c81197344
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->nextsong = -1;
749 status->nextsongid = -1;
750 status->elapsedTime = 0;
751 status->totalTime = 0;
752 status->bitRate = 0;
753 status->sampleRate = 0;
754 status->bits = 0;
755 status->channels = 0;
756 status->crossfade = -1;
757 status->error = NULL;
758 status->updatingDb = 0;
760 if(connection->error) {
761 free(status);
762 return NULL;
764 while(connection->returnElement) {
765 mpd_ReturnElement * re = connection->returnElement;
766 if(strcmp(re->name,"volume")==0) {
767 status->volume = atoi(re->value);
769 else if(strcmp(re->name,"repeat")==0) {
770 status->repeat = atoi(re->value);
772 else if(strcmp(re->name,"random")==0) {
773 status->random = atoi(re->value);
775 else if(strcmp(re->name,"playlist")==0) {
776 status->playlist = strtol(re->value,NULL,10);
778 else if(strcmp(re->name,"playlistlength")==0) {
779 status->playlistLength = atoi(re->value);
781 else if(strcmp(re->name,"bitrate")==0) {
782 status->bitRate = atoi(re->value);
784 else if(strcmp(re->name,"state")==0) {
785 if(strcmp(re->value,"play")==0) {
786 status->state = MPD_STATUS_STATE_PLAY;
788 else if(strcmp(re->value,"stop")==0) {
789 status->state = MPD_STATUS_STATE_STOP;
791 else if(strcmp(re->value,"pause")==0) {
792 status->state = MPD_STATUS_STATE_PAUSE;
794 else {
795 status->state = MPD_STATUS_STATE_UNKNOWN;
798 else if(strcmp(re->name,"song")==0) {
799 status->song = atoi(re->value);
801 else if(strcmp(re->name,"songid")==0) {
802 status->songid = atoi(re->value);
804 else if(strcmp(re->name,"nextsong")==0) {
805 status->nextsong = atoi(re->value);
807 else if(strcmp(re->name,"nextsongid")==0) {
808 status->nextsongid = atoi(re->value);
810 else if(strcmp(re->name,"time")==0) {
811 char * tok = strchr(re->value,':');
812 /* the second strchr below is a safety check */
813 if (tok && (strchr(tok,0) > (tok+1))) {
814 /* atoi stops at the first non-[0-9] char: */
815 status->elapsedTime = atoi(re->value);
816 status->totalTime = atoi(tok+1);
819 else if(strcmp(re->name,"error")==0) {
820 status->error = strdup(re->value);
822 else if(strcmp(re->name,"xfade")==0) {
823 status->crossfade = atoi(re->value);
825 else if(strcmp(re->name,"updating_db")==0) {
826 status->updatingDb = atoi(re->value);
828 else if(strcmp(re->name,"audio")==0) {
829 char * tok = strchr(re->value,':');
830 if (tok && (strchr(tok,0) > (tok+1))) {
831 status->sampleRate = atoi(re->value);
832 status->bits = atoi(++tok);
833 tok = strchr(tok,':');
834 if (tok && (strchr(tok,0) > (tok+1)))
835 status->channels = atoi(tok+1);
839 mpd_getNextReturnElement(connection);
840 if(connection->error) {
841 free(status);
842 return NULL;
846 if(connection->error) {
847 free(status);
848 return NULL;
850 else if(status->state<0) {
851 strcpy(connection->errorStr,"state not found");
852 connection->error = 1;
853 free(status);
854 return NULL;
857 return status;
860 void mpd_freeStatus(mpd_Status * status) {
861 if(status->error) free(status->error);
862 free(status);
865 void mpd_sendStatsCommand(mpd_Connection * connection) {
866 mpd_executeCommand(connection,"stats\n");
869 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
870 mpd_Stats * stats;
872 /*mpd_executeCommand(connection,"stats\n");
874 if(connection->error) return NULL;*/
876 if(connection->doneProcessing || (connection->listOks &&
877 connection->doneListOk))
879 return NULL;
882 if(!connection->returnElement) mpd_getNextReturnElement(connection);
884 stats = malloc(sizeof(mpd_Stats));
885 stats->numberOfArtists = 0;
886 stats->numberOfAlbums = 0;
887 stats->numberOfSongs = 0;
888 stats->uptime = 0;
889 stats->dbUpdateTime = 0;
890 stats->playTime = 0;
891 stats->dbPlayTime = 0;
893 if(connection->error) {
894 free(stats);
895 return NULL;
897 while(connection->returnElement) {
898 mpd_ReturnElement * re = connection->returnElement;
899 if(strcmp(re->name,"artists")==0) {
900 stats->numberOfArtists = atoi(re->value);
902 else if(strcmp(re->name,"albums")==0) {
903 stats->numberOfAlbums = atoi(re->value);
905 else if(strcmp(re->name,"songs")==0) {
906 stats->numberOfSongs = atoi(re->value);
908 else if(strcmp(re->name,"uptime")==0) {
909 stats->uptime = strtol(re->value,NULL,10);
911 else if(strcmp(re->name,"db_update")==0) {
912 stats->dbUpdateTime = strtol(re->value,NULL,10);
914 else if(strcmp(re->name,"playtime")==0) {
915 stats->playTime = strtol(re->value,NULL,10);
917 else if(strcmp(re->name,"db_playtime")==0) {
918 stats->dbPlayTime = strtol(re->value,NULL,10);
921 mpd_getNextReturnElement(connection);
922 if(connection->error) {
923 free(stats);
924 return NULL;
928 if(connection->error) {
929 free(stats);
930 return NULL;
933 return stats;
936 void mpd_freeStats(mpd_Stats * stats) {
937 free(stats);
940 mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection)
942 mpd_SearchStats * stats;
943 mpd_ReturnElement * re;
945 if (connection->doneProcessing ||
946 (connection->listOks && connection->doneListOk)) {
947 return NULL;
950 if (!connection->returnElement) mpd_getNextReturnElement(connection);
952 if (connection->error)
953 return NULL;
955 stats = malloc(sizeof(mpd_SearchStats));
956 stats->numberOfSongs = 0;
957 stats->playTime = 0;
959 while (connection->returnElement) {
960 re = connection->returnElement;
962 if (strcmp(re->name, "songs") == 0) {
963 stats->numberOfSongs = atoi(re->value);
964 } else if (strcmp(re->name, "playtime") == 0) {
965 stats->playTime = strtol(re->value, NULL, 10);
968 mpd_getNextReturnElement(connection);
969 if (connection->error) {
970 free(stats);
971 return NULL;
975 if (connection->error) {
976 free(stats);
977 return NULL;
980 return stats;
983 void mpd_freeSearchStats(mpd_SearchStats * stats)
985 free(stats);
988 static void mpd_initSong(mpd_Song * song) {
989 song->file = NULL;
990 song->artist = NULL;
991 song->album = NULL;
992 song->track = NULL;
993 song->title = NULL;
994 song->name = NULL;
995 song->date = NULL;
996 /* added by Qball */
997 song->genre = NULL;
998 song->composer = NULL;
999 song->performer = NULL;
1000 song->disc = NULL;
1001 song->comment = NULL;
1002 song->albumartist = NULL;
1004 song->time = MPD_SONG_NO_TIME;
1005 song->pos = MPD_SONG_NO_NUM;
1006 song->id = MPD_SONG_NO_ID;
1009 static void mpd_finishSong(mpd_Song * song) {
1010 if(song->file) free(song->file);
1011 if(song->artist) free(song->artist);
1012 if(song->album) free(song->album);
1013 if(song->title) free(song->title);
1014 if(song->track) free(song->track);
1015 if(song->name) free(song->name);
1016 if(song->date) free(song->date);
1017 if(song->genre) free(song->genre);
1018 if(song->composer) free(song->composer);
1019 if(song->performer) free(song->performer);
1020 if(song->disc) free(song->disc);
1021 if(song->comment) free(song->comment);
1022 if(song->albumartist) free(song->albumartist);
1025 mpd_Song * mpd_newSong(void) {
1026 mpd_Song * ret = malloc(sizeof(mpd_Song));
1028 mpd_initSong(ret);
1030 return ret;
1033 void mpd_freeSong(mpd_Song * song) {
1034 mpd_finishSong(song);
1035 free(song);
1038 mpd_Song * mpd_songDup(const mpd_Song * song) {
1039 mpd_Song * ret = mpd_newSong();
1041 if(song->file) ret->file = strdup(song->file);
1042 if(song->artist) ret->artist = strdup(song->artist);
1043 if(song->album) ret->album = strdup(song->album);
1044 if(song->title) ret->title = strdup(song->title);
1045 if(song->track) ret->track = strdup(song->track);
1046 if(song->name) ret->name = strdup(song->name);
1047 if(song->date) ret->date = strdup(song->date);
1048 if(song->genre) ret->genre= strdup(song->genre);
1049 if(song->composer) ret->composer= strdup(song->composer);
1050 if(song->performer) ret->performer = strdup(song->performer);
1051 if(song->disc) ret->disc = strdup(song->disc);
1052 if(song->comment) ret->comment = strdup(song->comment);
1053 if(song->albumartist) ret->albumartist = strdup(song->albumartist);
1054 ret->time = song->time;
1055 ret->pos = song->pos;
1056 ret->id = song->id;
1058 return ret;
1061 static void mpd_initDirectory(mpd_Directory * directory) {
1062 directory->path = NULL;
1065 static void mpd_finishDirectory(mpd_Directory * directory) {
1066 if(directory->path) free(directory->path);
1069 mpd_Directory * mpd_newDirectory(void) {
1070 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
1072 mpd_initDirectory(directory);
1074 return directory;
1077 void mpd_freeDirectory(mpd_Directory * directory) {
1078 mpd_finishDirectory(directory);
1080 free(directory);
1083 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
1084 mpd_Directory * ret = mpd_newDirectory();
1086 if(directory->path) ret->path = strdup(directory->path);
1088 return ret;
1091 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
1092 playlist->path = NULL;
1093 playlist->mtime = NULL;
1096 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
1097 if(playlist->path) free(playlist->path);
1098 if(playlist->mtime) free(playlist->mtime);
1101 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
1102 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
1104 mpd_initPlaylistFile(playlist);
1106 return playlist;
1109 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
1110 mpd_finishPlaylistFile(playlist);
1111 free(playlist);
1114 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1115 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1117 if(playlist->path) ret->path = strdup(playlist->path);
1118 if(playlist->mtime) ret->mtime = strdup(playlist->mtime);
1120 return ret;
1123 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
1124 entity->info.directory = NULL;
1127 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1128 if(entity->info.directory) {
1129 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1130 mpd_freeDirectory(entity->info.directory);
1132 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1133 mpd_freeSong(entity->info.song);
1135 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1136 mpd_freePlaylistFile(entity->info.playlistFile);
1141 mpd_InfoEntity * mpd_newInfoEntity(void) {
1142 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
1144 mpd_initInfoEntity(entity);
1146 return entity;
1149 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1150 mpd_finishInfoEntity(entity);
1151 free(entity);
1154 static void mpd_sendInfoCommand(mpd_Connection * connection,const char * command) {
1155 mpd_executeCommand(connection,command);
1158 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1159 mpd_InfoEntity * entity = NULL;
1161 if(connection->doneProcessing || (connection->listOks &&
1162 connection->doneListOk))
1164 return NULL;
1167 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1169 if(connection->returnElement) {
1170 if(strcmp(connection->returnElement->name,"file")==0) {
1171 entity = mpd_newInfoEntity();
1172 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1173 entity->info.song = mpd_newSong();
1174 entity->info.song->file =
1175 strdup(connection->returnElement->value);
1177 else if(strcmp(connection->returnElement->name,
1178 "directory")==0) {
1179 entity = mpd_newInfoEntity();
1180 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1181 entity->info.directory = mpd_newDirectory();
1182 entity->info.directory->path =
1183 strdup(connection->returnElement->value);
1185 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1186 entity = mpd_newInfoEntity();
1187 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1188 entity->info.playlistFile = mpd_newPlaylistFile();
1189 entity->info.playlistFile->path =
1190 strdup(connection->returnElement->value);
1192 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1193 entity = mpd_newInfoEntity();
1194 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1195 entity->info.song = mpd_newSong();
1196 entity->info.song->pos = atoi(connection->returnElement->value);
1198 else {
1199 connection->error = 1;
1200 strcpy(connection->errorStr,"problem parsing song info");
1201 return NULL;
1204 else return NULL;
1206 mpd_getNextReturnElement(connection);
1207 while(connection->returnElement) {
1208 mpd_ReturnElement * re = connection->returnElement;
1210 if(strcmp(re->name,"file")==0) return entity;
1211 else if(strcmp(re->name,"directory")==0) return entity;
1212 else if(strcmp(re->name,"playlist")==0) return entity;
1213 else if(strcmp(re->name,"cpos")==0) return entity;
1215 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1216 strlen(re->value)) {
1217 if(strcmp(re->name,"Artist")==0) {
1218 if(entity->info.song->artist) {
1219 int length = strlen(entity->info.song->artist);
1220 entity->info.song->artist = realloc(entity->info.song->artist,
1221 length + strlen(re->value) + 3);
1222 strcpy(&((entity->info.song->artist)[length]), ", ");
1223 strcpy(&((entity->info.song->artist)[length + 2]), re->value);
1225 else {
1226 entity->info.song->artist = strdup(re->value);
1229 else if(!entity->info.song->album &&
1230 strcmp(re->name,"Album")==0) {
1231 entity->info.song->album = strdup(re->value);
1233 else if(!entity->info.song->title &&
1234 strcmp(re->name,"Title")==0) {
1235 entity->info.song->title = strdup(re->value);
1237 else if(!entity->info.song->track &&
1238 strcmp(re->name,"Track")==0) {
1239 entity->info.song->track = strdup(re->value);
1241 else if(!entity->info.song->name &&
1242 strcmp(re->name,"Name")==0) {
1243 entity->info.song->name = strdup(re->value);
1245 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1246 strcmp(re->name,"Time")==0) {
1247 entity->info.song->time = atoi(re->value);
1249 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1250 strcmp(re->name,"Pos")==0) {
1251 entity->info.song->pos = atoi(re->value);
1253 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1254 strcmp(re->name,"Id")==0) {
1255 entity->info.song->id = atoi(re->value);
1257 else if(!entity->info.song->date &&
1258 strcmp(re->name, "Date") == 0) {
1259 entity->info.song->date = strdup(re->value);
1261 else if(!entity->info.song->genre &&
1262 strcmp(re->name, "Genre") == 0) {
1263 if(entity->info.song->genre) {
1264 int length = strlen(entity->info.song->genre);
1265 entity->info.song->genre = realloc(entity->info.song->genre,
1266 length + strlen(re->value) + 4);
1267 strcpy(&((entity->info.song->genre)[length]), ", ");
1268 strcpy(&((entity->info.song->genre)[length + 3]), re->value);
1270 else {
1271 entity->info.song->genre = strdup(re->value);
1274 else if(strcmp(re->name, "Composer") == 0) {
1275 if(entity->info.song->composer) {
1276 int length = strlen(entity->info.song->composer);
1277 entity->info.song->composer = realloc(entity->info.song->composer,
1278 length + strlen(re->value) + 3);
1279 strcpy(&((entity->info.song->composer)[length]), ", ");
1280 strcpy(&((entity->info.song->composer)[length + 2]), re->value);
1282 else {
1283 entity->info.song->composer = strdup(re->value);
1286 else if(strcmp(re->name, "Performer") == 0) {
1287 if(entity->info.song->performer) {
1288 int length = strlen(entity->info.song->performer);
1289 entity->info.song->performer = realloc(entity->info.song->performer,
1290 length + strlen(re->value) + 3);
1291 strcpy(&((entity->info.song->performer)[length]), ", ");
1292 strcpy(&((entity->info.song->performer)[length + 2]), re->value);
1294 else {
1295 entity->info.song->performer = strdup(re->value);
1298 else if(!entity->info.song->disc &&
1299 strcmp(re->name, "Disc") == 0) {
1300 entity->info.song->disc = strdup(re->value);
1302 else if(!entity->info.song->comment &&
1303 strcmp(re->name, "Comment") == 0) {
1304 entity->info.song->comment = strdup(re->value);
1307 else if(!entity->info.song->albumartist &&
1308 strcmp(re->name, "AlbumArtist") == 0) {
1309 entity->info.song->albumartist = strdup(re->value);
1312 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1314 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1315 if(!entity->info.playlistFile->mtime &&
1316 strcmp(re->name, "Last-Modified") == 0) {
1317 entity->info.playlistFile->mtime = strdup(re->value);
1321 mpd_getNextReturnElement(connection);
1324 return entity;
1327 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1328 const char * name)
1330 if(connection->doneProcessing || (connection->listOks &&
1331 connection->doneListOk))
1333 return NULL;
1336 mpd_getNextReturnElement(connection);
1337 while(connection->returnElement) {
1338 mpd_ReturnElement * re = connection->returnElement;
1340 if(strcmp(re->name,name)==0) return strdup(re->value);
1341 mpd_getNextReturnElement(connection);
1344 return NULL;
1347 char *mpd_getNextTag(mpd_Connection *connection, int type)
1349 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES ||
1350 type == MPD_TAG_ITEM_ANY)
1351 return NULL;
1352 if (type == MPD_TAG_ITEM_FILENAME)
1353 return mpd_getNextReturnElementNamed(connection, "file");
1354 return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
1357 char * mpd_getNextArtist(mpd_Connection * connection) {
1358 return mpd_getNextReturnElementNamed(connection,"Artist");
1361 char * mpd_getNextAlbum(mpd_Connection * connection) {
1362 return mpd_getNextReturnElementNamed(connection,"Album");
1365 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1366 int len = strlen("playlistinfo")+2+INTLEN+3;
1367 char *string = malloc(len);
1368 snprintf(string, len, "playlistinfo \"%i\"\n", songPos);
1369 mpd_sendInfoCommand(connection,string);
1370 free(string);
1373 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1374 int len = strlen("playlistid")+2+INTLEN+3;
1375 char *string = malloc(len);
1376 snprintf(string, len, "playlistid \"%i\"\n", id);
1377 mpd_sendInfoCommand(connection, string);
1378 free(string);
1381 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1382 int len = strlen("plchanges")+2+LONGLONGLEN+3;
1383 char *string = malloc(len);
1384 snprintf(string, len, "plchanges \"%lld\"\n", playlist);
1385 mpd_sendInfoCommand(connection,string);
1386 free(string);
1389 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1390 int len = strlen("plchangesposid")+2+LONGLONGLEN+3;
1391 char *string = malloc(len);
1392 snprintf(string, len, "plchangesposid \"%lld\"\n", playlist);
1393 mpd_sendInfoCommand(connection,string);
1394 free(string);
1397 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1398 char * sDir = mpd_sanitizeArg(dir);
1399 int len = strlen("listall")+2+strlen(sDir)+3;
1400 char *string = malloc(len);
1401 snprintf(string, len, "listall \"%s\"\n", sDir);
1402 mpd_sendInfoCommand(connection,string);
1403 free(string);
1404 free(sDir);
1407 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1408 char * sDir = mpd_sanitizeArg(dir);
1409 int len = strlen("listallinfo")+2+strlen(sDir)+3;
1410 char *string = malloc(len);
1411 snprintf(string, len, "listallinfo \"%s\"\n", sDir);
1412 mpd_sendInfoCommand(connection,string);
1413 free(string);
1414 free(sDir);
1417 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1418 char * sDir = mpd_sanitizeArg(dir);
1419 int len = strlen("lsinfo")+2+strlen(sDir)+3;
1420 char *string = malloc(len);
1421 snprintf(string, len, "lsinfo \"%s\"\n", sDir);
1422 mpd_sendInfoCommand(connection,string);
1423 free(string);
1424 free(sDir);
1427 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1428 mpd_executeCommand(connection,"currentsong\n");
1431 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1432 const char * str)
1434 mpd_startSearch(connection, 0);
1435 mpd_addConstraintSearch(connection, table, str);
1436 mpd_commitSearch(connection);
1439 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1440 const char * str)
1442 mpd_startSearch(connection, 1);
1443 mpd_addConstraintSearch(connection, table, str);
1444 mpd_commitSearch(connection);
1447 void mpd_sendListCommand(mpd_Connection * connection, int table,
1448 const char * arg1)
1450 char st[10];
1451 int len;
1452 char *string;
1453 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1454 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1455 else {
1456 connection->error = 1;
1457 strcpy(connection->errorStr,"unknown table for list");
1458 return;
1460 if(arg1) {
1461 char * sanitArg1 = mpd_sanitizeArg(arg1);
1462 len = strlen("list")+1+strlen(sanitArg1)+2+strlen(st)+3;
1463 string = malloc(len);
1464 snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1);
1465 free(sanitArg1);
1467 else {
1468 len = strlen("list")+1+strlen(st)+2;
1469 string = malloc(len);
1470 snprintf(string, len, "list %s\n", st);
1472 mpd_sendInfoCommand(connection,string);
1473 free(string);
1476 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1477 char * sFile = mpd_sanitizeArg(file);
1478 int len = strlen("add")+2+strlen(sFile)+3;
1479 char *string = malloc(len);
1480 snprintf(string, len, "add \"%s\"\n", sFile);
1481 mpd_executeCommand(connection,string);
1482 free(string);
1483 free(sFile);
1486 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1488 int retval = -1;
1489 char *sFile = mpd_sanitizeArg(file);
1490 int len = strlen("addid")+2+strlen(sFile)+3;
1491 char *string = malloc(len);
1493 snprintf(string, len, "addid \"%s\"\n", sFile);
1494 mpd_sendInfoCommand(connection, string);
1495 free(string);
1496 free(sFile);
1498 string = mpd_getNextReturnElementNamed(connection, "Id");
1499 if (string) {
1500 retval = atoi(string);
1501 free(string);
1504 return retval;
1507 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1508 int len = strlen("delete")+2+INTLEN+3;
1509 char *string = malloc(len);
1510 snprintf(string, len, "delete \"%i\"\n", songPos);
1511 mpd_sendInfoCommand(connection,string);
1512 free(string);
1515 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1516 int len = strlen("deleteid")+2+INTLEN+3;
1517 char *string = malloc(len);
1518 snprintf(string, len, "deleteid \"%i\"\n", id);
1519 mpd_sendInfoCommand(connection,string);
1520 free(string);
1523 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1524 char * sName = mpd_sanitizeArg(name);
1525 int len = strlen("save")+2+strlen(sName)+3;
1526 char *string = malloc(len);
1527 snprintf(string, len, "save \"%s\"\n", sName);
1528 mpd_executeCommand(connection,string);
1529 free(string);
1530 free(sName);
1533 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1534 char * sName = mpd_sanitizeArg(name);
1535 int len = strlen("load")+2+strlen(sName)+3;
1536 char *string = malloc(len);
1537 snprintf(string, len, "load \"%s\"\n", sName);
1538 mpd_executeCommand(connection,string);
1539 free(string);
1540 free(sName);
1543 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1544 char * sName = mpd_sanitizeArg(name);
1545 int len = strlen("rm")+2+strlen(sName)+3;
1546 char *string = malloc(len);
1547 snprintf(string, len, "rm \"%s\"\n", sName);
1548 mpd_executeCommand(connection,string);
1549 free(string);
1550 free(sName);
1553 void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
1554 const char *to)
1556 char *sFrom = mpd_sanitizeArg(from);
1557 char *sTo = mpd_sanitizeArg(to);
1558 int len = strlen("rename")+2+strlen(sFrom)+3+strlen(sTo)+3;
1559 char *string = malloc(len);
1560 snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo);
1561 mpd_executeCommand(connection, string);
1562 free(string);
1563 free(sFrom);
1564 free(sTo);
1567 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1568 mpd_executeCommand(connection,"shuffle\n");
1571 void mpd_sendClearCommand(mpd_Connection * connection) {
1572 mpd_executeCommand(connection,"clear\n");
1575 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1576 int len = strlen("play")+2+INTLEN+3;
1577 char *string = malloc(len);
1578 snprintf(string, len, "play \"%i\"\n", songPos);
1579 mpd_sendInfoCommand(connection,string);
1580 free(string);
1583 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1584 int len = strlen("playid")+2+INTLEN+3;
1585 char *string = malloc(len);
1586 snprintf(string, len, "playid \"%i\"\n", id);
1587 mpd_sendInfoCommand(connection,string);
1588 free(string);
1591 void mpd_sendStopCommand(mpd_Connection * connection) {
1592 mpd_executeCommand(connection,"stop\n");
1595 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1596 int len = strlen("pause")+2+INTLEN+3;
1597 char *string = malloc(len);
1598 snprintf(string, len, "pause \"%i\"\n", pauseMode);
1599 mpd_executeCommand(connection,string);
1600 free(string);
1603 void mpd_sendNextCommand(mpd_Connection * connection) {
1604 mpd_executeCommand(connection,"next\n");
1607 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1608 int len = strlen("move")+2+INTLEN+3+INTLEN+3;
1609 char *string = malloc(len);
1610 snprintf(string, len, "move \"%i\" \"%i\"\n", from, to);
1611 mpd_sendInfoCommand(connection,string);
1612 free(string);
1615 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1616 int len = strlen("moveid")+2+INTLEN+3+INTLEN+3;
1617 char *string = malloc(len);
1618 snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to);
1619 mpd_sendInfoCommand(connection,string);
1620 free(string);
1623 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1624 int len = strlen("swap")+2+INTLEN+3+INTLEN+3;
1625 char *string = malloc(len);
1626 snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2);
1627 mpd_sendInfoCommand(connection,string);
1628 free(string);
1631 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1632 int len = strlen("swapid")+2+INTLEN+3+INTLEN+3;
1633 char *string = malloc(len);
1634 snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2);
1635 mpd_sendInfoCommand(connection,string);
1636 free(string);
1639 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1640 int len = strlen("seek")+2+INTLEN+3+INTLEN+3;
1641 char *string = malloc(len);
1642 snprintf(string, len, "seek \"%i\" \"%i\"\n", song, time);
1643 mpd_sendInfoCommand(connection,string);
1644 free(string);
1647 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1648 int len = strlen("seekid")+2+INTLEN+3+INTLEN+3;
1649 char *string = malloc(len);
1650 snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, time);
1651 mpd_sendInfoCommand(connection,string);
1652 free(string);
1655 void mpd_sendUpdateCommand(mpd_Connection * connection,const char * path) {
1656 char * sPath = mpd_sanitizeArg(path);
1657 int len = strlen("update")+2+strlen(sPath)+3;
1658 char *string = malloc(len);
1659 snprintf(string, len, "update \"%s\"\n", sPath);
1660 mpd_sendInfoCommand(connection,string);
1661 free(string);
1662 free(sPath);
1665 int mpd_getUpdateId(mpd_Connection * connection) {
1666 char * jobid;
1667 int ret = 0;
1669 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1670 if(jobid) {
1671 ret = atoi(jobid);
1672 free(jobid);
1675 return ret;
1678 void mpd_sendPrevCommand(mpd_Connection * connection) {
1679 mpd_executeCommand(connection,"previous\n");
1682 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1683 int len = strlen("repeat")+2+INTLEN+3;
1684 char *string = malloc(len);
1685 snprintf(string, len, "repeat \"%i\"\n", repeatMode);
1686 mpd_executeCommand(connection,string);
1687 free(string);
1690 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1691 int len = strlen("random")+2+INTLEN+3;
1692 char *string = malloc(len);
1693 snprintf(string, len, "random \"%i\"\n", randomMode);
1694 mpd_executeCommand(connection,string);
1695 free(string);
1698 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1699 int len = strlen("setvol")+2+INTLEN+3;
1700 char *string = malloc(len);
1701 snprintf(string, len, "setvol \"%i\"\n", volumeChange);
1702 mpd_executeCommand(connection,string);
1703 free(string);
1706 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1707 int len = strlen("volume")+2+INTLEN+3;
1708 char *string = malloc(len);
1709 snprintf(string, len, "volume \"%i\"\n", volumeChange);
1710 mpd_executeCommand(connection,string);
1711 free(string);
1714 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1715 int len = strlen("crossfade")+2+INTLEN+3;
1716 char *string = malloc(len);
1717 snprintf(string, len, "crossfade \"%i\"\n", seconds);
1718 mpd_executeCommand(connection,string);
1719 free(string);
1722 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1723 char * sPass = mpd_sanitizeArg(pass);
1724 int len = strlen("password")+2+strlen(sPass)+3;
1725 char *string = malloc(len);
1726 snprintf(string, len, "password \"%s\"\n", sPass);
1727 mpd_executeCommand(connection,string);
1728 free(string);
1729 free(sPass);
1732 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1733 if(connection->commandList) {
1734 strcpy(connection->errorStr,"already in command list mode");
1735 connection->error = 1;
1736 return;
1738 connection->commandList = COMMAND_LIST;
1739 mpd_executeCommand(connection,"command_list_begin\n");
1742 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1743 if(connection->commandList) {
1744 strcpy(connection->errorStr,"already in command list mode");
1745 connection->error = 1;
1746 return;
1748 connection->commandList = COMMAND_LIST_OK;
1749 mpd_executeCommand(connection,"command_list_ok_begin\n");
1750 connection->listOks = 0;
1753 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1754 if(!connection->commandList) {
1755 strcpy(connection->errorStr,"not in command list mode");
1756 connection->error = 1;
1757 return;
1759 connection->commandList = 0;
1760 mpd_executeCommand(connection,"command_list_end\n");
1763 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1764 mpd_executeCommand(connection,"outputs\n");
1767 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1768 mpd_OutputEntity * output = NULL;
1770 if(connection->doneProcessing || (connection->listOks &&
1771 connection->doneListOk))
1773 return NULL;
1776 if(connection->error) return NULL;
1778 output = malloc(sizeof(mpd_OutputEntity));
1779 output->id = -10;
1780 output->name = NULL;
1781 output->enabled = 0;
1783 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1785 while(connection->returnElement) {
1786 mpd_ReturnElement * re = connection->returnElement;
1787 if(strcmp(re->name,"outputid")==0) {
1788 if(output!=NULL && output->id>=0) return output;
1789 output->id = atoi(re->value);
1791 else if(strcmp(re->name,"outputname")==0) {
1792 output->name = strdup(re->value);
1794 else if(strcmp(re->name,"outputenabled")==0) {
1795 output->enabled = atoi(re->value);
1798 mpd_getNextReturnElement(connection);
1799 if(connection->error) {
1800 free(output);
1801 return NULL;
1806 return output;
1809 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1810 int len = strlen("enableoutput")+2+INTLEN+3;
1811 char *string = malloc(len);
1812 snprintf(string, len, "enableoutput \"%i\"\n", outputId);
1813 mpd_executeCommand(connection,string);
1814 free(string);
1817 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1818 int len = strlen("disableoutput")+2+INTLEN+3;
1819 char *string = malloc(len);
1820 snprintf(string, len, "disableoutput \"%i\"\n", outputId);
1821 mpd_executeCommand(connection,string);
1822 free(string);
1825 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1826 free(output->name);
1827 free(output);
1831 * mpd_sendNotCommandsCommand
1832 * odd naming, but it gets the not allowed commands
1835 void mpd_sendNotCommandsCommand(mpd_Connection * connection)
1837 mpd_executeCommand(connection, "notcommands\n");
1841 * mpd_sendCommandsCommand
1842 * odd naming, but it gets the allowed commands
1844 void mpd_sendCommandsCommand(mpd_Connection * connection)
1846 mpd_executeCommand(connection, "commands\n");
1850 * Get the next returned command
1852 char * mpd_getNextCommand(mpd_Connection * connection)
1854 return mpd_getNextReturnElementNamed(connection, "command");
1857 void mpd_sendUrlHandlersCommand(mpd_Connection * connection)
1859 mpd_executeCommand(connection, "urlhandlers\n");
1862 char * mpd_getNextHandler(mpd_Connection * connection)
1864 return mpd_getNextReturnElementNamed(connection, "handler");
1867 void mpd_sendTagTypesCommand(mpd_Connection * connection)
1869 mpd_executeCommand(connection, "tagtypes\n");
1872 char * mpd_getNextTagType(mpd_Connection * connection)
1874 return mpd_getNextReturnElementNamed(connection, "tagtype");
1877 void mpd_startSearch(mpd_Connection *connection, int exact)
1879 if (connection->request) {
1880 strcpy(connection->errorStr, "search already in progress");
1881 connection->error = 1;
1882 return;
1885 if (exact) connection->request = strdup("find");
1886 else connection->request = strdup("search");
1889 void mpd_startStatsSearch(mpd_Connection *connection)
1891 if (connection->request) {
1892 strcpy(connection->errorStr, "search already in progress");
1893 connection->error = 1;
1894 return;
1897 connection->request = strdup("count");
1900 void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
1902 if (connection->request) {
1903 strcpy(connection->errorStr, "search already in progress");
1904 connection->error = 1;
1905 return;
1908 if (exact) connection->request = strdup("playlistfind");
1909 else connection->request = strdup("playlistsearch");
1912 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1914 char *strtype;
1915 int len;
1917 if (connection->request) {
1918 strcpy(connection->errorStr, "search already in progress");
1919 connection->error = 1;
1920 return;
1923 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1924 strcpy(connection->errorStr, "invalid type specified");
1925 connection->error = 1;
1926 return;
1929 strtype = mpdTagItemKeys[type];
1931 len = 5+strlen(strtype)+1;
1932 connection->request = malloc(len);
1934 snprintf(connection->request, len, "list %c%s",
1935 tolower(strtype[0]), strtype+1);
1938 void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name)
1940 char *strtype;
1941 char *arg;
1942 int len;
1943 char *string;
1945 if (!connection->request) {
1946 strcpy(connection->errorStr, "no search in progress");
1947 connection->error = 1;
1948 return;
1951 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1952 strcpy(connection->errorStr, "invalid type specified");
1953 connection->error = 1;
1954 return;
1957 if (name == NULL) {
1958 strcpy(connection->errorStr, "no name specified");
1959 connection->error = 1;
1960 return;
1963 string = strdup(connection->request);
1964 strtype = mpdTagItemKeys[type];
1965 arg = mpd_sanitizeArg(name);
1967 len = strlen(string)+1+strlen(strtype)+2+strlen(arg)+2;
1968 connection->request = realloc(connection->request, len);
1969 snprintf(connection->request, len, "%s %c%s \"%s\"",
1970 string, tolower(strtype[0]), strtype+1, arg);
1972 free(string);
1973 free(arg);
1976 void mpd_commitSearch(mpd_Connection *connection)
1978 int len;
1980 if (!connection->request) {
1981 strcpy(connection->errorStr, "no search in progress");
1982 connection->error = 1;
1983 return;
1986 len = strlen(connection->request)+2;
1987 connection->request = realloc(connection->request, len);
1988 connection->request[len-2] = '\n';
1989 connection->request[len-1] = '\0';
1990 mpd_sendInfoCommand(connection, connection->request);
1992 free(connection->request);
1993 connection->request = NULL;
1997 * @param connection a MpdConnection
1998 * @param path the path to the playlist.
2000 * List the content, with full metadata, of a stored playlist.
2003 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection,const char *path)
2005 char *arg = mpd_sanitizeArg(path);
2006 int len = strlen("listplaylistinfo")+2+strlen(arg)+3;
2007 char *query = malloc(len);
2008 snprintf(query, len, "listplaylistinfo \"%s\"\n", arg);
2009 mpd_sendInfoCommand(connection, query);
2010 free(arg);
2011 free(query);
2015 * @param connection a MpdConnection
2016 * @param path the path to the playlist.
2018 * List the content of a stored playlist.
2021 void mpd_sendListPlaylistCommand(mpd_Connection *connection,const char *path)
2023 char *arg = mpd_sanitizeArg(path);
2024 int len = strlen("listplaylist")+2+strlen(arg)+3;
2025 char *query = malloc(len);
2026 snprintf(query, len, "listplaylist \"%s\"\n", arg);
2027 mpd_sendInfoCommand(connection, query);
2028 free(arg);
2029 free(query);
2032 void mpd_sendPlaylistClearCommand(mpd_Connection *connection,const char *path)
2034 char *sPath = mpd_sanitizeArg(path);
2035 int len = strlen("playlistclear")+2+strlen(sPath)+3;
2036 char *string = malloc(len);
2037 snprintf(string, len, "playlistclear \"%s\"\n", sPath);
2038 mpd_executeCommand(connection, string);
2039 free(sPath);
2040 free(string);
2043 void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
2044 const char *playlist,const char *path)
2046 char *sPlaylist = mpd_sanitizeArg(playlist);
2047 char *sPath = mpd_sanitizeArg(path);
2048 int len = strlen("playlistadd")+2+strlen(sPlaylist)+3+strlen(sPath)+3;
2049 char *string = malloc(len);
2050 snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath);
2051 mpd_executeCommand(connection, string);
2052 free(sPlaylist);
2053 free(sPath);
2054 free(string);
2057 void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
2058 const char *playlist, int from, int to)
2060 char *sPlaylist = mpd_sanitizeArg(playlist);
2061 int len = strlen("playlistmove")+
2062 2+strlen(sPlaylist)+3+INTLEN+3+INTLEN+3;
2063 char *string = malloc(len);
2064 snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2065 sPlaylist, from, to);
2066 mpd_executeCommand(connection, string);
2067 free(sPlaylist);
2068 free(string);
2071 void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
2072 const char *playlist, int pos)
2074 char *sPlaylist = mpd_sanitizeArg(playlist);
2075 int len = strlen("playlistdelete")+2+strlen(sPlaylist)+3+INTLEN+3;
2076 char *string = malloc(len);
2077 snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos);
2078 mpd_executeCommand(connection, string);
2079 free(sPlaylist);
2080 free(string);
2082 void mpd_sendClearErrorCommand(mpd_Connection * connection) {
2083 mpd_executeCommand(connection,"clearerror\n");
2087 void mpd_sendGetEventsCommand(mpd_Connection *connection) {
2088 mpd_executeCommand(connection, "idle\nnoidle\n");
2089 // mpd_executeCommand(connection, "noidle\n");
2092 char * mpd_getNextEvent(mpd_Connection *connection)
2094 return mpd_getNextReturnElementNamed(connection, "changed");
2097 void mpd_sendListPlaylistsCommand(mpd_Connection * connection) {
2098 mpd_sendInfoCommand(connection, "listplaylists\n");
2101 char * mpd_getNextSticker (mpd_Connection * connection)
2103 return mpd_getNextReturnElementNamed(connection, "sticker");
2105 void mpd_sendGetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker)
2107 char *sSong = mpd_sanitizeArg(song_path);
2108 char *sSticker = mpd_sanitizeArg(sticker);
2109 int len = strlen("sticker get song ")+strlen(sSong)+3+strlen(sSticker)+4;
2110 char *string = malloc(len);
2111 snprintf(string, len, "sticker get song \"%s\" \"%s\"\n", sSong, sSticker);
2112 mpd_executeCommand(connection, string);
2113 free(string);
2114 free(sSong);
2115 free(sSticker);
2118 void mpd_sendSetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker, const char *value)
2121 char *sSong = mpd_sanitizeArg(song_path);
2122 char *sSticker = mpd_sanitizeArg(sticker);
2123 char *sValue = mpd_sanitizeArg(value);
2124 int len = strlen("sticker set song ")+strlen(sSong)+3+strlen(sSticker)+3+strlen(sValue)+4;
2125 char *string = malloc(len);
2126 snprintf(string, len, "sticker set song \"%s\" \"%s\" \"%s\"\n", sSong, sSticker,sValue);
2127 mpd_sendInfoCommand(connection, string);
2128 free(string);
2129 free(sSong);
2130 free(sSticker);
2131 free(sValue);