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