Code

mpdclient: moved code to filelist.c
[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"
34 #include "resolver.h"
35 #include "str_pool.h"
37 #include <assert.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <stdio.h>
41 #include <sys/param.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45 #include <fcntl.h>
47 #ifdef WIN32
48 #  include <ws2tcpip.h>
49 #  include <winsock.h>
50 #else
51 #  include <netinet/in.h>
52 #  include <arpa/inet.h>
53 #  include <sys/socket.h>
54 #  include <netdb.h>
55 #endif
57 #ifndef WIN32
58 #include <sys/un.h>
59 #endif
61 #ifndef MSG_DONTWAIT
62 #  define MSG_DONTWAIT 0
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)
81 {
82         WSADATA wsaData;
83         if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
84                         LOBYTE(wsaData.wVersion) != 2 ||
85                         HIBYTE(wsaData.wVersion) != 2 ) {
86                 snprintf(connection->errorStr, sizeof(connection->errorStr),
87                          "Could not find usable WinSock DLL.");
88                 connection->error = MPD_ERROR_SYSTEM;
89                 return 1;
90         }
91         return 0;
92 }
94 static int do_connect_fail(mpd_Connection *connection,
95                            const struct sockaddr *serv_addr, int addrlen)
96 {
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 const char *const mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
115         "Artist",
116         "Album",
117         "Title",
118         "Track",
119         "Name",
120         "Genre",
121         "Date",
122         "Composer",
123         "Performer",
124         "Comment",
125         "Disc",
126         "filename"
127 };
129 static char * mpd_sanitizeArg(const char * arg) {
130         size_t i;
131         char * ret;
132         register const char *c;
133         register char *rc;
135         /* instead of counting in that loop above, just
136          * use a bit more memory and half running time
137          */
138         ret = malloc(strlen(arg) * 2 + 1);
140         c = arg;
141         rc = ret;
142         for(i = strlen(arg)+1; i != 0; --i) {
143                 if(*c=='"' || *c=='\\')
144                         *rc++ = '\\';
145                 *(rc++) = *(c++);
146         }
148         return ret;
151 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
153         mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
155         ret->name = str_pool_get(name);
156         ret->value = str_pool_get(value);
158         return ret;
161 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
162         str_pool_put(re->name);
163         str_pool_put(re->value);
164         free(re);
167 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
168         connection->timeout.tv_sec = (int)timeout;
169         connection->timeout.tv_usec = (int)(timeout*1e6 -
170                                             connection->timeout.tv_sec*1000000 +
171                                             0.5);
174 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
175                             char * output) {
176         char * tmp;
177         char * test;
178         int i;
180         if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
181                 snprintf(connection->errorStr, sizeof(connection->errorStr),
182                          "mpd not running on port %i on host \"%s\"",
183                          port,host);
184                 connection->error = MPD_ERROR_NOTMPD;
185                 return 1;
186         }
188         tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
190         for(i=0;i<3;i++) {
191                 if(tmp) connection->version[i] = strtol(tmp,&test,10);
193                 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
194                         snprintf(connection->errorStr, sizeof(connection->errorStr),
195                                  "error parsing version number at "
196                                  "\"%s\"",
197                                  &output[strlen(MPD_WELCOME_MESSAGE)]);
198                         connection->error = MPD_ERROR_NOTMPD;
199                         return 1;
200                 }
201                 tmp = ++test;
202         }
204         return 0;
207 /**
208  * Wait for the socket to become readable.
209  */
210 static int mpd_wait(mpd_Connection *connection)
212         struct timeval tv;
213         fd_set fds;
214         int ret;
216         while (1) {
217                 tv = connection->timeout;
218                 FD_ZERO(&fds);
219                 FD_SET(connection->sock, &fds);
221                 ret = select(connection->sock + 1, &fds, NULL, NULL, &tv);
222                 if (ret > 0)
223                         return 0;
225                 if (ret == 0 || !SELECT_ERRNO_IGNORE)
226                         return -1;
227         }
230 /**
231  * Wait until the socket is connected and check its result.  Returns 1
232  * on success, 0 on timeout, -errno on error.
233  */
234 static int mpd_wait_connected(mpd_Connection *connection)
236         int ret;
237         int s_err = 0;
238         socklen_t s_err_size = sizeof(s_err);
240         ret = mpd_wait(connection);
241         if (ret < 0)
242                 return 0;
244         ret = getsockopt(connection->sock, SOL_SOCKET, SO_ERROR,
245                          (char*)&s_err, &s_err_size);
246         if (ret < 0)
247                 return -errno;
249         if (s_err != 0)
250                 return -s_err;
252         return 1;
255 /**
256  * Attempt to read data from the socket into the input buffer.
257  * Returns 0 on success, -1 on error.
258  */
259 static int mpd_recv(mpd_Connection *connection)
261         int ret;
262         ssize_t nbytes;
264         assert(connection != NULL);
265         assert(connection->buflen <= sizeof(connection->buffer));
266         assert(connection->bufstart <= connection->buflen);
268         if (connection->buflen >= sizeof(connection->buffer)) {
269                 /* delete consumed data from beginning of buffer */
270                 connection->buflen -= connection->bufstart;
271                 memmove(connection->buffer,
272                         connection->buffer + connection->bufstart,
273                         connection->buflen);
274                 connection->bufstart = 0;
275         }
277         if (connection->buflen >= sizeof(connection->buffer)) {
278                 strcpy(connection->errorStr, "buffer overrun");
279                 connection->error = MPD_ERROR_BUFFEROVERRUN;
280                 connection->doneProcessing = 1;
281                 connection->doneListOk = 0;
282                 return -1;
283         }
285         while (1) {
286                 ret = mpd_wait(connection);
287                 if (ret < 0) {
288                         strcpy(connection->errorStr, "connection timeout");
289                         connection->error = MPD_ERROR_TIMEOUT;
290                         connection->doneProcessing = 1;
291                         connection->doneListOk = 0;
292                         return -1;
293                 }
295                 nbytes = read(connection->sock,
296                               connection->buffer + connection->buflen,
297                               sizeof(connection->buffer) - connection->buflen);
298                 if (nbytes > 0) {
299                         connection->buflen += nbytes;
300                         return 0;
301                 }
303                 if (nbytes == 0 || !SENDRECV_ERRNO_IGNORE) {
304                         strcpy(connection->errorStr, "connection closed");
305                         connection->error = MPD_ERROR_CONNCLOSED;
306                         connection->doneProcessing = 1;
307                         connection->doneListOk = 0;
308                         return -1;
309                 }
310         }
313 static int
314 mpd_connect(mpd_Connection *connection, const char * host, int port)
316         struct resolver *resolver;
317         const struct resolver_address *address;
318         int ret;
320         resolver = resolver_new(host, port);
321         if (resolver == NULL) {
322                 snprintf(connection->errorStr, sizeof(connection->errorStr),
323                          "host \"%s\" not found", host);
324                 connection->error = MPD_ERROR_UNKHOST;
325                 return -1;
326         }
328         while ((address = resolver_next(resolver)) != NULL) {
329                 connection->sock = socket(address->family, SOCK_STREAM,
330                                           address->protocol);
331                 if (connection->sock < 0) {
332                         snprintf(connection->errorStr,
333                                  sizeof(connection->errorStr),
334                                  "problems creating socket: %s",
335                                  strerror(errno));
336                         connection->error = MPD_ERROR_SYSTEM;
337                         continue;
338                 }
340                 ret = do_connect_fail(connection,
341                                       address->addr, address->addrlen);
342                 if (ret != 0) {
343                         snprintf(connection->errorStr,
344                                  sizeof(connection->errorStr),
345                                  "problems connecting to \"%s\" on port"
346                                  " %i: %s", host, port, strerror(errno));
347                         connection->error = MPD_ERROR_CONNPORT;
349                         closesocket(connection->sock);
350                         connection->sock = -1;
351                         continue;
352                 }
354                 ret = mpd_wait_connected(connection);
355                 if (ret > 0) {
356                         resolver_free(resolver);
357                         mpd_clearError(connection);
358                         return 0;
359                 }
361                 if (ret == 0) {
362                         snprintf(connection->errorStr,
363                                  sizeof(connection->errorStr),
364                                  "timeout in attempting to get a response from"
365                                  " \"%s\" on port %i", host, port);
366                         connection->error = MPD_ERROR_NORESPONSE;
367                 } else if (ret < 0) {
368                         snprintf(connection->errorStr,
369                                  sizeof(connection->errorStr),
370                                  "problems connecting to \"%s\" on port %i: %s",
371                                  host, port, strerror(-ret));
372                         connection->error = MPD_ERROR_CONNPORT;
373                 }
375                 closesocket(connection->sock);
376                 connection->sock = -1;
377         }
379         resolver_free(resolver);
380         return -1;
383 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
384         int err;
385         char * rt;
386         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
388         connection->buflen = 0;
389         connection->bufstart = 0;
390         mpd_clearError(connection);
391         connection->doneProcessing = 0;
392         connection->commandList = 0;
393         connection->listOks = 0;
394         connection->doneListOk = 0;
395         connection->returnElement = NULL;
396         connection->request = NULL;
398         if (winsock_dll_error(connection))
399                 return connection;
401         mpd_setConnectionTimeout(connection,timeout);
403         err = mpd_connect(connection, host, port);
404         if (err < 0)
405                 return connection;
407         while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
408                 err = mpd_recv(connection);
409                 if (err < 0)
410                         return connection;
411         }
413         *rt = '\0';
414         if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
415                 connection->doneProcessing = 1;
417         connection->buflen -= rt + 1 - connection->buffer;
418         memmove(connection->buffer, rt + 1, connection->buflen);
420         return connection;
423 void mpd_clearError(mpd_Connection * connection) {
424         connection->error = 0;
425         connection->errorStr[0] = '\0';
428 void mpd_closeConnection(mpd_Connection * connection) {
429         closesocket(connection->sock);
430         if(connection->returnElement) free(connection->returnElement);
431         if(connection->request) free(connection->request);
432         free(connection);
433         WSACleanup();
436 static void mpd_executeCommand(mpd_Connection *connection,
437                                const char *command) {
438         int ret;
439         struct timeval tv;
440         fd_set fds;
441         const char *commandPtr = command;
442         int commandLen = strlen(command);
444         if (!connection->doneProcessing && !connection->commandList) {
445                 strcpy(connection->errorStr,
446                        "not done processing current command");
447                 connection->error = 1;
448                 return;
449         }
451         mpd_clearError(connection);
453         FD_ZERO(&fds);
454         FD_SET(connection->sock,&fds);
455         tv.tv_sec = connection->timeout.tv_sec;
456         tv.tv_usec = connection->timeout.tv_usec;
458         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
459                         (ret==-1 && SELECT_ERRNO_IGNORE)) {
460                 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
461                 if(ret<=0)
462                 {
463                         if (SENDRECV_ERRNO_IGNORE) continue;
464                         snprintf(connection->errorStr, sizeof(connection->errorStr),
465                                  "problems giving command \"%s\"",command);
466                         connection->error = MPD_ERROR_SENDING;
467                         return;
468                 }
469                 else {
470                         commandPtr+=ret;
471                         commandLen-=ret;
472                 }
474                 if(commandLen<=0) break;
475         }
477         if(commandLen>0) {
478                 perror("");
479                 snprintf(connection->errorStr, sizeof(connection->errorStr),
480                          "timeout sending command \"%s\"",command);
481                 connection->error = MPD_ERROR_TIMEOUT;
482                 return;
483         }
485         if(!connection->commandList) connection->doneProcessing = 0;
486         else if(connection->commandList == COMMAND_LIST_OK) {
487                 connection->listOks++;
488         }
491 static void mpd_getNextReturnElement(mpd_Connection * connection) {
492         char * output = NULL;
493         char * rt = NULL;
494         char * name = NULL;
495         char * value = NULL;
496         char * tok = NULL;
497         int err;
498         int pos;
500         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
501         connection->returnElement = NULL;
503         if (connection->doneProcessing ||
504             (connection->listOks && connection->doneListOk)) {
505                 strcpy(connection->errorStr,"already done processing current command");
506                 connection->error = 1;
507                 return;
508         }
510         while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
511                              connection->buflen - connection->bufstart))) {
512                 err = mpd_recv(connection);
513                 if (err < 0)
514                         return;
515         }
517         *rt = '\0';
518         output = connection->buffer+connection->bufstart;
519         connection->bufstart = rt - connection->buffer + 1;
521         if(strcmp(output,"OK")==0) {
522                 if(connection->listOks > 0) {
523                         strcpy(connection->errorStr, "expected more list_OK's");
524                         connection->error = 1;
525                 }
526                 connection->listOks = 0;
527                 connection->doneProcessing = 1;
528                 connection->doneListOk = 0;
529                 return;
530         }
532         if(strcmp(output, "list_OK") == 0) {
533                 if(!connection->listOks) {
534                         strcpy(connection->errorStr,
535                                         "got an unexpected list_OK");
536                         connection->error = 1;
537                 }
538                 else {
539                         connection->doneListOk = 1;
540                         connection->listOks--;
541                 }
542                 return;
543         }
545         if(strncmp(output,"ACK",strlen("ACK"))==0) {
546                 size_t length = strlen(output);
547                 char * test;
548                 char * needle;
549                 int val;
551                 if (length >= sizeof(connection->errorStr))
552                         length = sizeof(connection->errorStr) - 1;
554                 memcpy(connection->errorStr, output, length);
555                 connection->errorStr[length] = 0;
556                 connection->error = MPD_ERROR_ACK;
557                 connection->errorCode = MPD_ACK_ERROR_UNK;
558                 connection->errorAt = MPD_ERROR_AT_UNK;
559                 connection->doneProcessing = 1;
560                 connection->doneListOk = 0;
562                 needle = strchr(output, '[');
563                 if(!needle) return;
564                 val = strtol(needle+1, &test, 10);
565                 if(*test != '@') return;
566                 connection->errorCode = val;
567                 val = strtol(test+1, &test, 10);
568                 if(*test != ']') return;
569                 connection->errorAt = val;
570                 return;
571         }
573         tok = strchr(output, ':');
574         if (!tok) return;
575         pos = tok - output;
576         value = ++tok;
577         name = output;
578         name[pos] = '\0';
580         if(value[0]==' ') {
581                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
582         }
583         else {
584                 snprintf(connection->errorStr, sizeof(connection->errorStr),
585                          "error parsing: %s:%s",name,value);
586                 connection->error = 1;
587         }
590 void mpd_finishCommand(mpd_Connection * connection) {
591         while(!connection->doneProcessing) {
592                 if(connection->doneListOk) connection->doneListOk = 0;
593                 mpd_getNextReturnElement(connection);
594         }
597 static void mpd_finishListOkCommand(mpd_Connection * connection) {
598         while(!connection->doneProcessing && connection->listOks &&
599                         !connection->doneListOk)
600         {
601                 mpd_getNextReturnElement(connection);
602         }
605 int mpd_nextListOkCommand(mpd_Connection * connection) {
606         mpd_finishListOkCommand(connection);
607         if(!connection->doneProcessing) connection->doneListOk = 0;
608         if(connection->listOks == 0 || connection->doneProcessing) return -1;
609         return 0;
612 void mpd_sendStatusCommand(mpd_Connection * connection) {
613         mpd_executeCommand(connection,"status\n");
616 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
617         mpd_Status * status;
619         /*mpd_executeCommand(connection,"status\n");
621         if(connection->error) return NULL;*/
623         if(connection->doneProcessing || (connection->listOks &&
624            connection->doneListOk))
625         {
626                 return NULL;
627         }
629         if(!connection->returnElement) mpd_getNextReturnElement(connection);
631         status = malloc(sizeof(mpd_Status));
632         status->volume = -1;
633         status->repeat = 0;
634         status->random = 0;
635         status->playlist = -1;
636         status->playlistLength = -1;
637         status->state = -1;
638         status->song = 0;
639         status->songid = 0;
640         status->elapsedTime = 0;
641         status->totalTime = 0;
642         status->bitRate = 0;
643         status->sampleRate = 0;
644         status->bits = 0;
645         status->channels = 0;
646         status->crossfade = -1;
647         status->error = NULL;
648         status->updatingDb = 0;
650         if(connection->error) {
651                 free(status);
652                 return NULL;
653         }
654         while(connection->returnElement) {
655                 mpd_ReturnElement * re = connection->returnElement;
656                 if(strcmp(re->name,"volume")==0) {
657                         status->volume = atoi(re->value);
658                 }
659                 else if(strcmp(re->name,"repeat")==0) {
660                         status->repeat = atoi(re->value);
661                 }
662                 else if(strcmp(re->name,"random")==0) {
663                         status->random = atoi(re->value);
664                 }
665                 else if(strcmp(re->name,"playlist")==0) {
666                         status->playlist = strtol(re->value,NULL,10);
667                 }
668                 else if(strcmp(re->name,"playlistlength")==0) {
669                         status->playlistLength = atoi(re->value);
670                 }
671                 else if(strcmp(re->name,"bitrate")==0) {
672                         status->bitRate = atoi(re->value);
673                 }
674                 else if(strcmp(re->name,"state")==0) {
675                         if(strcmp(re->value,"play")==0) {
676                                 status->state = MPD_STATUS_STATE_PLAY;
677                         }
678                         else if(strcmp(re->value,"stop")==0) {
679                                 status->state = MPD_STATUS_STATE_STOP;
680                         }
681                         else if(strcmp(re->value,"pause")==0) {
682                                 status->state = MPD_STATUS_STATE_PAUSE;
683                         }
684                         else {
685                                 status->state = MPD_STATUS_STATE_UNKNOWN;
686                         }
687                 }
688                 else if(strcmp(re->name,"song")==0) {
689                         status->song = atoi(re->value);
690                 }
691                 else if(strcmp(re->name,"songid")==0) {
692                         status->songid = atoi(re->value);
693                 }
694                 else if(strcmp(re->name,"time")==0) {
695                         char * tok = strchr(re->value,':');
696                         /* the second strchr below is a safety check */
697                         if (tok && (strchr(tok,0) > (tok+1))) {
698                                 /* atoi stops at the first non-[0-9] char: */
699                                 status->elapsedTime = atoi(re->value);
700                                 status->totalTime = atoi(tok+1);
701                         }
702                 }
703                 else if(strcmp(re->name,"error")==0) {
704                         status->error = strdup(re->value);
705                 }
706                 else if(strcmp(re->name,"xfade")==0) {
707                         status->crossfade = atoi(re->value);
708                 }
709                 else if(strcmp(re->name,"updating_db")==0) {
710                         status->updatingDb = atoi(re->value);
711                 }
712                 else if(strcmp(re->name,"audio")==0) {
713                         char * tok = strchr(re->value,':');
714                         if (tok && (strchr(tok,0) > (tok+1))) {
715                                 status->sampleRate = atoi(re->value);
716                                 status->bits = atoi(++tok);
717                                 tok = strchr(tok,':');
718                                 if (tok && (strchr(tok,0) > (tok+1)))
719                                         status->channels = atoi(tok+1);
720                         }
721                 }
723                 mpd_getNextReturnElement(connection);
724                 if(connection->error) {
725                         free(status);
726                         return NULL;
727                 }
728         }
730         if(connection->error) {
731                 free(status);
732                 return NULL;
733         }
734         else if(status->state<0) {
735                 strcpy(connection->errorStr,"state not found");
736                 connection->error = 1;
737                 free(status);
738                 return NULL;
739         }
741         return status;
744 void mpd_freeStatus(mpd_Status * status) {
745         if(status->error) free(status->error);
746         free(status);
749 void mpd_sendStatsCommand(mpd_Connection * connection) {
750         mpd_executeCommand(connection,"stats\n");
753 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
754         mpd_Stats * stats;
756         /*mpd_executeCommand(connection,"stats\n");
758         if(connection->error) return NULL;*/
760         if(connection->doneProcessing || (connection->listOks &&
761            connection->doneListOk))
762         {
763                 return NULL;
764         }
766         if(!connection->returnElement) mpd_getNextReturnElement(connection);
768         stats = malloc(sizeof(mpd_Stats));
769         stats->numberOfArtists = 0;
770         stats->numberOfAlbums = 0;
771         stats->numberOfSongs = 0;
772         stats->uptime = 0;
773         stats->dbUpdateTime = 0;
774         stats->playTime = 0;
775         stats->dbPlayTime = 0;
777         if(connection->error) {
778                 free(stats);
779                 return NULL;
780         }
781         while(connection->returnElement) {
782                 mpd_ReturnElement * re = connection->returnElement;
783                 if(strcmp(re->name,"artists")==0) {
784                         stats->numberOfArtists = atoi(re->value);
785                 }
786                 else if(strcmp(re->name,"albums")==0) {
787                         stats->numberOfAlbums = atoi(re->value);
788                 }
789                 else if(strcmp(re->name,"songs")==0) {
790                         stats->numberOfSongs = atoi(re->value);
791                 }
792                 else if(strcmp(re->name,"uptime")==0) {
793                         stats->uptime = strtol(re->value,NULL,10);
794                 }
795                 else if(strcmp(re->name,"db_update")==0) {
796                         stats->dbUpdateTime = strtol(re->value,NULL,10);
797                 }
798                 else if(strcmp(re->name,"playtime")==0) {
799                         stats->playTime = strtol(re->value,NULL,10);
800                 }
801                 else if(strcmp(re->name,"db_playtime")==0) {
802                         stats->dbPlayTime = strtol(re->value,NULL,10);
803                 }
805                 mpd_getNextReturnElement(connection);
806                 if(connection->error) {
807                         free(stats);
808                         return NULL;
809                 }
810         }
812         if(connection->error) {
813                 free(stats);
814                 return NULL;
815         }
817         return stats;
820 void mpd_freeStats(mpd_Stats * stats) {
821         free(stats);
824 static void mpd_initDirectory(mpd_Directory * directory) {
825         directory->path = NULL;
828 static void mpd_finishDirectory(mpd_Directory * directory) {
829         if (directory->path)
830                 str_pool_put(directory->path);
833 mpd_Directory * mpd_newDirectory(void) {
834         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
836         mpd_initDirectory(directory);
838         return directory;
841 void mpd_freeDirectory(mpd_Directory * directory) {
842         mpd_finishDirectory(directory);
844         free(directory);
847 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
848         mpd_Directory * ret = mpd_newDirectory();
850         if (directory->path)
851                 ret->path = str_pool_dup(directory->path);
853         return ret;
856 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
857         playlist->path = NULL;
860 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
861         if (playlist->path)
862                 str_pool_put(playlist->path);
865 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
866         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
868         mpd_initPlaylistFile(playlist);
870         return playlist;
873 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
874         mpd_finishPlaylistFile(playlist);
875         free(playlist);
878 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
879         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
881         if (playlist->path)
882                 ret->path = str_pool_dup(playlist->path);
884         return ret;
887 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
888         entity->info.directory = NULL;
891 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
892         if(entity->info.directory) {
893                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
894                         mpd_freeDirectory(entity->info.directory);
895                 }
896                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
897                         mpd_freeSong(entity->info.song);
898                 }
899                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
900                         mpd_freePlaylistFile(entity->info.playlistFile);
901                 }
902         }
905 mpd_InfoEntity * mpd_newInfoEntity(void) {
906         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
908         mpd_initInfoEntity(entity);
910         return entity;
913 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
914         mpd_finishInfoEntity(entity);
915         free(entity);
918 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
919         mpd_executeCommand(connection,command);
922 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
923         mpd_InfoEntity * entity = NULL;
925         if(connection->doneProcessing || (connection->listOks &&
926            connection->doneListOk)) {
927                 return NULL;
928         }
930         if(!connection->returnElement) mpd_getNextReturnElement(connection);
932         if(connection->returnElement) {
933                 if(strcmp(connection->returnElement->name,"file")==0) {
934                         entity = mpd_newInfoEntity();
935                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
936                         entity->info.song = mpd_newSong();
937                         entity->info.song->file =
938                                 str_pool_dup(connection->returnElement->value);
939                 }
940                 else if(strcmp(connection->returnElement->name,
941                                         "directory")==0) {
942                         entity = mpd_newInfoEntity();
943                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
944                         entity->info.directory = mpd_newDirectory();
945                         entity->info.directory->path =
946                                 str_pool_dup(connection->returnElement->value);
947                 }
948                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
949                         entity = mpd_newInfoEntity();
950                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
951                         entity->info.playlistFile = mpd_newPlaylistFile();
952                         entity->info.playlistFile->path =
953                                 str_pool_dup(connection->returnElement->value);
954                 }
955                 else if(strcmp(connection->returnElement->name, "cpos") == 0){
956                         entity = mpd_newInfoEntity();
957                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
958                         entity->info.song = mpd_newSong();
959                         entity->info.song->pos = atoi(connection->returnElement->value);
960                 }
961                 else {
962                         connection->error = 1;
963                         strcpy(connection->errorStr,"problem parsing song info");
964                         return NULL;
965                 }
966         }
967         else return NULL;
969         mpd_getNextReturnElement(connection);
970         while(connection->returnElement) {
971                 mpd_ReturnElement * re = connection->returnElement;
973                 if(strcmp(re->name,"file")==0) return entity;
974                 else if(strcmp(re->name,"directory")==0) return entity;
975                 else if(strcmp(re->name,"playlist")==0) return entity;
976                 else if(strcmp(re->name,"cpos")==0) return entity;
978                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
979                                 strlen(re->value)) {
980                         if(!entity->info.song->artist &&
981                                         strcmp(re->name,"Artist")==0) {
982                                 entity->info.song->artist = str_pool_dup(re->value);
983                         }
984                         else if(!entity->info.song->album &&
985                                         strcmp(re->name,"Album")==0) {
986                                 entity->info.song->album = str_pool_dup(re->value);
987                         }
988                         else if(!entity->info.song->title &&
989                                         strcmp(re->name,"Title")==0) {
990                                 entity->info.song->title = str_pool_dup(re->value);
991                         }
992                         else if(!entity->info.song->track &&
993                                         strcmp(re->name,"Track")==0) {
994                                 entity->info.song->track = str_pool_dup(re->value);
995                         }
996                         else if(!entity->info.song->name &&
997                                         strcmp(re->name,"Name")==0) {
998                                 entity->info.song->name = str_pool_dup(re->value);
999                         }
1000                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1001                                         strcmp(re->name,"Time")==0) {
1002                                 entity->info.song->time = atoi(re->value);
1003                         }
1004                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1005                                         strcmp(re->name,"Pos")==0) {
1006                                 entity->info.song->pos = atoi(re->value);
1007                         }
1008                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
1009                                         strcmp(re->name,"Id")==0) {
1010                                 entity->info.song->id = atoi(re->value);
1011                         }
1012                         else if(!entity->info.song->date &&
1013                                         strcmp(re->name, "Date") == 0) {
1014                                 entity->info.song->date = str_pool_dup(re->value);
1015                         }
1016                         else if(!entity->info.song->genre &&
1017                                         strcmp(re->name, "Genre") == 0) {
1018                                 entity->info.song->genre = str_pool_dup(re->value);
1019                         }
1020                         else if(!entity->info.song->composer &&
1021                                         strcmp(re->name, "Composer") == 0) {
1022                                 entity->info.song->composer = str_pool_dup(re->value);
1023                         }
1024                         else if(!entity->info.song->disc &&
1025                                         strcmp(re->name, "Disc") == 0) {
1026                                 entity->info.song->disc = str_pool_dup(re->value);
1027                         }
1028                         else if(!entity->info.song->comment &&
1029                                         strcmp(re->name, "Comment") == 0) {
1030                                 entity->info.song->comment = str_pool_dup(re->value);
1031                         }
1032                 }
1033                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1034                 }
1035                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1036                 }
1038                 mpd_getNextReturnElement(connection);
1039         }
1041         return entity;
1044 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1045                 const char * name)
1047         if(connection->doneProcessing || (connection->listOks &&
1048                                 connection->doneListOk))
1049         {
1050                 return NULL;
1051         }
1053         mpd_getNextReturnElement(connection);
1054         while(connection->returnElement) {
1055                 mpd_ReturnElement * re = connection->returnElement;
1057                 if(strcmp(re->name,name)==0) return strdup(re->value);
1058                 mpd_getNextReturnElement(connection);
1059         }
1061         return NULL;
1064 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1065         if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1066         {
1067                 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1068         }
1069         return NULL;
1072 char * mpd_getNextArtist(mpd_Connection * connection) {
1073         return mpd_getNextReturnElementNamed(connection,"Artist");
1076 char * mpd_getNextAlbum(mpd_Connection * connection) {
1077         return mpd_getNextReturnElementNamed(connection,"Album");
1080 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1081         char * string = malloc(strlen("playlistinfo")+25);
1082         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1083         mpd_sendInfoCommand(connection,string);
1084         free(string);
1087 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1088         char * string = malloc(strlen("playlistid")+25);
1089         sprintf(string, "playlistid \"%i\"\n", id);
1090         mpd_sendInfoCommand(connection, string);
1091         free(string);
1094 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1095         char * string = malloc(strlen("plchanges")+25);
1096         sprintf(string,"plchanges \"%lld\"\n",playlist);
1097         mpd_sendInfoCommand(connection,string);
1098         free(string);
1101 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1102         char * string = malloc(strlen("plchangesposid")+25);
1103         sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1104         mpd_sendInfoCommand(connection,string);
1105         free(string);
1108 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1109         char * sDir = mpd_sanitizeArg(dir);
1110         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1111         sprintf(string,"listall \"%s\"\n",sDir);
1112         mpd_sendInfoCommand(connection,string);
1113         free(string);
1114         free(sDir);
1117 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1118         char * sDir = mpd_sanitizeArg(dir);
1119         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1120         sprintf(string,"listallinfo \"%s\"\n",sDir);
1121         mpd_sendInfoCommand(connection,string);
1122         free(string);
1123         free(sDir);
1126 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1127         char * sDir = mpd_sanitizeArg(dir);
1128         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1129         sprintf(string,"lsinfo \"%s\"\n",sDir);
1130         mpd_sendInfoCommand(connection,string);
1131         free(string);
1132         free(sDir);
1135 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1136         mpd_executeCommand(connection,"currentsong\n");
1139 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1140                 const char * str)
1142         char st[10];
1143         char * string;
1144         char * sanitStr = mpd_sanitizeArg(str);
1145         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1146         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1147         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1148         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1149         else {
1150                 connection->error = 1;
1151                 strcpy(connection->errorStr,"unknown table for search");
1152                 return;
1153         }
1154         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1155         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1156         mpd_sendInfoCommand(connection,string);
1157         free(string);
1158         free(sanitStr);
1161 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1162                 const char * str)
1164         char st[10];
1165         char * string;
1166         char * sanitStr = mpd_sanitizeArg(str);
1167         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1168         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1169         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1170         else {
1171                 connection->error = 1;
1172                 strcpy(connection->errorStr,"unknown table for find");
1173                 return;
1174         }
1175         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1176         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1177         mpd_sendInfoCommand(connection,string);
1178         free(string);
1179         free(sanitStr);
1182 void mpd_sendListCommand(mpd_Connection * connection, int table,
1183                 const char * arg1)
1185         char st[10];
1186         char * string;
1187         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1188         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1189         else {
1190                 connection->error = 1;
1191                 strcpy(connection->errorStr,"unknown table for list");
1192                 return;
1193         }
1194         if(arg1) {
1195                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1196                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1197                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1198                 free(sanitArg1);
1199         }
1200         else {
1201                 string = malloc(strlen("list")+strlen(st)+3);
1202                 sprintf(string,"list %s\n",st);
1203         }
1204         mpd_sendInfoCommand(connection,string);
1205         free(string);
1208 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1209         char * sFile = mpd_sanitizeArg(file);
1210         char * string = malloc(strlen("add")+strlen(sFile)+5);
1211         sprintf(string,"add \"%s\"\n",sFile);
1212         mpd_executeCommand(connection,string);
1213         free(string);
1214         free(sFile);
1217 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1218         char * string = malloc(strlen("delete")+25);
1219         sprintf(string,"delete \"%i\"\n",songPos);
1220         mpd_sendInfoCommand(connection,string);
1221         free(string);
1224 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1225         char * string = malloc(strlen("deleteid")+25);
1226         sprintf(string, "deleteid \"%i\"\n", id);
1227         mpd_sendInfoCommand(connection,string);
1228         free(string);
1231 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1232         char * sName = mpd_sanitizeArg(name);
1233         char * string = malloc(strlen("save")+strlen(sName)+5);
1234         sprintf(string,"save \"%s\"\n",sName);
1235         mpd_executeCommand(connection,string);
1236         free(string);
1237         free(sName);
1240 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1241         char * sName = mpd_sanitizeArg(name);
1242         char * string = malloc(strlen("load")+strlen(sName)+5);
1243         sprintf(string,"load \"%s\"\n",sName);
1244         mpd_executeCommand(connection,string);
1245         free(string);
1246         free(sName);
1249 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1250         char * sName = mpd_sanitizeArg(name);
1251         char * string = malloc(strlen("rm")+strlen(sName)+5);
1252         sprintf(string,"rm \"%s\"\n",sName);
1253         mpd_executeCommand(connection,string);
1254         free(string);
1255         free(sName);
1258 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1259         mpd_executeCommand(connection,"shuffle\n");
1262 void mpd_sendClearCommand(mpd_Connection * connection) {
1263         mpd_executeCommand(connection,"clear\n");
1266 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1267         char * string = malloc(strlen("play")+25);
1268         sprintf(string,"play \"%i\"\n",songPos);
1269         mpd_sendInfoCommand(connection,string);
1270         free(string);
1273 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1274         char * string = malloc(strlen("playid")+25);
1275         sprintf(string,"playid \"%i\"\n",id);
1276         mpd_sendInfoCommand(connection,string);
1277         free(string);
1280 void mpd_sendStopCommand(mpd_Connection * connection) {
1281         mpd_executeCommand(connection,"stop\n");
1284 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1285         char * string = malloc(strlen("pause")+25);
1286         sprintf(string,"pause \"%i\"\n",pauseMode);
1287         mpd_executeCommand(connection,string);
1288         free(string);
1291 void mpd_sendNextCommand(mpd_Connection * connection) {
1292         mpd_executeCommand(connection,"next\n");
1295 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1296         char * string = malloc(strlen("move")+25);
1297         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1298         mpd_sendInfoCommand(connection,string);
1299         free(string);
1302 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1303         char * string = malloc(strlen("moveid")+25);
1304         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1305         mpd_sendInfoCommand(connection,string);
1306         free(string);
1309 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1310         char * string = malloc(strlen("swap")+25);
1311         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1312         mpd_sendInfoCommand(connection,string);
1313         free(string);
1316 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1317         char * string = malloc(strlen("swapid")+25);
1318         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1319         mpd_sendInfoCommand(connection,string);
1320         free(string);
1323 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1324         char * string = malloc(strlen("seek")+25);
1325         sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1326         mpd_sendInfoCommand(connection,string);
1327         free(string);
1330 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1331         char * string = malloc(strlen("seekid")+25);
1332         sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1333         mpd_sendInfoCommand(connection,string);
1334         free(string);
1337 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1338         char *sPath = mpd_sanitizeArg(path);
1339         char * string = malloc(strlen("update")+strlen(sPath)+5);
1340         sprintf(string,"update \"%s\"\n",sPath);
1341         mpd_sendInfoCommand(connection,string);
1342         free(string);
1343         free(sPath);
1346 int mpd_getUpdateId(mpd_Connection * connection) {
1347         char * jobid;
1348         int ret = 0;
1350         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1351         if(jobid) {
1352                 ret = atoi(jobid);
1353                 free(jobid);
1354         }
1356         return ret;
1359 void mpd_sendPrevCommand(mpd_Connection * connection) {
1360         mpd_executeCommand(connection,"previous\n");
1363 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1364         char * string = malloc(strlen("repeat")+25);
1365         sprintf(string,"repeat \"%i\"\n",repeatMode);
1366         mpd_executeCommand(connection,string);
1367         free(string);
1370 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1371         char * string = malloc(strlen("random")+25);
1372         sprintf(string,"random \"%i\"\n",randomMode);
1373         mpd_executeCommand(connection,string);
1374         free(string);
1377 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1378         char * string = malloc(strlen("setvol")+25);
1379         sprintf(string,"setvol \"%i\"\n",volumeChange);
1380         mpd_executeCommand(connection,string);
1381         free(string);
1384 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1385         char * string = malloc(strlen("volume")+25);
1386         sprintf(string,"volume \"%i\"\n",volumeChange);
1387         mpd_executeCommand(connection,string);
1388         free(string);
1391 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1392         char * string = malloc(strlen("crossfade")+25);
1393         sprintf(string,"crossfade \"%i\"\n",seconds);
1394         mpd_executeCommand(connection,string);
1395         free(string);
1398 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1399         char * sPass = mpd_sanitizeArg(pass);
1400         char * string = malloc(strlen("password")+strlen(sPass)+5);
1401         sprintf(string,"password \"%s\"\n",sPass);
1402         mpd_executeCommand(connection,string);
1403         free(string);
1404         free(sPass);
1407 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1408         if(connection->commandList) {
1409                 strcpy(connection->errorStr,"already in command list mode");
1410                 connection->error = 1;
1411                 return;
1412         }
1413         connection->commandList = COMMAND_LIST;
1414         mpd_executeCommand(connection,"command_list_begin\n");
1417 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1418         if(connection->commandList) {
1419                 strcpy(connection->errorStr,"already in command list mode");
1420                 connection->error = 1;
1421                 return;
1422         }
1423         connection->commandList = COMMAND_LIST_OK;
1424         mpd_executeCommand(connection,"command_list_ok_begin\n");
1425         connection->listOks = 0;
1428 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1429         if(!connection->commandList) {
1430                 strcpy(connection->errorStr,"not in command list mode");
1431                 connection->error = 1;
1432                 return;
1433         }
1434         connection->commandList = 0;
1435         mpd_executeCommand(connection,"command_list_end\n");
1438 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1439         mpd_executeCommand(connection,"outputs\n");
1442 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1443         mpd_OutputEntity * output = NULL;
1445         if(connection->doneProcessing || (connection->listOks &&
1446                                 connection->doneListOk))
1447         {
1448                 return NULL;
1449         }
1451         if(connection->error) return NULL;
1453         output = malloc(sizeof(mpd_OutputEntity));
1454         output->id = -10;
1455         output->name = NULL;
1456         output->enabled = 0;
1458         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1460         while(connection->returnElement) {
1461                 mpd_ReturnElement * re = connection->returnElement;
1462                 if(strcmp(re->name,"outputid")==0) {
1463                         if(output!=NULL && output->id>=0) return output;
1464                         output->id = atoi(re->value);
1465                 }
1466                 else if(strcmp(re->name,"outputname")==0) {
1467                         output->name = strdup(re->value);
1468                 }
1469                 else if(strcmp(re->name,"outputenabled")==0) {
1470                         output->enabled = atoi(re->value);
1471                 }
1473                 mpd_getNextReturnElement(connection);
1474                 if(connection->error) {
1475                         free(output);
1476                         return NULL;
1477                 }
1479         }
1481         return output;
1484 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1485         char * string = malloc(strlen("enableoutput")+25);
1486         sprintf(string,"enableoutput \"%i\"\n",outputId);
1487         mpd_executeCommand(connection,string);
1488         free(string);
1491 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1492         char * string = malloc(strlen("disableoutput")+25);
1493         sprintf(string,"disableoutput \"%i\"\n",outputId);
1494         mpd_executeCommand(connection,string);
1495         free(string);
1498 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1499         free(output->name);
1500         free(output);
1503 /**
1504  * mpd_sendNotCommandsCommand
1505  * odd naming, but it gets the not allowed commands
1506  */
1508 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1509         mpd_executeCommand(connection,"notcommands\n");
1512 /**
1513  * mpd_sendCommandsCommand
1514  * odd naming, but it gets the allowed commands
1515  */
1517 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1518         mpd_executeCommand(connection,"commands\n");
1520 /**
1521  * Get the next returned command
1522  */
1523 char * mpd_getNextCommand(mpd_Connection * connection) {
1524         return mpd_getNextReturnElementNamed(connection,"command");
1527 void mpd_startSearch(mpd_Connection * connection,int exact) {
1528         if(connection->request) {
1529                 /* search/find allready in progress */
1530                 /* TODO: set error here?  */
1531                 return;
1532         }
1533         if(exact){
1534                 connection->request = strdup("find");
1535         }
1536         else{
1537                 connection->request = strdup("search");
1538         }
1542 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1543         if(connection->request) {
1544                 /* search/find allready in progress */
1545                 /* TODO: set error here?  */
1546                 return;
1547         }
1548         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1549                 /* set error here */
1550                 return;
1551         }
1553         connection->request = malloc(sizeof(char)*(
1554                                 /* length of the field name */
1555                                 strlen(mpdTagItemKeys[field])+
1556                                 /* "list"+space+\0 */
1557                                 6
1558                                 ));
1559         sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1564 void mpd_addConstraintSearch(mpd_Connection *connection,
1565                 int field,
1566                 char *name)
1568         char *arg = NULL;
1569         if(!connection->request){
1570                 return;
1571         }
1572         if(name == NULL) {
1573                 return;
1574         }
1575         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1576                 return;
1577         }
1578         /* clean up the query */
1579         arg = mpd_sanitizeArg(name);
1580         /* create space for the query */
1581         connection->request = realloc(connection->request, (
1582                          /* length of the old string */
1583                          strlen(connection->request)+
1584                          /* space between */
1585                          1+
1586                          /* length of the field name */
1587                          strlen(mpdTagItemKeys[field])+
1588                          /* space plus starting " */
1589                          2+
1590                          /* length of search term */
1591                          strlen(arg)+
1592                          /* closign " +\0 that is added sprintf */
1593                          2
1594                         )*sizeof(char));
1595         /* and form the query */
1596         sprintf(connection->request, "%s %s \"%s\"",
1597                         connection->request,
1598                         mpdTagItemKeys[field],
1599                         arg);
1600         free(arg);
1604 void mpd_commitSearch(mpd_Connection *connection)
1606         if(connection->request)
1607         {
1608                 int length = strlen(connection->request);
1609                 /* fixing up the string for mpd to like */
1610                 connection->request = realloc(connection->request,
1611                                 (length+        /* old length */
1612                                  2              /* closing \n and \0 */
1613                                 )*sizeof(char));
1614                 connection->request[length] = '\n';
1615                 connection->request[length+1] = '\0';
1616                 /* and off we go */
1617                 mpd_sendInfoCommand(connection, connection->request);
1618                 /* clean up a bit */
1619                 free(connection->request);
1620                 connection->request = NULL;
1621         }
1624 /**
1625  * @param connection a MpdConnection
1626  * @param path  the path to the playlist.
1627  *
1628  * List the content, with full metadata, of a stored playlist.
1629  *
1630  */
1631 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1633         char *arg = mpd_sanitizeArg(path);
1634         char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1635         sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1636         mpd_sendInfoCommand(connection, query);
1637         free(arg);
1638         free(query);
1641 /**
1642  * @param connection a MpdConnection
1643  * @param path  the path to the playlist.
1644  *
1645  * List the content of a stored playlist.
1646  *
1647  */
1648 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1650         char *arg = mpd_sanitizeArg(path);
1651         char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1652         sprintf(query, "listplaylist \"%s\"\n",arg);
1653         mpd_sendInfoCommand(connection, query);
1654         free(arg);
1655         free(query);