Code

disable more features with --enable-mini
[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         assert(connection->sock >= 0);
218         while (1) {
219                 tv = connection->timeout;
220                 FD_ZERO(&fds);
221                 FD_SET(connection->sock, &fds);
223                 ret = select(connection->sock + 1, &fds, NULL, NULL, &tv);
224                 if (ret > 0)
225                         return 0;
227                 if (ret == 0 || !SELECT_ERRNO_IGNORE)
228                         return -1;
229         }
232 /**
233  * Wait until the socket is connected and check its result.  Returns 1
234  * on success, 0 on timeout, -errno on error.
235  */
236 static int mpd_wait_connected(mpd_Connection *connection)
238         int ret;
239         int s_err = 0;
240         socklen_t s_err_size = sizeof(s_err);
242         ret = mpd_wait(connection);
243         if (ret < 0)
244                 return 0;
246         ret = getsockopt(connection->sock, SOL_SOCKET, SO_ERROR,
247                          (char*)&s_err, &s_err_size);
248         if (ret < 0)
249                 return -errno;
251         if (s_err != 0)
252                 return -s_err;
254         return 1;
257 /**
258  * Attempt to read data from the socket into the input buffer.
259  * Returns 0 on success, -1 on error.
260  */
261 static int mpd_recv(mpd_Connection *connection)
263         int ret;
264         ssize_t nbytes;
266         assert(connection != NULL);
267         assert(connection->buflen <= sizeof(connection->buffer));
268         assert(connection->bufstart <= connection->buflen);
270         if (connection->sock < 0) {
271                 strcpy(connection->errorStr, "not connected");
272                 connection->error = MPD_ERROR_CONNCLOSED;
273                 connection->doneProcessing = 1;
274                 connection->doneListOk = 0;
275                 return -1;
276         }
278         if (connection->buflen >= sizeof(connection->buffer)) {
279                 /* delete consumed data from beginning of buffer */
280                 connection->buflen -= connection->bufstart;
281                 memmove(connection->buffer,
282                         connection->buffer + connection->bufstart,
283                         connection->buflen);
284                 connection->bufstart = 0;
285         }
287         if (connection->buflen >= sizeof(connection->buffer)) {
288                 strcpy(connection->errorStr, "buffer overrun");
289                 connection->error = MPD_ERROR_BUFFEROVERRUN;
290                 connection->doneProcessing = 1;
291                 connection->doneListOk = 0;
292                 return -1;
293         }
295         while (1) {
296                 ret = mpd_wait(connection);
297                 if (ret < 0) {
298                         strcpy(connection->errorStr, "connection timeout");
299                         connection->error = MPD_ERROR_TIMEOUT;
300                         connection->doneProcessing = 1;
301                         connection->doneListOk = 0;
302                         return -1;
303                 }
305                 nbytes = read(connection->sock,
306                               connection->buffer + connection->buflen,
307                               sizeof(connection->buffer) - connection->buflen);
308                 if (nbytes > 0) {
309                         connection->buflen += nbytes;
310                         return 0;
311                 }
313                 if (nbytes == 0 || !SENDRECV_ERRNO_IGNORE) {
314                         strcpy(connection->errorStr, "connection closed");
315                         connection->error = MPD_ERROR_CONNCLOSED;
316                         connection->doneProcessing = 1;
317                         connection->doneListOk = 0;
318                         return -1;
319                 }
320         }
323 static int
324 mpd_connect(mpd_Connection *connection, const char * host, int port)
326         struct resolver *resolver;
327         const struct resolver_address *address;
328         int ret;
330         resolver = resolver_new(host, port);
331         if (resolver == NULL) {
332                 snprintf(connection->errorStr, sizeof(connection->errorStr),
333                          "host \"%s\" not found", host);
334                 connection->error = MPD_ERROR_UNKHOST;
335                 return -1;
336         }
338         while ((address = resolver_next(resolver)) != NULL) {
339                 connection->sock = socket(address->family, SOCK_STREAM,
340                                           address->protocol);
341                 if (connection->sock < 0) {
342                         snprintf(connection->errorStr,
343                                  sizeof(connection->errorStr),
344                                  "problems creating socket: %s",
345                                  strerror(errno));
346                         connection->error = MPD_ERROR_SYSTEM;
347                         continue;
348                 }
350                 ret = do_connect_fail(connection,
351                                       address->addr, address->addrlen);
352                 if (ret != 0) {
353                         snprintf(connection->errorStr,
354                                  sizeof(connection->errorStr),
355                                  "problems connecting to \"%s\" on port"
356                                  " %i: %s", host, port, strerror(errno));
357                         connection->error = MPD_ERROR_CONNPORT;
359                         closesocket(connection->sock);
360                         connection->sock = -1;
361                         continue;
362                 }
364                 ret = mpd_wait_connected(connection);
365                 if (ret > 0) {
366                         resolver_free(resolver);
367                         mpd_clearError(connection);
368                         return 0;
369                 }
371                 if (ret == 0) {
372                         snprintf(connection->errorStr,
373                                  sizeof(connection->errorStr),
374                                  "timeout in attempting to get a response from"
375                                  " \"%s\" on port %i", host, port);
376                         connection->error = MPD_ERROR_NORESPONSE;
377                 } else if (ret < 0) {
378                         snprintf(connection->errorStr,
379                                  sizeof(connection->errorStr),
380                                  "problems connecting to \"%s\" on port %i: %s",
381                                  host, port, strerror(-ret));
382                         connection->error = MPD_ERROR_CONNPORT;
383                 }
385                 closesocket(connection->sock);
386                 connection->sock = -1;
387         }
389         resolver_free(resolver);
390         return -1;
393 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
394         int err;
395         char * rt;
396         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
398         connection->buflen = 0;
399         connection->bufstart = 0;
400         mpd_clearError(connection);
401         connection->doneProcessing = 0;
402         connection->commandList = 0;
403         connection->listOks = 0;
404         connection->doneListOk = 0;
405         connection->returnElement = NULL;
406         connection->request = NULL;
408         if (winsock_dll_error(connection))
409                 return connection;
411         mpd_setConnectionTimeout(connection,timeout);
413         err = mpd_connect(connection, host, port);
414         if (err < 0)
415                 return connection;
417         while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
418                 err = mpd_recv(connection);
419                 if (err < 0)
420                         return connection;
421         }
423         *rt = '\0';
424         if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
425                 connection->doneProcessing = 1;
427         connection->buflen -= rt + 1 - connection->buffer;
428         memmove(connection->buffer, rt + 1, connection->buflen);
430         return connection;
433 void mpd_clearError(mpd_Connection * connection) {
434         connection->error = 0;
435         connection->errorStr[0] = '\0';
438 void mpd_closeConnection(mpd_Connection * connection) {
439         closesocket(connection->sock);
440         if(connection->returnElement) free(connection->returnElement);
441         if(connection->request) free(connection->request);
442         free(connection);
443         WSACleanup();
446 static void mpd_executeCommand(mpd_Connection *connection,
447                                const char *command) {
448         int ret;
449         struct timeval tv;
450         fd_set fds;
451         const char *commandPtr = command;
452         int commandLen = strlen(command);
454         if (connection->sock < 0) {
455                 strcpy(connection->errorStr, "not connected");
456                 connection->error = MPD_ERROR_CONNCLOSED;
457                 return;
458         }
460         if (!connection->doneProcessing && !connection->commandList) {
461                 strcpy(connection->errorStr,
462                        "not done processing current command");
463                 connection->error = 1;
464                 return;
465         }
467         mpd_clearError(connection);
469         FD_ZERO(&fds);
470         FD_SET(connection->sock,&fds);
471         tv.tv_sec = connection->timeout.tv_sec;
472         tv.tv_usec = connection->timeout.tv_usec;
474         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
475                         (ret==-1 && SELECT_ERRNO_IGNORE)) {
476                 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
477                 if(ret<=0)
478                 {
479                         if (SENDRECV_ERRNO_IGNORE) continue;
480                         snprintf(connection->errorStr, sizeof(connection->errorStr),
481                                  "problems giving command \"%s\"",command);
482                         connection->error = MPD_ERROR_SENDING;
483                         return;
484                 }
485                 else {
486                         commandPtr+=ret;
487                         commandLen-=ret;
488                 }
490                 if(commandLen<=0) break;
491         }
493         if(commandLen>0) {
494                 perror("");
495                 snprintf(connection->errorStr, sizeof(connection->errorStr),
496                          "timeout sending command \"%s\"",command);
497                 connection->error = MPD_ERROR_TIMEOUT;
498                 return;
499         }
501         if(!connection->commandList) connection->doneProcessing = 0;
502         else if(connection->commandList == COMMAND_LIST_OK) {
503                 connection->listOks++;
504         }
507 static void mpd_getNextReturnElement(mpd_Connection * connection) {
508         char * output = NULL;
509         char * rt = NULL;
510         char * name = NULL;
511         char * value = NULL;
512         char * tok = NULL;
513         int err;
514         int pos;
516         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
517         connection->returnElement = NULL;
519         if (connection->doneProcessing ||
520             (connection->listOks && connection->doneListOk)) {
521                 strcpy(connection->errorStr,"already done processing current command");
522                 connection->error = 1;
523                 return;
524         }
526         while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
527                              connection->buflen - connection->bufstart))) {
528                 err = mpd_recv(connection);
529                 if (err < 0)
530                         return;
531         }
533         *rt = '\0';
534         output = connection->buffer+connection->bufstart;
535         connection->bufstart = rt - connection->buffer + 1;
537         if(strcmp(output,"OK")==0) {
538                 if(connection->listOks > 0) {
539                         strcpy(connection->errorStr, "expected more list_OK's");
540                         connection->error = 1;
541                 }
542                 connection->listOks = 0;
543                 connection->doneProcessing = 1;
544                 connection->doneListOk = 0;
545                 return;
546         }
548         if(strcmp(output, "list_OK") == 0) {
549                 if(!connection->listOks) {
550                         strcpy(connection->errorStr,
551                                         "got an unexpected list_OK");
552                         connection->error = 1;
553                 }
554                 else {
555                         connection->doneListOk = 1;
556                         connection->listOks--;
557                 }
558                 return;
559         }
561         if(strncmp(output,"ACK",strlen("ACK"))==0) {
562                 size_t length = strlen(output);
563                 char * test;
564                 char * needle;
565                 int val;
567                 if (length >= sizeof(connection->errorStr))
568                         length = sizeof(connection->errorStr) - 1;
570                 memcpy(connection->errorStr, output, length);
571                 connection->errorStr[length] = 0;
572                 connection->error = MPD_ERROR_ACK;
573                 connection->errorCode = MPD_ACK_ERROR_UNK;
574                 connection->errorAt = MPD_ERROR_AT_UNK;
575                 connection->doneProcessing = 1;
576                 connection->doneListOk = 0;
578                 needle = strchr(output, '[');
579                 if(!needle) return;
580                 val = strtol(needle+1, &test, 10);
581                 if(*test != '@') return;
582                 connection->errorCode = val;
583                 val = strtol(test+1, &test, 10);
584                 if(*test != ']') return;
585                 connection->errorAt = val;
586                 return;
587         }
589         tok = strchr(output, ':');
590         if (!tok) return;
591         pos = tok - output;
592         value = ++tok;
593         name = output;
594         name[pos] = '\0';
596         if(value[0]==' ') {
597                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
598         }
599         else {
600                 snprintf(connection->errorStr, sizeof(connection->errorStr),
601                          "error parsing: %s:%s",name,value);
602                 connection->error = 1;
603         }
606 void mpd_finishCommand(mpd_Connection * connection) {
607         while(!connection->doneProcessing) {
608                 if(connection->doneListOk) connection->doneListOk = 0;
609                 mpd_getNextReturnElement(connection);
610         }
613 static void mpd_finishListOkCommand(mpd_Connection * connection) {
614         while(!connection->doneProcessing && connection->listOks &&
615                         !connection->doneListOk)
616         {
617                 mpd_getNextReturnElement(connection);
618         }
621 int mpd_nextListOkCommand(mpd_Connection * connection) {
622         mpd_finishListOkCommand(connection);
623         if(!connection->doneProcessing) connection->doneListOk = 0;
624         if(connection->listOks == 0 || connection->doneProcessing) return -1;
625         return 0;
628 void mpd_sendStatusCommand(mpd_Connection * connection) {
629         mpd_executeCommand(connection,"status\n");
632 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
633         mpd_Status * status;
635         /*mpd_executeCommand(connection,"status\n");
637         if(connection->error) return NULL;*/
639         if(connection->doneProcessing || (connection->listOks &&
640            connection->doneListOk))
641         {
642                 return NULL;
643         }
645         if(!connection->returnElement) mpd_getNextReturnElement(connection);
647         status = malloc(sizeof(mpd_Status));
648         status->volume = -1;
649         status->repeat = 0;
650         status->random = 0;
651         status->playlist = -1;
652         status->playlistLength = -1;
653         status->state = -1;
654         status->song = 0;
655         status->songid = 0;
656         status->elapsedTime = 0;
657         status->totalTime = 0;
658         status->bitRate = 0;
659         status->sampleRate = 0;
660         status->bits = 0;
661         status->channels = 0;
662         status->crossfade = -1;
663         status->error = NULL;
664         status->updatingDb = 0;
666         if(connection->error) {
667                 free(status);
668                 return NULL;
669         }
670         while(connection->returnElement) {
671                 mpd_ReturnElement * re = connection->returnElement;
672                 if(strcmp(re->name,"volume")==0) {
673                         status->volume = atoi(re->value);
674                 }
675                 else if(strcmp(re->name,"repeat")==0) {
676                         status->repeat = atoi(re->value);
677                 }
678                 else if(strcmp(re->name,"random")==0) {
679                         status->random = atoi(re->value);
680                 }
681                 else if(strcmp(re->name,"playlist")==0) {
682                         status->playlist = strtol(re->value,NULL,10);
683                 }
684                 else if(strcmp(re->name,"playlistlength")==0) {
685                         status->playlistLength = atoi(re->value);
686                 }
687                 else if(strcmp(re->name,"bitrate")==0) {
688                         status->bitRate = atoi(re->value);
689                 }
690                 else if(strcmp(re->name,"state")==0) {
691                         if(strcmp(re->value,"play")==0) {
692                                 status->state = MPD_STATUS_STATE_PLAY;
693                         }
694                         else if(strcmp(re->value,"stop")==0) {
695                                 status->state = MPD_STATUS_STATE_STOP;
696                         }
697                         else if(strcmp(re->value,"pause")==0) {
698                                 status->state = MPD_STATUS_STATE_PAUSE;
699                         }
700                         else {
701                                 status->state = MPD_STATUS_STATE_UNKNOWN;
702                         }
703                 }
704                 else if(strcmp(re->name,"song")==0) {
705                         status->song = atoi(re->value);
706                 }
707                 else if(strcmp(re->name,"songid")==0) {
708                         status->songid = atoi(re->value);
709                 }
710                 else if(strcmp(re->name,"time")==0) {
711                         char * tok = strchr(re->value,':');
712                         /* the second strchr below is a safety check */
713                         if (tok && (strchr(tok,0) > (tok+1))) {
714                                 /* atoi stops at the first non-[0-9] char: */
715                                 status->elapsedTime = atoi(re->value);
716                                 status->totalTime = atoi(tok+1);
717                         }
718                 }
719                 else if(strcmp(re->name,"error")==0) {
720                         status->error = strdup(re->value);
721                 }
722                 else if(strcmp(re->name,"xfade")==0) {
723                         status->crossfade = atoi(re->value);
724                 }
725                 else if(strcmp(re->name,"updating_db")==0) {
726                         status->updatingDb = atoi(re->value);
727                 }
728                 else if(strcmp(re->name,"audio")==0) {
729                         char * tok = strchr(re->value,':');
730                         if (tok && (strchr(tok,0) > (tok+1))) {
731                                 status->sampleRate = atoi(re->value);
732                                 status->bits = atoi(++tok);
733                                 tok = strchr(tok,':');
734                                 if (tok && (strchr(tok,0) > (tok+1)))
735                                         status->channels = atoi(tok+1);
736                         }
737                 }
739                 mpd_getNextReturnElement(connection);
740                 if(connection->error) {
741                         free(status);
742                         return NULL;
743                 }
744         }
746         if(connection->error) {
747                 free(status);
748                 return NULL;
749         }
750         else if(status->state<0) {
751                 strcpy(connection->errorStr,"state not found");
752                 connection->error = 1;
753                 free(status);
754                 return NULL;
755         }
757         return status;
760 void mpd_freeStatus(mpd_Status * status) {
761         if(status->error) free(status->error);
762         free(status);
765 void mpd_sendStatsCommand(mpd_Connection * connection) {
766         mpd_executeCommand(connection,"stats\n");
769 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
770         mpd_Stats * stats;
772         /*mpd_executeCommand(connection,"stats\n");
774         if(connection->error) return NULL;*/
776         if(connection->doneProcessing || (connection->listOks &&
777            connection->doneListOk))
778         {
779                 return NULL;
780         }
782         if(!connection->returnElement) mpd_getNextReturnElement(connection);
784         stats = malloc(sizeof(mpd_Stats));
785         stats->numberOfArtists = 0;
786         stats->numberOfAlbums = 0;
787         stats->numberOfSongs = 0;
788         stats->uptime = 0;
789         stats->dbUpdateTime = 0;
790         stats->playTime = 0;
791         stats->dbPlayTime = 0;
793         if(connection->error) {
794                 free(stats);
795                 return NULL;
796         }
797         while(connection->returnElement) {
798                 mpd_ReturnElement * re = connection->returnElement;
799                 if(strcmp(re->name,"artists")==0) {
800                         stats->numberOfArtists = atoi(re->value);
801                 }
802                 else if(strcmp(re->name,"albums")==0) {
803                         stats->numberOfAlbums = atoi(re->value);
804                 }
805                 else if(strcmp(re->name,"songs")==0) {
806                         stats->numberOfSongs = atoi(re->value);
807                 }
808                 else if(strcmp(re->name,"uptime")==0) {
809                         stats->uptime = strtol(re->value,NULL,10);
810                 }
811                 else if(strcmp(re->name,"db_update")==0) {
812                         stats->dbUpdateTime = strtol(re->value,NULL,10);
813                 }
814                 else if(strcmp(re->name,"playtime")==0) {
815                         stats->playTime = strtol(re->value,NULL,10);
816                 }
817                 else if(strcmp(re->name,"db_playtime")==0) {
818                         stats->dbPlayTime = strtol(re->value,NULL,10);
819                 }
821                 mpd_getNextReturnElement(connection);
822                 if(connection->error) {
823                         free(stats);
824                         return NULL;
825                 }
826         }
828         if(connection->error) {
829                 free(stats);
830                 return NULL;
831         }
833         return stats;
836 void mpd_freeStats(mpd_Stats * stats) {
837         free(stats);
840 static void mpd_initDirectory(mpd_Directory * directory) {
841         directory->path = NULL;
844 static void mpd_finishDirectory(mpd_Directory * directory) {
845         if (directory->path)
846                 str_pool_put(directory->path);
849 mpd_Directory * mpd_newDirectory(void) {
850         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
852         mpd_initDirectory(directory);
854         return directory;
857 void mpd_freeDirectory(mpd_Directory * directory) {
858         mpd_finishDirectory(directory);
860         free(directory);
863 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
864         mpd_Directory * ret = mpd_newDirectory();
866         if (directory->path)
867                 ret->path = str_pool_dup(directory->path);
869         return ret;
872 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
873         playlist->path = NULL;
876 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
877         if (playlist->path)
878                 str_pool_put(playlist->path);
881 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
882         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
884         mpd_initPlaylistFile(playlist);
886         return playlist;
889 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
890         mpd_finishPlaylistFile(playlist);
891         free(playlist);
894 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
895         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
897         if (playlist->path)
898                 ret->path = str_pool_dup(playlist->path);
900         return ret;
903 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
904         entity->info.directory = NULL;
907 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
908         if(entity->info.directory) {
909                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
910                         mpd_freeDirectory(entity->info.directory);
911                 }
912                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
913                         mpd_freeSong(entity->info.song);
914                 }
915                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
916                         mpd_freePlaylistFile(entity->info.playlistFile);
917                 }
918         }
921 mpd_InfoEntity * mpd_newInfoEntity(void) {
922         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
924         mpd_initInfoEntity(entity);
926         return entity;
929 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
930         mpd_finishInfoEntity(entity);
931         free(entity);
934 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
935         mpd_executeCommand(connection,command);
938 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
939         mpd_InfoEntity * entity = NULL;
941         if(connection->doneProcessing || (connection->listOks &&
942            connection->doneListOk)) {
943                 return NULL;
944         }
946         if(!connection->returnElement) mpd_getNextReturnElement(connection);
948         if(connection->returnElement) {
949                 if(strcmp(connection->returnElement->name,"file")==0) {
950                         entity = mpd_newInfoEntity();
951                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
952                         entity->info.song = mpd_newSong();
953                         entity->info.song->file =
954                                 str_pool_dup(connection->returnElement->value);
955                 }
956                 else if(strcmp(connection->returnElement->name,
957                                         "directory")==0) {
958                         entity = mpd_newInfoEntity();
959                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
960                         entity->info.directory = mpd_newDirectory();
961                         entity->info.directory->path =
962                                 str_pool_dup(connection->returnElement->value);
963                 }
964                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
965                         entity = mpd_newInfoEntity();
966                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
967                         entity->info.playlistFile = mpd_newPlaylistFile();
968                         entity->info.playlistFile->path =
969                                 str_pool_dup(connection->returnElement->value);
970                 }
971                 else if(strcmp(connection->returnElement->name, "cpos") == 0){
972                         entity = mpd_newInfoEntity();
973                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
974                         entity->info.song = mpd_newSong();
975                         entity->info.song->pos = atoi(connection->returnElement->value);
976                 }
977                 else {
978                         connection->error = 1;
979                         strcpy(connection->errorStr,"problem parsing song info");
980                         return NULL;
981                 }
982         }
983         else return NULL;
985         mpd_getNextReturnElement(connection);
986         while(connection->returnElement) {
987                 mpd_ReturnElement * re = connection->returnElement;
989                 if(strcmp(re->name,"file")==0) return entity;
990                 else if(strcmp(re->name,"directory")==0) return entity;
991                 else if(strcmp(re->name,"playlist")==0) return entity;
992                 else if(strcmp(re->name,"cpos")==0) return entity;
994                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
995                                 strlen(re->value)) {
996                         if(!entity->info.song->artist &&
997                                         strcmp(re->name,"Artist")==0) {
998                                 entity->info.song->artist = str_pool_dup(re->value);
999                         }
1000                         else if(!entity->info.song->album &&
1001                                         strcmp(re->name,"Album")==0) {
1002                                 entity->info.song->album = str_pool_dup(re->value);
1003                         }
1004                         else if(!entity->info.song->title &&
1005                                         strcmp(re->name,"Title")==0) {
1006                                 entity->info.song->title = str_pool_dup(re->value);
1007                         }
1008                         else if(!entity->info.song->track &&
1009                                         strcmp(re->name,"Track")==0) {
1010                                 entity->info.song->track = str_pool_dup(re->value);
1011                         }
1012                         else if(!entity->info.song->name &&
1013                                         strcmp(re->name,"Name")==0) {
1014                                 entity->info.song->name = str_pool_dup(re->value);
1015                         }
1016                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1017                                         strcmp(re->name,"Time")==0) {
1018                                 entity->info.song->time = atoi(re->value);
1019                         }
1020                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1021                                         strcmp(re->name,"Pos")==0) {
1022                                 entity->info.song->pos = atoi(re->value);
1023                         }
1024                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
1025                                         strcmp(re->name,"Id")==0) {
1026                                 entity->info.song->id = atoi(re->value);
1027                         }
1028                         else if(!entity->info.song->date &&
1029                                         strcmp(re->name, "Date") == 0) {
1030                                 entity->info.song->date = str_pool_dup(re->value);
1031                         }
1032                         else if(!entity->info.song->genre &&
1033                                         strcmp(re->name, "Genre") == 0) {
1034                                 entity->info.song->genre = str_pool_dup(re->value);
1035                         }
1036                         else if(!entity->info.song->composer &&
1037                                         strcmp(re->name, "Composer") == 0) {
1038                                 entity->info.song->composer = str_pool_dup(re->value);
1039                         }
1040                         else if(!entity->info.song->disc &&
1041                                         strcmp(re->name, "Disc") == 0) {
1042                                 entity->info.song->disc = str_pool_dup(re->value);
1043                         }
1044                         else if(!entity->info.song->comment &&
1045                                         strcmp(re->name, "Comment") == 0) {
1046                                 entity->info.song->comment = str_pool_dup(re->value);
1047                         }
1048                 }
1049                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1050                 }
1051                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1052                 }
1054                 mpd_getNextReturnElement(connection);
1055         }
1057         return entity;
1060 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1061                 const char * name)
1063         if(connection->doneProcessing || (connection->listOks &&
1064                                 connection->doneListOk))
1065         {
1066                 return NULL;
1067         }
1069         mpd_getNextReturnElement(connection);
1070         while(connection->returnElement) {
1071                 mpd_ReturnElement * re = connection->returnElement;
1073                 if(strcmp(re->name,name)==0) return strdup(re->value);
1074                 mpd_getNextReturnElement(connection);
1075         }
1077         return NULL;
1080 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1081         if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1082         {
1083                 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1084         }
1085         return NULL;
1088 char * mpd_getNextArtist(mpd_Connection * connection) {
1089         return mpd_getNextReturnElementNamed(connection,"Artist");
1092 char * mpd_getNextAlbum(mpd_Connection * connection) {
1093         return mpd_getNextReturnElementNamed(connection,"Album");
1096 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1097         char * string = malloc(strlen("playlistinfo")+25);
1098         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1099         mpd_sendInfoCommand(connection,string);
1100         free(string);
1103 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1104         char * string = malloc(strlen("playlistid")+25);
1105         sprintf(string, "playlistid \"%i\"\n", id);
1106         mpd_sendInfoCommand(connection, string);
1107         free(string);
1110 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1111         char * string = malloc(strlen("plchanges")+25);
1112         sprintf(string,"plchanges \"%lld\"\n",playlist);
1113         mpd_sendInfoCommand(connection,string);
1114         free(string);
1117 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1118         char * string = malloc(strlen("plchangesposid")+25);
1119         sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1120         mpd_sendInfoCommand(connection,string);
1121         free(string);
1124 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1125         char * sDir = mpd_sanitizeArg(dir);
1126         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1127         sprintf(string,"listall \"%s\"\n",sDir);
1128         mpd_sendInfoCommand(connection,string);
1129         free(string);
1130         free(sDir);
1133 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1134         char * sDir = mpd_sanitizeArg(dir);
1135         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1136         sprintf(string,"listallinfo \"%s\"\n",sDir);
1137         mpd_sendInfoCommand(connection,string);
1138         free(string);
1139         free(sDir);
1142 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1143         char * sDir = mpd_sanitizeArg(dir);
1144         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1145         sprintf(string,"lsinfo \"%s\"\n",sDir);
1146         mpd_sendInfoCommand(connection,string);
1147         free(string);
1148         free(sDir);
1151 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1152         mpd_executeCommand(connection,"currentsong\n");
1155 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1156                 const char * str)
1158         char st[10];
1159         char * string;
1160         char * sanitStr = mpd_sanitizeArg(str);
1161         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1162         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1163         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1164         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1165         else {
1166                 connection->error = 1;
1167                 strcpy(connection->errorStr,"unknown table for search");
1168                 return;
1169         }
1170         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1171         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1172         mpd_sendInfoCommand(connection,string);
1173         free(string);
1174         free(sanitStr);
1177 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1178                 const char * str)
1180         char st[10];
1181         char * string;
1182         char * sanitStr = mpd_sanitizeArg(str);
1183         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1184         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1185         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1186         else {
1187                 connection->error = 1;
1188                 strcpy(connection->errorStr,"unknown table for find");
1189                 return;
1190         }
1191         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1192         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1193         mpd_sendInfoCommand(connection,string);
1194         free(string);
1195         free(sanitStr);
1198 void mpd_sendListCommand(mpd_Connection * connection, int table,
1199                 const char * arg1)
1201         char st[10];
1202         char * string;
1203         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1204         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1205         else {
1206                 connection->error = 1;
1207                 strcpy(connection->errorStr,"unknown table for list");
1208                 return;
1209         }
1210         if(arg1) {
1211                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1212                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1213                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1214                 free(sanitArg1);
1215         }
1216         else {
1217                 string = malloc(strlen("list")+strlen(st)+3);
1218                 sprintf(string,"list %s\n",st);
1219         }
1220         mpd_sendInfoCommand(connection,string);
1221         free(string);
1224 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1225         char * sFile = mpd_sanitizeArg(file);
1226         char * string = malloc(strlen("add")+strlen(sFile)+5);
1227         sprintf(string,"add \"%s\"\n",sFile);
1228         mpd_executeCommand(connection,string);
1229         free(string);
1230         free(sFile);
1233 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1234         char * string = malloc(strlen("delete")+25);
1235         sprintf(string,"delete \"%i\"\n",songPos);
1236         mpd_sendInfoCommand(connection,string);
1237         free(string);
1240 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1241         char * string = malloc(strlen("deleteid")+25);
1242         sprintf(string, "deleteid \"%i\"\n", id);
1243         mpd_sendInfoCommand(connection,string);
1244         free(string);
1247 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1248         char * sName = mpd_sanitizeArg(name);
1249         char * string = malloc(strlen("save")+strlen(sName)+5);
1250         sprintf(string,"save \"%s\"\n",sName);
1251         mpd_executeCommand(connection,string);
1252         free(string);
1253         free(sName);
1256 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1257         char * sName = mpd_sanitizeArg(name);
1258         char * string = malloc(strlen("load")+strlen(sName)+5);
1259         sprintf(string,"load \"%s\"\n",sName);
1260         mpd_executeCommand(connection,string);
1261         free(string);
1262         free(sName);
1265 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1266         char * sName = mpd_sanitizeArg(name);
1267         char * string = malloc(strlen("rm")+strlen(sName)+5);
1268         sprintf(string,"rm \"%s\"\n",sName);
1269         mpd_executeCommand(connection,string);
1270         free(string);
1271         free(sName);
1274 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1275         mpd_executeCommand(connection,"shuffle\n");
1278 void mpd_sendClearCommand(mpd_Connection * connection) {
1279         mpd_executeCommand(connection,"clear\n");
1282 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1283         char * string = malloc(strlen("play")+25);
1284         sprintf(string,"play \"%i\"\n",songPos);
1285         mpd_sendInfoCommand(connection,string);
1286         free(string);
1289 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1290         char * string = malloc(strlen("playid")+25);
1291         sprintf(string,"playid \"%i\"\n",id);
1292         mpd_sendInfoCommand(connection,string);
1293         free(string);
1296 void mpd_sendStopCommand(mpd_Connection * connection) {
1297         mpd_executeCommand(connection,"stop\n");
1300 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1301         char * string = malloc(strlen("pause")+25);
1302         sprintf(string,"pause \"%i\"\n",pauseMode);
1303         mpd_executeCommand(connection,string);
1304         free(string);
1307 void mpd_sendNextCommand(mpd_Connection * connection) {
1308         mpd_executeCommand(connection,"next\n");
1311 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1312         char * string = malloc(strlen("move")+25);
1313         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1314         mpd_sendInfoCommand(connection,string);
1315         free(string);
1318 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1319         char * string = malloc(strlen("moveid")+25);
1320         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1321         mpd_sendInfoCommand(connection,string);
1322         free(string);
1325 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1326         char * string = malloc(strlen("swap")+25);
1327         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1328         mpd_sendInfoCommand(connection,string);
1329         free(string);
1332 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1333         char * string = malloc(strlen("swapid")+25);
1334         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1335         mpd_sendInfoCommand(connection,string);
1336         free(string);
1339 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1340         char * string = malloc(strlen("seek")+25);
1341         sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1342         mpd_sendInfoCommand(connection,string);
1343         free(string);
1346 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1347         char * string = malloc(strlen("seekid")+25);
1348         sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1349         mpd_sendInfoCommand(connection,string);
1350         free(string);
1353 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1354         char *sPath = mpd_sanitizeArg(path);
1355         char * string = malloc(strlen("update")+strlen(sPath)+5);
1356         sprintf(string,"update \"%s\"\n",sPath);
1357         mpd_sendInfoCommand(connection,string);
1358         free(string);
1359         free(sPath);
1362 int mpd_getUpdateId(mpd_Connection * connection) {
1363         char * jobid;
1364         int ret = 0;
1366         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1367         if(jobid) {
1368                 ret = atoi(jobid);
1369                 free(jobid);
1370         }
1372         return ret;
1375 void mpd_sendPrevCommand(mpd_Connection * connection) {
1376         mpd_executeCommand(connection,"previous\n");
1379 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1380         char * string = malloc(strlen("repeat")+25);
1381         sprintf(string,"repeat \"%i\"\n",repeatMode);
1382         mpd_executeCommand(connection,string);
1383         free(string);
1386 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1387         char * string = malloc(strlen("random")+25);
1388         sprintf(string,"random \"%i\"\n",randomMode);
1389         mpd_executeCommand(connection,string);
1390         free(string);
1393 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1394         char * string = malloc(strlen("setvol")+25);
1395         sprintf(string,"setvol \"%i\"\n",volumeChange);
1396         mpd_executeCommand(connection,string);
1397         free(string);
1400 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1401         char * string = malloc(strlen("volume")+25);
1402         sprintf(string,"volume \"%i\"\n",volumeChange);
1403         mpd_executeCommand(connection,string);
1404         free(string);
1407 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1408         char * string = malloc(strlen("crossfade")+25);
1409         sprintf(string,"crossfade \"%i\"\n",seconds);
1410         mpd_executeCommand(connection,string);
1411         free(string);
1414 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1415         char * sPass = mpd_sanitizeArg(pass);
1416         char * string = malloc(strlen("password")+strlen(sPass)+5);
1417         sprintf(string,"password \"%s\"\n",sPass);
1418         mpd_executeCommand(connection,string);
1419         free(string);
1420         free(sPass);
1423 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1424         if(connection->commandList) {
1425                 strcpy(connection->errorStr,"already in command list mode");
1426                 connection->error = 1;
1427                 return;
1428         }
1429         connection->commandList = COMMAND_LIST;
1430         mpd_executeCommand(connection,"command_list_begin\n");
1433 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1434         if(connection->commandList) {
1435                 strcpy(connection->errorStr,"already in command list mode");
1436                 connection->error = 1;
1437                 return;
1438         }
1439         connection->commandList = COMMAND_LIST_OK;
1440         mpd_executeCommand(connection,"command_list_ok_begin\n");
1441         connection->listOks = 0;
1444 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1445         if(!connection->commandList) {
1446                 strcpy(connection->errorStr,"not in command list mode");
1447                 connection->error = 1;
1448                 return;
1449         }
1450         connection->commandList = 0;
1451         mpd_executeCommand(connection,"command_list_end\n");
1454 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1455         mpd_executeCommand(connection,"outputs\n");
1458 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1459         mpd_OutputEntity * output = NULL;
1461         if(connection->doneProcessing || (connection->listOks &&
1462                                 connection->doneListOk))
1463         {
1464                 return NULL;
1465         }
1467         if(connection->error) return NULL;
1469         output = malloc(sizeof(mpd_OutputEntity));
1470         output->id = -10;
1471         output->name = NULL;
1472         output->enabled = 0;
1474         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1476         while(connection->returnElement) {
1477                 mpd_ReturnElement * re = connection->returnElement;
1478                 if(strcmp(re->name,"outputid")==0) {
1479                         if(output!=NULL && output->id>=0) return output;
1480                         output->id = atoi(re->value);
1481                 }
1482                 else if(strcmp(re->name,"outputname")==0) {
1483                         output->name = strdup(re->value);
1484                 }
1485                 else if(strcmp(re->name,"outputenabled")==0) {
1486                         output->enabled = atoi(re->value);
1487                 }
1489                 mpd_getNextReturnElement(connection);
1490                 if(connection->error) {
1491                         free(output);
1492                         return NULL;
1493                 }
1495         }
1497         return output;
1500 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1501         char * string = malloc(strlen("enableoutput")+25);
1502         sprintf(string,"enableoutput \"%i\"\n",outputId);
1503         mpd_executeCommand(connection,string);
1504         free(string);
1507 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1508         char * string = malloc(strlen("disableoutput")+25);
1509         sprintf(string,"disableoutput \"%i\"\n",outputId);
1510         mpd_executeCommand(connection,string);
1511         free(string);
1514 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1515         free(output->name);
1516         free(output);
1519 /**
1520  * mpd_sendNotCommandsCommand
1521  * odd naming, but it gets the not allowed commands
1522  */
1524 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1525         mpd_executeCommand(connection,"notcommands\n");
1528 /**
1529  * mpd_sendCommandsCommand
1530  * odd naming, but it gets the allowed commands
1531  */
1533 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1534         mpd_executeCommand(connection,"commands\n");
1536 /**
1537  * Get the next returned command
1538  */
1539 char * mpd_getNextCommand(mpd_Connection * connection) {
1540         return mpd_getNextReturnElementNamed(connection,"command");
1543 void mpd_startSearch(mpd_Connection * connection,int exact) {
1544         if(connection->request) {
1545                 /* search/find allready in progress */
1546                 /* TODO: set error here?  */
1547                 return;
1548         }
1549         if(exact){
1550                 connection->request = strdup("find");
1551         }
1552         else{
1553                 connection->request = strdup("search");
1554         }
1558 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1559         if(connection->request) {
1560                 /* search/find allready in progress */
1561                 /* TODO: set error here?  */
1562                 return;
1563         }
1564         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1565                 /* set error here */
1566                 return;
1567         }
1569         connection->request = malloc(sizeof(char)*(
1570                                 /* length of the field name */
1571                                 strlen(mpdTagItemKeys[field])+
1572                                 /* "list"+space+\0 */
1573                                 6
1574                                 ));
1575         sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1580 void mpd_addConstraintSearch(mpd_Connection *connection,
1581                 int field,
1582                 char *name)
1584         char *arg = NULL;
1585         if(!connection->request){
1586                 return;
1587         }
1588         if(name == NULL) {
1589                 return;
1590         }
1591         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1592                 return;
1593         }
1594         /* clean up the query */
1595         arg = mpd_sanitizeArg(name);
1596         /* create space for the query */
1597         connection->request = realloc(connection->request, (
1598                          /* length of the old string */
1599                          strlen(connection->request)+
1600                          /* space between */
1601                          1+
1602                          /* length of the field name */
1603                          strlen(mpdTagItemKeys[field])+
1604                          /* space plus starting " */
1605                          2+
1606                          /* length of search term */
1607                          strlen(arg)+
1608                          /* closign " +\0 that is added sprintf */
1609                          2
1610                         )*sizeof(char));
1611         /* and form the query */
1612         sprintf(connection->request, "%s %s \"%s\"",
1613                         connection->request,
1614                         mpdTagItemKeys[field],
1615                         arg);
1616         free(arg);
1620 void mpd_commitSearch(mpd_Connection *connection)
1622         if(connection->request)
1623         {
1624                 int length = strlen(connection->request);
1625                 /* fixing up the string for mpd to like */
1626                 connection->request = realloc(connection->request,
1627                                 (length+        /* old length */
1628                                  2              /* closing \n and \0 */
1629                                 )*sizeof(char));
1630                 connection->request[length] = '\n';
1631                 connection->request[length+1] = '\0';
1632                 /* and off we go */
1633                 mpd_sendInfoCommand(connection, connection->request);
1634                 /* clean up a bit */
1635                 free(connection->request);
1636                 connection->request = NULL;
1637         }
1640 /**
1641  * @param connection a MpdConnection
1642  * @param path  the path to the playlist.
1643  *
1644  * List the content, with full metadata, of a stored playlist.
1645  *
1646  */
1647 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1649         char *arg = mpd_sanitizeArg(path);
1650         char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1651         sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1652         mpd_sendInfoCommand(connection, query);
1653         free(arg);
1654         free(query);
1657 /**
1658  * @param connection a MpdConnection
1659  * @param path  the path to the playlist.
1660  *
1661  * List the content of a stored playlist.
1662  *
1663  */
1664 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1666         char *arg = mpd_sanitizeArg(path);
1667         char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1668         sprintf(query, "listplaylist \"%s\"\n",arg);
1669         mpd_sendInfoCommand(connection, query);
1670         free(arg);
1671         free(query);