Code

libmpdclient: moved code to mpd_recv(), mpd_wait()
[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 "str_pool.h"
36 #include <assert.h>
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <stdio.h>
40 #include <sys/param.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
46 #ifdef WIN32
47 #  include <ws2tcpip.h>
48 #  include <winsock.h>
49 #else
50 #  include <netinet/in.h>
51 #  include <arpa/inet.h>
52 #  include <sys/socket.h>
53 #  include <netdb.h>
54 #endif
56 #ifndef WIN32
57 #include <sys/un.h>
58 #endif
60 #ifndef MSG_DONTWAIT
61 #  define MSG_DONTWAIT 0
62 #endif
64 #ifndef MPD_NO_GAI
65 #  ifdef AI_ADDRCONFIG
66 #    define MPD_HAVE_GAI
67 #  endif
68 #endif
70 #define COMMAND_LIST    1
71 #define COMMAND_LIST_OK 2
73 #ifdef WIN32
74 #  define SELECT_ERRNO_IGNORE   (errno == WSAEINTR || errno == WSAEINPROGRESS)
75 #  define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
76 #else
77 #  define SELECT_ERRNO_IGNORE   (errno == EINTR)
78 #  define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
79 #  define winsock_dll_error(c)  0
80 #  define closesocket(s)        close(s)
81 #  define WSACleanup()          do { /* nothing */ } while (0)
82 #endif
84 #ifdef WIN32
85 static int winsock_dll_error(mpd_Connection *connection)
86 {
87         WSADATA wsaData;
88         if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
89                         LOBYTE(wsaData.wVersion) != 2 ||
90                         HIBYTE(wsaData.wVersion) != 2 ) {
91                 snprintf(connection->errorStr, sizeof(connection->errorStr),
92                          "Could not find usable WinSock DLL.");
93                 connection->error = MPD_ERROR_SYSTEM;
94                 return 1;
95         }
96         return 0;
97 }
99 static int do_connect_fail(mpd_Connection *connection,
100                            const struct sockaddr *serv_addr, int addrlen)
102         int iMode = 1; /* 0 = blocking, else non-blocking */
103         ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
104         return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR
105                         && WSAGetLastError() != WSAEWOULDBLOCK);
107 #else /* !WIN32 (sane operating systems) */
108 static int do_connect_fail(mpd_Connection *connection,
109                            const struct sockaddr *serv_addr, int addrlen)
111         int flags = fcntl(connection->sock, F_GETFL, 0);
112         fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
113         return (connect(connection->sock,serv_addr,addrlen)<0 &&
114                                 errno!=EINPROGRESS);
116 #endif /* !WIN32 */
118 #ifdef MPD_HAVE_GAI
119 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
120                        float timeout)
122         int error;
123         char service[20];
124         struct addrinfo hints;
125         struct addrinfo *res = NULL;
126         struct addrinfo *addrinfo = NULL;
128         /**
129          * Setup hints
130          */
131         hints.ai_flags     = AI_ADDRCONFIG;
132         hints.ai_family    = PF_UNSPEC;
133         hints.ai_socktype  = SOCK_STREAM;
134         hints.ai_protocol  = IPPROTO_TCP;
135         hints.ai_addrlen   = 0;
136         hints.ai_addr      = NULL;
137         hints.ai_canonname = NULL;
138         hints.ai_next      = NULL;
140         snprintf(service, sizeof(service), "%d", port);
142         error = getaddrinfo(host, service, &hints, &addrinfo);
144         if (error) {
145                 snprintf(connection->errorStr, sizeof(connection->errorStr),
146                          "host \"%s\" not found: %s",host, gai_strerror(error));
147                 connection->error = MPD_ERROR_UNKHOST;
148                 return -1;
149         }
151         for (res = addrinfo; res; res = res->ai_next) {
152                 /* create socket */
153                 connection->sock = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
154                 if (connection->sock < 0) {
155                         snprintf(connection->errorStr, sizeof(connection->errorStr),
156                                  "problems creating socket: %s",
157                                  strerror(errno));
158                         connection->error = MPD_ERROR_SYSTEM;
159                         freeaddrinfo(addrinfo);
160                         return -1;
161                 }
163                 mpd_setConnectionTimeout(connection,timeout);
165                 /* connect stuff */
166                 if (do_connect_fail(connection, res->ai_addr, res->ai_addrlen)) {
167                         /* try the next address family */
168                         closesocket(connection->sock);
169                         connection->sock = -1;
170                         continue;
171                 }
172         }
173         freeaddrinfo(addrinfo);
175         if (connection->sock < 0) {
176                 snprintf(connection->errorStr, sizeof(connection->errorStr),
177                          "problems connecting to \"%s\" on port"
178                          " %i: %s",host,port, strerror(errno));
179                 connection->error = MPD_ERROR_CONNPORT;
181                 return -1;
182         }
184         return 0;
186 #else /* !MPD_HAVE_GAI */
187 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
188                        float timeout)
190         struct hostent * he;
191         struct sockaddr * dest;
192         int destlen;
193         struct sockaddr_in sin;
195         if(!(he=gethostbyname(host))) {
196                 snprintf(connection->errorStr, sizeof(connection->errorStr),
197                          "host \"%s\" not found",host);
198                 connection->error = MPD_ERROR_UNKHOST;
199                 return -1;
200         }
202         memset(&sin,0,sizeof(struct sockaddr_in));
203         /*dest.sin_family = he->h_addrtype;*/
204         sin.sin_family = AF_INET;
205         sin.sin_port = htons(port);
207         switch(he->h_addrtype) {
208         case AF_INET:
209                 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
210                                 he->h_length);
211                 dest = (struct sockaddr *)&sin;
212                 destlen = sizeof(struct sockaddr_in);
213                 break;
214         default:
215                 strcpy(connection->errorStr,"address type is not IPv4\n");
216                 connection->error = MPD_ERROR_SYSTEM;
217                 return -1;
218                 break;
219         }
221         if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
222                 strcpy(connection->errorStr,"problems creating socket");
223                 connection->error = MPD_ERROR_SYSTEM;
224                 return -1;
225         }
227         mpd_setConnectionTimeout(connection,timeout);
229         /* connect stuff */
230         if (do_connect_fail(connection, dest, destlen)) {
231                 snprintf(connection->errorStr, sizeof(connection->errorStr),
232                          "problems connecting to \"%s\" on port"
233                          " %i",host,port);
234                 connection->error = MPD_ERROR_CONNPORT;
235                 return -1;
236         }
238         return 0;
240 #endif /* !MPD_HAVE_GAI */
242 const char *const mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
244         "Artist",
245         "Album",
246         "Title",
247         "Track",
248         "Name",
249         "Genre",
250         "Date",
251         "Composer",
252         "Performer",
253         "Comment",
254         "Disc",
255         "filename"
256 };
258 static char * mpd_sanitizeArg(const char * arg) {
259         size_t i;
260         char * ret;
261         register const char *c;
262         register char *rc;
264         /* instead of counting in that loop above, just
265          * use a bit more memory and half running time
266          */
267         ret = malloc(strlen(arg) * 2 + 1);
269         c = arg;
270         rc = ret;
271         for(i = strlen(arg)+1; i != 0; --i) {
272                 if(*c=='"' || *c=='\\')
273                         *rc++ = '\\';
274                 *(rc++) = *(c++);
275         }
277         return ret;
280 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
282         mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
284         ret->name = str_pool_get(name);
285         ret->value = str_pool_get(value);
287         return ret;
290 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
291         str_pool_put(re->name);
292         str_pool_put(re->value);
293         free(re);
296 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
297         connection->timeout.tv_sec = (int)timeout;
298         connection->timeout.tv_usec = (int)(timeout*1e6 -
299                                             connection->timeout.tv_sec*1000000 +
300                                             0.5);
303 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
304                             char * output) {
305         char * tmp;
306         char * test;
307         int i;
309         if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
310                 snprintf(connection->errorStr, sizeof(connection->errorStr),
311                          "mpd not running on port %i on host \"%s\"",
312                          port,host);
313                 connection->error = MPD_ERROR_NOTMPD;
314                 return 1;
315         }
317         tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
319         for(i=0;i<3;i++) {
320                 if(tmp) connection->version[i] = strtol(tmp,&test,10);
322                 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
323                         snprintf(connection->errorStr, sizeof(connection->errorStr),
324                                  "error parsing version number at "
325                                  "\"%s\"",
326                                  &output[strlen(MPD_WELCOME_MESSAGE)]);
327                         connection->error = MPD_ERROR_NOTMPD;
328                         return 1;
329                 }
330                 tmp = ++test;
331         }
333         return 0;
336 #ifndef WIN32
337 static int mpd_connect_un(mpd_Connection * connection,
338                           const char * host, float timeout)
340         int error, flags;
341         size_t path_length;
342         struct sockaddr_un sun;
344         path_length = strlen(host);
345         if (path_length >= sizeof(sun.sun_path)) {
346                 strcpy(connection->errorStr, "unix socket path is too long");
347                 connection->error = MPD_ERROR_UNKHOST;
348                 return -1;
349         }
351         sun.sun_family = AF_UNIX;
352         memcpy(sun.sun_path, host, path_length + 1);
354         connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
355         if (connection->sock < 0) {
356                 strcpy(connection->errorStr, "problems creating socket");
357                 connection->error = MPD_ERROR_SYSTEM;
358                 return -1;
359         }
361         mpd_setConnectionTimeout(connection, timeout);
363         flags = fcntl(connection->sock, F_GETFL, 0);
364         fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
366         error = connect(connection->sock, (struct sockaddr*)&sun, sizeof(sun));
367         if (error < 0) {
368                 /* try the next address family */
369                 close(connection->sock);
370                 connection->sock = 0;
372                 snprintf(connection->errorStr, sizeof(connection->errorStr),
373                          "problems connecting to \"%s\": %s",
374                          host, strerror(errno));
375                 connection->error = MPD_ERROR_CONNPORT;
376                 return -1;
377         }
379         return 0;
381 #endif /* WIN32 */
383 /**
384  * Wait for the socket to become readable.
385  */
386 static int mpd_wait(mpd_Connection *connection)
388         struct timeval tv;
389         fd_set fds;
390         int ret;
392         while (1) {
393                 tv = connection->timeout;
394                 FD_ZERO(&fds);
395                 FD_SET(connection->sock, &fds);
397                 ret = select(connection->sock + 1, &fds, NULL, NULL, &tv);
398                 if (ret > 0)
399                         return 0;
401                 if (ret == 0 || !SELECT_ERRNO_IGNORE)
402                         return -1;
403         }
406 /**
407  * Wait until the socket is connected and check its result.  Returns 1
408  * on success, 0 on timeout, -errno on error.
409  */
410 static int mpd_wait_connected(mpd_Connection *connection)
412         int ret;
413         int s_err = 0;
414         socklen_t s_err_size = sizeof(s_err);
416         ret = mpd_wait(connection);
417         if (ret < 0)
418                 return 0;
420         ret = getsockopt(connection->sock, SOL_SOCKET, SO_ERROR,
421                          (char*)&s_err, &s_err_size);
422         if (ret < 0)
423                 return -errno;
425         if (s_err != 0)
426                 return -s_err;
428         return 1;
431 /**
432  * Attempt to read data from the socket into the input buffer.
433  * Returns 0 on success, -1 on error.
434  */
435 static int mpd_recv(mpd_Connection *connection)
437         int ret;
438         ssize_t nbytes;
440         assert(connection != NULL);
441         assert(connection->buflen <= sizeof(connection->buffer));
442         assert(connection->bufstart <= connection->buflen);
444         if (connection->buflen >= sizeof(connection->buffer)) {
445                 /* delete consumed data from beginning of buffer */
446                 connection->buflen -= connection->bufstart;
447                 memmove(connection->buffer,
448                         connection->buffer + connection->bufstart,
449                         connection->buflen);
450                 connection->bufstart = 0;
451         }
453         if (connection->buflen >= sizeof(connection->buffer)) {
454                 strcpy(connection->errorStr, "buffer overrun");
455                 connection->error = MPD_ERROR_BUFFEROVERRUN;
456                 connection->doneProcessing = 1;
457                 connection->doneListOk = 0;
458                 return -1;
459         }
461         while (1) {
462                 ret = mpd_wait(connection);
463                 if (ret < 0) {
464                         strcpy(connection->errorStr, "connection timeout");
465                         connection->error = MPD_ERROR_TIMEOUT;
466                         connection->doneProcessing = 1;
467                         connection->doneListOk = 0;
468                         return -1;
469                 }
471                 nbytes = read(connection->sock,
472                               connection->buffer + connection->buflen,
473                               sizeof(connection->buffer) - connection->buflen);
474                 if (nbytes > 0) {
475                         connection->buflen += nbytes;
476                         return 0;
477                 }
479                 if (nbytes == 0 || !SENDRECV_ERRNO_IGNORE) {
480                         strcpy(connection->errorStr, "connection closed");
481                         connection->error = MPD_ERROR_CONNCLOSED;
482                         connection->doneProcessing = 1;
483                         connection->doneListOk = 0;
484                         return -1;
485                 }
486         }
489 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
490         int err;
491         char * rt;
492         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
494         connection->buflen = 0;
495         connection->bufstart = 0;
496         mpd_clearError(connection);
497         connection->doneProcessing = 0;
498         connection->commandList = 0;
499         connection->listOks = 0;
500         connection->doneListOk = 0;
501         connection->returnElement = NULL;
502         connection->request = NULL;
504         if (winsock_dll_error(connection))
505                 return connection;
507 #ifndef WIN32
508         if (host[0] == '/')
509                 err = mpd_connect_un(connection, host, timeout);
510         else
511 #endif
512                 err = mpd_connect(connection, host, port, timeout);
513         if (err < 0)
514                 return connection;
516         err = mpd_wait_connected(connection);
517         if (err == 0) {
518                 snprintf(connection->errorStr, sizeof(connection->errorStr),
519                          "timeout in attempting to get a response from"
520                          " \"%s\" on port %i",host,port);
521                 connection->error = MPD_ERROR_NORESPONSE;
522                 return connection;
523         } else if (err < 0) {
524                 snprintf(connection->errorStr,
525                          sizeof(connection->errorStr),
526                          "problems connecting to \"%s\" on port %i: %s",
527                          host, port, strerror(-err));
528                 connection->error = MPD_ERROR_CONNPORT;
529                 return connection;
530         }
532         while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
533                 err = mpd_recv(connection);
534                 if (err < 0)
535                         return connection;
536         }
538         *rt = '\0';
539         if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
540                 connection->doneProcessing = 1;
542         connection->buflen -= rt + 1 - connection->buffer;
543         memmove(connection->buffer, rt + 1, connection->buflen);
545         return connection;
548 void mpd_clearError(mpd_Connection * connection) {
549         connection->error = 0;
550         connection->errorStr[0] = '\0';
553 void mpd_closeConnection(mpd_Connection * connection) {
554         closesocket(connection->sock);
555         if(connection->returnElement) free(connection->returnElement);
556         if(connection->request) free(connection->request);
557         free(connection);
558         WSACleanup();
561 static void mpd_executeCommand(mpd_Connection *connection,
562                                const char *command) {
563         int ret;
564         struct timeval tv;
565         fd_set fds;
566         const char *commandPtr = command;
567         int commandLen = strlen(command);
569         if (!connection->doneProcessing && !connection->commandList) {
570                 strcpy(connection->errorStr,
571                        "not done processing current command");
572                 connection->error = 1;
573                 return;
574         }
576         mpd_clearError(connection);
578         FD_ZERO(&fds);
579         FD_SET(connection->sock,&fds);
580         tv.tv_sec = connection->timeout.tv_sec;
581         tv.tv_usec = connection->timeout.tv_usec;
583         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
584                         (ret==-1 && SELECT_ERRNO_IGNORE)) {
585                 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
586                 if(ret<=0)
587                 {
588                         if (SENDRECV_ERRNO_IGNORE) continue;
589                         snprintf(connection->errorStr, sizeof(connection->errorStr),
590                                  "problems giving command \"%s\"",command);
591                         connection->error = MPD_ERROR_SENDING;
592                         return;
593                 }
594                 else {
595                         commandPtr+=ret;
596                         commandLen-=ret;
597                 }
599                 if(commandLen<=0) break;
600         }
602         if(commandLen>0) {
603                 perror("");
604                 snprintf(connection->errorStr, sizeof(connection->errorStr),
605                          "timeout sending command \"%s\"",command);
606                 connection->error = MPD_ERROR_TIMEOUT;
607                 return;
608         }
610         if(!connection->commandList) connection->doneProcessing = 0;
611         else if(connection->commandList == COMMAND_LIST_OK) {
612                 connection->listOks++;
613         }
616 static void mpd_getNextReturnElement(mpd_Connection * connection) {
617         char * output = NULL;
618         char * rt = NULL;
619         char * name = NULL;
620         char * value = NULL;
621         char * tok = NULL;
622         int err;
623         int pos;
625         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
626         connection->returnElement = NULL;
628         if (connection->doneProcessing ||
629             (connection->listOks && connection->doneListOk)) {
630                 strcpy(connection->errorStr,"already done processing current command");
631                 connection->error = 1;
632                 return;
633         }
635         while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
636                              connection->buflen - connection->bufstart))) {
637                 err = mpd_recv(connection);
638                 if (err < 0)
639                         return;
640         }
642         *rt = '\0';
643         output = connection->buffer+connection->bufstart;
644         connection->bufstart = rt - connection->buffer + 1;
646         if(strcmp(output,"OK")==0) {
647                 if(connection->listOks > 0) {
648                         strcpy(connection->errorStr, "expected more list_OK's");
649                         connection->error = 1;
650                 }
651                 connection->listOks = 0;
652                 connection->doneProcessing = 1;
653                 connection->doneListOk = 0;
654                 return;
655         }
657         if(strcmp(output, "list_OK") == 0) {
658                 if(!connection->listOks) {
659                         strcpy(connection->errorStr,
660                                         "got an unexpected list_OK");
661                         connection->error = 1;
662                 }
663                 else {
664                         connection->doneListOk = 1;
665                         connection->listOks--;
666                 }
667                 return;
668         }
670         if(strncmp(output,"ACK",strlen("ACK"))==0) {
671                 size_t length = strlen(output);
672                 char * test;
673                 char * needle;
674                 int val;
676                 if (length >= sizeof(connection->errorStr))
677                         length = sizeof(connection->errorStr) - 1;
679                 memcpy(connection->errorStr, output, length);
680                 connection->errorStr[length] = 0;
681                 connection->error = MPD_ERROR_ACK;
682                 connection->errorCode = MPD_ACK_ERROR_UNK;
683                 connection->errorAt = MPD_ERROR_AT_UNK;
684                 connection->doneProcessing = 1;
685                 connection->doneListOk = 0;
687                 needle = strchr(output, '[');
688                 if(!needle) return;
689                 val = strtol(needle+1, &test, 10);
690                 if(*test != '@') return;
691                 connection->errorCode = val;
692                 val = strtol(test+1, &test, 10);
693                 if(*test != ']') return;
694                 connection->errorAt = val;
695                 return;
696         }
698         tok = strchr(output, ':');
699         if (!tok) return;
700         pos = tok - output;
701         value = ++tok;
702         name = output;
703         name[pos] = '\0';
705         if(value[0]==' ') {
706                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
707         }
708         else {
709                 snprintf(connection->errorStr, sizeof(connection->errorStr),
710                          "error parsing: %s:%s",name,value);
711                 connection->error = 1;
712         }
715 void mpd_finishCommand(mpd_Connection * connection) {
716         while(!connection->doneProcessing) {
717                 if(connection->doneListOk) connection->doneListOk = 0;
718                 mpd_getNextReturnElement(connection);
719         }
722 static void mpd_finishListOkCommand(mpd_Connection * connection) {
723         while(!connection->doneProcessing && connection->listOks &&
724                         !connection->doneListOk)
725         {
726                 mpd_getNextReturnElement(connection);
727         }
730 int mpd_nextListOkCommand(mpd_Connection * connection) {
731         mpd_finishListOkCommand(connection);
732         if(!connection->doneProcessing) connection->doneListOk = 0;
733         if(connection->listOks == 0 || connection->doneProcessing) return -1;
734         return 0;
737 void mpd_sendStatusCommand(mpd_Connection * connection) {
738         mpd_executeCommand(connection,"status\n");
741 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
742         mpd_Status * status;
744         /*mpd_executeCommand(connection,"status\n");
746         if(connection->error) return NULL;*/
748         if(connection->doneProcessing || (connection->listOks &&
749            connection->doneListOk))
750         {
751                 return NULL;
752         }
754         if(!connection->returnElement) mpd_getNextReturnElement(connection);
756         status = malloc(sizeof(mpd_Status));
757         status->volume = -1;
758         status->repeat = 0;
759         status->random = 0;
760         status->playlist = -1;
761         status->playlistLength = -1;
762         status->state = -1;
763         status->song = 0;
764         status->songid = 0;
765         status->elapsedTime = 0;
766         status->totalTime = 0;
767         status->bitRate = 0;
768         status->sampleRate = 0;
769         status->bits = 0;
770         status->channels = 0;
771         status->crossfade = -1;
772         status->error = NULL;
773         status->updatingDb = 0;
775         if(connection->error) {
776                 free(status);
777                 return NULL;
778         }
779         while(connection->returnElement) {
780                 mpd_ReturnElement * re = connection->returnElement;
781                 if(strcmp(re->name,"volume")==0) {
782                         status->volume = atoi(re->value);
783                 }
784                 else if(strcmp(re->name,"repeat")==0) {
785                         status->repeat = atoi(re->value);
786                 }
787                 else if(strcmp(re->name,"random")==0) {
788                         status->random = atoi(re->value);
789                 }
790                 else if(strcmp(re->name,"playlist")==0) {
791                         status->playlist = strtol(re->value,NULL,10);
792                 }
793                 else if(strcmp(re->name,"playlistlength")==0) {
794                         status->playlistLength = atoi(re->value);
795                 }
796                 else if(strcmp(re->name,"bitrate")==0) {
797                         status->bitRate = atoi(re->value);
798                 }
799                 else if(strcmp(re->name,"state")==0) {
800                         if(strcmp(re->value,"play")==0) {
801                                 status->state = MPD_STATUS_STATE_PLAY;
802                         }
803                         else if(strcmp(re->value,"stop")==0) {
804                                 status->state = MPD_STATUS_STATE_STOP;
805                         }
806                         else if(strcmp(re->value,"pause")==0) {
807                                 status->state = MPD_STATUS_STATE_PAUSE;
808                         }
809                         else {
810                                 status->state = MPD_STATUS_STATE_UNKNOWN;
811                         }
812                 }
813                 else if(strcmp(re->name,"song")==0) {
814                         status->song = atoi(re->value);
815                 }
816                 else if(strcmp(re->name,"songid")==0) {
817                         status->songid = atoi(re->value);
818                 }
819                 else if(strcmp(re->name,"time")==0) {
820                         char * tok = strchr(re->value,':');
821                         /* the second strchr below is a safety check */
822                         if (tok && (strchr(tok,0) > (tok+1))) {
823                                 /* atoi stops at the first non-[0-9] char: */
824                                 status->elapsedTime = atoi(re->value);
825                                 status->totalTime = atoi(tok+1);
826                         }
827                 }
828                 else if(strcmp(re->name,"error")==0) {
829                         status->error = strdup(re->value);
830                 }
831                 else if(strcmp(re->name,"xfade")==0) {
832                         status->crossfade = atoi(re->value);
833                 }
834                 else if(strcmp(re->name,"updating_db")==0) {
835                         status->updatingDb = atoi(re->value);
836                 }
837                 else if(strcmp(re->name,"audio")==0) {
838                         char * tok = strchr(re->value,':');
839                         if (tok && (strchr(tok,0) > (tok+1))) {
840                                 status->sampleRate = atoi(re->value);
841                                 status->bits = atoi(++tok);
842                                 tok = strchr(tok,':');
843                                 if (tok && (strchr(tok,0) > (tok+1)))
844                                         status->channels = atoi(tok+1);
845                         }
846                 }
848                 mpd_getNextReturnElement(connection);
849                 if(connection->error) {
850                         free(status);
851                         return NULL;
852                 }
853         }
855         if(connection->error) {
856                 free(status);
857                 return NULL;
858         }
859         else if(status->state<0) {
860                 strcpy(connection->errorStr,"state not found");
861                 connection->error = 1;
862                 free(status);
863                 return NULL;
864         }
866         return status;
869 void mpd_freeStatus(mpd_Status * status) {
870         if(status->error) free(status->error);
871         free(status);
874 void mpd_sendStatsCommand(mpd_Connection * connection) {
875         mpd_executeCommand(connection,"stats\n");
878 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
879         mpd_Stats * stats;
881         /*mpd_executeCommand(connection,"stats\n");
883         if(connection->error) return NULL;*/
885         if(connection->doneProcessing || (connection->listOks &&
886            connection->doneListOk))
887         {
888                 return NULL;
889         }
891         if(!connection->returnElement) mpd_getNextReturnElement(connection);
893         stats = malloc(sizeof(mpd_Stats));
894         stats->numberOfArtists = 0;
895         stats->numberOfAlbums = 0;
896         stats->numberOfSongs = 0;
897         stats->uptime = 0;
898         stats->dbUpdateTime = 0;
899         stats->playTime = 0;
900         stats->dbPlayTime = 0;
902         if(connection->error) {
903                 free(stats);
904                 return NULL;
905         }
906         while(connection->returnElement) {
907                 mpd_ReturnElement * re = connection->returnElement;
908                 if(strcmp(re->name,"artists")==0) {
909                         stats->numberOfArtists = atoi(re->value);
910                 }
911                 else if(strcmp(re->name,"albums")==0) {
912                         stats->numberOfAlbums = atoi(re->value);
913                 }
914                 else if(strcmp(re->name,"songs")==0) {
915                         stats->numberOfSongs = atoi(re->value);
916                 }
917                 else if(strcmp(re->name,"uptime")==0) {
918                         stats->uptime = strtol(re->value,NULL,10);
919                 }
920                 else if(strcmp(re->name,"db_update")==0) {
921                         stats->dbUpdateTime = strtol(re->value,NULL,10);
922                 }
923                 else if(strcmp(re->name,"playtime")==0) {
924                         stats->playTime = strtol(re->value,NULL,10);
925                 }
926                 else if(strcmp(re->name,"db_playtime")==0) {
927                         stats->dbPlayTime = strtol(re->value,NULL,10);
928                 }
930                 mpd_getNextReturnElement(connection);
931                 if(connection->error) {
932                         free(stats);
933                         return NULL;
934                 }
935         }
937         if(connection->error) {
938                 free(stats);
939                 return NULL;
940         }
942         return stats;
945 void mpd_freeStats(mpd_Stats * stats) {
946         free(stats);
949 static void mpd_initDirectory(mpd_Directory * directory) {
950         directory->path = NULL;
953 static void mpd_finishDirectory(mpd_Directory * directory) {
954         if (directory->path)
955                 str_pool_put(directory->path);
958 mpd_Directory * mpd_newDirectory(void) {
959         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
961         mpd_initDirectory(directory);
963         return directory;
966 void mpd_freeDirectory(mpd_Directory * directory) {
967         mpd_finishDirectory(directory);
969         free(directory);
972 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
973         mpd_Directory * ret = mpd_newDirectory();
975         if (directory->path)
976                 ret->path = str_pool_dup(directory->path);
978         return ret;
981 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
982         playlist->path = NULL;
985 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
986         if (playlist->path)
987                 str_pool_put(playlist->path);
990 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
991         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
993         mpd_initPlaylistFile(playlist);
995         return playlist;
998 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
999         mpd_finishPlaylistFile(playlist);
1000         free(playlist);
1003 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1004         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1006         if (playlist->path)
1007                 ret->path = str_pool_dup(playlist->path);
1009         return ret;
1012 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
1013         entity->info.directory = NULL;
1016 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1017         if(entity->info.directory) {
1018                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1019                         mpd_freeDirectory(entity->info.directory);
1020                 }
1021                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1022                         mpd_freeSong(entity->info.song);
1023                 }
1024                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1025                         mpd_freePlaylistFile(entity->info.playlistFile);
1026                 }
1027         }
1030 mpd_InfoEntity * mpd_newInfoEntity(void) {
1031         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
1033         mpd_initInfoEntity(entity);
1035         return entity;
1038 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1039         mpd_finishInfoEntity(entity);
1040         free(entity);
1043 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1044         mpd_executeCommand(connection,command);
1047 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1048         mpd_InfoEntity * entity = NULL;
1050         if(connection->doneProcessing || (connection->listOks &&
1051            connection->doneListOk)) {
1052                 return NULL;
1053         }
1055         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1057         if(connection->returnElement) {
1058                 if(strcmp(connection->returnElement->name,"file")==0) {
1059                         entity = mpd_newInfoEntity();
1060                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1061                         entity->info.song = mpd_newSong();
1062                         entity->info.song->file =
1063                                 str_pool_dup(connection->returnElement->value);
1064                 }
1065                 else if(strcmp(connection->returnElement->name,
1066                                         "directory")==0) {
1067                         entity = mpd_newInfoEntity();
1068                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1069                         entity->info.directory = mpd_newDirectory();
1070                         entity->info.directory->path =
1071                                 str_pool_dup(connection->returnElement->value);
1072                 }
1073                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1074                         entity = mpd_newInfoEntity();
1075                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1076                         entity->info.playlistFile = mpd_newPlaylistFile();
1077                         entity->info.playlistFile->path =
1078                                 str_pool_dup(connection->returnElement->value);
1079                 }
1080                 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1081                         entity = mpd_newInfoEntity();
1082                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1083                         entity->info.song = mpd_newSong();
1084                         entity->info.song->pos = atoi(connection->returnElement->value);
1085                 }
1086                 else {
1087                         connection->error = 1;
1088                         strcpy(connection->errorStr,"problem parsing song info");
1089                         return NULL;
1090                 }
1091         }
1092         else return NULL;
1094         mpd_getNextReturnElement(connection);
1095         while(connection->returnElement) {
1096                 mpd_ReturnElement * re = connection->returnElement;
1098                 if(strcmp(re->name,"file")==0) return entity;
1099                 else if(strcmp(re->name,"directory")==0) return entity;
1100                 else if(strcmp(re->name,"playlist")==0) return entity;
1101                 else if(strcmp(re->name,"cpos")==0) return entity;
1103                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1104                                 strlen(re->value)) {
1105                         if(!entity->info.song->artist &&
1106                                         strcmp(re->name,"Artist")==0) {
1107                                 entity->info.song->artist = str_pool_dup(re->value);
1108                         }
1109                         else if(!entity->info.song->album &&
1110                                         strcmp(re->name,"Album")==0) {
1111                                 entity->info.song->album = str_pool_dup(re->value);
1112                         }
1113                         else if(!entity->info.song->title &&
1114                                         strcmp(re->name,"Title")==0) {
1115                                 entity->info.song->title = str_pool_dup(re->value);
1116                         }
1117                         else if(!entity->info.song->track &&
1118                                         strcmp(re->name,"Track")==0) {
1119                                 entity->info.song->track = str_pool_dup(re->value);
1120                         }
1121                         else if(!entity->info.song->name &&
1122                                         strcmp(re->name,"Name")==0) {
1123                                 entity->info.song->name = str_pool_dup(re->value);
1124                         }
1125                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1126                                         strcmp(re->name,"Time")==0) {
1127                                 entity->info.song->time = atoi(re->value);
1128                         }
1129                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1130                                         strcmp(re->name,"Pos")==0) {
1131                                 entity->info.song->pos = atoi(re->value);
1132                         }
1133                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
1134                                         strcmp(re->name,"Id")==0) {
1135                                 entity->info.song->id = atoi(re->value);
1136                         }
1137                         else if(!entity->info.song->date &&
1138                                         strcmp(re->name, "Date") == 0) {
1139                                 entity->info.song->date = str_pool_dup(re->value);
1140                         }
1141                         else if(!entity->info.song->genre &&
1142                                         strcmp(re->name, "Genre") == 0) {
1143                                 entity->info.song->genre = str_pool_dup(re->value);
1144                         }
1145                         else if(!entity->info.song->composer &&
1146                                         strcmp(re->name, "Composer") == 0) {
1147                                 entity->info.song->composer = str_pool_dup(re->value);
1148                         }
1149                         else if(!entity->info.song->disc &&
1150                                         strcmp(re->name, "Disc") == 0) {
1151                                 entity->info.song->disc = str_pool_dup(re->value);
1152                         }
1153                         else if(!entity->info.song->comment &&
1154                                         strcmp(re->name, "Comment") == 0) {
1155                                 entity->info.song->comment = str_pool_dup(re->value);
1156                         }
1157                 }
1158                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1159                 }
1160                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1161                 }
1163                 mpd_getNextReturnElement(connection);
1164         }
1166         return entity;
1169 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1170                 const char * name)
1172         if(connection->doneProcessing || (connection->listOks &&
1173                                 connection->doneListOk))
1174         {
1175                 return NULL;
1176         }
1178         mpd_getNextReturnElement(connection);
1179         while(connection->returnElement) {
1180                 mpd_ReturnElement * re = connection->returnElement;
1182                 if(strcmp(re->name,name)==0) return strdup(re->value);
1183                 mpd_getNextReturnElement(connection);
1184         }
1186         return NULL;
1189 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1190         if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1191         {
1192                 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1193         }
1194         return NULL;
1197 char * mpd_getNextArtist(mpd_Connection * connection) {
1198         return mpd_getNextReturnElementNamed(connection,"Artist");
1201 char * mpd_getNextAlbum(mpd_Connection * connection) {
1202         return mpd_getNextReturnElementNamed(connection,"Album");
1205 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1206         char * string = malloc(strlen("playlistinfo")+25);
1207         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1208         mpd_sendInfoCommand(connection,string);
1209         free(string);
1212 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1213         char * string = malloc(strlen("playlistid")+25);
1214         sprintf(string, "playlistid \"%i\"\n", id);
1215         mpd_sendInfoCommand(connection, string);
1216         free(string);
1219 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1220         char * string = malloc(strlen("plchanges")+25);
1221         sprintf(string,"plchanges \"%lld\"\n",playlist);
1222         mpd_sendInfoCommand(connection,string);
1223         free(string);
1226 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1227         char * string = malloc(strlen("plchangesposid")+25);
1228         sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1229         mpd_sendInfoCommand(connection,string);
1230         free(string);
1233 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1234         char * sDir = mpd_sanitizeArg(dir);
1235         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1236         sprintf(string,"listall \"%s\"\n",sDir);
1237         mpd_sendInfoCommand(connection,string);
1238         free(string);
1239         free(sDir);
1242 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1243         char * sDir = mpd_sanitizeArg(dir);
1244         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1245         sprintf(string,"listallinfo \"%s\"\n",sDir);
1246         mpd_sendInfoCommand(connection,string);
1247         free(string);
1248         free(sDir);
1251 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1252         char * sDir = mpd_sanitizeArg(dir);
1253         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1254         sprintf(string,"lsinfo \"%s\"\n",sDir);
1255         mpd_sendInfoCommand(connection,string);
1256         free(string);
1257         free(sDir);
1260 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1261         mpd_executeCommand(connection,"currentsong\n");
1264 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1265                 const char * str)
1267         char st[10];
1268         char * string;
1269         char * sanitStr = mpd_sanitizeArg(str);
1270         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1271         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1272         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1273         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1274         else {
1275                 connection->error = 1;
1276                 strcpy(connection->errorStr,"unknown table for search");
1277                 return;
1278         }
1279         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1280         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1281         mpd_sendInfoCommand(connection,string);
1282         free(string);
1283         free(sanitStr);
1286 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1287                 const char * str)
1289         char st[10];
1290         char * string;
1291         char * sanitStr = mpd_sanitizeArg(str);
1292         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1293         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1294         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1295         else {
1296                 connection->error = 1;
1297                 strcpy(connection->errorStr,"unknown table for find");
1298                 return;
1299         }
1300         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1301         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1302         mpd_sendInfoCommand(connection,string);
1303         free(string);
1304         free(sanitStr);
1307 void mpd_sendListCommand(mpd_Connection * connection, int table,
1308                 const char * arg1)
1310         char st[10];
1311         char * string;
1312         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1313         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1314         else {
1315                 connection->error = 1;
1316                 strcpy(connection->errorStr,"unknown table for list");
1317                 return;
1318         }
1319         if(arg1) {
1320                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1321                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1322                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1323                 free(sanitArg1);
1324         }
1325         else {
1326                 string = malloc(strlen("list")+strlen(st)+3);
1327                 sprintf(string,"list %s\n",st);
1328         }
1329         mpd_sendInfoCommand(connection,string);
1330         free(string);
1333 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1334         char * sFile = mpd_sanitizeArg(file);
1335         char * string = malloc(strlen("add")+strlen(sFile)+5);
1336         sprintf(string,"add \"%s\"\n",sFile);
1337         mpd_executeCommand(connection,string);
1338         free(string);
1339         free(sFile);
1342 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1343         char * string = malloc(strlen("delete")+25);
1344         sprintf(string,"delete \"%i\"\n",songPos);
1345         mpd_sendInfoCommand(connection,string);
1346         free(string);
1349 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1350         char * string = malloc(strlen("deleteid")+25);
1351         sprintf(string, "deleteid \"%i\"\n", id);
1352         mpd_sendInfoCommand(connection,string);
1353         free(string);
1356 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1357         char * sName = mpd_sanitizeArg(name);
1358         char * string = malloc(strlen("save")+strlen(sName)+5);
1359         sprintf(string,"save \"%s\"\n",sName);
1360         mpd_executeCommand(connection,string);
1361         free(string);
1362         free(sName);
1365 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1366         char * sName = mpd_sanitizeArg(name);
1367         char * string = malloc(strlen("load")+strlen(sName)+5);
1368         sprintf(string,"load \"%s\"\n",sName);
1369         mpd_executeCommand(connection,string);
1370         free(string);
1371         free(sName);
1374 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1375         char * sName = mpd_sanitizeArg(name);
1376         char * string = malloc(strlen("rm")+strlen(sName)+5);
1377         sprintf(string,"rm \"%s\"\n",sName);
1378         mpd_executeCommand(connection,string);
1379         free(string);
1380         free(sName);
1383 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1384         mpd_executeCommand(connection,"shuffle\n");
1387 void mpd_sendClearCommand(mpd_Connection * connection) {
1388         mpd_executeCommand(connection,"clear\n");
1391 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1392         char * string = malloc(strlen("play")+25);
1393         sprintf(string,"play \"%i\"\n",songPos);
1394         mpd_sendInfoCommand(connection,string);
1395         free(string);
1398 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1399         char * string = malloc(strlen("playid")+25);
1400         sprintf(string,"playid \"%i\"\n",id);
1401         mpd_sendInfoCommand(connection,string);
1402         free(string);
1405 void mpd_sendStopCommand(mpd_Connection * connection) {
1406         mpd_executeCommand(connection,"stop\n");
1409 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1410         char * string = malloc(strlen("pause")+25);
1411         sprintf(string,"pause \"%i\"\n",pauseMode);
1412         mpd_executeCommand(connection,string);
1413         free(string);
1416 void mpd_sendNextCommand(mpd_Connection * connection) {
1417         mpd_executeCommand(connection,"next\n");
1420 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1421         char * string = malloc(strlen("move")+25);
1422         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1423         mpd_sendInfoCommand(connection,string);
1424         free(string);
1427 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1428         char * string = malloc(strlen("moveid")+25);
1429         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1430         mpd_sendInfoCommand(connection,string);
1431         free(string);
1434 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1435         char * string = malloc(strlen("swap")+25);
1436         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1437         mpd_sendInfoCommand(connection,string);
1438         free(string);
1441 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1442         char * string = malloc(strlen("swapid")+25);
1443         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1444         mpd_sendInfoCommand(connection,string);
1445         free(string);
1448 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1449         char * string = malloc(strlen("seek")+25);
1450         sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1451         mpd_sendInfoCommand(connection,string);
1452         free(string);
1455 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1456         char * string = malloc(strlen("seekid")+25);
1457         sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1458         mpd_sendInfoCommand(connection,string);
1459         free(string);
1462 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1463         char *sPath = mpd_sanitizeArg(path);
1464         char * string = malloc(strlen("update")+strlen(sPath)+5);
1465         sprintf(string,"update \"%s\"\n",sPath);
1466         mpd_sendInfoCommand(connection,string);
1467         free(string);
1468         free(sPath);
1471 int mpd_getUpdateId(mpd_Connection * connection) {
1472         char * jobid;
1473         int ret = 0;
1475         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1476         if(jobid) {
1477                 ret = atoi(jobid);
1478                 free(jobid);
1479         }
1481         return ret;
1484 void mpd_sendPrevCommand(mpd_Connection * connection) {
1485         mpd_executeCommand(connection,"previous\n");
1488 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1489         char * string = malloc(strlen("repeat")+25);
1490         sprintf(string,"repeat \"%i\"\n",repeatMode);
1491         mpd_executeCommand(connection,string);
1492         free(string);
1495 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1496         char * string = malloc(strlen("random")+25);
1497         sprintf(string,"random \"%i\"\n",randomMode);
1498         mpd_executeCommand(connection,string);
1499         free(string);
1502 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1503         char * string = malloc(strlen("setvol")+25);
1504         sprintf(string,"setvol \"%i\"\n",volumeChange);
1505         mpd_executeCommand(connection,string);
1506         free(string);
1509 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1510         char * string = malloc(strlen("volume")+25);
1511         sprintf(string,"volume \"%i\"\n",volumeChange);
1512         mpd_executeCommand(connection,string);
1513         free(string);
1516 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1517         char * string = malloc(strlen("crossfade")+25);
1518         sprintf(string,"crossfade \"%i\"\n",seconds);
1519         mpd_executeCommand(connection,string);
1520         free(string);
1523 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1524         char * sPass = mpd_sanitizeArg(pass);
1525         char * string = malloc(strlen("password")+strlen(sPass)+5);
1526         sprintf(string,"password \"%s\"\n",sPass);
1527         mpd_executeCommand(connection,string);
1528         free(string);
1529         free(sPass);
1532 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1533         if(connection->commandList) {
1534                 strcpy(connection->errorStr,"already in command list mode");
1535                 connection->error = 1;
1536                 return;
1537         }
1538         connection->commandList = COMMAND_LIST;
1539         mpd_executeCommand(connection,"command_list_begin\n");
1542 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1543         if(connection->commandList) {
1544                 strcpy(connection->errorStr,"already in command list mode");
1545                 connection->error = 1;
1546                 return;
1547         }
1548         connection->commandList = COMMAND_LIST_OK;
1549         mpd_executeCommand(connection,"command_list_ok_begin\n");
1550         connection->listOks = 0;
1553 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1554         if(!connection->commandList) {
1555                 strcpy(connection->errorStr,"not in command list mode");
1556                 connection->error = 1;
1557                 return;
1558         }
1559         connection->commandList = 0;
1560         mpd_executeCommand(connection,"command_list_end\n");
1563 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1564         mpd_executeCommand(connection,"outputs\n");
1567 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1568         mpd_OutputEntity * output = NULL;
1570         if(connection->doneProcessing || (connection->listOks &&
1571                                 connection->doneListOk))
1572         {
1573                 return NULL;
1574         }
1576         if(connection->error) return NULL;
1578         output = malloc(sizeof(mpd_OutputEntity));
1579         output->id = -10;
1580         output->name = NULL;
1581         output->enabled = 0;
1583         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1585         while(connection->returnElement) {
1586                 mpd_ReturnElement * re = connection->returnElement;
1587                 if(strcmp(re->name,"outputid")==0) {
1588                         if(output!=NULL && output->id>=0) return output;
1589                         output->id = atoi(re->value);
1590                 }
1591                 else if(strcmp(re->name,"outputname")==0) {
1592                         output->name = strdup(re->value);
1593                 }
1594                 else if(strcmp(re->name,"outputenabled")==0) {
1595                         output->enabled = atoi(re->value);
1596                 }
1598                 mpd_getNextReturnElement(connection);
1599                 if(connection->error) {
1600                         free(output);
1601                         return NULL;
1602                 }
1604         }
1606         return output;
1609 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1610         char * string = malloc(strlen("enableoutput")+25);
1611         sprintf(string,"enableoutput \"%i\"\n",outputId);
1612         mpd_executeCommand(connection,string);
1613         free(string);
1616 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1617         char * string = malloc(strlen("disableoutput")+25);
1618         sprintf(string,"disableoutput \"%i\"\n",outputId);
1619         mpd_executeCommand(connection,string);
1620         free(string);
1623 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1624         free(output->name);
1625         free(output);
1628 /**
1629  * mpd_sendNotCommandsCommand
1630  * odd naming, but it gets the not allowed commands
1631  */
1633 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1634         mpd_executeCommand(connection,"notcommands\n");
1637 /**
1638  * mpd_sendCommandsCommand
1639  * odd naming, but it gets the allowed commands
1640  */
1642 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1643         mpd_executeCommand(connection,"commands\n");
1645 /**
1646  * Get the next returned command
1647  */
1648 char * mpd_getNextCommand(mpd_Connection * connection) {
1649         return mpd_getNextReturnElementNamed(connection,"command");
1652 void mpd_startSearch(mpd_Connection * connection,int exact) {
1653         if(connection->request) {
1654                 /* search/find allready in progress */
1655                 /* TODO: set error here?  */
1656                 return;
1657         }
1658         if(exact){
1659                 connection->request = strdup("find");
1660         }
1661         else{
1662                 connection->request = strdup("search");
1663         }
1667 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1668         if(connection->request) {
1669                 /* search/find allready in progress */
1670                 /* TODO: set error here?  */
1671                 return;
1672         }
1673         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1674                 /* set error here */
1675                 return;
1676         }
1678         connection->request = malloc(sizeof(char)*(
1679                                 /* length of the field name */
1680                                 strlen(mpdTagItemKeys[field])+
1681                                 /* "list"+space+\0 */
1682                                 6
1683                                 ));
1684         sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1689 void mpd_addConstraintSearch(mpd_Connection *connection,
1690                 int field,
1691                 char *name)
1693         char *arg = NULL;
1694         if(!connection->request){
1695                 return;
1696         }
1697         if(name == NULL) {
1698                 return;
1699         }
1700         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1701                 return;
1702         }
1703         /* clean up the query */
1704         arg = mpd_sanitizeArg(name);
1705         /* create space for the query */
1706         connection->request = realloc(connection->request, (
1707                          /* length of the old string */
1708                          strlen(connection->request)+
1709                          /* space between */
1710                          1+
1711                          /* length of the field name */
1712                          strlen(mpdTagItemKeys[field])+
1713                          /* space plus starting " */
1714                          2+
1715                          /* length of search term */
1716                          strlen(arg)+
1717                          /* closign " +\0 that is added sprintf */
1718                          2
1719                         )*sizeof(char));
1720         /* and form the query */
1721         sprintf(connection->request, "%s %s \"%s\"",
1722                         connection->request,
1723                         mpdTagItemKeys[field],
1724                         arg);
1725         free(arg);
1729 void mpd_commitSearch(mpd_Connection *connection)
1731         if(connection->request)
1732         {
1733                 int length = strlen(connection->request);
1734                 /* fixing up the string for mpd to like */
1735                 connection->request = realloc(connection->request,
1736                                 (length+        /* old length */
1737                                  2              /* closing \n and \0 */
1738                                 )*sizeof(char));
1739                 connection->request[length] = '\n';
1740                 connection->request[length+1] = '\0';
1741                 /* and off we go */
1742                 mpd_sendInfoCommand(connection, connection->request);
1743                 /* clean up a bit */
1744                 free(connection->request);
1745                 connection->request = NULL;
1746         }
1749 /**
1750  * @param connection a MpdConnection
1751  * @param path  the path to the playlist.
1752  *
1753  * List the content, with full metadata, of a stored playlist.
1754  *
1755  */
1756 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1758         char *arg = mpd_sanitizeArg(path);
1759         char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1760         sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1761         mpd_sendInfoCommand(connection, query);
1762         free(arg);
1763         free(query);
1766 /**
1767  * @param connection a MpdConnection
1768  * @param path  the path to the playlist.
1769  *
1770  * List the content of a stored playlist.
1771  *
1772  */
1773 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1775         char *arg = mpd_sanitizeArg(path);
1776         char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1777         sprintf(query, "listplaylist \"%s\"\n",arg);
1778         mpd_sendInfoCommand(connection, query);
1779         free(arg);
1780         free(query);