Code

display songs time in playlist
[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
168 mpd_setConnectionTimeout(mpd_Connection *connection, float timeout_) {
169         connection->timeout.tv_sec = (int)timeout_;
170         connection->timeout.tv_usec = (int)(timeout_ * 1e6 -
171                                             connection->timeout.tv_sec*1000000 +
172                                             0.5);
175 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
176                             char * output) {
177         char * tmp;
178         char * test;
179         int i;
181         if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
182                 snprintf(connection->errorStr, sizeof(connection->errorStr),
183                          "mpd not running on port %i on host \"%s\"",
184                          port,host);
185                 connection->error = MPD_ERROR_NOTMPD;
186                 return 1;
187         }
189         tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
191         for(i=0;i<3;i++) {
192                 if(tmp) connection->version[i] = strtol(tmp,&test,10);
194                 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
195                         snprintf(connection->errorStr, sizeof(connection->errorStr),
196                                  "error parsing version number at "
197                                  "\"%s\"",
198                                  &output[strlen(MPD_WELCOME_MESSAGE)]);
199                         connection->error = MPD_ERROR_NOTMPD;
200                         return 1;
201                 }
202                 tmp = ++test;
203         }
205         return 0;
208 /**
209  * Wait for the socket to become readable.
210  */
211 static int mpd_wait(mpd_Connection *connection)
213         struct timeval tv;
214         fd_set fds;
215         int ret;
217         assert(connection->sock >= 0);
219         while (1) {
220                 tv = connection->timeout;
221                 FD_ZERO(&fds);
222                 FD_SET(connection->sock, &fds);
224                 ret = select(connection->sock + 1, &fds, NULL, NULL, &tv);
225                 if (ret > 0)
226                         return 0;
228                 if (ret == 0 || !SELECT_ERRNO_IGNORE)
229                         return -1;
230         }
233 /**
234  * Wait until the socket is connected and check its result.  Returns 1
235  * on success, 0 on timeout, -errno on error.
236  */
237 static int mpd_wait_connected(mpd_Connection *connection)
239         int ret;
240         int s_err = 0;
241         socklen_t s_err_size = sizeof(s_err);
243         ret = mpd_wait(connection);
244         if (ret < 0)
245                 return 0;
247         ret = getsockopt(connection->sock, SOL_SOCKET, SO_ERROR,
248                          (char*)&s_err, &s_err_size);
249         if (ret < 0)
250                 return -errno;
252         if (s_err != 0)
253                 return -s_err;
255         return 1;
258 /**
259  * Attempt to read data from the socket into the input buffer.
260  * Returns 0 on success, -1 on error.
261  */
262 static int mpd_recv(mpd_Connection *connection)
264         int ret;
265         ssize_t nbytes;
267         assert(connection != NULL);
268         assert(connection->buflen <= sizeof(connection->buffer));
269         assert(connection->bufstart <= connection->buflen);
271         if (connection->sock < 0) {
272                 strcpy(connection->errorStr, "not connected");
273                 connection->error = MPD_ERROR_CONNCLOSED;
274                 connection->doneProcessing = 1;
275                 connection->doneListOk = 0;
276                 return -1;
277         }
279         if (connection->buflen >= sizeof(connection->buffer)) {
280                 /* delete consumed data from beginning of buffer */
281                 connection->buflen -= connection->bufstart;
282                 memmove(connection->buffer,
283                         connection->buffer + connection->bufstart,
284                         connection->buflen);
285                 connection->bufstart = 0;
286         }
288         if (connection->buflen >= sizeof(connection->buffer)) {
289                 strcpy(connection->errorStr, "buffer overrun");
290                 connection->error = MPD_ERROR_BUFFEROVERRUN;
291                 connection->doneProcessing = 1;
292                 connection->doneListOk = 0;
293                 return -1;
294         }
296         while (1) {
297                 ret = mpd_wait(connection);
298                 if (ret < 0) {
299                         strcpy(connection->errorStr, "connection timeout");
300                         connection->error = MPD_ERROR_TIMEOUT;
301                         connection->doneProcessing = 1;
302                         connection->doneListOk = 0;
303                         return -1;
304                 }
306                 nbytes = read(connection->sock,
307                               connection->buffer + connection->buflen,
308                               sizeof(connection->buffer) - connection->buflen);
309                 if (nbytes > 0) {
310                         connection->buflen += nbytes;
311                         return 0;
312                 }
314                 if (nbytes == 0 || !SENDRECV_ERRNO_IGNORE) {
315                         strcpy(connection->errorStr, "connection closed");
316                         connection->error = MPD_ERROR_CONNCLOSED;
317                         connection->doneProcessing = 1;
318                         connection->doneListOk = 0;
319                         return -1;
320                 }
321         }
324 static int
325 mpd_connect(mpd_Connection *connection, const char * host, int port)
327         struct resolver *resolver;
328         const struct resolver_address *address;
329         int ret;
331         resolver = resolver_new(host, port);
332         if (resolver == NULL) {
333                 snprintf(connection->errorStr, sizeof(connection->errorStr),
334                          "host \"%s\" not found", host);
335                 connection->error = MPD_ERROR_UNKHOST;
336                 return -1;
337         }
339         while ((address = resolver_next(resolver)) != NULL) {
340                 connection->sock = socket(address->family, SOCK_STREAM,
341                                           address->protocol);
342                 if (connection->sock < 0) {
343                         snprintf(connection->errorStr,
344                                  sizeof(connection->errorStr),
345                                  "problems creating socket: %s",
346                                  strerror(errno));
347                         connection->error = MPD_ERROR_SYSTEM;
348                         continue;
349                 }
351                 ret = do_connect_fail(connection,
352                                       address->addr, address->addrlen);
353                 if (ret != 0) {
354                         snprintf(connection->errorStr,
355                                  sizeof(connection->errorStr),
356                                  "problems connecting to \"%s\" on port"
357                                  " %i: %s", host, port, strerror(errno));
358                         connection->error = MPD_ERROR_CONNPORT;
360                         closesocket(connection->sock);
361                         connection->sock = -1;
362                         continue;
363                 }
365                 ret = mpd_wait_connected(connection);
366                 if (ret > 0) {
367                         resolver_free(resolver);
368                         mpd_clearError(connection);
369                         return 0;
370                 }
372                 if (ret == 0) {
373                         snprintf(connection->errorStr,
374                                  sizeof(connection->errorStr),
375                                  "timeout in attempting to get a response from"
376                                  " \"%s\" on port %i", host, port);
377                         connection->error = MPD_ERROR_NORESPONSE;
378                 } else if (ret < 0) {
379                         snprintf(connection->errorStr,
380                                  sizeof(connection->errorStr),
381                                  "problems connecting to \"%s\" on port %i: %s",
382                                  host, port, strerror(-ret));
383                         connection->error = MPD_ERROR_CONNPORT;
384                 }
386                 closesocket(connection->sock);
387                 connection->sock = -1;
388         }
390         resolver_free(resolver);
391         return -1;
394 mpd_Connection *
395 mpd_newConnection(const char *host, int port, float timeout_) {
396         int err;
397         char * rt;
398         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
400         connection->buflen = 0;
401         connection->bufstart = 0;
402         mpd_clearError(connection);
403         connection->doneProcessing = 0;
404         connection->commandList = 0;
405         connection->listOks = 0;
406         connection->doneListOk = 0;
407         connection->returnElement = NULL;
408         connection->request = NULL;
410         if (winsock_dll_error(connection))
411                 return connection;
413         mpd_setConnectionTimeout(connection, timeout_);
415         err = mpd_connect(connection, host, port);
416         if (err < 0)
417                 return connection;
419         while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
420                 err = mpd_recv(connection);
421                 if (err < 0)
422                         return connection;
423         }
425         *rt = '\0';
426         if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
427                 connection->doneProcessing = 1;
429         connection->buflen -= rt + 1 - connection->buffer;
430         memmove(connection->buffer, rt + 1, connection->buflen);
432         return connection;
435 void mpd_clearError(mpd_Connection * connection) {
436         connection->error = 0;
437         connection->errorStr[0] = '\0';
440 void mpd_closeConnection(mpd_Connection * connection) {
441         closesocket(connection->sock);
442         if(connection->returnElement) free(connection->returnElement);
443         if(connection->request) free(connection->request);
444         free(connection);
445         WSACleanup();
448 static void mpd_executeCommand(mpd_Connection *connection,
449                                const char *command) {
450         int ret;
451         struct timeval tv;
452         fd_set fds;
453         const char *commandPtr = command;
454         int commandLen = strlen(command);
456         if (connection->sock < 0) {
457                 strcpy(connection->errorStr, "not connected");
458                 connection->error = MPD_ERROR_CONNCLOSED;
459                 return;
460         }
462         if (!connection->doneProcessing && !connection->commandList) {
463                 strcpy(connection->errorStr,
464                        "not done processing current command");
465                 connection->error = 1;
466                 return;
467         }
469         mpd_clearError(connection);
471         FD_ZERO(&fds);
472         FD_SET(connection->sock,&fds);
473         tv.tv_sec = connection->timeout.tv_sec;
474         tv.tv_usec = connection->timeout.tv_usec;
476         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
477                         (ret==-1 && SELECT_ERRNO_IGNORE)) {
478                 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
479                 if(ret<=0)
480                 {
481                         if (SENDRECV_ERRNO_IGNORE) continue;
482                         snprintf(connection->errorStr, sizeof(connection->errorStr),
483                                  "problems giving command \"%s\"",command);
484                         connection->error = MPD_ERROR_SENDING;
485                         return;
486                 }
487                 else {
488                         commandPtr+=ret;
489                         commandLen-=ret;
490                 }
492                 if(commandLen<=0) break;
493         }
495         if(commandLen>0) {
496                 perror("");
497                 snprintf(connection->errorStr, sizeof(connection->errorStr),
498                          "timeout sending command \"%s\"",command);
499                 connection->error = MPD_ERROR_TIMEOUT;
500                 return;
501         }
503         if(!connection->commandList) connection->doneProcessing = 0;
504         else if(connection->commandList == COMMAND_LIST_OK) {
505                 connection->listOks++;
506         }
509 static void mpd_getNextReturnElement(mpd_Connection * connection) {
510         char * output = NULL;
511         char * rt = NULL;
512         char * name = NULL;
513         char * value = NULL;
514         char * tok = NULL;
515         int err;
516         int pos;
518         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
519         connection->returnElement = NULL;
521         if (connection->doneProcessing ||
522             (connection->listOks && connection->doneListOk)) {
523                 strcpy(connection->errorStr,"already done processing current command");
524                 connection->error = 1;
525                 return;
526         }
528         while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
529                              connection->buflen - connection->bufstart))) {
530                 err = mpd_recv(connection);
531                 if (err < 0)
532                         return;
533         }
535         *rt = '\0';
536         output = connection->buffer+connection->bufstart;
537         connection->bufstart = rt - connection->buffer + 1;
539         if(strcmp(output,"OK")==0) {
540                 if(connection->listOks > 0) {
541                         strcpy(connection->errorStr, "expected more list_OK's");
542                         connection->error = 1;
543                 }
544                 connection->listOks = 0;
545                 connection->doneProcessing = 1;
546                 connection->doneListOk = 0;
547                 return;
548         }
550         if(strcmp(output, "list_OK") == 0) {
551                 if(!connection->listOks) {
552                         strcpy(connection->errorStr,
553                                         "got an unexpected list_OK");
554                         connection->error = 1;
555                 }
556                 else {
557                         connection->doneListOk = 1;
558                         connection->listOks--;
559                 }
560                 return;
561         }
563         if(strncmp(output,"ACK",strlen("ACK"))==0) {
564                 size_t length = strlen(output);
565                 char * test;
566                 char * needle;
567                 int val;
569                 if (length >= sizeof(connection->errorStr))
570                         length = sizeof(connection->errorStr) - 1;
572                 memcpy(connection->errorStr, output, length);
573                 connection->errorStr[length] = 0;
574                 connection->error = MPD_ERROR_ACK;
575                 connection->errorCode = MPD_ACK_ERROR_UNK;
576                 connection->errorAt = MPD_ERROR_AT_UNK;
577                 connection->doneProcessing = 1;
578                 connection->doneListOk = 0;
580                 needle = strchr(output, '[');
581                 if(!needle) return;
582                 val = strtol(needle+1, &test, 10);
583                 if(*test != '@') return;
584                 connection->errorCode = val;
585                 val = strtol(test+1, &test, 10);
586                 if(*test != ']') return;
587                 connection->errorAt = val;
588                 return;
589         }
591         tok = strchr(output, ':');
592         if (!tok) return;
593         pos = tok - output;
594         value = ++tok;
595         name = output;
596         name[pos] = '\0';
598         if(value[0]==' ') {
599                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
600         }
601         else {
602                 snprintf(connection->errorStr, sizeof(connection->errorStr),
603                          "error parsing: %s:%s",name,value);
604                 connection->error = 1;
605         }
608 void mpd_finishCommand(mpd_Connection * connection) {
609         while(!connection->doneProcessing) {
610                 if(connection->doneListOk) connection->doneListOk = 0;
611                 mpd_getNextReturnElement(connection);
612         }
615 static void mpd_finishListOkCommand(mpd_Connection * connection) {
616         while(!connection->doneProcessing && connection->listOks &&
617                         !connection->doneListOk)
618         {
619                 mpd_getNextReturnElement(connection);
620         }
623 int mpd_nextListOkCommand(mpd_Connection * connection) {
624         mpd_finishListOkCommand(connection);
625         if(!connection->doneProcessing) connection->doneListOk = 0;
626         if(connection->listOks == 0 || connection->doneProcessing) return -1;
627         return 0;
630 void mpd_sendStatusCommand(mpd_Connection * connection) {
631         mpd_executeCommand(connection,"status\n");
634 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
635         mpd_Status * status;
637         /*mpd_executeCommand(connection,"status\n");
639         if(connection->error) return NULL;*/
641         if(connection->doneProcessing || (connection->listOks &&
642            connection->doneListOk))
643         {
644                 return NULL;
645         }
647         if(!connection->returnElement) mpd_getNextReturnElement(connection);
649         status = malloc(sizeof(mpd_Status));
650         status->volume = -1;
651         status->repeat = 0;
652         status->random = 0;
653         status->single = 0;
654         status->consume = 0;
655         status->playlist = -1;
656         status->playlistLength = -1;
657         status->state = -1;
658         status->song = 0;
659         status->songid = 0;
660         status->elapsedTime = 0;
661         status->totalTime = 0;
662         status->bitRate = 0;
663         status->sampleRate = 0;
664         status->bits = 0;
665         status->channels = 0;
666         status->crossfade = -1;
667         status->error = NULL;
668         status->updatingDb = 0;
670         if(connection->error) {
671                 free(status);
672                 return NULL;
673         }
674         while(connection->returnElement) {
675                 mpd_ReturnElement * re = connection->returnElement;
676                 if(strcmp(re->name,"volume")==0) {
677                         status->volume = atoi(re->value);
678                 }
679                 else if(strcmp(re->name,"repeat")==0) {
680                         status->repeat = atoi(re->value);
681                 }
682                 else if(strcmp(re->name,"random")==0) {
683                         status->random = atoi(re->value);
684                 }
685                 else if(strcmp(re->name,"single")==0) {
686                         status->single = atoi(re->value);
687                 }
688                 else if(strcmp(re->name,"consume")==0) {
689                         status->consume = atoi(re->value);
690                 }
691                 else if(strcmp(re->name,"playlist")==0) {
692                         status->playlist = strtol(re->value,NULL,10);
693                 }
694                 else if(strcmp(re->name,"playlistlength")==0) {
695                         status->playlistLength = atoi(re->value);
696                 }
697                 else if(strcmp(re->name,"bitrate")==0) {
698                         status->bitRate = atoi(re->value);
699                 }
700                 else if(strcmp(re->name,"state")==0) {
701                         if(strcmp(re->value,"play")==0) {
702                                 status->state = MPD_STATUS_STATE_PLAY;
703                         }
704                         else if(strcmp(re->value,"stop")==0) {
705                                 status->state = MPD_STATUS_STATE_STOP;
706                         }
707                         else if(strcmp(re->value,"pause")==0) {
708                                 status->state = MPD_STATUS_STATE_PAUSE;
709                         }
710                         else {
711                                 status->state = MPD_STATUS_STATE_UNKNOWN;
712                         }
713                 }
714                 else if(strcmp(re->name,"song")==0) {
715                         status->song = atoi(re->value);
716                 }
717                 else if(strcmp(re->name,"songid")==0) {
718                         status->songid = atoi(re->value);
719                 }
720                 else if(strcmp(re->name,"time")==0) {
721                         char * tok = strchr(re->value,':');
722                         /* the second strchr below is a safety check */
723                         if (tok && (strchr(tok,0) > (tok+1))) {
724                                 /* atoi stops at the first non-[0-9] char: */
725                                 status->elapsedTime = atoi(re->value);
726                                 status->totalTime = atoi(tok+1);
727                         }
728                 }
729                 else if(strcmp(re->name,"error")==0) {
730                         status->error = strdup(re->value);
731                 }
732                 else if(strcmp(re->name,"xfade")==0) {
733                         status->crossfade = atoi(re->value);
734                 }
735                 else if(strcmp(re->name,"updating_db")==0) {
736                         status->updatingDb = atoi(re->value);
737                 }
738                 else if(strcmp(re->name,"audio")==0) {
739                         char * tok = strchr(re->value,':');
740                         if (tok && (strchr(tok,0) > (tok+1))) {
741                                 status->sampleRate = atoi(re->value);
742                                 status->bits = atoi(++tok);
743                                 tok = strchr(tok,':');
744                                 if (tok && (strchr(tok,0) > (tok+1)))
745                                         status->channels = atoi(tok+1);
746                         }
747                 }
749                 mpd_getNextReturnElement(connection);
750                 if(connection->error) {
751                         free(status);
752                         return NULL;
753                 }
754         }
756         if(connection->error) {
757                 free(status);
758                 return NULL;
759         }
760         else if(status->state<0) {
761                 strcpy(connection->errorStr,"state not found");
762                 connection->error = 1;
763                 free(status);
764                 return NULL;
765         }
767         return status;
770 void mpd_freeStatus(mpd_Status * status) {
771         if(status->error) free(status->error);
772         free(status);
775 void mpd_sendStatsCommand(mpd_Connection * connection) {
776         mpd_executeCommand(connection,"stats\n");
779 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
780         mpd_Stats * stats;
782         /*mpd_executeCommand(connection,"stats\n");
784         if(connection->error) return NULL;*/
786         if(connection->doneProcessing || (connection->listOks &&
787            connection->doneListOk))
788         {
789                 return NULL;
790         }
792         if(!connection->returnElement) mpd_getNextReturnElement(connection);
794         stats = malloc(sizeof(mpd_Stats));
795         stats->numberOfArtists = 0;
796         stats->numberOfAlbums = 0;
797         stats->numberOfSongs = 0;
798         stats->uptime = 0;
799         stats->dbUpdateTime = 0;
800         stats->playTime = 0;
801         stats->dbPlayTime = 0;
803         if(connection->error) {
804                 free(stats);
805                 return NULL;
806         }
807         while(connection->returnElement) {
808                 mpd_ReturnElement * re = connection->returnElement;
809                 if(strcmp(re->name,"artists")==0) {
810                         stats->numberOfArtists = atoi(re->value);
811                 }
812                 else if(strcmp(re->name,"albums")==0) {
813                         stats->numberOfAlbums = atoi(re->value);
814                 }
815                 else if(strcmp(re->name,"songs")==0) {
816                         stats->numberOfSongs = atoi(re->value);
817                 }
818                 else if(strcmp(re->name,"uptime")==0) {
819                         stats->uptime = strtol(re->value,NULL,10);
820                 }
821                 else if(strcmp(re->name,"db_update")==0) {
822                         stats->dbUpdateTime = strtol(re->value,NULL,10);
823                 }
824                 else if(strcmp(re->name,"playtime")==0) {
825                         stats->playTime = strtol(re->value,NULL,10);
826                 }
827                 else if(strcmp(re->name,"db_playtime")==0) {
828                         stats->dbPlayTime = strtol(re->value,NULL,10);
829                 }
831                 mpd_getNextReturnElement(connection);
832                 if(connection->error) {
833                         free(stats);
834                         return NULL;
835                 }
836         }
838         if(connection->error) {
839                 free(stats);
840                 return NULL;
841         }
843         return stats;
846 void mpd_freeStats(mpd_Stats * stats) {
847         free(stats);
850 static void mpd_initDirectory(mpd_Directory * directory) {
851         directory->path = NULL;
854 static void mpd_finishDirectory(mpd_Directory * directory) {
855         if (directory->path)
856                 str_pool_put(directory->path);
859 mpd_Directory * mpd_newDirectory(void) {
860         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
862         mpd_initDirectory(directory);
864         return directory;
867 void mpd_freeDirectory(mpd_Directory * directory) {
868         mpd_finishDirectory(directory);
870         free(directory);
873 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
874         mpd_Directory * ret = mpd_newDirectory();
876         if (directory->path)
877                 ret->path = str_pool_dup(directory->path);
879         return ret;
882 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
883         playlist->path = NULL;
886 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
887         if (playlist->path)
888                 str_pool_put(playlist->path);
891 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
892         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
894         mpd_initPlaylistFile(playlist);
896         return playlist;
899 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
900         mpd_finishPlaylistFile(playlist);
901         free(playlist);
904 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
905         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
907         if (playlist->path)
908                 ret->path = str_pool_dup(playlist->path);
910         return ret;
913 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
914         entity->info.directory = NULL;
917 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
918         if(entity->info.directory) {
919                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
920                         mpd_freeDirectory(entity->info.directory);
921                 }
922                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
923                         mpd_freeSong(entity->info.song);
924                 }
925                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
926                         mpd_freePlaylistFile(entity->info.playlistFile);
927                 }
928         }
931 mpd_InfoEntity * mpd_newInfoEntity(void) {
932         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
934         mpd_initInfoEntity(entity);
936         return entity;
939 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
940         mpd_finishInfoEntity(entity);
941         free(entity);
944 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
945         mpd_executeCommand(connection,command);
948 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
949         mpd_InfoEntity * entity = NULL;
951         if(connection->doneProcessing || (connection->listOks &&
952            connection->doneListOk)) {
953                 return NULL;
954         }
956         if(!connection->returnElement) mpd_getNextReturnElement(connection);
958         if(connection->returnElement) {
959                 if(strcmp(connection->returnElement->name,"file")==0) {
960                         entity = mpd_newInfoEntity();
961                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
962                         entity->info.song = mpd_newSong();
963                         entity->info.song->file =
964                                 str_pool_dup(connection->returnElement->value);
965                 }
966                 else if(strcmp(connection->returnElement->name,
967                                         "directory")==0) {
968                         entity = mpd_newInfoEntity();
969                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
970                         entity->info.directory = mpd_newDirectory();
971                         entity->info.directory->path =
972                                 str_pool_dup(connection->returnElement->value);
973                 }
974                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
975                         entity = mpd_newInfoEntity();
976                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
977                         entity->info.playlistFile = mpd_newPlaylistFile();
978                         entity->info.playlistFile->path =
979                                 str_pool_dup(connection->returnElement->value);
980                 }
981                 else if(strcmp(connection->returnElement->name, "cpos") == 0){
982                         entity = mpd_newInfoEntity();
983                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
984                         entity->info.song = mpd_newSong();
985                         entity->info.song->pos = atoi(connection->returnElement->value);
986                 }
987                 else {
988                         connection->error = 1;
989                         strcpy(connection->errorStr,"problem parsing song info");
990                         return NULL;
991                 }
992         }
993         else return NULL;
995         mpd_getNextReturnElement(connection);
996         while(connection->returnElement) {
997                 mpd_ReturnElement * re = connection->returnElement;
999                 if(strcmp(re->name,"file")==0) return entity;
1000                 else if(strcmp(re->name,"directory")==0) return entity;
1001                 else if(strcmp(re->name,"playlist")==0) return entity;
1002                 else if(strcmp(re->name,"cpos")==0) return entity;
1004                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1005                                 strlen(re->value)) {
1006                         if(!entity->info.song->artist &&
1007                                         strcmp(re->name,"Artist")==0) {
1008                                 entity->info.song->artist = str_pool_dup(re->value);
1009                         }
1010                         else if(!entity->info.song->album &&
1011                                         strcmp(re->name,"Album")==0) {
1012                                 entity->info.song->album = str_pool_dup(re->value);
1013                         }
1014                         else if(!entity->info.song->title &&
1015                                         strcmp(re->name,"Title")==0) {
1016                                 entity->info.song->title = str_pool_dup(re->value);
1017                         }
1018                         else if(!entity->info.song->track &&
1019                                         strcmp(re->name,"Track")==0) {
1020                                 entity->info.song->track = str_pool_dup(re->value);
1021                         }
1022                         else if(!entity->info.song->name &&
1023                                         strcmp(re->name,"Name")==0) {
1024                                 entity->info.song->name = str_pool_dup(re->value);
1025                         }
1026                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1027                                         strcmp(re->name,"Time")==0) {
1028                                 entity->info.song->time = atoi(re->value);
1029                         }
1030                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1031                                         strcmp(re->name,"Pos")==0) {
1032                                 entity->info.song->pos = atoi(re->value);
1033                         }
1034                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
1035                                         strcmp(re->name,"Id")==0) {
1036                                 entity->info.song->id = atoi(re->value);
1037                         }
1038                         else if(!entity->info.song->date &&
1039                                         strcmp(re->name, "Date") == 0) {
1040                                 entity->info.song->date = str_pool_dup(re->value);
1041                         }
1042                         else if(!entity->info.song->genre &&
1043                                         strcmp(re->name, "Genre") == 0) {
1044                                 entity->info.song->genre = str_pool_dup(re->value);
1045                         }
1046                         else if(!entity->info.song->composer &&
1047                                         strcmp(re->name, "Composer") == 0) {
1048                                 entity->info.song->composer = str_pool_dup(re->value);
1049                         }
1050                         else if(!entity->info.song->disc &&
1051                                         strcmp(re->name, "Disc") == 0) {
1052                                 entity->info.song->disc = str_pool_dup(re->value);
1053                         }
1054                         else if(!entity->info.song->comment &&
1055                                         strcmp(re->name, "Comment") == 0) {
1056                                 entity->info.song->comment = str_pool_dup(re->value);
1057                         }
1058                 }
1059                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1060                 }
1061                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1062                 }
1064                 mpd_getNextReturnElement(connection);
1065         }
1067         return entity;
1070 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1071                 const char * name)
1073         if(connection->doneProcessing || (connection->listOks &&
1074                                 connection->doneListOk))
1075         {
1076                 return NULL;
1077         }
1079         mpd_getNextReturnElement(connection);
1080         while(connection->returnElement) {
1081                 mpd_ReturnElement * re = connection->returnElement;
1083                 if(strcmp(re->name,name)==0) return strdup(re->value);
1084                 mpd_getNextReturnElement(connection);
1085         }
1087         return NULL;
1090 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1091         if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1092         {
1093                 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1094         }
1095         return NULL;
1098 char * mpd_getNextArtist(mpd_Connection * connection) {
1099         return mpd_getNextReturnElementNamed(connection,"Artist");
1102 char * mpd_getNextAlbum(mpd_Connection * connection) {
1103         return mpd_getNextReturnElementNamed(connection,"Album");
1106 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1107         char * string = malloc(strlen("playlistinfo")+25);
1108         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1109         mpd_sendInfoCommand(connection,string);
1110         free(string);
1113 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1114         char * string = malloc(strlen("playlistid")+25);
1115         sprintf(string, "playlistid \"%i\"\n", id);
1116         mpd_sendInfoCommand(connection, string);
1117         free(string);
1120 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1121         char * string = malloc(strlen("plchanges")+25);
1122         sprintf(string,"plchanges \"%lld\"\n",playlist);
1123         mpd_sendInfoCommand(connection,string);
1124         free(string);
1127 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1128         char * string = malloc(strlen("plchangesposid")+25);
1129         sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1130         mpd_sendInfoCommand(connection,string);
1131         free(string);
1134 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1135         char * sDir = mpd_sanitizeArg(dir);
1136         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1137         sprintf(string,"listall \"%s\"\n",sDir);
1138         mpd_sendInfoCommand(connection,string);
1139         free(string);
1140         free(sDir);
1143 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1144         char * sDir = mpd_sanitizeArg(dir);
1145         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1146         sprintf(string,"listallinfo \"%s\"\n",sDir);
1147         mpd_sendInfoCommand(connection,string);
1148         free(string);
1149         free(sDir);
1152 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1153         char * sDir = mpd_sanitizeArg(dir);
1154         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1155         sprintf(string,"lsinfo \"%s\"\n",sDir);
1156         mpd_sendInfoCommand(connection,string);
1157         free(string);
1158         free(sDir);
1161 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1162         mpd_executeCommand(connection,"currentsong\n");
1165 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1166                 const char * str)
1168         char st[10];
1169         char * string;
1170         char * sanitStr = mpd_sanitizeArg(str);
1171         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1172         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1173         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1174         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1175         else {
1176                 connection->error = 1;
1177                 strcpy(connection->errorStr,"unknown table for search");
1178                 return;
1179         }
1180         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1181         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1182         mpd_sendInfoCommand(connection,string);
1183         free(string);
1184         free(sanitStr);
1187 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1188                 const char * str)
1190         char st[10];
1191         char * string;
1192         char * sanitStr = mpd_sanitizeArg(str);
1193         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1194         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1195         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1196         else {
1197                 connection->error = 1;
1198                 strcpy(connection->errorStr,"unknown table for find");
1199                 return;
1200         }
1201         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1202         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1203         mpd_sendInfoCommand(connection,string);
1204         free(string);
1205         free(sanitStr);
1208 void mpd_sendListCommand(mpd_Connection * connection, int table,
1209                 const char * arg1)
1211         char st[10];
1212         char * string;
1213         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1214         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1215         else {
1216                 connection->error = 1;
1217                 strcpy(connection->errorStr,"unknown table for list");
1218                 return;
1219         }
1220         if(arg1) {
1221                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1222                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1223                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1224                 free(sanitArg1);
1225         }
1226         else {
1227                 string = malloc(strlen("list")+strlen(st)+3);
1228                 sprintf(string,"list %s\n",st);
1229         }
1230         mpd_sendInfoCommand(connection,string);
1231         free(string);
1234 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1235         char * sFile = mpd_sanitizeArg(file);
1236         char * string = malloc(strlen("add")+strlen(sFile)+5);
1237         sprintf(string,"add \"%s\"\n",sFile);
1238         mpd_executeCommand(connection,string);
1239         free(string);
1240         free(sFile);
1243 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1244         char * string = malloc(strlen("delete")+25);
1245         sprintf(string,"delete \"%i\"\n",songPos);
1246         mpd_sendInfoCommand(connection,string);
1247         free(string);
1250 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1251         char * string = malloc(strlen("deleteid")+25);
1252         sprintf(string, "deleteid \"%i\"\n", id);
1253         mpd_sendInfoCommand(connection,string);
1254         free(string);
1257 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1258         char * sName = mpd_sanitizeArg(name);
1259         char * string = malloc(strlen("save")+strlen(sName)+5);
1260         sprintf(string,"save \"%s\"\n",sName);
1261         mpd_executeCommand(connection,string);
1262         free(string);
1263         free(sName);
1266 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1267         char * sName = mpd_sanitizeArg(name);
1268         char * string = malloc(strlen("load")+strlen(sName)+5);
1269         sprintf(string,"load \"%s\"\n",sName);
1270         mpd_executeCommand(connection,string);
1271         free(string);
1272         free(sName);
1275 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1276         char * sName = mpd_sanitizeArg(name);
1277         char * string = malloc(strlen("rm")+strlen(sName)+5);
1278         sprintf(string,"rm \"%s\"\n",sName);
1279         mpd_executeCommand(connection,string);
1280         free(string);
1281         free(sName);
1284 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1285         mpd_executeCommand(connection,"shuffle\n");
1288 void mpd_sendShuffleRangeCommand(mpd_Connection * connection, unsigned start, unsigned end) {
1289         char * string = malloc(strlen("shufflerange")+25);
1290         sprintf(string,"shuffle \"%u:%u\"\n", start, end);
1291         mpd_executeCommand(connection,string);
1292         free(string);
1295 void mpd_sendClearCommand(mpd_Connection * connection) {
1296         mpd_executeCommand(connection,"clear\n");
1299 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1300         char * string = malloc(strlen("play")+25);
1301         sprintf(string,"play \"%i\"\n",songPos);
1302         mpd_sendInfoCommand(connection,string);
1303         free(string);
1306 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1307         char * string = malloc(strlen("playid")+25);
1308         sprintf(string,"playid \"%i\"\n",id);
1309         mpd_sendInfoCommand(connection,string);
1310         free(string);
1313 void mpd_sendStopCommand(mpd_Connection * connection) {
1314         mpd_executeCommand(connection,"stop\n");
1317 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1318         char * string = malloc(strlen("pause")+25);
1319         sprintf(string,"pause \"%i\"\n",pauseMode);
1320         mpd_executeCommand(connection,string);
1321         free(string);
1324 void mpd_sendNextCommand(mpd_Connection * connection) {
1325         mpd_executeCommand(connection,"next\n");
1328 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1329         char * string = malloc(strlen("move")+25);
1330         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1331         mpd_sendInfoCommand(connection,string);
1332         free(string);
1335 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1336         char * string = malloc(strlen("moveid")+25);
1337         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1338         mpd_sendInfoCommand(connection,string);
1339         free(string);
1342 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1343         char * string = malloc(strlen("swap")+25);
1344         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1345         mpd_sendInfoCommand(connection,string);
1346         free(string);
1349 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1350         char * string = malloc(strlen("swapid")+25);
1351         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1352         mpd_sendInfoCommand(connection,string);
1353         free(string);
1356 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int to) {
1357         char * string = malloc(strlen("seek")+25);
1358         sprintf(string,"seek \"%i\" \"%i\"\n", song, to);
1359         mpd_sendInfoCommand(connection,string);
1360         free(string);
1363 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int to) {
1364         char * string = malloc(strlen("seekid")+25);
1365         sprintf(string,"seekid \"%i\" \"%i\"\n", id, to);
1366         mpd_sendInfoCommand(connection,string);
1367         free(string);
1370 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1371         char *sPath = mpd_sanitizeArg(path);
1372         char * string = malloc(strlen("update")+strlen(sPath)+5);
1373         sprintf(string,"update \"%s\"\n",sPath);
1374         mpd_sendInfoCommand(connection,string);
1375         free(string);
1376         free(sPath);
1379 int mpd_getUpdateId(mpd_Connection * connection) {
1380         char * jobid;
1381         int ret = 0;
1383         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1384         if(jobid) {
1385                 ret = atoi(jobid);
1386                 free(jobid);
1387         }
1389         return ret;
1392 void mpd_sendPrevCommand(mpd_Connection * connection) {
1393         mpd_executeCommand(connection,"previous\n");
1396 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1397         char * string = malloc(strlen("repeat")+25);
1398         sprintf(string,"repeat \"%i\"\n",repeatMode);
1399         mpd_executeCommand(connection,string);
1400         free(string);
1403 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1404         char * string = malloc(strlen("random")+25);
1405         sprintf(string,"random \"%i\"\n",randomMode);
1406         mpd_executeCommand(connection,string);
1407         free(string);
1410 void mpd_sendSingleCommand(mpd_Connection * connection, int singleMode) {
1411         char * string = malloc(strlen("single")+25);
1412         sprintf(string,"single \"%i\"\n",singleMode);
1413         mpd_executeCommand(connection,string);
1414         free(string);
1417 void mpd_sendConsumeCommand(mpd_Connection * connection, int consumeMode) {
1418         char * string = malloc(strlen("consume")+25);
1419         sprintf(string,"consume \"%i\"\n",consumeMode);
1420         mpd_executeCommand(connection,string);
1421         free(string);
1424 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1425         char * string = malloc(strlen("setvol")+25);
1426         sprintf(string,"setvol \"%i\"\n",volumeChange);
1427         mpd_executeCommand(connection,string);
1428         free(string);
1431 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1432         char * string = malloc(strlen("volume")+25);
1433         sprintf(string,"volume \"%i\"\n",volumeChange);
1434         mpd_executeCommand(connection,string);
1435         free(string);
1438 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1439         char * string = malloc(strlen("crossfade")+25);
1440         sprintf(string,"crossfade \"%i\"\n",seconds);
1441         mpd_executeCommand(connection,string);
1442         free(string);
1445 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1446         char * sPass = mpd_sanitizeArg(pass);
1447         char * string = malloc(strlen("password")+strlen(sPass)+5);
1448         sprintf(string,"password \"%s\"\n",sPass);
1449         mpd_executeCommand(connection,string);
1450         free(string);
1451         free(sPass);
1454 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1455         if(connection->commandList) {
1456                 strcpy(connection->errorStr,"already in command list mode");
1457                 connection->error = 1;
1458                 return;
1459         }
1460         connection->commandList = COMMAND_LIST;
1461         mpd_executeCommand(connection,"command_list_begin\n");
1464 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1465         if(connection->commandList) {
1466                 strcpy(connection->errorStr,"already in command list mode");
1467                 connection->error = 1;
1468                 return;
1469         }
1470         connection->commandList = COMMAND_LIST_OK;
1471         mpd_executeCommand(connection,"command_list_ok_begin\n");
1472         connection->listOks = 0;
1475 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1476         if(!connection->commandList) {
1477                 strcpy(connection->errorStr,"not in command list mode");
1478                 connection->error = 1;
1479                 return;
1480         }
1481         connection->commandList = 0;
1482         mpd_executeCommand(connection,"command_list_end\n");
1485 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1486         mpd_executeCommand(connection,"outputs\n");
1489 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1490         mpd_OutputEntity * output = NULL;
1492         if(connection->doneProcessing || (connection->listOks &&
1493                                 connection->doneListOk))
1494         {
1495                 return NULL;
1496         }
1498         if(connection->error) return NULL;
1500         output = malloc(sizeof(mpd_OutputEntity));
1501         output->id = -10;
1502         output->name = NULL;
1503         output->enabled = 0;
1505         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1507         while(connection->returnElement) {
1508                 mpd_ReturnElement * re = connection->returnElement;
1509                 if(strcmp(re->name,"outputid")==0) {
1510                         if(output!=NULL && output->id>=0) return output;
1511                         output->id = atoi(re->value);
1512                 }
1513                 else if(strcmp(re->name,"outputname")==0) {
1514                         output->name = strdup(re->value);
1515                 }
1516                 else if(strcmp(re->name,"outputenabled")==0) {
1517                         output->enabled = atoi(re->value);
1518                 }
1520                 mpd_getNextReturnElement(connection);
1521                 if(connection->error) {
1522                         free(output);
1523                         return NULL;
1524                 }
1526         }
1528         return output;
1531 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1532         char * string = malloc(strlen("enableoutput")+25);
1533         sprintf(string,"enableoutput \"%i\"\n",outputId);
1534         mpd_executeCommand(connection,string);
1535         free(string);
1538 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1539         char * string = malloc(strlen("disableoutput")+25);
1540         sprintf(string,"disableoutput \"%i\"\n",outputId);
1541         mpd_executeCommand(connection,string);
1542         free(string);
1545 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1546         free(output->name);
1547         free(output);
1550 /**
1551  * mpd_sendNotCommandsCommand
1552  * odd naming, but it gets the not allowed commands
1553  */
1555 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1556         mpd_executeCommand(connection,"notcommands\n");
1559 /**
1560  * mpd_sendCommandsCommand
1561  * odd naming, but it gets the allowed commands
1562  */
1564 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1565         mpd_executeCommand(connection,"commands\n");
1567 /**
1568  * Get the next returned command
1569  */
1570 char * mpd_getNextCommand(mpd_Connection * connection) {
1571         return mpd_getNextReturnElementNamed(connection,"command");
1574 void mpd_startSearch(mpd_Connection * connection,int exact) {
1575         if(connection->request) {
1576                 /* search/find already in progress */
1577                 /* TODO: set error here?  */
1578                 return;
1579         }
1580         if(exact){
1581                 connection->request = strdup("find");
1582         }
1583         else{
1584                 connection->request = strdup("search");
1585         }
1589 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1590         if(connection->request) {
1591                 /* search/find already in progress */
1592                 /* TODO: set error here?  */
1593                 return;
1594         }
1595         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1596                 /* set error here */
1597                 return;
1598         }
1600         connection->request = malloc(sizeof(char)*(
1601                                 /* length of the field name */
1602                                 strlen(mpdTagItemKeys[field])+
1603                                 /* "list"+space+\0 */
1604                                 6
1605                                 ));
1606         sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1611 void mpd_addConstraintSearch(mpd_Connection *connection,
1612                 int field,
1613                 char *name)
1615         char *arg = NULL;
1616         if(!connection->request){
1617                 return;
1618         }
1619         if(name == NULL) {
1620                 return;
1621         }
1622         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1623                 return;
1624         }
1625         /* clean up the query */
1626         arg = mpd_sanitizeArg(name);
1627         /* create space for the query */
1628         connection->request = realloc(connection->request, (
1629                          /* length of the old string */
1630                          strlen(connection->request)+
1631                          /* space between */
1632                          1+
1633                          /* length of the field name */
1634                          strlen(mpdTagItemKeys[field])+
1635                          /* space plus starting " */
1636                          2+
1637                          /* length of search term */
1638                          strlen(arg)+
1639                          /* closing " +\0 that is added sprintf */
1640                          2
1641                         )*sizeof(char));
1642         /* and form the query */
1643         sprintf(connection->request, "%s %s \"%s\"",
1644                         connection->request,
1645                         mpdTagItemKeys[field],
1646                         arg);
1647         free(arg);
1651 void mpd_commitSearch(mpd_Connection *connection)
1653         if(connection->request)
1654         {
1655                 int length = strlen(connection->request);
1656                 /* fixing up the string for mpd to like */
1657                 connection->request = realloc(connection->request,
1658                                 (length+        /* old length */
1659                                  2              /* closing \n and \0 */
1660                                 )*sizeof(char));
1661                 connection->request[length] = '\n';
1662                 connection->request[length+1] = '\0';
1663                 /* and off we go */
1664                 mpd_sendInfoCommand(connection, connection->request);
1665                 /* clean up a bit */
1666                 free(connection->request);
1667                 connection->request = NULL;
1668         }
1671 /**
1672  * @param connection a MpdConnection
1673  * @param path  the path to the playlist.
1674  *
1675  * List the content, with full metadata, of a stored playlist.
1676  *
1677  */
1678 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1680         char *arg = mpd_sanitizeArg(path);
1681         char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1682         sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1683         mpd_sendInfoCommand(connection, query);
1684         free(arg);
1685         free(query);
1688 /**
1689  * @param connection a MpdConnection
1690  * @param path  the path to the playlist.
1691  *
1692  * List the content of a stored playlist.
1693  *
1694  */
1695 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1697         char *arg = mpd_sanitizeArg(path);
1698         char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1699         sprintf(query, "listplaylist \"%s\"\n",arg);
1700         mpd_sendInfoCommand(connection, query);
1701         free(arg);
1702         free(query);