Code

go to root/parent dir key added
[ncmpc.git] / src / libmpdclient.c
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.
31 */
33 #include "libmpdclient.h"
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <stdio.h>
38 #include <sys/param.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <fcntl.h>
44 #ifdef WIN32
45 #  include <ws2tcpip.h>
46 #  include <winsock.h>
47 #else
48 #  include <netinet/in.h>
49 #  include <arpa/inet.h>
50 #  include <sys/socket.h>
51 #  include <netdb.h>
52 #endif
54 #ifndef MSG_DONTWAIT
55 #  define MSG_DONTWAIT 0
56 #endif
58 #ifndef MPD_NO_GAI
59 #  ifdef AI_ADDRCONFIG
60 #    define MPD_HAVE_GAI
61 #  endif
62 #endif
64 #define COMMAND_LIST    1
65 #define COMMAND_LIST_OK 2
67 #ifdef WIN32
68 #  define SELECT_ERRNO_IGNORE   (errno == WSAEINTR || errno == WSAEINPROGRESS)
69 #  define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
70 #else
71 #  define SELECT_ERRNO_IGNORE   (errno == EINTR)
72 #  define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
73 #  define winsock_dll_error(c)  0
74 #  define closesocket(s)        close(s)
75 #  define WSACleanup()          do { /* nothing */ } while (0)
76 #endif
78 #ifdef WIN32
79 static int winsock_dll_error(mpd_Connection *connection)
80 {
81         WSADATA wsaData;
82         if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
83                         LOBYTE(wsaData.wVersion) != 2 ||
84                         HIBYTE(wsaData.wVersion) != 2 ) {
85                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
86                                 "Could not find usable WinSock DLL.");
87                 connection->error = MPD_ERROR_SYSTEM;
88                 return 1;
89         }
90         return 0;
91 }
93 static int do_connect_fail(mpd_Connection *connection,
94                            const struct sockaddr *serv_addr, int addrlen)
95 {
96         int iMode = 1; /* 0 = blocking, else non-blocking */
97         ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
98         return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR
99                         && WSAGetLastError() != WSAEWOULDBLOCK);
101 #else /* !WIN32 (sane operating systems) */
102 static int do_connect_fail(mpd_Connection *connection,
103                            const struct sockaddr *serv_addr, int addrlen)
105         int flags = fcntl(connection->sock, F_GETFL, 0);
106         fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
107         return (connect(connection->sock,serv_addr,addrlen)<0 &&
108                                 errno!=EINPROGRESS);
110 #endif /* !WIN32 */
112 #ifdef MPD_HAVE_GAI
113 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
114                        float timeout)
116         int error;
117         char service[20];
118         struct addrinfo hints;
119         struct addrinfo *res = NULL;
120         struct addrinfo *addrinfo = NULL;
122         /**
123          * Setup hints
124          */
125         hints.ai_flags     = AI_ADDRCONFIG;
126         hints.ai_family    = PF_UNSPEC;
127         hints.ai_socktype  = SOCK_STREAM;
128         hints.ai_protocol  = IPPROTO_TCP;
129         hints.ai_addrlen   = 0;
130         hints.ai_addr      = NULL;
131         hints.ai_canonname = NULL;
132         hints.ai_next      = NULL;
134         snprintf(service, sizeof(service), "%d", port);
136         error = getaddrinfo(host, service, &hints, &addrinfo);
138         if (error) {
139                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
140                                 "host \"%s\" not found: %s",host, gai_strerror(error));
141                 connection->error = MPD_ERROR_UNKHOST;
142                 return -1;
143         }
145         for (res = addrinfo; res; res = res->ai_next) {
146                 /* create socket */
147                 connection->sock = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
148                 if (connection->sock < 0) {
149                         snprintf(connection->errorStr, MPD_BUFFER_MAX_LENGTH,
150                                  "problems creating socket: %s",
151                                  strerror(errno));
152                         connection->error = MPD_ERROR_SYSTEM;
153                         freeaddrinfo(addrinfo);
154                         return -1;
155                 }
157                 mpd_setConnectionTimeout(connection,timeout);
159                 /* connect stuff */
160                 if (do_connect_fail(connection, res->ai_addr, res->ai_addrlen)) {
161                         /* try the next address family */
162                         closesocket(connection->sock);
163                         connection->sock = -1;
164                         continue;
165                 }
166         }
167         freeaddrinfo(addrinfo);
169         if (connection->sock < 0) {
170                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
171                                 "problems connecting to \"%s\" on port"
172                                 " %i: %s",host,port, strerror(errno));
173                 connection->error = MPD_ERROR_CONNPORT;
175                 return -1;
176         }
178         return 0;
180 #else /* !MPD_HAVE_GAI */
181 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
182                        float timeout)
184         struct hostent * he;
185         struct sockaddr * dest;
186         int destlen;
187         struct sockaddr_in sin;
189         if(!(he=gethostbyname(host))) {
190                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
191                                 "host \"%s\" not found",host);
192                 connection->error = MPD_ERROR_UNKHOST;
193                 return -1;
194         }
196         memset(&sin,0,sizeof(struct sockaddr_in));
197         /*dest.sin_family = he->h_addrtype;*/
198         sin.sin_family = AF_INET;
199         sin.sin_port = htons(port);
201         switch(he->h_addrtype) {
202         case AF_INET:
203                 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
204                                 he->h_length);
205                 dest = (struct sockaddr *)&sin;
206                 destlen = sizeof(struct sockaddr_in);
207                 break;
208         default:
209                 strcpy(connection->errorStr,"address type is not IPv4\n");
210                 connection->error = MPD_ERROR_SYSTEM;
211                 return -1;
212                 break;
213         }
215         if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
216                 strcpy(connection->errorStr,"problems creating socket");
217                 connection->error = MPD_ERROR_SYSTEM;
218                 return -1;
219         }
221         mpd_setConnectionTimeout(connection,timeout);
223         /* connect stuff */
224         if (do_connect_fail(connection, dest, destlen)) {
225                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
226                                 "problems connecting to \"%s\" on port"
227                                 " %i",host,port);
228                 connection->error = MPD_ERROR_CONNPORT;
229                 return -1;
230         }
232         return 0;
234 #endif /* !MPD_HAVE_GAI */
236 char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
238         "Artist",
239         "Album",
240         "Title",
241         "Track",
242         "Name",
243         "Genre",
244         "Date",
245         "Composer",
246         "Performer",
247         "Comment",
248         "Disc",
249         "filename"
250 };
252 static char * mpd_sanitizeArg(const char * arg) {
253         size_t i;
254         char * ret;
255         register const char *c;
256         register char *rc;
258         /* instead of counting in that loop above, just
259          * use a bit more memory and half running time
260          */
261         ret = malloc(strlen(arg) * 2 + 1);
263         c = arg;
264         rc = ret;
265         for(i = strlen(arg)+1; i != 0; --i) {
266                 if(*c=='"' || *c=='\\')
267                         *rc++ = '\\';
268                 *(rc++) = *(c++);
269         }
271         return ret;
274 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
276         mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
278         ret->name = strdup(name);
279         ret->value = strdup(value);
281         return ret;
284 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
285         free(re->name);
286         free(re->value);
287         free(re);
290 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
291         connection->timeout.tv_sec = (int)timeout;
292         connection->timeout.tv_usec = (int)(timeout*1e6 -
293                                             connection->timeout.tv_sec*1000000 +
294                                             0.5);
297 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
298                             char * rt, char * output) {
299         char * tmp;
300         char * test;
301         int i;
303         if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
304                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
305                                 "mpd not running on port %i on host \"%s\"",
306                                 port,host);
307                 connection->error = MPD_ERROR_NOTMPD;
308                 return 1;
309         }
311         tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
313         for(i=0;i<3;i++) {
314                 if(tmp) connection->version[i] = strtol(tmp,&test,10);
316                 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
317                         snprintf(connection->errorStr,
318                                  MPD_BUFFER_MAX_LENGTH,
319                                  "error parsing version number at "
320                                  "\"%s\"",
321                                  &output[strlen(MPD_WELCOME_MESSAGE)]);
322                         connection->error = MPD_ERROR_NOTMPD;
323                         return 1;
324                 }
325                 tmp = ++test;
326         }
328         return 0;
331 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
332         int err;
333         char * rt;
334         char * output =  NULL;
335         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
336         struct timeval tv;
337         fd_set fds;
338         strcpy(connection->buffer,"");
339         connection->buflen = 0;
340         connection->bufstart = 0;
341         strcpy(connection->errorStr,"");
342         connection->error = 0;
343         connection->doneProcessing = 0;
344         connection->commandList = 0;
345         connection->listOks = 0;
346         connection->doneListOk = 0;
347         connection->returnElement = NULL;
348         connection->request = NULL;
350         if (winsock_dll_error(connection))
351                 return connection;
353         if (mpd_connect(connection, host, port, timeout) < 0)
354                 return connection;
356         while(!(rt = strstr(connection->buffer,"\n"))) {
357                 tv.tv_sec = connection->timeout.tv_sec;
358                 tv.tv_usec = connection->timeout.tv_usec;
359                 FD_ZERO(&fds);
360                 FD_SET(connection->sock,&fds);
361                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
362                         int readed;
363                         readed = recv(connection->sock,
364                                         &(connection->buffer[connection->buflen]),
365                                         MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
366                         if(readed<=0) {
367                                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
368                                                 "problems getting a response from"
369                                                 " \"%s\" on port %i : %s",host,
370                                                 port, strerror(errno));
371                                 connection->error = MPD_ERROR_NORESPONSE;
372                                 return connection;
373                         }
374                         connection->buflen+=readed;
375                         connection->buffer[connection->buflen] = '\0';
376                 }
377                 else if(err<0) {
378                         if (SELECT_ERRNO_IGNORE)
379                                 continue;
380                         snprintf(connection->errorStr,
381                                         MPD_BUFFER_MAX_LENGTH,
382                                         "problems connecting to \"%s\" on port"
383                                         " %i",host,port);
384                         connection->error = MPD_ERROR_CONNPORT;
385                         return connection;
386                 }
387                 else {
388                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
389                                         "timeout in attempting to get a response from"
390                                         " \"%s\" on port %i",host,port);
391                         connection->error = MPD_ERROR_NORESPONSE;
392                         return connection;
393                 }
394         }
396         *rt = '\0';
397         output = strdup(connection->buffer);
398         strcpy(connection->buffer,rt+1);
399         connection->buflen = strlen(connection->buffer);
401         if(mpd_parseWelcome(connection,host,port,rt,output) == 0) connection->doneProcessing = 1;
403         free(output);
405         return connection;
408 void mpd_clearError(mpd_Connection * connection) {
409         connection->error = 0;
410         connection->errorStr[0] = '\0';
413 void mpd_closeConnection(mpd_Connection * connection) {
414         closesocket(connection->sock);
415         if(connection->returnElement) free(connection->returnElement);
416         if(connection->request) free(connection->request);
417         free(connection);
418         WSACleanup();
421 static void mpd_executeCommand(mpd_Connection * connection, char * command) {
422         int ret;
423         struct timeval tv;
424         fd_set fds;
425         char * commandPtr = command;
426         int commandLen = strlen(command);
428         if(!connection->doneProcessing && !connection->commandList) {
429                 strcpy(connection->errorStr,"not done processing current command");
430                 connection->error = 1;
431                 return;
432         }
434         mpd_clearError(connection);
436         FD_ZERO(&fds);
437         FD_SET(connection->sock,&fds);
438         tv.tv_sec = connection->timeout.tv_sec;
439         tv.tv_usec = connection->timeout.tv_usec;
441         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
442                         (ret==-1 && SELECT_ERRNO_IGNORE)) {
443                 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
444                 if(ret<=0)
445                 {
446                         if (SENDRECV_ERRNO_IGNORE) continue;
447                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
448                                  "problems giving command \"%s\"",command);
449                         connection->error = MPD_ERROR_SENDING;
450                         return;
451                 }
452                 else {
453                         commandPtr+=ret;
454                         commandLen-=ret;
455                 }
457                 if(commandLen<=0) break;
458         }
460         if(commandLen>0) {
461                 perror("");
462                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
463                          "timeout sending command \"%s\"",command);
464                 connection->error = MPD_ERROR_TIMEOUT;
465                 return;
466         }
468         if(!connection->commandList) connection->doneProcessing = 0;
469         else if(connection->commandList == COMMAND_LIST_OK) {
470                 connection->listOks++;
471         }
474 static void mpd_getNextReturnElement(mpd_Connection * connection) {
475         char * output = NULL;
476         char * rt = NULL;
477         char * name = NULL;
478         char * value = NULL;
479         fd_set fds;
480         struct timeval tv;
481         char * tok = NULL;
482         int readed;
483         char * bufferCheck = NULL;
484         int err;
485         int pos;
487         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
488         connection->returnElement = NULL;
490         if(connection->doneProcessing || (connection->listOks &&
491            connection->doneListOk))
492         {
493                 strcpy(connection->errorStr,"already done processing current command");
494                 connection->error = 1;
495                 return;
496         }
498         bufferCheck = connection->buffer+connection->bufstart;
499         while(connection->bufstart>=connection->buflen ||
500                         !(rt = strchr(bufferCheck,'\n'))) {
501                 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
502                         memmove(connection->buffer,
503                                         connection->buffer+
504                                         connection->bufstart,
505                                         connection->buflen-
506                                         connection->bufstart+1);
507                         connection->buflen-=connection->bufstart;
508                         connection->bufstart = 0;
509                 }
510                 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
511                         strcpy(connection->errorStr,"buffer overrun");
512                         connection->error = MPD_ERROR_BUFFEROVERRUN;
513                         connection->doneProcessing = 1;
514                         connection->doneListOk = 0;
515                         return;
516                 }
517                 bufferCheck = connection->buffer+connection->buflen;
518                 tv.tv_sec = connection->timeout.tv_sec;
519                 tv.tv_usec = connection->timeout.tv_usec;
520                 FD_ZERO(&fds);
521                 FD_SET(connection->sock,&fds);
522                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
523                         readed = recv(connection->sock,
524                                         connection->buffer+connection->buflen,
525                                         MPD_BUFFER_MAX_LENGTH-connection->buflen,
526                                         MSG_DONTWAIT);
527                         if(readed<0 && SENDRECV_ERRNO_IGNORE) {
528                                 continue;
529                         }
530                         if(readed<=0) {
531                                 strcpy(connection->errorStr,"connection"
532                                        " closed");
533                                 connection->error = MPD_ERROR_CONNCLOSED;
534                                 connection->doneProcessing = 1;
535                                 connection->doneListOk = 0;
536                                 return;
537                         }
538                         connection->buflen+=readed;
539                         connection->buffer[connection->buflen] = '\0';
540                 }
541                 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
542                 else {
543                         strcpy(connection->errorStr,"connection timeout");
544                         connection->error = MPD_ERROR_TIMEOUT;
545                         connection->doneProcessing = 1;
546                         connection->doneListOk = 0;
547                         return;
548                 }
549         }
551         *rt = '\0';
552         output = connection->buffer+connection->bufstart;
553         connection->bufstart = rt - connection->buffer + 1;
555         if(strcmp(output,"OK")==0) {
556                 if(connection->listOks > 0) {
557                         strcpy(connection->errorStr, "expected more list_OK's");
558                         connection->error = 1;
559                 }
560                 connection->listOks = 0;
561                 connection->doneProcessing = 1;
562                 connection->doneListOk = 0;
563                 return;
564         }
566         if(strcmp(output, "list_OK") == 0) {
567                 if(!connection->listOks) {
568                         strcpy(connection->errorStr,
569                                         "got an unexpected list_OK");
570                         connection->error = 1;
571                 }
572                 else {
573                         connection->doneListOk = 1;
574                         connection->listOks--;
575                 }
576                 return;
577         }
579         if(strncmp(output,"ACK",strlen("ACK"))==0) {
580                 char * test;
581                 char * needle;
582                 int val;
584                 strcpy(connection->errorStr, output);
585                 connection->error = MPD_ERROR_ACK;
586                 connection->errorCode = MPD_ACK_ERROR_UNK;
587                 connection->errorAt = MPD_ERROR_AT_UNK;
588                 connection->doneProcessing = 1;
589                 connection->doneListOk = 0;
591                 needle = strchr(output, '[');
592                 if(!needle) return;
593                 val = strtol(needle+1, &test, 10);
594                 if(*test != '@') return;
595                 connection->errorCode = val;
596                 val = strtol(test+1, &test, 10);
597                 if(*test != ']') return;
598                 connection->errorAt = val;
599                 return;
600         }
602         tok = strchr(output, ':');
603         if (!tok) return;
604         pos = tok - output;
605         value = ++tok;
606         name = output;
607         name[pos] = '\0';
609         if(value[0]==' ') {
610                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
611         }
612         else {
613                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
614                                         "error parsing: %s:%s",name,value);
615                 connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0';
616                 connection->error = 1;
617         }
620 void mpd_finishCommand(mpd_Connection * connection) {
621         while(!connection->doneProcessing) {
622                 if(connection->doneListOk) connection->doneListOk = 0;
623                 mpd_getNextReturnElement(connection);
624         }
627 static void mpd_finishListOkCommand(mpd_Connection * connection) {
628         while(!connection->doneProcessing && connection->listOks &&
629                         !connection->doneListOk)
630         {
631                 mpd_getNextReturnElement(connection);
632         }
635 int mpd_nextListOkCommand(mpd_Connection * connection) {
636         mpd_finishListOkCommand(connection);
637         if(!connection->doneProcessing) connection->doneListOk = 0;
638         if(connection->listOks == 0 || connection->doneProcessing) return -1;
639         return 0;
642 void mpd_sendStatusCommand(mpd_Connection * connection) {
643         mpd_executeCommand(connection,"status\n");
646 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
647         mpd_Status * status;
649         /*mpd_executeCommand(connection,"status\n");
651         if(connection->error) return NULL;*/
653         if(connection->doneProcessing || (connection->listOks &&
654            connection->doneListOk))
655         {
656                 return NULL;
657         }
659         if(!connection->returnElement) mpd_getNextReturnElement(connection);
661         status = malloc(sizeof(mpd_Status));
662         status->volume = -1;
663         status->repeat = 0;
664         status->random = 0;
665         status->playlist = -1;
666         status->playlistLength = -1;
667         status->state = -1;
668         status->song = 0;
669         status->songid = 0;
670         status->elapsedTime = 0;
671         status->totalTime = 0;
672         status->bitRate = 0;
673         status->sampleRate = 0;
674         status->bits = 0;
675         status->channels = 0;
676         status->crossfade = -1;
677         status->error = NULL;
678         status->updatingDb = 0;
680         if(connection->error) {
681                 free(status);
682                 return NULL;
683         }
684         while(connection->returnElement) {
685                 mpd_ReturnElement * re = connection->returnElement;
686                 if(strcmp(re->name,"volume")==0) {
687                         status->volume = atoi(re->value);
688                 }
689                 else if(strcmp(re->name,"repeat")==0) {
690                         status->repeat = atoi(re->value);
691                 }
692                 else if(strcmp(re->name,"random")==0) {
693                         status->random = atoi(re->value);
694                 }
695                 else if(strcmp(re->name,"playlist")==0) {
696                         status->playlist = strtol(re->value,NULL,10);
697                 }
698                 else if(strcmp(re->name,"playlistlength")==0) {
699                         status->playlistLength = atoi(re->value);
700                 }
701                 else if(strcmp(re->name,"bitrate")==0) {
702                         status->bitRate = atoi(re->value);
703                 }
704                 else if(strcmp(re->name,"state")==0) {
705                         if(strcmp(re->value,"play")==0) {
706                                 status->state = MPD_STATUS_STATE_PLAY;
707                         }
708                         else if(strcmp(re->value,"stop")==0) {
709                                 status->state = MPD_STATUS_STATE_STOP;
710                         }
711                         else if(strcmp(re->value,"pause")==0) {
712                                 status->state = MPD_STATUS_STATE_PAUSE;
713                         }
714                         else {
715                                 status->state = MPD_STATUS_STATE_UNKNOWN;
716                         }
717                 }
718                 else if(strcmp(re->name,"song")==0) {
719                         status->song = atoi(re->value);
720                 }
721                 else if(strcmp(re->name,"songid")==0) {
722                         status->songid = atoi(re->value);
723                 }
724                 else if(strcmp(re->name,"time")==0) {
725                         char * tok = strchr(re->value,':');
726                         /* the second strchr below is a safety check */
727                         if (tok && (strchr(tok,0) > (tok+1))) {
728                                 /* atoi stops at the first non-[0-9] char: */
729                                 status->elapsedTime = atoi(re->value);
730                                 status->totalTime = atoi(tok+1);
731                         }
732                 }
733                 else if(strcmp(re->name,"error")==0) {
734                         status->error = strdup(re->value);
735                 }
736                 else if(strcmp(re->name,"xfade")==0) {
737                         status->crossfade = atoi(re->value);
738                 }
739                 else if(strcmp(re->name,"updating_db")==0) {
740                         status->updatingDb = atoi(re->value);
741                 }
742                 else if(strcmp(re->name,"audio")==0) {
743                         char * tok = strchr(re->value,':');
744                         if (tok && (strchr(tok,0) > (tok+1))) {
745                                 status->sampleRate = atoi(re->value);
746                                 status->bits = atoi(++tok);
747                                 tok = strchr(tok,':');
748                                 if (tok && (strchr(tok,0) > (tok+1)))
749                                         status->channels = atoi(tok+1);
750                         }
751                 }
753                 mpd_getNextReturnElement(connection);
754                 if(connection->error) {
755                         free(status);
756                         return NULL;
757                 }
758         }
760         if(connection->error) {
761                 free(status);
762                 return NULL;
763         }
764         else if(status->state<0) {
765                 strcpy(connection->errorStr,"state not found");
766                 connection->error = 1;
767                 free(status);
768                 return NULL;
769         }
771         return status;
774 void mpd_freeStatus(mpd_Status * status) {
775         if(status->error) free(status->error);
776         free(status);
779 void mpd_sendStatsCommand(mpd_Connection * connection) {
780         mpd_executeCommand(connection,"stats\n");
783 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
784         mpd_Stats * stats;
786         /*mpd_executeCommand(connection,"stats\n");
788         if(connection->error) return NULL;*/
790         if(connection->doneProcessing || (connection->listOks &&
791            connection->doneListOk))
792         {
793                 return NULL;
794         }
796         if(!connection->returnElement) mpd_getNextReturnElement(connection);
798         stats = malloc(sizeof(mpd_Stats));
799         stats->numberOfArtists = 0;
800         stats->numberOfAlbums = 0;
801         stats->numberOfSongs = 0;
802         stats->uptime = 0;
803         stats->dbUpdateTime = 0;
804         stats->playTime = 0;
805         stats->dbPlayTime = 0;
807         if(connection->error) {
808                 free(stats);
809                 return NULL;
810         }
811         while(connection->returnElement) {
812                 mpd_ReturnElement * re = connection->returnElement;
813                 if(strcmp(re->name,"artists")==0) {
814                         stats->numberOfArtists = atoi(re->value);
815                 }
816                 else if(strcmp(re->name,"albums")==0) {
817                         stats->numberOfAlbums = atoi(re->value);
818                 }
819                 else if(strcmp(re->name,"songs")==0) {
820                         stats->numberOfSongs = atoi(re->value);
821                 }
822                 else if(strcmp(re->name,"uptime")==0) {
823                         stats->uptime = strtol(re->value,NULL,10);
824                 }
825                 else if(strcmp(re->name,"db_update")==0) {
826                         stats->dbUpdateTime = strtol(re->value,NULL,10);
827                 }
828                 else if(strcmp(re->name,"playtime")==0) {
829                         stats->playTime = strtol(re->value,NULL,10);
830                 }
831                 else if(strcmp(re->name,"db_playtime")==0) {
832                         stats->dbPlayTime = strtol(re->value,NULL,10);
833                 }
835                 mpd_getNextReturnElement(connection);
836                 if(connection->error) {
837                         free(stats);
838                         return NULL;
839                 }
840         }
842         if(connection->error) {
843                 free(stats);
844                 return NULL;
845         }
847         return stats;
850 void mpd_freeStats(mpd_Stats * stats) {
851         free(stats);
854 static void mpd_initSong(mpd_Song * song) {
855         song->file = NULL;
856         song->artist = NULL;
857         song->album = NULL;
858         song->track = NULL;
859         song->title = NULL;
860         song->name = NULL;
861         song->date = NULL;
862         /* added by Qball */
863         song->genre = NULL;
864         song->composer = NULL;
865         song->disc = NULL;
866         song->comment = NULL;
868         song->time = MPD_SONG_NO_TIME;
869         song->pos = MPD_SONG_NO_NUM;
870         song->id = MPD_SONG_NO_ID;
873 static void mpd_finishSong(mpd_Song * song) {
874         if(song->file) free(song->file);
875         if(song->artist) free(song->artist);
876         if(song->album) free(song->album);
877         if(song->title) free(song->title);
878         if(song->track) free(song->track);
879         if(song->name) free(song->name);
880         if(song->date) free(song->date);
881         if(song->genre) free(song->genre);
882         if(song->composer) free(song->composer);
883         if(song->disc) free(song->disc);
884         if(song->comment) free(song->comment);
887 mpd_Song * mpd_newSong(void) {
888         mpd_Song * ret = malloc(sizeof(mpd_Song));
890         mpd_initSong(ret);
892         return ret;
895 void mpd_freeSong(mpd_Song * song) {
896         mpd_finishSong(song);
897         free(song);
900 mpd_Song * mpd_songDup(mpd_Song * song) {
901         mpd_Song * ret = mpd_newSong();
903         if(song->file) ret->file = strdup(song->file);
904         if(song->artist) ret->artist = strdup(song->artist);
905         if(song->album) ret->album = strdup(song->album);
906         if(song->title) ret->title = strdup(song->title);
907         if(song->track) ret->track = strdup(song->track);
908         if(song->name) ret->name = strdup(song->name);
909         if(song->date) ret->date = strdup(song->date);
910         if(song->genre) ret->genre= strdup(song->genre);
911         if(song->composer) ret->composer= strdup(song->composer);
912         if(song->disc) ret->disc = strdup(song->disc);
913         if(song->comment) ret->comment = strdup(song->comment);
914         ret->time = song->time;
915         ret->pos = song->pos;
916         ret->id = song->id;
918         return ret;
921 static void mpd_initDirectory(mpd_Directory * directory) {
922         directory->path = NULL;
925 static void mpd_finishDirectory(mpd_Directory * directory) {
926         if(directory->path) free(directory->path);
929 mpd_Directory * mpd_newDirectory(void) {
930         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
932         mpd_initDirectory(directory);
934         return directory;
937 void mpd_freeDirectory(mpd_Directory * directory) {
938         mpd_finishDirectory(directory);
940         free(directory);
943 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
944         mpd_Directory * ret = mpd_newDirectory();
946         if(directory->path) ret->path = strdup(directory->path);
948         return ret;
951 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
952         playlist->path = NULL;
955 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
956         if(playlist->path) free(playlist->path);
959 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
960         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
962         mpd_initPlaylistFile(playlist);
964         return playlist;
967 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
968         mpd_finishPlaylistFile(playlist);
969         free(playlist);
972 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
973         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
975         if(playlist->path) ret->path = strdup(playlist->path);
977         return ret;
980 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
981         entity->info.directory = NULL;
984 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
985         if(entity->info.directory) {
986                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
987                         mpd_freeDirectory(entity->info.directory);
988                 }
989                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
990                         mpd_freeSong(entity->info.song);
991                 }
992                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
993                         mpd_freePlaylistFile(entity->info.playlistFile);
994                 }
995         }
998 mpd_InfoEntity * mpd_newInfoEntity(void) {
999         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
1001         mpd_initInfoEntity(entity);
1003         return entity;
1006 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1007         mpd_finishInfoEntity(entity);
1008         free(entity);
1011 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1012         mpd_executeCommand(connection,command);
1015 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1016         mpd_InfoEntity * entity = NULL;
1018         if(connection->doneProcessing || (connection->listOks &&
1019            connection->doneListOk))
1020         {
1021                 return NULL;
1022         }
1024         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1026         if(connection->returnElement) {
1027                 if(strcmp(connection->returnElement->name,"file")==0) {
1028                         entity = mpd_newInfoEntity();
1029                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1030                         entity->info.song = mpd_newSong();
1031                         entity->info.song->file =
1032                                 strdup(connection->returnElement->value);
1033                 }
1034                 else if(strcmp(connection->returnElement->name,
1035                                         "directory")==0) {
1036                         entity = mpd_newInfoEntity();
1037                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1038                         entity->info.directory = mpd_newDirectory();
1039                         entity->info.directory->path =
1040                                 strdup(connection->returnElement->value);
1041                 }
1042                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1043                         entity = mpd_newInfoEntity();
1044                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1045                         entity->info.playlistFile = mpd_newPlaylistFile();
1046                         entity->info.playlistFile->path =
1047                                 strdup(connection->returnElement->value);
1048                 }
1049                 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1050                         entity = mpd_newInfoEntity();
1051                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1052                         entity->info.song = mpd_newSong();
1053                         entity->info.song->pos = atoi(connection->returnElement->value);
1054                 }
1055                 else {
1056                         connection->error = 1;
1057                         strcpy(connection->errorStr,"problem parsing song info");
1058                         return NULL;
1059                 }
1060         }
1061         else return NULL;
1063         mpd_getNextReturnElement(connection);
1064         while(connection->returnElement) {
1065                 mpd_ReturnElement * re = connection->returnElement;
1067                 if(strcmp(re->name,"file")==0) return entity;
1068                 else if(strcmp(re->name,"directory")==0) return entity;
1069                 else if(strcmp(re->name,"playlist")==0) return entity;
1070                 else if(strcmp(re->name,"cpos")==0) return entity;
1072                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1073                                 strlen(re->value)) {
1074                         if(!entity->info.song->artist &&
1075                                         strcmp(re->name,"Artist")==0) {
1076                                 entity->info.song->artist = strdup(re->value);
1077                         }
1078                         else if(!entity->info.song->album &&
1079                                         strcmp(re->name,"Album")==0) {
1080                                 entity->info.song->album = strdup(re->value);
1081                         }
1082                         else if(!entity->info.song->title &&
1083                                         strcmp(re->name,"Title")==0) {
1084                                 entity->info.song->title = strdup(re->value);
1085                         }
1086                         else if(!entity->info.song->track &&
1087                                         strcmp(re->name,"Track")==0) {
1088                                 entity->info.song->track = strdup(re->value);
1089                         }
1090                         else if(!entity->info.song->name &&
1091                                         strcmp(re->name,"Name")==0) {
1092                                 entity->info.song->name = strdup(re->value);
1093                         }
1094                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1095                                         strcmp(re->name,"Time")==0) {
1096                                 entity->info.song->time = atoi(re->value);
1097                         }
1098                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1099                                         strcmp(re->name,"Pos")==0) {
1100                                 entity->info.song->pos = atoi(re->value);
1101                         }
1102                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
1103                                         strcmp(re->name,"Id")==0) {
1104                                 entity->info.song->id = atoi(re->value);
1105                         }
1106                         else if(!entity->info.song->date &&
1107                                         strcmp(re->name, "Date") == 0) {
1108                                 entity->info.song->date = strdup(re->value);
1109                         }
1110                         else if(!entity->info.song->genre &&
1111                                         strcmp(re->name, "Genre") == 0) {
1112                                 entity->info.song->genre = strdup(re->value);
1113                         }
1114                         else if(!entity->info.song->composer &&
1115                                         strcmp(re->name, "Composer") == 0) {
1116                                 entity->info.song->composer = strdup(re->value);
1117                         }
1118                         else if(!entity->info.song->disc &&
1119                                         strcmp(re->name, "Disc") == 0) {
1120                                 entity->info.song->disc = strdup(re->value);
1121                         }
1122                         else if(!entity->info.song->comment &&
1123                                         strcmp(re->name, "Comment") == 0) {
1124                                 entity->info.song->comment = strdup(re->value);
1125                         }
1126                 }
1127                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1128                 }
1129                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1130                 }
1132                 mpd_getNextReturnElement(connection);
1133         }
1135         return entity;
1138 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1139                 const char * name)
1141         if(connection->doneProcessing || (connection->listOks &&
1142                                 connection->doneListOk))
1143         {
1144                 return NULL;
1145         }
1147         mpd_getNextReturnElement(connection);
1148         while(connection->returnElement) {
1149                 mpd_ReturnElement * re = connection->returnElement;
1151                 if(strcmp(re->name,name)==0) return strdup(re->value);
1152                 mpd_getNextReturnElement(connection);
1153         }
1155         return NULL;
1158 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1159         if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1160         {
1161                 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1162         }
1163         return NULL;
1166 char * mpd_getNextArtist(mpd_Connection * connection) {
1167         return mpd_getNextReturnElementNamed(connection,"Artist");
1170 char * mpd_getNextAlbum(mpd_Connection * connection) {
1171         return mpd_getNextReturnElementNamed(connection,"Album");
1174 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1175         char * string = malloc(strlen("playlistinfo")+25);
1176         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1177         mpd_sendInfoCommand(connection,string);
1178         free(string);
1181 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1182         char * string = malloc(strlen("playlistid")+25);
1183         sprintf(string, "playlistid \"%i\"\n", id);
1184         mpd_sendInfoCommand(connection, string);
1185         free(string);
1188 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1189         char * string = malloc(strlen("plchanges")+25);
1190         sprintf(string,"plchanges \"%lld\"\n",playlist);
1191         mpd_sendInfoCommand(connection,string);
1192         free(string);
1195 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1196         char * string = malloc(strlen("plchangesposid")+25);
1197         sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1198         mpd_sendInfoCommand(connection,string);
1199         free(string);
1202 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1203         char * sDir = mpd_sanitizeArg(dir);
1204         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1205         sprintf(string,"listall \"%s\"\n",sDir);
1206         mpd_sendInfoCommand(connection,string);
1207         free(string);
1208         free(sDir);
1211 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1212         char * sDir = mpd_sanitizeArg(dir);
1213         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1214         sprintf(string,"listallinfo \"%s\"\n",sDir);
1215         mpd_sendInfoCommand(connection,string);
1216         free(string);
1217         free(sDir);
1220 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1221         char * sDir = mpd_sanitizeArg(dir);
1222         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1223         sprintf(string,"lsinfo \"%s\"\n",sDir);
1224         mpd_sendInfoCommand(connection,string);
1225         free(string);
1226         free(sDir);
1229 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1230         mpd_executeCommand(connection,"currentsong\n");
1233 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1234                 const char * str)
1236         char st[10];
1237         char * string;
1238         char * sanitStr = mpd_sanitizeArg(str);
1239         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1240         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1241         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1242         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1243         else {
1244                 connection->error = 1;
1245                 strcpy(connection->errorStr,"unknown table for search");
1246                 return;
1247         }
1248         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1249         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1250         mpd_sendInfoCommand(connection,string);
1251         free(string);
1252         free(sanitStr);
1255 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1256                 const char * str)
1258         char st[10];
1259         char * string;
1260         char * sanitStr = mpd_sanitizeArg(str);
1261         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1262         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1263         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1264         else {
1265                 connection->error = 1;
1266                 strcpy(connection->errorStr,"unknown table for find");
1267                 return;
1268         }
1269         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1270         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1271         mpd_sendInfoCommand(connection,string);
1272         free(string);
1273         free(sanitStr);
1276 void mpd_sendListCommand(mpd_Connection * connection, int table,
1277                 const char * arg1)
1279         char st[10];
1280         char * string;
1281         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1282         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1283         else {
1284                 connection->error = 1;
1285                 strcpy(connection->errorStr,"unknown table for list");
1286                 return;
1287         }
1288         if(arg1) {
1289                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1290                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1291                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1292                 free(sanitArg1);
1293         }
1294         else {
1295                 string = malloc(strlen("list")+strlen(st)+3);
1296                 sprintf(string,"list %s\n",st);
1297         }
1298         mpd_sendInfoCommand(connection,string);
1299         free(string);
1302 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1303         char * sFile = mpd_sanitizeArg(file);
1304         char * string = malloc(strlen("add")+strlen(sFile)+5);
1305         sprintf(string,"add \"%s\"\n",sFile);
1306         mpd_executeCommand(connection,string);
1307         free(string);
1308         free(sFile);
1311 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1312         char * string = malloc(strlen("delete")+25);
1313         sprintf(string,"delete \"%i\"\n",songPos);
1314         mpd_sendInfoCommand(connection,string);
1315         free(string);
1318 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1319         char * string = malloc(strlen("deleteid")+25);
1320         sprintf(string, "deleteid \"%i\"\n", id);
1321         mpd_sendInfoCommand(connection,string);
1322         free(string);
1325 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1326         char * sName = mpd_sanitizeArg(name);
1327         char * string = malloc(strlen("save")+strlen(sName)+5);
1328         sprintf(string,"save \"%s\"\n",sName);
1329         mpd_executeCommand(connection,string);
1330         free(string);
1331         free(sName);
1334 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1335         char * sName = mpd_sanitizeArg(name);
1336         char * string = malloc(strlen("load")+strlen(sName)+5);
1337         sprintf(string,"load \"%s\"\n",sName);
1338         mpd_executeCommand(connection,string);
1339         free(string);
1340         free(sName);
1343 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1344         char * sName = mpd_sanitizeArg(name);
1345         char * string = malloc(strlen("rm")+strlen(sName)+5);
1346         sprintf(string,"rm \"%s\"\n",sName);
1347         mpd_executeCommand(connection,string);
1348         free(string);
1349         free(sName);
1352 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1353         mpd_executeCommand(connection,"shuffle\n");
1356 void mpd_sendClearCommand(mpd_Connection * connection) {
1357         mpd_executeCommand(connection,"clear\n");
1360 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1361         char * string = malloc(strlen("play")+25);
1362         sprintf(string,"play \"%i\"\n",songPos);
1363         mpd_sendInfoCommand(connection,string);
1364         free(string);
1367 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1368         char * string = malloc(strlen("playid")+25);
1369         sprintf(string,"playid \"%i\"\n",id);
1370         mpd_sendInfoCommand(connection,string);
1371         free(string);
1374 void mpd_sendStopCommand(mpd_Connection * connection) {
1375         mpd_executeCommand(connection,"stop\n");
1378 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1379         char * string = malloc(strlen("pause")+25);
1380         sprintf(string,"pause \"%i\"\n",pauseMode);
1381         mpd_executeCommand(connection,string);
1382         free(string);
1385 void mpd_sendNextCommand(mpd_Connection * connection) {
1386         mpd_executeCommand(connection,"next\n");
1389 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1390         char * string = malloc(strlen("move")+25);
1391         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1392         mpd_sendInfoCommand(connection,string);
1393         free(string);
1396 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1397         char * string = malloc(strlen("moveid")+25);
1398         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1399         mpd_sendInfoCommand(connection,string);
1400         free(string);
1403 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1404         char * string = malloc(strlen("swap")+25);
1405         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1406         mpd_sendInfoCommand(connection,string);
1407         free(string);
1410 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1411         char * string = malloc(strlen("swapid")+25);
1412         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1413         mpd_sendInfoCommand(connection,string);
1414         free(string);
1417 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1418         char * string = malloc(strlen("seek")+25);
1419         sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1420         mpd_sendInfoCommand(connection,string);
1421         free(string);
1424 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1425         char * string = malloc(strlen("seekid")+25);
1426         sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1427         mpd_sendInfoCommand(connection,string);
1428         free(string);
1431 void mpd_sendUpdateCommand(mpd_Connection * connection, char * path) {
1432         char * sPath = mpd_sanitizeArg(path);
1433         char * string = malloc(strlen("update")+strlen(sPath)+5);
1434         sprintf(string,"update \"%s\"\n",sPath);
1435         mpd_sendInfoCommand(connection,string);
1436         free(string);
1437         free(sPath);
1440 int mpd_getUpdateId(mpd_Connection * connection) {
1441         char * jobid;
1442         int ret = 0;
1444         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1445         if(jobid) {
1446                 ret = atoi(jobid);
1447                 free(jobid);
1448         }
1450         return ret;
1453 void mpd_sendPrevCommand(mpd_Connection * connection) {
1454         mpd_executeCommand(connection,"previous\n");
1457 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1458         char * string = malloc(strlen("repeat")+25);
1459         sprintf(string,"repeat \"%i\"\n",repeatMode);
1460         mpd_executeCommand(connection,string);
1461         free(string);
1464 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1465         char * string = malloc(strlen("random")+25);
1466         sprintf(string,"random \"%i\"\n",randomMode);
1467         mpd_executeCommand(connection,string);
1468         free(string);
1471 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1472         char * string = malloc(strlen("setvol")+25);
1473         sprintf(string,"setvol \"%i\"\n",volumeChange);
1474         mpd_executeCommand(connection,string);
1475         free(string);
1478 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1479         char * string = malloc(strlen("volume")+25);
1480         sprintf(string,"volume \"%i\"\n",volumeChange);
1481         mpd_executeCommand(connection,string);
1482         free(string);
1485 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1486         char * string = malloc(strlen("crossfade")+25);
1487         sprintf(string,"crossfade \"%i\"\n",seconds);
1488         mpd_executeCommand(connection,string);
1489         free(string);
1492 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1493         char * sPass = mpd_sanitizeArg(pass);
1494         char * string = malloc(strlen("password")+strlen(sPass)+5);
1495         sprintf(string,"password \"%s\"\n",sPass);
1496         mpd_executeCommand(connection,string);
1497         free(string);
1498         free(sPass);
1501 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1502         if(connection->commandList) {
1503                 strcpy(connection->errorStr,"already in command list mode");
1504                 connection->error = 1;
1505                 return;
1506         }
1507         connection->commandList = COMMAND_LIST;
1508         mpd_executeCommand(connection,"command_list_begin\n");
1511 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1512         if(connection->commandList) {
1513                 strcpy(connection->errorStr,"already in command list mode");
1514                 connection->error = 1;
1515                 return;
1516         }
1517         connection->commandList = COMMAND_LIST_OK;
1518         mpd_executeCommand(connection,"command_list_ok_begin\n");
1519         connection->listOks = 0;
1522 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1523         if(!connection->commandList) {
1524                 strcpy(connection->errorStr,"not in command list mode");
1525                 connection->error = 1;
1526                 return;
1527         }
1528         connection->commandList = 0;
1529         mpd_executeCommand(connection,"command_list_end\n");
1532 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1533         mpd_executeCommand(connection,"outputs\n");
1536 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1537         mpd_OutputEntity * output = NULL;
1539         if(connection->doneProcessing || (connection->listOks &&
1540                                 connection->doneListOk))
1541         {
1542                 return NULL;
1543         }
1545         if(connection->error) return NULL;
1547         output = malloc(sizeof(mpd_OutputEntity));
1548         output->id = -10;
1549         output->name = NULL;
1550         output->enabled = 0;
1552         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1554         while(connection->returnElement) {
1555                 mpd_ReturnElement * re = connection->returnElement;
1556                 if(strcmp(re->name,"outputid")==0) {
1557                         if(output!=NULL && output->id>=0) return output;
1558                         output->id = atoi(re->value);
1559                 }
1560                 else if(strcmp(re->name,"outputname")==0) {
1561                         output->name = strdup(re->value);
1562                 }
1563                 else if(strcmp(re->name,"outputenabled")==0) {
1564                         output->enabled = atoi(re->value);
1565                 }
1567                 mpd_getNextReturnElement(connection);
1568                 if(connection->error) {
1569                         free(output);
1570                         return NULL;
1571                 }
1573         }
1575         return output;
1578 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1579         char * string = malloc(strlen("enableoutput")+25);
1580         sprintf(string,"enableoutput \"%i\"\n",outputId);
1581         mpd_executeCommand(connection,string);
1582         free(string);
1585 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1586         char * string = malloc(strlen("disableoutput")+25);
1587         sprintf(string,"disableoutput \"%i\"\n",outputId);
1588         mpd_executeCommand(connection,string);
1589         free(string);
1592 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1593         free(output->name);
1594         free(output);
1597 /**
1598  * mpd_sendNotCommandsCommand
1599  * odd naming, but it gets the not allowed commands
1600  */
1602 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1603         mpd_executeCommand(connection,"notcommands\n");
1606 /**
1607  * mpd_sendCommandsCommand
1608  * odd naming, but it gets the allowed commands
1609  */
1611 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1612         mpd_executeCommand(connection,"commands\n");
1614 /**
1615  * Get the next returned command
1616  */
1617 char * mpd_getNextCommand(mpd_Connection * connection) {
1618         return mpd_getNextReturnElementNamed(connection,"command");
1621 void mpd_startSearch(mpd_Connection * connection,int exact) {
1622         if(connection->request) {
1623                 /* search/find allready in progress */
1624                 /* TODO: set error here?  */
1625                 return;
1626         }
1627         if(exact){
1628                 connection->request = strdup("find");
1629         }
1630         else{
1631                 connection->request = strdup("search");
1632         }
1636 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1637         if(connection->request) {
1638                 /* search/find allready in progress */
1639                 /* TODO: set error here?  */
1640                 return;
1641         }
1642         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1643                 /* set error here */
1644                 return;
1645         }
1647         connection->request = malloc(sizeof(char)*(
1648                                 /* length of the field name */
1649                                 strlen(mpdTagItemKeys[field])+
1650                                 /* "list"+space+\0 */
1651                                 6
1652                                 ));
1653         sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1658 void mpd_addConstraintSearch(mpd_Connection *connection,
1659                 int field,
1660                 char *name)
1662         char *arg = NULL;
1663         if(!connection->request){
1664                 return;
1665         }
1666         if(name == NULL) {
1667                 return;
1668         }
1669         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1670                 return;
1671         }
1672         /* clean up the query */
1673         arg = mpd_sanitizeArg(name);
1674         /* create space for the query */
1675         connection->request = realloc(connection->request, (
1676                          /* length of the old string */
1677                          strlen(connection->request)+
1678                          /* space between */
1679                          1+
1680                          /* length of the field name */
1681                          strlen(mpdTagItemKeys[field])+
1682                          /* space plus starting " */
1683                          2+
1684                          /* length of search term */
1685                          strlen(arg)+
1686                          /* closign " +\0 that is added sprintf */
1687                          2
1688                         )*sizeof(char));
1689         /* and form the query */
1690         sprintf(connection->request, "%s %s \"%s\"",
1691                         connection->request,
1692                         mpdTagItemKeys[field],
1693                         arg);
1694         free(arg);
1698 void mpd_commitSearch(mpd_Connection *connection)
1700         if(connection->request)
1701         {
1702                 int length = strlen(connection->request);
1703                 /* fixing up the string for mpd to like */
1704                 connection->request = realloc(connection->request,
1705                                 (length+        /* old length */
1706                                  2              /* closing \n and \0 */
1707                                 )*sizeof(char));
1708                 connection->request[length] = '\n';
1709                 connection->request[length+1] = '\0';
1710                 /* and off we go */
1711                 mpd_sendInfoCommand(connection, connection->request);
1712                 /* clean up a bit */
1713                 free(connection->request);
1714                 connection->request = NULL;
1715         }
1718 /**
1719  * @param connection a MpdConnection
1720  * @param path  the path to the playlist.
1721  *
1722  * List the content, with full metadata, of a stored playlist.
1723  *
1724  */
1725 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1727         char *arg = mpd_sanitizeArg(path);
1728         char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1729         sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1730         mpd_sendInfoCommand(connection, query);
1731         free(arg);
1732         free(query);
1735 /**
1736  * @param connection a MpdConnection
1737  * @param path  the path to the playlist.
1738  *
1739  * List the content of a stored playlist.
1740  *
1741  */
1742 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1744         char *arg = mpd_sanitizeArg(path);
1745         char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1746         sprintf(query, "listplaylist \"%s\"\n",arg);
1747         mpd_sendInfoCommand(connection, query);
1748         free(arg);
1749         free(query);