updated on Thu Jan 26 16:09:46 UTC 2012
[aur-mirror.git] / mpd-notify / libmpdclient.c
bloba83c5060d3320f57269d603feb2b9da61dc9e2cd
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>
45 #ifdef WIN32
46 # include <ws2tcpip.h>
47 # include <winsock.h>
48 #else
49 # include <netinet/in.h>
50 # include <arpa/inet.h>
51 # include <sys/socket.h>
52 # include <netdb.h>
53 #endif
55 #ifndef MSG_DONTWAIT
56 # define MSG_DONTWAIT 0
57 #endif
59 #ifndef MPD_NO_GAI
60 # ifdef AI_ADDRCONFIG
61 # define MPD_HAVE_GAI
62 # endif
63 #endif
65 #define COMMAND_LIST 1
66 #define COMMAND_LIST_OK 2
68 #ifdef WIN32
69 # define SELECT_ERRNO_IGNORE (errno == WSAEINTR || errno == WSAEINPROGRESS)
70 # define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
71 #else
72 # define SELECT_ERRNO_IGNORE (errno == EINTR)
73 # define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
74 # define winsock_dll_error(c) 0
75 # define closesocket(s) close(s)
76 # define WSACleanup() do { /* nothing */ } while (0)
77 #endif
79 #ifdef WIN32
80 static int winsock_dll_error(mpd_Connection *connection)
82 WSADATA wsaData;
83 if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
84 LOBYTE(wsaData.wVersion) != 2 ||
85 HIBYTE(wsaData.wVersion) != 2 ) {
86 strcpy(connection->errorStr,
87 "Could not find usable WinSock DLL.");
88 connection->error = MPD_ERROR_SYSTEM;
89 return 1;
91 return 0;
94 static int do_connect_fail(mpd_Connection *connection,
95 const struct sockaddr *serv_addr, int addrlen)
97 int iMode = 1; /* 0 = blocking, else non-blocking */
98 ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
99 return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR
100 && WSAGetLastError() != WSAEWOULDBLOCK);
102 #else /* !WIN32 (sane operating systems) */
103 static int do_connect_fail(mpd_Connection *connection,
104 const struct sockaddr *serv_addr, int addrlen)
106 int flags = fcntl(connection->sock, F_GETFL, 0);
107 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
108 return (connect(connection->sock,serv_addr,addrlen)<0 &&
109 errno!=EINPROGRESS);
111 #endif /* !WIN32 */
113 #ifdef MPD_HAVE_GAI
114 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
115 float timeout)
117 int error;
118 char service[20];
119 struct addrinfo hints;
120 struct addrinfo *res = NULL;
121 struct addrinfo *addrinfo = NULL;
124 * Setup hints
126 hints.ai_flags = AI_ADDRCONFIG;
127 hints.ai_family = PF_UNSPEC;
128 hints.ai_socktype = SOCK_STREAM;
129 hints.ai_protocol = IPPROTO_TCP;
130 hints.ai_addrlen = 0;
131 hints.ai_addr = NULL;
132 hints.ai_canonname = NULL;
133 hints.ai_next = NULL;
135 snprintf(service, sizeof(service), "%d", port);
137 error = getaddrinfo(host, service, &hints, &addrinfo);
139 if (error) {
140 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
141 "host \"%s\" not found: %s",host, gai_strerror(error));
142 connection->error = MPD_ERROR_UNKHOST;
143 return -1;
146 for (res = addrinfo; res; res = res->ai_next) {
147 /* create socket */
148 connection->sock = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
149 if (connection->sock < 0) {
150 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
151 "problems creating socket: %s",
152 strerror(errno));
153 connection->error = MPD_ERROR_SYSTEM;
154 freeaddrinfo(addrinfo);
155 return -1;
158 mpd_setConnectionTimeout(connection,timeout);
160 /* connect stuff */
161 if (do_connect_fail(connection, res->ai_addr, res->ai_addrlen)) {
162 /* try the next address family */
163 closesocket(connection->sock);
164 connection->sock = -1;
165 continue;
168 freeaddrinfo(addrinfo);
170 if (connection->sock < 0) {
171 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
172 "problems connecting to \"%s\" on port"
173 " %i: %s",host,port, strerror(errno));
174 connection->error = MPD_ERROR_CONNPORT;
176 return -1;
179 return 0;
181 #else /* !MPD_HAVE_GAI */
182 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
183 float timeout)
185 struct hostent * he;
186 struct sockaddr * dest;
187 int destlen;
188 struct sockaddr_in sin;
190 if(!(he=gethostbyname(host))) {
191 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
192 "host \"%s\" not found",host);
193 connection->error = MPD_ERROR_UNKHOST;
194 return -1;
197 memset(&sin,0,sizeof(struct sockaddr_in));
198 /*dest.sin_family = he->h_addrtype;*/
199 sin.sin_family = AF_INET;
200 sin.sin_port = htons(port);
202 switch(he->h_addrtype) {
203 case AF_INET:
204 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
205 he->h_length);
206 dest = (struct sockaddr *)&sin;
207 destlen = sizeof(struct sockaddr_in);
208 break;
209 default:
210 strcpy(connection->errorStr,"address type is not IPv4");
211 connection->error = MPD_ERROR_SYSTEM;
212 return -1;
213 break;
216 if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
217 strcpy(connection->errorStr,"problems creating socket");
218 connection->error = MPD_ERROR_SYSTEM;
219 return -1;
222 mpd_setConnectionTimeout(connection,timeout);
224 /* connect stuff */
225 if (do_connect_fail(connection, dest, destlen)) {
226 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
227 "problems connecting to \"%s\" on port"
228 " %i",host,port);
229 connection->error = MPD_ERROR_CONNPORT;
230 return -1;
233 return 0;
235 #endif /* !MPD_HAVE_GAI */
237 char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
239 "Artist",
240 "Album",
241 "Title",
242 "Track",
243 "Name",
244 "Genre",
245 "Date",
246 "Composer",
247 "Performer",
248 "Comment",
249 "Disc",
250 "Filename",
251 "Any"
254 static char * mpd_sanitizeArg(const char * arg) {
255 size_t i;
256 char * ret;
257 register const char *c;
258 register char *rc;
260 /* instead of counting in that loop above, just
261 * use a bit more memory and half running time
263 ret = malloc(strlen(arg) * 2 + 1);
265 c = arg;
266 rc = ret;
267 for(i = strlen(arg)+1; i != 0; --i) {
268 if(*c=='"' || *c=='\\')
269 *rc++ = '\\';
270 *(rc++) = *(c++);
273 return ret;
276 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
278 mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
280 ret->name = strdup(name);
281 ret->value = strdup(value);
283 return ret;
286 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
287 free(re->name);
288 free(re->value);
289 free(re);
292 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
293 connection->timeout.tv_sec = (int)timeout;
294 connection->timeout.tv_usec = (int)(timeout*1e6 -
295 connection->timeout.tv_sec*1000000 +
296 0.5);
299 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
300 char * rt, char * output) {
301 char * tmp;
302 char * test;
303 int i;
305 if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
306 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
307 "mpd not running on port %i on host \"%s\"",
308 port,host);
309 connection->error = MPD_ERROR_NOTMPD;
310 return 1;
313 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
315 for(i=0;i<3;i++) {
316 if(tmp) connection->version[i] = strtol(tmp,&test,10);
318 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
319 snprintf(connection->errorStr,
320 MPD_ERRORSTR_MAX_LENGTH,
321 "error parsing version number at "
322 "\"%s\"",
323 &output[strlen(MPD_WELCOME_MESSAGE)]);
324 connection->error = MPD_ERROR_NOTMPD;
325 return 1;
327 tmp = ++test;
330 return 0;
333 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
334 int err;
335 char * rt;
336 char * output = NULL;
337 mpd_Connection * connection = malloc(sizeof(mpd_Connection));
338 struct timeval tv;
339 fd_set fds;
340 strcpy(connection->buffer,"");
341 connection->buflen = 0;
342 connection->bufstart = 0;
343 strcpy(connection->errorStr,"");
344 connection->error = 0;
345 connection->doneProcessing = 0;
346 connection->commandList = 0;
347 connection->listOks = 0;
348 connection->doneListOk = 0;
349 connection->returnElement = NULL;
350 connection->request = NULL;
352 if (winsock_dll_error(connection))
353 return connection;
355 if (mpd_connect(connection, host, port, timeout) < 0)
356 return connection;
358 while(!(rt = strstr(connection->buffer,"\n"))) {
359 tv.tv_sec = connection->timeout.tv_sec;
360 tv.tv_usec = connection->timeout.tv_usec;
361 FD_ZERO(&fds);
362 FD_SET(connection->sock,&fds);
363 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
364 int readed;
365 readed = recv(connection->sock,
366 &(connection->buffer[connection->buflen]),
367 MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
368 if(readed<=0) {
369 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
370 "problems getting a response from"
371 " \"%s\" on port %i : %s",host,
372 port, strerror(errno));
373 connection->error = MPD_ERROR_NORESPONSE;
374 return connection;
376 connection->buflen+=readed;
377 connection->buffer[connection->buflen] = '\0';
379 else if(err<0) {
380 if (SELECT_ERRNO_IGNORE)
381 continue;
382 snprintf(connection->errorStr,
383 MPD_ERRORSTR_MAX_LENGTH,
384 "problems connecting to \"%s\" on port"
385 " %i",host,port);
386 connection->error = MPD_ERROR_CONNPORT;
387 return connection;
389 else {
390 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
391 "timeout in attempting to get a response from"
392 " \"%s\" on port %i",host,port);
393 connection->error = MPD_ERROR_NORESPONSE;
394 return connection;
398 *rt = '\0';
399 output = strdup(connection->buffer);
400 strcpy(connection->buffer,rt+1);
401 connection->buflen = strlen(connection->buffer);
403 if(mpd_parseWelcome(connection,host,port,rt,output) == 0) connection->doneProcessing = 1;
405 free(output);
407 return connection;
410 void mpd_clearError(mpd_Connection * connection) {
411 connection->error = 0;
412 connection->errorStr[0] = '\0';
415 void mpd_closeConnection(mpd_Connection * connection) {
416 closesocket(connection->sock);
417 if(connection->returnElement) free(connection->returnElement);
418 if(connection->request) free(connection->request);
419 free(connection);
420 WSACleanup();
423 static void mpd_executeCommand(mpd_Connection * connection, char * command) {
424 int ret;
425 struct timeval tv;
426 fd_set fds;
427 char * commandPtr = command;
428 int commandLen = strlen(command);
430 if(!connection->doneProcessing && !connection->commandList) {
431 strcpy(connection->errorStr,"not done processing current command");
432 connection->error = 1;
433 return;
436 mpd_clearError(connection);
438 FD_ZERO(&fds);
439 FD_SET(connection->sock,&fds);
440 tv.tv_sec = connection->timeout.tv_sec;
441 tv.tv_usec = connection->timeout.tv_usec;
443 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
444 (ret==-1 && SELECT_ERRNO_IGNORE)) {
445 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
446 if(ret<=0)
448 if (SENDRECV_ERRNO_IGNORE) continue;
449 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
450 "problems giving command \"%s\"",command);
451 connection->error = MPD_ERROR_SENDING;
452 return;
454 else {
455 commandPtr+=ret;
456 commandLen-=ret;
459 if(commandLen<=0) break;
462 if(commandLen>0) {
463 perror("");
464 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
465 "timeout sending command \"%s\"",command);
466 connection->error = MPD_ERROR_TIMEOUT;
467 return;
470 if(!connection->commandList) connection->doneProcessing = 0;
471 else if(connection->commandList == COMMAND_LIST_OK) {
472 connection->listOks++;
476 static void mpd_getNextReturnElement(mpd_Connection * connection) {
477 char * output = NULL;
478 char * rt = NULL;
479 char * name = NULL;
480 char * value = NULL;
481 fd_set fds;
482 struct timeval tv;
483 char * tok = NULL;
484 int readed;
485 char * bufferCheck = NULL;
486 int err;
487 int pos;
489 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
490 connection->returnElement = NULL;
492 if(connection->doneProcessing || (connection->listOks &&
493 connection->doneListOk))
495 strcpy(connection->errorStr,"already done processing current command");
496 connection->error = 1;
497 return;
500 bufferCheck = connection->buffer+connection->bufstart;
501 while(connection->bufstart>=connection->buflen ||
502 !(rt = strchr(bufferCheck,'\n'))) {
503 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
504 memmove(connection->buffer,
505 connection->buffer+
506 connection->bufstart,
507 connection->buflen-
508 connection->bufstart+1);
509 connection->buflen-=connection->bufstart;
510 connection->bufstart = 0;
512 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
513 strcpy(connection->errorStr,"buffer overrun");
514 connection->error = MPD_ERROR_BUFFEROVERRUN;
515 connection->doneProcessing = 1;
516 connection->doneListOk = 0;
517 return;
519 bufferCheck = connection->buffer+connection->buflen;
520 tv.tv_sec = connection->timeout.tv_sec;
521 tv.tv_usec = connection->timeout.tv_usec;
522 FD_ZERO(&fds);
523 FD_SET(connection->sock,&fds);
524 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
525 readed = recv(connection->sock,
526 connection->buffer+connection->buflen,
527 MPD_BUFFER_MAX_LENGTH-connection->buflen,
528 MSG_DONTWAIT);
529 if(readed<0 && SENDRECV_ERRNO_IGNORE) {
530 continue;
532 if(readed<=0) {
533 strcpy(connection->errorStr,"connection"
534 " closed");
535 connection->error = MPD_ERROR_CONNCLOSED;
536 connection->doneProcessing = 1;
537 connection->doneListOk = 0;
538 return;
540 connection->buflen+=readed;
541 connection->buffer[connection->buflen] = '\0';
543 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
544 else {
545 strcpy(connection->errorStr,"connection timeout");
546 connection->error = MPD_ERROR_TIMEOUT;
547 connection->doneProcessing = 1;
548 connection->doneListOk = 0;
549 return;
553 *rt = '\0';
554 output = connection->buffer+connection->bufstart;
555 connection->bufstart = rt - connection->buffer + 1;
557 if(strcmp(output,"OK")==0) {
558 if(connection->listOks > 0) {
559 strcpy(connection->errorStr, "expected more list_OK's");
560 connection->error = 1;
562 connection->listOks = 0;
563 connection->doneProcessing = 1;
564 connection->doneListOk = 0;
565 return;
568 if(strcmp(output, "list_OK") == 0) {
569 if(!connection->listOks) {
570 strcpy(connection->errorStr,
571 "got an unexpected list_OK");
572 connection->error = 1;
574 else {
575 connection->doneListOk = 1;
576 connection->listOks--;
578 return;
581 if(strncmp(output,"ACK",strlen("ACK"))==0) {
582 char * test;
583 char * needle;
584 int val;
586 strcpy(connection->errorStr, output);
587 connection->error = MPD_ERROR_ACK;
588 connection->errorCode = MPD_ACK_ERROR_UNK;
589 connection->errorAt = MPD_ERROR_AT_UNK;
590 connection->doneProcessing = 1;
591 connection->doneListOk = 0;
593 needle = strchr(output, '[');
594 if(!needle) return;
595 val = strtol(needle+1, &test, 10);
596 if(*test != '@') return;
597 connection->errorCode = val;
598 val = strtol(test+1, &test, 10);
599 if(*test != ']') return;
600 connection->errorAt = val;
601 return;
604 tok = strchr(output, ':');
605 if (!tok) return;
606 pos = tok - output;
607 value = ++tok;
608 name = output;
609 name[pos] = '\0';
611 if(value[0]==' ') {
612 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
614 else {
615 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
616 "error parsing: %s:%s",name,value);
617 connection->error = 1;
621 void mpd_finishCommand(mpd_Connection * connection) {
622 while(!connection->doneProcessing) {
623 if(connection->doneListOk) connection->doneListOk = 0;
624 mpd_getNextReturnElement(connection);
628 static void mpd_finishListOkCommand(mpd_Connection * connection) {
629 while(!connection->doneProcessing && connection->listOks &&
630 !connection->doneListOk)
632 mpd_getNextReturnElement(connection);
636 int mpd_nextListOkCommand(mpd_Connection * connection) {
637 mpd_finishListOkCommand(connection);
638 if(!connection->doneProcessing) connection->doneListOk = 0;
639 if(connection->listOks == 0 || connection->doneProcessing) return -1;
640 return 0;
643 void mpd_sendStatusCommand(mpd_Connection * connection) {
644 mpd_executeCommand(connection,"status\n");
647 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
648 mpd_Status * status;
650 /*mpd_executeCommand(connection,"status\n");
652 if(connection->error) return NULL;*/
654 if(connection->doneProcessing || (connection->listOks &&
655 connection->doneListOk))
657 return NULL;
660 if(!connection->returnElement) mpd_getNextReturnElement(connection);
662 status = malloc(sizeof(mpd_Status));
663 status->volume = -1;
664 status->repeat = 0;
665 status->random = 0;
666 status->playlist = -1;
667 status->playlistLength = -1;
668 status->state = -1;
669 status->song = 0;
670 status->songid = 0;
671 status->elapsedTime = 0;
672 status->totalTime = 0;
673 status->bitRate = 0;
674 status->sampleRate = 0;
675 status->bits = 0;
676 status->channels = 0;
677 status->crossfade = -1;
678 status->error = NULL;
679 status->updatingDb = 0;
681 if(connection->error) {
682 free(status);
683 return NULL;
685 while(connection->returnElement) {
686 mpd_ReturnElement * re = connection->returnElement;
687 if(strcmp(re->name,"volume")==0) {
688 status->volume = atoi(re->value);
690 else if(strcmp(re->name,"repeat")==0) {
691 status->repeat = atoi(re->value);
693 else if(strcmp(re->name,"random")==0) {
694 status->random = atoi(re->value);
696 else if(strcmp(re->name,"playlist")==0) {
697 status->playlist = strtol(re->value,NULL,10);
699 else if(strcmp(re->name,"playlistlength")==0) {
700 status->playlistLength = atoi(re->value);
702 else if(strcmp(re->name,"bitrate")==0) {
703 status->bitRate = atoi(re->value);
705 else if(strcmp(re->name,"state")==0) {
706 if(strcmp(re->value,"play")==0) {
707 status->state = MPD_STATUS_STATE_PLAY;
709 else if(strcmp(re->value,"stop")==0) {
710 status->state = MPD_STATUS_STATE_STOP;
712 else if(strcmp(re->value,"pause")==0) {
713 status->state = MPD_STATUS_STATE_PAUSE;
715 else {
716 status->state = MPD_STATUS_STATE_UNKNOWN;
719 else if(strcmp(re->name,"song")==0) {
720 status->song = atoi(re->value);
722 else if(strcmp(re->name,"songid")==0) {
723 status->songid = atoi(re->value);
725 else if(strcmp(re->name,"time")==0) {
726 char * tok = strchr(re->value,':');
727 /* the second strchr below is a safety check */
728 if (tok && (strchr(tok,0) > (tok+1))) {
729 /* atoi stops at the first non-[0-9] char: */
730 status->elapsedTime = atoi(re->value);
731 status->totalTime = atoi(tok+1);
734 else if(strcmp(re->name,"error")==0) {
735 status->error = strdup(re->value);
737 else if(strcmp(re->name,"xfade")==0) {
738 status->crossfade = atoi(re->value);
740 else if(strcmp(re->name,"updating_db")==0) {
741 status->updatingDb = atoi(re->value);
743 else if(strcmp(re->name,"audio")==0) {
744 char * tok = strchr(re->value,':');
745 if (tok && (strchr(tok,0) > (tok+1))) {
746 status->sampleRate = atoi(re->value);
747 status->bits = atoi(++tok);
748 tok = strchr(tok,':');
749 if (tok && (strchr(tok,0) > (tok+1)))
750 status->channels = atoi(tok+1);
754 mpd_getNextReturnElement(connection);
755 if(connection->error) {
756 free(status);
757 return NULL;
761 if(connection->error) {
762 free(status);
763 return NULL;
765 else if(status->state<0) {
766 strcpy(connection->errorStr,"state not found");
767 connection->error = 1;
768 free(status);
769 return NULL;
772 return status;
775 void mpd_freeStatus(mpd_Status * status) {
776 if(status->error) free(status->error);
777 free(status);
780 void mpd_sendStatsCommand(mpd_Connection * connection) {
781 mpd_executeCommand(connection,"stats\n");
784 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
785 mpd_Stats * stats;
787 /*mpd_executeCommand(connection,"stats\n");
789 if(connection->error) return NULL;*/
791 if(connection->doneProcessing || (connection->listOks &&
792 connection->doneListOk))
794 return NULL;
797 if(!connection->returnElement) mpd_getNextReturnElement(connection);
799 stats = malloc(sizeof(mpd_Stats));
800 stats->numberOfArtists = 0;
801 stats->numberOfAlbums = 0;
802 stats->numberOfSongs = 0;
803 stats->uptime = 0;
804 stats->dbUpdateTime = 0;
805 stats->playTime = 0;
806 stats->dbPlayTime = 0;
808 if(connection->error) {
809 free(stats);
810 return NULL;
812 while(connection->returnElement) {
813 mpd_ReturnElement * re = connection->returnElement;
814 if(strcmp(re->name,"artists")==0) {
815 stats->numberOfArtists = atoi(re->value);
817 else if(strcmp(re->name,"albums")==0) {
818 stats->numberOfAlbums = atoi(re->value);
820 else if(strcmp(re->name,"songs")==0) {
821 stats->numberOfSongs = atoi(re->value);
823 else if(strcmp(re->name,"uptime")==0) {
824 stats->uptime = strtol(re->value,NULL,10);
826 else if(strcmp(re->name,"db_update")==0) {
827 stats->dbUpdateTime = strtol(re->value,NULL,10);
829 else if(strcmp(re->name,"playtime")==0) {
830 stats->playTime = strtol(re->value,NULL,10);
832 else if(strcmp(re->name,"db_playtime")==0) {
833 stats->dbPlayTime = strtol(re->value,NULL,10);
836 mpd_getNextReturnElement(connection);
837 if(connection->error) {
838 free(stats);
839 return NULL;
843 if(connection->error) {
844 free(stats);
845 return NULL;
848 return stats;
851 void mpd_freeStats(mpd_Stats * stats) {
852 free(stats);
855 static void mpd_initSong(mpd_Song * song) {
856 song->file = NULL;
857 song->artist = NULL;
858 song->album = NULL;
859 song->track = NULL;
860 song->title = NULL;
861 song->name = NULL;
862 song->date = NULL;
863 /* added by Qball */
864 song->genre = NULL;
865 song->composer = NULL;
866 song->disc = NULL;
867 song->comment = NULL;
869 song->time = MPD_SONG_NO_TIME;
870 song->pos = MPD_SONG_NO_NUM;
871 song->id = MPD_SONG_NO_ID;
874 static void mpd_finishSong(mpd_Song * song) {
875 if(song->file) free(song->file);
876 if(song->artist) free(song->artist);
877 if(song->album) free(song->album);
878 if(song->title) free(song->title);
879 if(song->track) free(song->track);
880 if(song->name) free(song->name);
881 if(song->date) free(song->date);
882 if(song->genre) free(song->genre);
883 if(song->composer) free(song->composer);
884 if(song->disc) free(song->disc);
885 if(song->comment) free(song->comment);
888 mpd_Song * mpd_newSong(void) {
889 mpd_Song * ret = malloc(sizeof(mpd_Song));
891 mpd_initSong(ret);
893 return ret;
896 void mpd_freeSong(mpd_Song * song) {
897 mpd_finishSong(song);
898 free(song);
901 mpd_Song * mpd_songDup(mpd_Song * song) {
902 mpd_Song * ret = mpd_newSong();
904 if(song->file) ret->file = strdup(song->file);
905 if(song->artist) ret->artist = strdup(song->artist);
906 if(song->album) ret->album = strdup(song->album);
907 if(song->title) ret->title = strdup(song->title);
908 if(song->track) ret->track = strdup(song->track);
909 if(song->name) ret->name = strdup(song->name);
910 if(song->date) ret->date = strdup(song->date);
911 if(song->genre) ret->genre= strdup(song->genre);
912 if(song->composer) ret->composer= strdup(song->composer);
913 if(song->disc) ret->disc = strdup(song->disc);
914 if(song->comment) ret->comment = strdup(song->comment);
915 ret->time = song->time;
916 ret->pos = song->pos;
917 ret->id = song->id;
919 return ret;
922 static void mpd_initDirectory(mpd_Directory * directory) {
923 directory->path = NULL;
926 static void mpd_finishDirectory(mpd_Directory * directory) {
927 if(directory->path) free(directory->path);
930 mpd_Directory * mpd_newDirectory(void) {
931 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
933 mpd_initDirectory(directory);
935 return directory;
938 void mpd_freeDirectory(mpd_Directory * directory) {
939 mpd_finishDirectory(directory);
941 free(directory);
944 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
945 mpd_Directory * ret = mpd_newDirectory();
947 if(directory->path) ret->path = strdup(directory->path);
949 return ret;
952 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
953 playlist->path = NULL;
956 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
957 if(playlist->path) free(playlist->path);
960 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
961 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
963 mpd_initPlaylistFile(playlist);
965 return playlist;
968 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
969 mpd_finishPlaylistFile(playlist);
970 free(playlist);
973 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
974 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
976 if(playlist->path) ret->path = strdup(playlist->path);
978 return ret;
981 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
982 entity->info.directory = NULL;
985 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
986 if(entity->info.directory) {
987 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
988 mpd_freeDirectory(entity->info.directory);
990 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
991 mpd_freeSong(entity->info.song);
993 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
994 mpd_freePlaylistFile(entity->info.playlistFile);
999 mpd_InfoEntity * mpd_newInfoEntity(void) {
1000 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
1002 mpd_initInfoEntity(entity);
1004 return entity;
1007 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1008 mpd_finishInfoEntity(entity);
1009 free(entity);
1012 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1013 mpd_executeCommand(connection,command);
1016 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1017 mpd_InfoEntity * entity = NULL;
1019 if(connection->doneProcessing || (connection->listOks &&
1020 connection->doneListOk))
1022 return NULL;
1025 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1027 if(connection->returnElement) {
1028 if(strcmp(connection->returnElement->name,"file")==0) {
1029 entity = mpd_newInfoEntity();
1030 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1031 entity->info.song = mpd_newSong();
1032 entity->info.song->file =
1033 strdup(connection->returnElement->value);
1035 else if(strcmp(connection->returnElement->name,
1036 "directory")==0) {
1037 entity = mpd_newInfoEntity();
1038 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1039 entity->info.directory = mpd_newDirectory();
1040 entity->info.directory->path =
1041 strdup(connection->returnElement->value);
1043 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1044 entity = mpd_newInfoEntity();
1045 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1046 entity->info.playlistFile = mpd_newPlaylistFile();
1047 entity->info.playlistFile->path =
1048 strdup(connection->returnElement->value);
1050 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1051 entity = mpd_newInfoEntity();
1052 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1053 entity->info.song = mpd_newSong();
1054 entity->info.song->pos = atoi(connection->returnElement->value);
1056 else {
1057 connection->error = 1;
1058 strcpy(connection->errorStr,"problem parsing song info");
1059 return NULL;
1062 else return NULL;
1064 mpd_getNextReturnElement(connection);
1065 while(connection->returnElement) {
1066 mpd_ReturnElement * re = connection->returnElement;
1068 if(strcmp(re->name,"file")==0) return entity;
1069 else if(strcmp(re->name,"directory")==0) return entity;
1070 else if(strcmp(re->name,"playlist")==0) return entity;
1071 else if(strcmp(re->name,"cpos")==0) return entity;
1073 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1074 strlen(re->value)) {
1075 if(!entity->info.song->artist &&
1076 strcmp(re->name,"Artist")==0) {
1077 entity->info.song->artist = strdup(re->value);
1079 else if(!entity->info.song->album &&
1080 strcmp(re->name,"Album")==0) {
1081 entity->info.song->album = strdup(re->value);
1083 else if(!entity->info.song->title &&
1084 strcmp(re->name,"Title")==0) {
1085 entity->info.song->title = strdup(re->value);
1087 else if(!entity->info.song->track &&
1088 strcmp(re->name,"Track")==0) {
1089 entity->info.song->track = strdup(re->value);
1091 else if(!entity->info.song->name &&
1092 strcmp(re->name,"Name")==0) {
1093 entity->info.song->name = strdup(re->value);
1095 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1096 strcmp(re->name,"Time")==0) {
1097 entity->info.song->time = atoi(re->value);
1099 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1100 strcmp(re->name,"Pos")==0) {
1101 entity->info.song->pos = atoi(re->value);
1103 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1104 strcmp(re->name,"Id")==0) {
1105 entity->info.song->id = atoi(re->value);
1107 else if(!entity->info.song->date &&
1108 strcmp(re->name, "Date") == 0) {
1109 entity->info.song->date = strdup(re->value);
1111 else if(!entity->info.song->genre &&
1112 strcmp(re->name, "Genre") == 0) {
1113 entity->info.song->genre = strdup(re->value);
1115 else if(!entity->info.song->composer &&
1116 strcmp(re->name, "Composer") == 0) {
1117 entity->info.song->composer = strdup(re->value);
1119 else if(!entity->info.song->disc &&
1120 strcmp(re->name, "Disc") == 0) {
1121 entity->info.song->disc = strdup(re->value);
1123 else if(!entity->info.song->comment &&
1124 strcmp(re->name, "Comment") == 0) {
1125 entity->info.song->comment = strdup(re->value);
1128 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1130 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1133 mpd_getNextReturnElement(connection);
1136 return entity;
1139 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1140 const char * name)
1142 if(connection->doneProcessing || (connection->listOks &&
1143 connection->doneListOk))
1145 return NULL;
1148 mpd_getNextReturnElement(connection);
1149 while(connection->returnElement) {
1150 mpd_ReturnElement * re = connection->returnElement;
1152 if(strcmp(re->name,name)==0) return strdup(re->value);
1153 mpd_getNextReturnElement(connection);
1156 return NULL;
1159 char *mpd_getNextTag(mpd_Connection *connection, int type)
1161 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES ||
1162 type == MPD_TAG_ITEM_ANY)
1163 return NULL;
1164 if (type == MPD_TAG_ITEM_FILENAME)
1165 return mpd_getNextReturnElementNamed(connection, "file");
1166 return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
1169 char * mpd_getNextArtist(mpd_Connection * connection) {
1170 return mpd_getNextReturnElementNamed(connection,"Artist");
1173 char * mpd_getNextAlbum(mpd_Connection * connection) {
1174 return mpd_getNextReturnElementNamed(connection,"Album");
1177 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1178 char * string = malloc(strlen("playlistinfo")+25);
1179 sprintf(string,"playlistinfo \"%i\"\n",songPos);
1180 mpd_sendInfoCommand(connection,string);
1181 free(string);
1184 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1185 char * string = malloc(strlen("playlistid")+25);
1186 sprintf(string, "playlistid \"%i\"\n", id);
1187 mpd_sendInfoCommand(connection, string);
1188 free(string);
1191 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1192 char * string = malloc(strlen("plchanges")+25);
1193 sprintf(string,"plchanges \"%lld\"\n",playlist);
1194 mpd_sendInfoCommand(connection,string);
1195 free(string);
1198 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1199 char * string = malloc(strlen("plchangesposid")+25);
1200 sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1201 mpd_sendInfoCommand(connection,string);
1202 free(string);
1205 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1206 char * sDir = mpd_sanitizeArg(dir);
1207 char * string = malloc(strlen("listall")+strlen(sDir)+5);
1208 sprintf(string,"listall \"%s\"\n",sDir);
1209 mpd_sendInfoCommand(connection,string);
1210 free(string);
1211 free(sDir);
1214 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1215 char * sDir = mpd_sanitizeArg(dir);
1216 char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1217 sprintf(string,"listallinfo \"%s\"\n",sDir);
1218 mpd_sendInfoCommand(connection,string);
1219 free(string);
1220 free(sDir);
1223 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1224 char * sDir = mpd_sanitizeArg(dir);
1225 char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1226 sprintf(string,"lsinfo \"%s\"\n",sDir);
1227 mpd_sendInfoCommand(connection,string);
1228 free(string);
1229 free(sDir);
1232 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1233 mpd_executeCommand(connection,"currentsong\n");
1236 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1237 const char * str)
1239 char st[10];
1240 char * string;
1241 char * sanitStr = mpd_sanitizeArg(str);
1242 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1243 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1244 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1245 else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1246 else {
1247 connection->error = 1;
1248 strcpy(connection->errorStr,"unknown table for search");
1249 return;
1251 string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1252 sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1253 mpd_sendInfoCommand(connection,string);
1254 free(string);
1255 free(sanitStr);
1258 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1259 const char * str)
1261 char st[10];
1262 char * string;
1263 char * sanitStr = mpd_sanitizeArg(str);
1264 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1265 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1266 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1267 else {
1268 connection->error = 1;
1269 strcpy(connection->errorStr,"unknown table for find");
1270 return;
1272 string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1273 sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1274 mpd_sendInfoCommand(connection,string);
1275 free(string);
1276 free(sanitStr);
1279 void mpd_sendListCommand(mpd_Connection * connection, int table,
1280 const char * arg1)
1282 char st[10];
1283 char * string;
1284 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1285 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1286 else {
1287 connection->error = 1;
1288 strcpy(connection->errorStr,"unknown table for list");
1289 return;
1291 if(arg1) {
1292 char * sanitArg1 = mpd_sanitizeArg(arg1);
1293 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1294 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1295 free(sanitArg1);
1297 else {
1298 string = malloc(strlen("list")+strlen(st)+3);
1299 sprintf(string,"list %s\n",st);
1301 mpd_sendInfoCommand(connection,string);
1302 free(string);
1305 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1306 char * sFile = mpd_sanitizeArg(file);
1307 char * string = malloc(strlen("add")+strlen(sFile)+5);
1308 sprintf(string,"add \"%s\"\n",sFile);
1309 mpd_executeCommand(connection,string);
1310 free(string);
1311 free(sFile);
1314 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1316 int retval = -1;
1317 char *sFile = mpd_sanitizeArg(file);
1318 char *string = malloc(strlen("addid")+strlen(sFile)+5);
1320 sprintf(string, "addid \"%s\"\n", sFile);
1321 mpd_sendInfoCommand(connection, string);
1322 free(string);
1323 free(sFile);
1325 string = mpd_getNextReturnElementNamed(connection, "Id");
1326 if (string) {
1327 retval = atoi(string);
1328 free(string);
1331 return retval;
1334 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1335 char * string = malloc(strlen("delete")+25);
1336 sprintf(string,"delete \"%i\"\n",songPos);
1337 mpd_sendInfoCommand(connection,string);
1338 free(string);
1341 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1342 char * string = malloc(strlen("deleteid")+25);
1343 sprintf(string, "deleteid \"%i\"\n", id);
1344 mpd_sendInfoCommand(connection,string);
1345 free(string);
1348 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1349 char * sName = mpd_sanitizeArg(name);
1350 char * string = malloc(strlen("save")+strlen(sName)+5);
1351 sprintf(string,"save \"%s\"\n",sName);
1352 mpd_executeCommand(connection,string);
1353 free(string);
1354 free(sName);
1357 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1358 char * sName = mpd_sanitizeArg(name);
1359 char * string = malloc(strlen("load")+strlen(sName)+5);
1360 sprintf(string,"load \"%s\"\n",sName);
1361 mpd_executeCommand(connection,string);
1362 free(string);
1363 free(sName);
1366 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1367 char * sName = mpd_sanitizeArg(name);
1368 char * string = malloc(strlen("rm")+strlen(sName)+5);
1369 sprintf(string,"rm \"%s\"\n",sName);
1370 mpd_executeCommand(connection,string);
1371 free(string);
1372 free(sName);
1375 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1376 mpd_executeCommand(connection,"shuffle\n");
1379 void mpd_sendClearCommand(mpd_Connection * connection) {
1380 mpd_executeCommand(connection,"clear\n");
1383 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1384 char * string = malloc(strlen("play")+25);
1385 sprintf(string,"play \"%i\"\n",songPos);
1386 mpd_sendInfoCommand(connection,string);
1387 free(string);
1390 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1391 char * string = malloc(strlen("playid")+25);
1392 sprintf(string,"playid \"%i\"\n",id);
1393 mpd_sendInfoCommand(connection,string);
1394 free(string);
1397 void mpd_sendStopCommand(mpd_Connection * connection) {
1398 mpd_executeCommand(connection,"stop\n");
1401 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1402 char * string = malloc(strlen("pause")+25);
1403 sprintf(string,"pause \"%i\"\n",pauseMode);
1404 mpd_executeCommand(connection,string);
1405 free(string);
1408 void mpd_sendNextCommand(mpd_Connection * connection) {
1409 mpd_executeCommand(connection,"next\n");
1412 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1413 char * string = malloc(strlen("move")+25);
1414 sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1415 mpd_sendInfoCommand(connection,string);
1416 free(string);
1419 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1420 char * string = malloc(strlen("moveid")+25);
1421 sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1422 mpd_sendInfoCommand(connection,string);
1423 free(string);
1426 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1427 char * string = malloc(strlen("swap")+25);
1428 sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1429 mpd_sendInfoCommand(connection,string);
1430 free(string);
1433 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1434 char * string = malloc(strlen("swapid")+25);
1435 sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1436 mpd_sendInfoCommand(connection,string);
1437 free(string);
1440 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1441 char * string = malloc(strlen("seek")+25);
1442 sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1443 mpd_sendInfoCommand(connection,string);
1444 free(string);
1447 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1448 char * string = malloc(strlen("seekid")+25);
1449 sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1450 mpd_sendInfoCommand(connection,string);
1451 free(string);
1454 void mpd_sendUpdateCommand(mpd_Connection * connection, char * path) {
1455 char * sPath = mpd_sanitizeArg(path);
1456 char * string = malloc(strlen("update")+strlen(sPath)+5);
1457 sprintf(string,"update \"%s\"\n",sPath);
1458 mpd_sendInfoCommand(connection,string);
1459 free(string);
1460 free(sPath);
1463 int mpd_getUpdateId(mpd_Connection * connection) {
1464 char * jobid;
1465 int ret = 0;
1467 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1468 if(jobid) {
1469 ret = atoi(jobid);
1470 free(jobid);
1473 return ret;
1476 void mpd_sendPrevCommand(mpd_Connection * connection) {
1477 mpd_executeCommand(connection,"previous\n");
1480 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1481 char * string = malloc(strlen("repeat")+25);
1482 sprintf(string,"repeat \"%i\"\n",repeatMode);
1483 mpd_executeCommand(connection,string);
1484 free(string);
1487 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1488 char * string = malloc(strlen("random")+25);
1489 sprintf(string,"random \"%i\"\n",randomMode);
1490 mpd_executeCommand(connection,string);
1491 free(string);
1494 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1495 char * string = malloc(strlen("setvol")+25);
1496 sprintf(string,"setvol \"%i\"\n",volumeChange);
1497 mpd_executeCommand(connection,string);
1498 free(string);
1501 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1502 char * string = malloc(strlen("volume")+25);
1503 sprintf(string,"volume \"%i\"\n",volumeChange);
1504 mpd_executeCommand(connection,string);
1505 free(string);
1508 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1509 char * string = malloc(strlen("crossfade")+25);
1510 sprintf(string,"crossfade \"%i\"\n",seconds);
1511 mpd_executeCommand(connection,string);
1512 free(string);
1515 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1516 char * sPass = mpd_sanitizeArg(pass);
1517 char * string = malloc(strlen("password")+strlen(sPass)+5);
1518 sprintf(string,"password \"%s\"\n",sPass);
1519 mpd_executeCommand(connection,string);
1520 free(string);
1521 free(sPass);
1524 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1525 if(connection->commandList) {
1526 strcpy(connection->errorStr,"already in command list mode");
1527 connection->error = 1;
1528 return;
1530 connection->commandList = COMMAND_LIST;
1531 mpd_executeCommand(connection,"command_list_begin\n");
1534 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1535 if(connection->commandList) {
1536 strcpy(connection->errorStr,"already in command list mode");
1537 connection->error = 1;
1538 return;
1540 connection->commandList = COMMAND_LIST_OK;
1541 mpd_executeCommand(connection,"command_list_ok_begin\n");
1542 connection->listOks = 0;
1545 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1546 if(!connection->commandList) {
1547 strcpy(connection->errorStr,"not in command list mode");
1548 connection->error = 1;
1549 return;
1551 connection->commandList = 0;
1552 mpd_executeCommand(connection,"command_list_end\n");
1555 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1556 mpd_executeCommand(connection,"outputs\n");
1559 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1560 mpd_OutputEntity * output = NULL;
1562 if(connection->doneProcessing || (connection->listOks &&
1563 connection->doneListOk))
1565 return NULL;
1568 if(connection->error) return NULL;
1570 output = malloc(sizeof(mpd_OutputEntity));
1571 output->id = -10;
1572 output->name = NULL;
1573 output->enabled = 0;
1575 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1577 while(connection->returnElement) {
1578 mpd_ReturnElement * re = connection->returnElement;
1579 if(strcmp(re->name,"outputid")==0) {
1580 if(output!=NULL && output->id>=0) return output;
1581 output->id = atoi(re->value);
1583 else if(strcmp(re->name,"outputname")==0) {
1584 output->name = strdup(re->value);
1586 else if(strcmp(re->name,"outputenabled")==0) {
1587 output->enabled = atoi(re->value);
1590 mpd_getNextReturnElement(connection);
1591 if(connection->error) {
1592 free(output);
1593 return NULL;
1598 return output;
1601 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1602 char * string = malloc(strlen("enableoutput")+25);
1603 sprintf(string,"enableoutput \"%i\"\n",outputId);
1604 mpd_executeCommand(connection,string);
1605 free(string);
1608 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1609 char * string = malloc(strlen("disableoutput")+25);
1610 sprintf(string,"disableoutput \"%i\"\n",outputId);
1611 mpd_executeCommand(connection,string);
1612 free(string);
1615 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1616 free(output->name);
1617 free(output);
1621 * mpd_sendNotCommandsCommand
1622 * odd naming, but it gets the not allowed commands
1625 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1626 mpd_executeCommand(connection,"notcommands\n");
1630 * mpd_sendCommandsCommand
1631 * odd naming, but it gets the allowed commands
1634 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1635 mpd_executeCommand(connection,"commands\n");
1638 * Get the next returned command
1640 char * mpd_getNextCommand(mpd_Connection * connection) {
1641 return mpd_getNextReturnElementNamed(connection,"command");
1644 void mpd_startSearch(mpd_Connection *connection, int exact)
1646 if (connection->request) {
1647 strcpy(connection->errorStr, "search already in progress");
1648 connection->error = 1;
1649 return;
1652 if (exact) connection->request = strdup("find");
1653 else connection->request = strdup("search");
1656 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1658 char *strtype;
1660 if (connection->request) {
1661 strcpy(connection->errorStr, "search already in progress");
1662 connection->error = 1;
1663 return;
1666 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1667 strcpy(connection->errorStr, "invalid type specified");
1668 connection->error = 1;
1669 return;
1672 strtype = mpdTagItemKeys[type];
1674 connection->request = malloc(strlen(strtype)+6 /* "list"+space+\0 */);
1676 sprintf(connection->request, "list %c%s",
1677 tolower(strtype[0]), strtype+1);
1680 void mpd_addConstraintSearch(mpd_Connection *connection, int type, char *name)
1682 char *strtype;
1683 char *arg;
1685 if (!connection->request) {
1686 strcpy(connection->errorStr, "no search in progress");
1687 connection->error = 1;
1688 return;
1691 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1692 strcpy(connection->errorStr, "invalid type specified");
1693 connection->error = 1;
1694 return;
1697 if (name == NULL) {
1698 strcpy(connection->errorStr, "no name specified");
1699 connection->error = 1;
1700 return;
1703 strtype = mpdTagItemKeys[type];
1704 arg = mpd_sanitizeArg(name);
1706 connection->request = realloc(connection->request,
1707 strlen(connection->request)+
1708 strlen(strtype)+strlen(arg)+
1709 5 /* two spaces+two quotes+\0 */);
1711 sprintf(connection->request, "%s %c%s \"%s\"", connection->request,
1712 tolower(strtype[0]), strtype+1, arg);
1714 free(arg);
1717 void mpd_commitSearch(mpd_Connection *connection)
1719 int length;
1721 if (!connection->request) {
1722 strcpy(connection->errorStr, "no search in progress");
1723 connection->error = 1;
1724 return;
1727 length = strlen(connection->request);
1728 connection->request = realloc(connection->request,
1729 length+2 /* old length+\n+\0 */);
1730 connection->request[length] = '\n';
1731 connection->request[length+1] = '\0';
1732 mpd_sendInfoCommand(connection, connection->request);
1734 free(connection->request);
1735 connection->request = NULL;
1739 * @param connection a MpdConnection
1740 * @param path the path to the playlist.
1742 * List the content, with full metadata, of a stored playlist.
1745 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1747 char *arg = mpd_sanitizeArg(path);
1748 char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1749 sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1750 mpd_sendInfoCommand(connection, query);
1751 free(arg);
1752 free(query);
1756 * @param connection a MpdConnection
1757 * @param path the path to the playlist.
1759 * List the content of a stored playlist.
1762 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1764 char *arg = mpd_sanitizeArg(path);
1765 char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1766 sprintf(query, "listplaylist \"%s\"\n",arg);
1767 mpd_sendInfoCommand(connection, query);
1768 free(arg);
1769 free(query);