Code

1b0851ff125c08b4d8d86dc9806b5af676a6345d
[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 <errno.h>
37 #include <sys/types.h>
38 #include <stdio.h>
39 #include <sys/param.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <fcntl.h>
45 #ifdef WIN32
46 #  include <ws2tcpip.h>
47 #  include <winsock.h>
48 #else
49 #  include <netinet/in.h>
50 #  include <arpa/inet.h>
51 #  include <sys/socket.h>
52 #  include <netdb.h>
53 #endif
55 #ifndef WIN32
56 #include <sys/un.h>
57 #endif
59 #ifndef MSG_DONTWAIT
60 #  define MSG_DONTWAIT 0
61 #endif
63 #ifndef MPD_NO_GAI
64 #  ifdef AI_ADDRCONFIG
65 #    define MPD_HAVE_GAI
66 #  endif
67 #endif
69 #define COMMAND_LIST    1
70 #define COMMAND_LIST_OK 2
72 #ifdef WIN32
73 #  define SELECT_ERRNO_IGNORE   (errno == WSAEINTR || errno == WSAEINPROGRESS)
74 #  define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
75 #else
76 #  define SELECT_ERRNO_IGNORE   (errno == EINTR)
77 #  define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
78 #  define winsock_dll_error(c)  0
79 #  define closesocket(s)        close(s)
80 #  define WSACleanup()          do { /* nothing */ } while (0)
81 #endif
83 #ifdef WIN32
84 static int winsock_dll_error(mpd_Connection *connection)
85 {
86         WSADATA wsaData;
87         if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
88                         LOBYTE(wsaData.wVersion) != 2 ||
89                         HIBYTE(wsaData.wVersion) != 2 ) {
90                 snprintf(connection->errorStr, sizeof(connection->errorStr),
91                          "Could not find usable WinSock DLL.");
92                 connection->error = MPD_ERROR_SYSTEM;
93                 return 1;
94         }
95         return 0;
96 }
98 static int do_connect_fail(mpd_Connection *connection,
99                            const struct sockaddr *serv_addr, int addrlen)
101         int iMode = 1; /* 0 = blocking, else non-blocking */
102         ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
103         return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR
104                         && WSAGetLastError() != WSAEWOULDBLOCK);
106 #else /* !WIN32 (sane operating systems) */
107 static int do_connect_fail(mpd_Connection *connection,
108                            const struct sockaddr *serv_addr, int addrlen)
110         int flags = fcntl(connection->sock, F_GETFL, 0);
111         fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
112         return (connect(connection->sock,serv_addr,addrlen)<0 &&
113                                 errno!=EINPROGRESS);
115 #endif /* !WIN32 */
117 #ifdef MPD_HAVE_GAI
118 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
119                        float timeout)
121         int error;
122         char service[20];
123         struct addrinfo hints;
124         struct addrinfo *res = NULL;
125         struct addrinfo *addrinfo = NULL;
127         /**
128          * Setup hints
129          */
130         hints.ai_flags     = AI_ADDRCONFIG;
131         hints.ai_family    = PF_UNSPEC;
132         hints.ai_socktype  = SOCK_STREAM;
133         hints.ai_protocol  = IPPROTO_TCP;
134         hints.ai_addrlen   = 0;
135         hints.ai_addr      = NULL;
136         hints.ai_canonname = NULL;
137         hints.ai_next      = NULL;
139         snprintf(service, sizeof(service), "%d", port);
141         error = getaddrinfo(host, service, &hints, &addrinfo);
143         if (error) {
144                 snprintf(connection->errorStr, sizeof(connection->errorStr),
145                          "host \"%s\" not found: %s",host, gai_strerror(error));
146                 connection->error = MPD_ERROR_UNKHOST;
147                 return -1;
148         }
150         for (res = addrinfo; res; res = res->ai_next) {
151                 /* create socket */
152                 connection->sock = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
153                 if (connection->sock < 0) {
154                         snprintf(connection->errorStr, sizeof(connection->errorStr),
155                                  "problems creating socket: %s",
156                                  strerror(errno));
157                         connection->error = MPD_ERROR_SYSTEM;
158                         freeaddrinfo(addrinfo);
159                         return -1;
160                 }
162                 mpd_setConnectionTimeout(connection,timeout);
164                 /* connect stuff */
165                 if (do_connect_fail(connection, res->ai_addr, res->ai_addrlen)) {
166                         /* try the next address family */
167                         closesocket(connection->sock);
168                         connection->sock = -1;
169                         continue;
170                 }
171         }
172         freeaddrinfo(addrinfo);
174         if (connection->sock < 0) {
175                 snprintf(connection->errorStr, sizeof(connection->errorStr),
176                          "problems connecting to \"%s\" on port"
177                          " %i: %s",host,port, strerror(errno));
178                 connection->error = MPD_ERROR_CONNPORT;
180                 return -1;
181         }
183         return 0;
185 #else /* !MPD_HAVE_GAI */
186 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
187                        float timeout)
189         struct hostent * he;
190         struct sockaddr * dest;
191         int destlen;
192         struct sockaddr_in sin;
194         if(!(he=gethostbyname(host))) {
195                 snprintf(connection->errorStr, sizeof(connection->errorStr),
196                          "host \"%s\" not found",host);
197                 connection->error = MPD_ERROR_UNKHOST;
198                 return -1;
199         }
201         memset(&sin,0,sizeof(struct sockaddr_in));
202         /*dest.sin_family = he->h_addrtype;*/
203         sin.sin_family = AF_INET;
204         sin.sin_port = htons(port);
206         switch(he->h_addrtype) {
207         case AF_INET:
208                 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
209                                 he->h_length);
210                 dest = (struct sockaddr *)&sin;
211                 destlen = sizeof(struct sockaddr_in);
212                 break;
213         default:
214                 strcpy(connection->errorStr,"address type is not IPv4\n");
215                 connection->error = MPD_ERROR_SYSTEM;
216                 return -1;
217                 break;
218         }
220         if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
221                 strcpy(connection->errorStr,"problems creating socket");
222                 connection->error = MPD_ERROR_SYSTEM;
223                 return -1;
224         }
226         mpd_setConnectionTimeout(connection,timeout);
228         /* connect stuff */
229         if (do_connect_fail(connection, dest, destlen)) {
230                 snprintf(connection->errorStr, sizeof(connection->errorStr),
231                          "problems connecting to \"%s\" on port"
232                          " %i",host,port);
233                 connection->error = MPD_ERROR_CONNPORT;
234                 return -1;
235         }
237         return 0;
239 #endif /* !MPD_HAVE_GAI */
241 const char *const mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
243         "Artist",
244         "Album",
245         "Title",
246         "Track",
247         "Name",
248         "Genre",
249         "Date",
250         "Composer",
251         "Performer",
252         "Comment",
253         "Disc",
254         "filename"
255 };
257 static char * mpd_sanitizeArg(const char * arg) {
258         size_t i;
259         char * ret;
260         register const char *c;
261         register char *rc;
263         /* instead of counting in that loop above, just
264          * use a bit more memory and half running time
265          */
266         ret = malloc(strlen(arg) * 2 + 1);
268         c = arg;
269         rc = ret;
270         for(i = strlen(arg)+1; i != 0; --i) {
271                 if(*c=='"' || *c=='\\')
272                         *rc++ = '\\';
273                 *(rc++) = *(c++);
274         }
276         return ret;
279 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
281         mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
283         ret->name = str_pool_get(name);
284         ret->value = str_pool_get(value);
286         return ret;
289 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
290         str_pool_put(re->name);
291         str_pool_put(re->value);
292         free(re);
295 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
296         connection->timeout.tv_sec = (int)timeout;
297         connection->timeout.tv_usec = (int)(timeout*1e6 -
298                                             connection->timeout.tv_sec*1000000 +
299                                             0.5);
302 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
303                             char * output) {
304         char * tmp;
305         char * test;
306         int i;
308         if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
309                 snprintf(connection->errorStr, sizeof(connection->errorStr),
310                          "mpd not running on port %i on host \"%s\"",
311                          port,host);
312                 connection->error = MPD_ERROR_NOTMPD;
313                 return 1;
314         }
316         tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
318         for(i=0;i<3;i++) {
319                 if(tmp) connection->version[i] = strtol(tmp,&test,10);
321                 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
322                         snprintf(connection->errorStr, sizeof(connection->errorStr),
323                                  "error parsing version number at "
324                                  "\"%s\"",
325                                  &output[strlen(MPD_WELCOME_MESSAGE)]);
326                         connection->error = MPD_ERROR_NOTMPD;
327                         return 1;
328                 }
329                 tmp = ++test;
330         }
332         return 0;
335 #ifndef WIN32
336 static int mpd_connect_un(mpd_Connection * connection,
337                           const char * host, float timeout)
339         int error, flags;
340         size_t path_length;
341         struct sockaddr_un sun;
343         path_length = strlen(host);
344         if (path_length >= sizeof(sun.sun_path)) {
345                 strcpy(connection->errorStr, "unix socket path is too long");
346                 connection->error = MPD_ERROR_UNKHOST;
347                 return -1;
348         }
350         sun.sun_family = AF_UNIX;
351         memcpy(sun.sun_path, host, path_length + 1);
353         connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
354         if (connection->sock < 0) {
355                 strcpy(connection->errorStr, "problems creating socket");
356                 connection->error = MPD_ERROR_SYSTEM;
357                 return -1;
358         }
360         mpd_setConnectionTimeout(connection, timeout);
362         flags = fcntl(connection->sock, F_GETFL, 0);
363         fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
365         error = connect(connection->sock, (struct sockaddr*)&sun, sizeof(sun));
366         if (error < 0) {
367                 /* try the next address family */
368                 close(connection->sock);
369                 connection->sock = 0;
371                 snprintf(connection->errorStr, sizeof(connection->errorStr),
372                          "problems connecting to \"%s\": %s",
373                          host, strerror(errno));
374                 connection->error = MPD_ERROR_CONNPORT;
375                 return -1;
376         }
378         return 0;
380 #endif /* WIN32 */
382 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
383         int err;
384         char * rt;
385         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
386         struct timeval tv;
387         fd_set fds;
389         connection->buflen = 0;
390         connection->bufstart = 0;
391         mpd_clearError(connection);
392         connection->doneProcessing = 0;
393         connection->commandList = 0;
394         connection->listOks = 0;
395         connection->doneListOk = 0;
396         connection->returnElement = NULL;
397         connection->request = NULL;
399         if (winsock_dll_error(connection))
400                 return connection;
402 #ifndef WIN32
403         if (host[0] == '/')
404                 err = mpd_connect_un(connection, host, timeout);
405         else
406 #endif
407                 err = mpd_connect(connection, host, port, timeout);
408         if (err < 0)
409                 return connection;
411         while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
412                 tv.tv_sec = connection->timeout.tv_sec;
413                 tv.tv_usec = connection->timeout.tv_usec;
414                 FD_ZERO(&fds);
415                 FD_SET(connection->sock,&fds);
416                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
417                         ssize_t readed;
418                         readed = recv(connection->sock,
419                                       &(connection->buffer[connection->buflen]),
420                                       sizeof(connection->buffer) - connection->buflen, 0);
421                         if(readed<=0) {
422                                 snprintf(connection->errorStr, sizeof(connection->errorStr),
423                                          "problems getting a response from"
424                                          " \"%s\" on port %i : %s",host,
425                                          port, strerror(errno));
426                                 connection->error = MPD_ERROR_NORESPONSE;
427                                 return connection;
428                         }
429                         connection->buflen+=readed;
430                 }
431                 else if(err<0) {
432                         if (SELECT_ERRNO_IGNORE)
433                                 continue;
434                         snprintf(connection->errorStr,
435                                  sizeof(connection->errorStr),
436                                  "problems connecting to \"%s\" on port"
437                                  " %i",host,port);
438                         connection->error = MPD_ERROR_CONNPORT;
439                         return connection;
440                 }
441                 else {
442                         snprintf(connection->errorStr, sizeof(connection->errorStr),
443                                  "timeout in attempting to get a response from"
444                                  " \"%s\" on port %i",host,port);
445                         connection->error = MPD_ERROR_NORESPONSE;
446                         return connection;
447                 }
448         }
450         *rt = '\0';
451         if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
452                 connection->doneProcessing = 1;
454         connection->buflen -= rt + 1 - connection->buffer;
455         memmove(connection->buffer, rt + 1, connection->buflen);
457         return connection;
460 void mpd_clearError(mpd_Connection * connection) {
461         connection->error = 0;
462         connection->errorStr[0] = '\0';
465 void mpd_closeConnection(mpd_Connection * connection) {
466         closesocket(connection->sock);
467         if(connection->returnElement) free(connection->returnElement);
468         if(connection->request) free(connection->request);
469         free(connection);
470         WSACleanup();
473 static void mpd_executeCommand(mpd_Connection *connection,
474                                const char *command) {
475         int ret;
476         struct timeval tv;
477         fd_set fds;
478         const char *commandPtr = command;
479         int commandLen = strlen(command);
481         if (!connection->doneProcessing && !connection->commandList) {
482                 strcpy(connection->errorStr,
483                        "not done processing current command");
484                 connection->error = 1;
485                 return;
486         }
488         mpd_clearError(connection);
490         FD_ZERO(&fds);
491         FD_SET(connection->sock,&fds);
492         tv.tv_sec = connection->timeout.tv_sec;
493         tv.tv_usec = connection->timeout.tv_usec;
495         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
496                         (ret==-1 && SELECT_ERRNO_IGNORE)) {
497                 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
498                 if(ret<=0)
499                 {
500                         if (SENDRECV_ERRNO_IGNORE) continue;
501                         snprintf(connection->errorStr, sizeof(connection->errorStr),
502                                  "problems giving command \"%s\"",command);
503                         connection->error = MPD_ERROR_SENDING;
504                         return;
505                 }
506                 else {
507                         commandPtr+=ret;
508                         commandLen-=ret;
509                 }
511                 if(commandLen<=0) break;
512         }
514         if(commandLen>0) {
515                 perror("");
516                 snprintf(connection->errorStr, sizeof(connection->errorStr),
517                          "timeout sending command \"%s\"",command);
518                 connection->error = MPD_ERROR_TIMEOUT;
519                 return;
520         }
522         if(!connection->commandList) connection->doneProcessing = 0;
523         else if(connection->commandList == COMMAND_LIST_OK) {
524                 connection->listOks++;
525         }
528 static void mpd_getNextReturnElement(mpd_Connection * connection) {
529         char * output = NULL;
530         char * rt = NULL;
531         char * name = NULL;
532         char * value = NULL;
533         fd_set fds;
534         struct timeval tv;
535         char * tok = NULL;
536         ssize_t readed;
537         char * bufferCheck = NULL;
538         int err;
539         int pos;
541         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
542         connection->returnElement = NULL;
544         if (connection->doneProcessing ||
545             (connection->listOks && connection->doneListOk)) {
546                 strcpy(connection->errorStr,"already done processing current command");
547                 connection->error = 1;
548                 return;
549         }
551         bufferCheck = connection->buffer+connection->bufstart;
552         while (connection->bufstart >= connection->buflen ||
553                !(rt = memchr(bufferCheck, '\n',
554                              connection->buffer + connection->buflen -
555                              bufferCheck))) {
556                 if (connection->buflen >= sizeof(connection->buffer)) {
557                         memmove(connection->buffer,
558                                 connection->buffer + connection->bufstart,
559                                 connection->buflen - connection->bufstart);
560                         connection->buflen -= connection->bufstart;
561                         connection->bufstart = 0;
562                 }
563                 if (connection->buflen >= sizeof(connection->buffer)) {
564                         strcpy(connection->errorStr,"buffer overrun");
565                         connection->error = MPD_ERROR_BUFFEROVERRUN;
566                         connection->doneProcessing = 1;
567                         connection->doneListOk = 0;
568                         return;
569                 }
570                 bufferCheck = connection->buffer+connection->buflen;
571                 tv.tv_sec = connection->timeout.tv_sec;
572                 tv.tv_usec = connection->timeout.tv_usec;
573                 FD_ZERO(&fds);
574                 FD_SET(connection->sock,&fds);
575                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
576                         readed = recv(connection->sock,
577                                       connection->buffer+connection->buflen,
578                                       sizeof(connection->buffer) - connection->buflen,
579                                       MSG_DONTWAIT);
580                         if(readed<0 && SENDRECV_ERRNO_IGNORE) {
581                                 continue;
582                         }
583                         if(readed<=0) {
584                                 strcpy(connection->errorStr,"connection"
585                                        " closed");
586                                 connection->error = MPD_ERROR_CONNCLOSED;
587                                 connection->doneProcessing = 1;
588                                 connection->doneListOk = 0;
589                                 return;
590                         }
591                         connection->buflen+=readed;
592                 }
593                 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
594                 else {
595                         strcpy(connection->errorStr,"connection timeout");
596                         connection->error = MPD_ERROR_TIMEOUT;
597                         connection->doneProcessing = 1;
598                         connection->doneListOk = 0;
599                         return;
600                 }
601         }
603         *rt = '\0';
604         output = connection->buffer+connection->bufstart;
605         connection->bufstart = rt - connection->buffer + 1;
607         if(strcmp(output,"OK")==0) {
608                 if(connection->listOks > 0) {
609                         strcpy(connection->errorStr, "expected more list_OK's");
610                         connection->error = 1;
611                 }
612                 connection->listOks = 0;
613                 connection->doneProcessing = 1;
614                 connection->doneListOk = 0;
615                 return;
616         }
618         if(strcmp(output, "list_OK") == 0) {
619                 if(!connection->listOks) {
620                         strcpy(connection->errorStr,
621                                         "got an unexpected list_OK");
622                         connection->error = 1;
623                 }
624                 else {
625                         connection->doneListOk = 1;
626                         connection->listOks--;
627                 }
628                 return;
629         }
631         if(strncmp(output,"ACK",strlen("ACK"))==0) {
632                 size_t length = strlen(output);
633                 char * test;
634                 char * needle;
635                 int val;
637                 if (length >= sizeof(connection->errorStr))
638                         length = sizeof(connection->errorStr) - 1;
640                 memcpy(connection->errorStr, output, length);
641                 connection->errorStr[length] = 0;
642                 connection->error = MPD_ERROR_ACK;
643                 connection->errorCode = MPD_ACK_ERROR_UNK;
644                 connection->errorAt = MPD_ERROR_AT_UNK;
645                 connection->doneProcessing = 1;
646                 connection->doneListOk = 0;
648                 needle = strchr(output, '[');
649                 if(!needle) return;
650                 val = strtol(needle+1, &test, 10);
651                 if(*test != '@') return;
652                 connection->errorCode = val;
653                 val = strtol(test+1, &test, 10);
654                 if(*test != ']') return;
655                 connection->errorAt = val;
656                 return;
657         }
659         tok = strchr(output, ':');
660         if (!tok) return;
661         pos = tok - output;
662         value = ++tok;
663         name = output;
664         name[pos] = '\0';
666         if(value[0]==' ') {
667                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
668         }
669         else {
670                 snprintf(connection->errorStr, sizeof(connection->errorStr),
671                          "error parsing: %s:%s",name,value);
672                 connection->error = 1;
673         }
676 void mpd_finishCommand(mpd_Connection * connection) {
677         while(!connection->doneProcessing) {
678                 if(connection->doneListOk) connection->doneListOk = 0;
679                 mpd_getNextReturnElement(connection);
680         }
683 static void mpd_finishListOkCommand(mpd_Connection * connection) {
684         while(!connection->doneProcessing && connection->listOks &&
685                         !connection->doneListOk)
686         {
687                 mpd_getNextReturnElement(connection);
688         }
691 int mpd_nextListOkCommand(mpd_Connection * connection) {
692         mpd_finishListOkCommand(connection);
693         if(!connection->doneProcessing) connection->doneListOk = 0;
694         if(connection->listOks == 0 || connection->doneProcessing) return -1;
695         return 0;
698 void mpd_sendStatusCommand(mpd_Connection * connection) {
699         mpd_executeCommand(connection,"status\n");
702 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
703         mpd_Status * status;
705         /*mpd_executeCommand(connection,"status\n");
707         if(connection->error) return NULL;*/
709         if(connection->doneProcessing || (connection->listOks &&
710            connection->doneListOk))
711         {
712                 return NULL;
713         }
715         if(!connection->returnElement) mpd_getNextReturnElement(connection);
717         status = malloc(sizeof(mpd_Status));
718         status->volume = -1;
719         status->repeat = 0;
720         status->random = 0;
721         status->playlist = -1;
722         status->playlistLength = -1;
723         status->state = -1;
724         status->song = 0;
725         status->songid = 0;
726         status->elapsedTime = 0;
727         status->totalTime = 0;
728         status->bitRate = 0;
729         status->sampleRate = 0;
730         status->bits = 0;
731         status->channels = 0;
732         status->crossfade = -1;
733         status->error = NULL;
734         status->updatingDb = 0;
736         if(connection->error) {
737                 free(status);
738                 return NULL;
739         }
740         while(connection->returnElement) {
741                 mpd_ReturnElement * re = connection->returnElement;
742                 if(strcmp(re->name,"volume")==0) {
743                         status->volume = atoi(re->value);
744                 }
745                 else if(strcmp(re->name,"repeat")==0) {
746                         status->repeat = atoi(re->value);
747                 }
748                 else if(strcmp(re->name,"random")==0) {
749                         status->random = atoi(re->value);
750                 }
751                 else if(strcmp(re->name,"playlist")==0) {
752                         status->playlist = strtol(re->value,NULL,10);
753                 }
754                 else if(strcmp(re->name,"playlistlength")==0) {
755                         status->playlistLength = atoi(re->value);
756                 }
757                 else if(strcmp(re->name,"bitrate")==0) {
758                         status->bitRate = atoi(re->value);
759                 }
760                 else if(strcmp(re->name,"state")==0) {
761                         if(strcmp(re->value,"play")==0) {
762                                 status->state = MPD_STATUS_STATE_PLAY;
763                         }
764                         else if(strcmp(re->value,"stop")==0) {
765                                 status->state = MPD_STATUS_STATE_STOP;
766                         }
767                         else if(strcmp(re->value,"pause")==0) {
768                                 status->state = MPD_STATUS_STATE_PAUSE;
769                         }
770                         else {
771                                 status->state = MPD_STATUS_STATE_UNKNOWN;
772                         }
773                 }
774                 else if(strcmp(re->name,"song")==0) {
775                         status->song = atoi(re->value);
776                 }
777                 else if(strcmp(re->name,"songid")==0) {
778                         status->songid = atoi(re->value);
779                 }
780                 else if(strcmp(re->name,"time")==0) {
781                         char * tok = strchr(re->value,':');
782                         /* the second strchr below is a safety check */
783                         if (tok && (strchr(tok,0) > (tok+1))) {
784                                 /* atoi stops at the first non-[0-9] char: */
785                                 status->elapsedTime = atoi(re->value);
786                                 status->totalTime = atoi(tok+1);
787                         }
788                 }
789                 else if(strcmp(re->name,"error")==0) {
790                         status->error = strdup(re->value);
791                 }
792                 else if(strcmp(re->name,"xfade")==0) {
793                         status->crossfade = atoi(re->value);
794                 }
795                 else if(strcmp(re->name,"updating_db")==0) {
796                         status->updatingDb = atoi(re->value);
797                 }
798                 else if(strcmp(re->name,"audio")==0) {
799                         char * tok = strchr(re->value,':');
800                         if (tok && (strchr(tok,0) > (tok+1))) {
801                                 status->sampleRate = atoi(re->value);
802                                 status->bits = atoi(++tok);
803                                 tok = strchr(tok,':');
804                                 if (tok && (strchr(tok,0) > (tok+1)))
805                                         status->channels = atoi(tok+1);
806                         }
807                 }
809                 mpd_getNextReturnElement(connection);
810                 if(connection->error) {
811                         free(status);
812                         return NULL;
813                 }
814         }
816         if(connection->error) {
817                 free(status);
818                 return NULL;
819         }
820         else if(status->state<0) {
821                 strcpy(connection->errorStr,"state not found");
822                 connection->error = 1;
823                 free(status);
824                 return NULL;
825         }
827         return status;
830 void mpd_freeStatus(mpd_Status * status) {
831         if(status->error) free(status->error);
832         free(status);
835 void mpd_sendStatsCommand(mpd_Connection * connection) {
836         mpd_executeCommand(connection,"stats\n");
839 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
840         mpd_Stats * stats;
842         /*mpd_executeCommand(connection,"stats\n");
844         if(connection->error) return NULL;*/
846         if(connection->doneProcessing || (connection->listOks &&
847            connection->doneListOk))
848         {
849                 return NULL;
850         }
852         if(!connection->returnElement) mpd_getNextReturnElement(connection);
854         stats = malloc(sizeof(mpd_Stats));
855         stats->numberOfArtists = 0;
856         stats->numberOfAlbums = 0;
857         stats->numberOfSongs = 0;
858         stats->uptime = 0;
859         stats->dbUpdateTime = 0;
860         stats->playTime = 0;
861         stats->dbPlayTime = 0;
863         if(connection->error) {
864                 free(stats);
865                 return NULL;
866         }
867         while(connection->returnElement) {
868                 mpd_ReturnElement * re = connection->returnElement;
869                 if(strcmp(re->name,"artists")==0) {
870                         stats->numberOfArtists = atoi(re->value);
871                 }
872                 else if(strcmp(re->name,"albums")==0) {
873                         stats->numberOfAlbums = atoi(re->value);
874                 }
875                 else if(strcmp(re->name,"songs")==0) {
876                         stats->numberOfSongs = atoi(re->value);
877                 }
878                 else if(strcmp(re->name,"uptime")==0) {
879                         stats->uptime = strtol(re->value,NULL,10);
880                 }
881                 else if(strcmp(re->name,"db_update")==0) {
882                         stats->dbUpdateTime = strtol(re->value,NULL,10);
883                 }
884                 else if(strcmp(re->name,"playtime")==0) {
885                         stats->playTime = strtol(re->value,NULL,10);
886                 }
887                 else if(strcmp(re->name,"db_playtime")==0) {
888                         stats->dbPlayTime = strtol(re->value,NULL,10);
889                 }
891                 mpd_getNextReturnElement(connection);
892                 if(connection->error) {
893                         free(stats);
894                         return NULL;
895                 }
896         }
898         if(connection->error) {
899                 free(stats);
900                 return NULL;
901         }
903         return stats;
906 void mpd_freeStats(mpd_Stats * stats) {
907         free(stats);
910 static void mpd_initDirectory(mpd_Directory * directory) {
911         directory->path = NULL;
914 static void mpd_finishDirectory(mpd_Directory * directory) {
915         if (directory->path)
916                 str_pool_put(directory->path);
919 mpd_Directory * mpd_newDirectory(void) {
920         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
922         mpd_initDirectory(directory);
924         return directory;
927 void mpd_freeDirectory(mpd_Directory * directory) {
928         mpd_finishDirectory(directory);
930         free(directory);
933 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
934         mpd_Directory * ret = mpd_newDirectory();
936         if (directory->path)
937                 ret->path = str_pool_dup(directory->path);
939         return ret;
942 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
943         playlist->path = NULL;
946 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
947         if (playlist->path)
948                 str_pool_put(playlist->path);
951 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
952         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
954         mpd_initPlaylistFile(playlist);
956         return playlist;
959 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
960         mpd_finishPlaylistFile(playlist);
961         free(playlist);
964 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
965         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
967         if (playlist->path)
968                 ret->path = str_pool_dup(playlist->path);
970         return ret;
973 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
974         entity->info.directory = NULL;
977 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
978         if(entity->info.directory) {
979                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
980                         mpd_freeDirectory(entity->info.directory);
981                 }
982                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
983                         mpd_freeSong(entity->info.song);
984                 }
985                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
986                         mpd_freePlaylistFile(entity->info.playlistFile);
987                 }
988         }
991 mpd_InfoEntity * mpd_newInfoEntity(void) {
992         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
994         mpd_initInfoEntity(entity);
996         return entity;
999 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1000         mpd_finishInfoEntity(entity);
1001         free(entity);
1004 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1005         mpd_executeCommand(connection,command);
1008 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1009         mpd_InfoEntity * entity = NULL;
1011         if(connection->doneProcessing || (connection->listOks &&
1012            connection->doneListOk)) {
1013                 return NULL;
1014         }
1016         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1018         if(connection->returnElement) {
1019                 if(strcmp(connection->returnElement->name,"file")==0) {
1020                         entity = mpd_newInfoEntity();
1021                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1022                         entity->info.song = mpd_newSong();
1023                         entity->info.song->file =
1024                                 str_pool_dup(connection->returnElement->value);
1025                 }
1026                 else if(strcmp(connection->returnElement->name,
1027                                         "directory")==0) {
1028                         entity = mpd_newInfoEntity();
1029                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1030                         entity->info.directory = mpd_newDirectory();
1031                         entity->info.directory->path =
1032                                 str_pool_dup(connection->returnElement->value);
1033                 }
1034                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1035                         entity = mpd_newInfoEntity();
1036                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1037                         entity->info.playlistFile = mpd_newPlaylistFile();
1038                         entity->info.playlistFile->path =
1039                                 str_pool_dup(connection->returnElement->value);
1040                 }
1041                 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1042                         entity = mpd_newInfoEntity();
1043                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1044                         entity->info.song = mpd_newSong();
1045                         entity->info.song->pos = atoi(connection->returnElement->value);
1046                 }
1047                 else {
1048                         connection->error = 1;
1049                         strcpy(connection->errorStr,"problem parsing song info");
1050                         return NULL;
1051                 }
1052         }
1053         else return NULL;
1055         mpd_getNextReturnElement(connection);
1056         while(connection->returnElement) {
1057                 mpd_ReturnElement * re = connection->returnElement;
1059                 if(strcmp(re->name,"file")==0) return entity;
1060                 else if(strcmp(re->name,"directory")==0) return entity;
1061                 else if(strcmp(re->name,"playlist")==0) return entity;
1062                 else if(strcmp(re->name,"cpos")==0) return entity;
1064                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1065                                 strlen(re->value)) {
1066                         if(!entity->info.song->artist &&
1067                                         strcmp(re->name,"Artist")==0) {
1068                                 entity->info.song->artist = str_pool_dup(re->value);
1069                         }
1070                         else if(!entity->info.song->album &&
1071                                         strcmp(re->name,"Album")==0) {
1072                                 entity->info.song->album = str_pool_dup(re->value);
1073                         }
1074                         else if(!entity->info.song->title &&
1075                                         strcmp(re->name,"Title")==0) {
1076                                 entity->info.song->title = str_pool_dup(re->value);
1077                         }
1078                         else if(!entity->info.song->track &&
1079                                         strcmp(re->name,"Track")==0) {
1080                                 entity->info.song->track = str_pool_dup(re->value);
1081                         }
1082                         else if(!entity->info.song->name &&
1083                                         strcmp(re->name,"Name")==0) {
1084                                 entity->info.song->name = str_pool_dup(re->value);
1085                         }
1086                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1087                                         strcmp(re->name,"Time")==0) {
1088                                 entity->info.song->time = atoi(re->value);
1089                         }
1090                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1091                                         strcmp(re->name,"Pos")==0) {
1092                                 entity->info.song->pos = atoi(re->value);
1093                         }
1094                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
1095                                         strcmp(re->name,"Id")==0) {
1096                                 entity->info.song->id = atoi(re->value);
1097                         }
1098                         else if(!entity->info.song->date &&
1099                                         strcmp(re->name, "Date") == 0) {
1100                                 entity->info.song->date = str_pool_dup(re->value);
1101                         }
1102                         else if(!entity->info.song->genre &&
1103                                         strcmp(re->name, "Genre") == 0) {
1104                                 entity->info.song->genre = str_pool_dup(re->value);
1105                         }
1106                         else if(!entity->info.song->composer &&
1107                                         strcmp(re->name, "Composer") == 0) {
1108                                 entity->info.song->composer = str_pool_dup(re->value);
1109                         }
1110                         else if(!entity->info.song->disc &&
1111                                         strcmp(re->name, "Disc") == 0) {
1112                                 entity->info.song->disc = str_pool_dup(re->value);
1113                         }
1114                         else if(!entity->info.song->comment &&
1115                                         strcmp(re->name, "Comment") == 0) {
1116                                 entity->info.song->comment = str_pool_dup(re->value);
1117                         }
1118                 }
1119                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1120                 }
1121                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1122                 }
1124                 mpd_getNextReturnElement(connection);
1125         }
1127         return entity;
1130 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1131                 const char * name)
1133         if(connection->doneProcessing || (connection->listOks &&
1134                                 connection->doneListOk))
1135         {
1136                 return NULL;
1137         }
1139         mpd_getNextReturnElement(connection);
1140         while(connection->returnElement) {
1141                 mpd_ReturnElement * re = connection->returnElement;
1143                 if(strcmp(re->name,name)==0) return strdup(re->value);
1144                 mpd_getNextReturnElement(connection);
1145         }
1147         return NULL;
1150 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1151         if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1152         {
1153                 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1154         }
1155         return NULL;
1158 char * mpd_getNextArtist(mpd_Connection * connection) {
1159         return mpd_getNextReturnElementNamed(connection,"Artist");
1162 char * mpd_getNextAlbum(mpd_Connection * connection) {
1163         return mpd_getNextReturnElementNamed(connection,"Album");
1166 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1167         char * string = malloc(strlen("playlistinfo")+25);
1168         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1169         mpd_sendInfoCommand(connection,string);
1170         free(string);
1173 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1174         char * string = malloc(strlen("playlistid")+25);
1175         sprintf(string, "playlistid \"%i\"\n", id);
1176         mpd_sendInfoCommand(connection, string);
1177         free(string);
1180 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1181         char * string = malloc(strlen("plchanges")+25);
1182         sprintf(string,"plchanges \"%lld\"\n",playlist);
1183         mpd_sendInfoCommand(connection,string);
1184         free(string);
1187 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1188         char * string = malloc(strlen("plchangesposid")+25);
1189         sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1190         mpd_sendInfoCommand(connection,string);
1191         free(string);
1194 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1195         char * sDir = mpd_sanitizeArg(dir);
1196         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1197         sprintf(string,"listall \"%s\"\n",sDir);
1198         mpd_sendInfoCommand(connection,string);
1199         free(string);
1200         free(sDir);
1203 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1204         char * sDir = mpd_sanitizeArg(dir);
1205         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1206         sprintf(string,"listallinfo \"%s\"\n",sDir);
1207         mpd_sendInfoCommand(connection,string);
1208         free(string);
1209         free(sDir);
1212 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1213         char * sDir = mpd_sanitizeArg(dir);
1214         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1215         sprintf(string,"lsinfo \"%s\"\n",sDir);
1216         mpd_sendInfoCommand(connection,string);
1217         free(string);
1218         free(sDir);
1221 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1222         mpd_executeCommand(connection,"currentsong\n");
1225 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1226                 const char * str)
1228         char st[10];
1229         char * string;
1230         char * sanitStr = mpd_sanitizeArg(str);
1231         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1232         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1233         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1234         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1235         else {
1236                 connection->error = 1;
1237                 strcpy(connection->errorStr,"unknown table for search");
1238                 return;
1239         }
1240         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1241         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1242         mpd_sendInfoCommand(connection,string);
1243         free(string);
1244         free(sanitStr);
1247 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1248                 const char * str)
1250         char st[10];
1251         char * string;
1252         char * sanitStr = mpd_sanitizeArg(str);
1253         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1254         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1255         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1256         else {
1257                 connection->error = 1;
1258                 strcpy(connection->errorStr,"unknown table for find");
1259                 return;
1260         }
1261         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1262         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1263         mpd_sendInfoCommand(connection,string);
1264         free(string);
1265         free(sanitStr);
1268 void mpd_sendListCommand(mpd_Connection * connection, int table,
1269                 const char * arg1)
1271         char st[10];
1272         char * string;
1273         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1274         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1275         else {
1276                 connection->error = 1;
1277                 strcpy(connection->errorStr,"unknown table for list");
1278                 return;
1279         }
1280         if(arg1) {
1281                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1282                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1283                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1284                 free(sanitArg1);
1285         }
1286         else {
1287                 string = malloc(strlen("list")+strlen(st)+3);
1288                 sprintf(string,"list %s\n",st);
1289         }
1290         mpd_sendInfoCommand(connection,string);
1291         free(string);
1294 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1295         char * sFile = mpd_sanitizeArg(file);
1296         char * string = malloc(strlen("add")+strlen(sFile)+5);
1297         sprintf(string,"add \"%s\"\n",sFile);
1298         mpd_executeCommand(connection,string);
1299         free(string);
1300         free(sFile);
1303 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1304         char * string = malloc(strlen("delete")+25);
1305         sprintf(string,"delete \"%i\"\n",songPos);
1306         mpd_sendInfoCommand(connection,string);
1307         free(string);
1310 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1311         char * string = malloc(strlen("deleteid")+25);
1312         sprintf(string, "deleteid \"%i\"\n", id);
1313         mpd_sendInfoCommand(connection,string);
1314         free(string);
1317 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1318         char * sName = mpd_sanitizeArg(name);
1319         char * string = malloc(strlen("save")+strlen(sName)+5);
1320         sprintf(string,"save \"%s\"\n",sName);
1321         mpd_executeCommand(connection,string);
1322         free(string);
1323         free(sName);
1326 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1327         char * sName = mpd_sanitizeArg(name);
1328         char * string = malloc(strlen("load")+strlen(sName)+5);
1329         sprintf(string,"load \"%s\"\n",sName);
1330         mpd_executeCommand(connection,string);
1331         free(string);
1332         free(sName);
1335 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1336         char * sName = mpd_sanitizeArg(name);
1337         char * string = malloc(strlen("rm")+strlen(sName)+5);
1338         sprintf(string,"rm \"%s\"\n",sName);
1339         mpd_executeCommand(connection,string);
1340         free(string);
1341         free(sName);
1344 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1345         mpd_executeCommand(connection,"shuffle\n");
1348 void mpd_sendClearCommand(mpd_Connection * connection) {
1349         mpd_executeCommand(connection,"clear\n");
1352 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1353         char * string = malloc(strlen("play")+25);
1354         sprintf(string,"play \"%i\"\n",songPos);
1355         mpd_sendInfoCommand(connection,string);
1356         free(string);
1359 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1360         char * string = malloc(strlen("playid")+25);
1361         sprintf(string,"playid \"%i\"\n",id);
1362         mpd_sendInfoCommand(connection,string);
1363         free(string);
1366 void mpd_sendStopCommand(mpd_Connection * connection) {
1367         mpd_executeCommand(connection,"stop\n");
1370 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1371         char * string = malloc(strlen("pause")+25);
1372         sprintf(string,"pause \"%i\"\n",pauseMode);
1373         mpd_executeCommand(connection,string);
1374         free(string);
1377 void mpd_sendNextCommand(mpd_Connection * connection) {
1378         mpd_executeCommand(connection,"next\n");
1381 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1382         char * string = malloc(strlen("move")+25);
1383         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1384         mpd_sendInfoCommand(connection,string);
1385         free(string);
1388 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1389         char * string = malloc(strlen("moveid")+25);
1390         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1391         mpd_sendInfoCommand(connection,string);
1392         free(string);
1395 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1396         char * string = malloc(strlen("swap")+25);
1397         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1398         mpd_sendInfoCommand(connection,string);
1399         free(string);
1402 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1403         char * string = malloc(strlen("swapid")+25);
1404         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1405         mpd_sendInfoCommand(connection,string);
1406         free(string);
1409 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1410         char * string = malloc(strlen("seek")+25);
1411         sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1412         mpd_sendInfoCommand(connection,string);
1413         free(string);
1416 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1417         char * string = malloc(strlen("seekid")+25);
1418         sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1419         mpd_sendInfoCommand(connection,string);
1420         free(string);
1423 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1424         char *sPath = mpd_sanitizeArg(path);
1425         char * string = malloc(strlen("update")+strlen(sPath)+5);
1426         sprintf(string,"update \"%s\"\n",sPath);
1427         mpd_sendInfoCommand(connection,string);
1428         free(string);
1429         free(sPath);
1432 int mpd_getUpdateId(mpd_Connection * connection) {
1433         char * jobid;
1434         int ret = 0;
1436         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1437         if(jobid) {
1438                 ret = atoi(jobid);
1439                 free(jobid);
1440         }
1442         return ret;
1445 void mpd_sendPrevCommand(mpd_Connection * connection) {
1446         mpd_executeCommand(connection,"previous\n");
1449 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1450         char * string = malloc(strlen("repeat")+25);
1451         sprintf(string,"repeat \"%i\"\n",repeatMode);
1452         mpd_executeCommand(connection,string);
1453         free(string);
1456 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1457         char * string = malloc(strlen("random")+25);
1458         sprintf(string,"random \"%i\"\n",randomMode);
1459         mpd_executeCommand(connection,string);
1460         free(string);
1463 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1464         char * string = malloc(strlen("setvol")+25);
1465         sprintf(string,"setvol \"%i\"\n",volumeChange);
1466         mpd_executeCommand(connection,string);
1467         free(string);
1470 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1471         char * string = malloc(strlen("volume")+25);
1472         sprintf(string,"volume \"%i\"\n",volumeChange);
1473         mpd_executeCommand(connection,string);
1474         free(string);
1477 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1478         char * string = malloc(strlen("crossfade")+25);
1479         sprintf(string,"crossfade \"%i\"\n",seconds);
1480         mpd_executeCommand(connection,string);
1481         free(string);
1484 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1485         char * sPass = mpd_sanitizeArg(pass);
1486         char * string = malloc(strlen("password")+strlen(sPass)+5);
1487         sprintf(string,"password \"%s\"\n",sPass);
1488         mpd_executeCommand(connection,string);
1489         free(string);
1490         free(sPass);
1493 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1494         if(connection->commandList) {
1495                 strcpy(connection->errorStr,"already in command list mode");
1496                 connection->error = 1;
1497                 return;
1498         }
1499         connection->commandList = COMMAND_LIST;
1500         mpd_executeCommand(connection,"command_list_begin\n");
1503 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1504         if(connection->commandList) {
1505                 strcpy(connection->errorStr,"already in command list mode");
1506                 connection->error = 1;
1507                 return;
1508         }
1509         connection->commandList = COMMAND_LIST_OK;
1510         mpd_executeCommand(connection,"command_list_ok_begin\n");
1511         connection->listOks = 0;
1514 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1515         if(!connection->commandList) {
1516                 strcpy(connection->errorStr,"not in command list mode");
1517                 connection->error = 1;
1518                 return;
1519         }
1520         connection->commandList = 0;
1521         mpd_executeCommand(connection,"command_list_end\n");
1524 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1525         mpd_executeCommand(connection,"outputs\n");
1528 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1529         mpd_OutputEntity * output = NULL;
1531         if(connection->doneProcessing || (connection->listOks &&
1532                                 connection->doneListOk))
1533         {
1534                 return NULL;
1535         }
1537         if(connection->error) return NULL;
1539         output = malloc(sizeof(mpd_OutputEntity));
1540         output->id = -10;
1541         output->name = NULL;
1542         output->enabled = 0;
1544         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1546         while(connection->returnElement) {
1547                 mpd_ReturnElement * re = connection->returnElement;
1548                 if(strcmp(re->name,"outputid")==0) {
1549                         if(output!=NULL && output->id>=0) return output;
1550                         output->id = atoi(re->value);
1551                 }
1552                 else if(strcmp(re->name,"outputname")==0) {
1553                         output->name = strdup(re->value);
1554                 }
1555                 else if(strcmp(re->name,"outputenabled")==0) {
1556                         output->enabled = atoi(re->value);
1557                 }
1559                 mpd_getNextReturnElement(connection);
1560                 if(connection->error) {
1561                         free(output);
1562                         return NULL;
1563                 }
1565         }
1567         return output;
1570 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1571         char * string = malloc(strlen("enableoutput")+25);
1572         sprintf(string,"enableoutput \"%i\"\n",outputId);
1573         mpd_executeCommand(connection,string);
1574         free(string);
1577 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1578         char * string = malloc(strlen("disableoutput")+25);
1579         sprintf(string,"disableoutput \"%i\"\n",outputId);
1580         mpd_executeCommand(connection,string);
1581         free(string);
1584 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1585         free(output->name);
1586         free(output);
1589 /**
1590  * mpd_sendNotCommandsCommand
1591  * odd naming, but it gets the not allowed commands
1592  */
1594 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1595         mpd_executeCommand(connection,"notcommands\n");
1598 /**
1599  * mpd_sendCommandsCommand
1600  * odd naming, but it gets the allowed commands
1601  */
1603 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1604         mpd_executeCommand(connection,"commands\n");
1606 /**
1607  * Get the next returned command
1608  */
1609 char * mpd_getNextCommand(mpd_Connection * connection) {
1610         return mpd_getNextReturnElementNamed(connection,"command");
1613 void mpd_startSearch(mpd_Connection * connection,int exact) {
1614         if(connection->request) {
1615                 /* search/find allready in progress */
1616                 /* TODO: set error here?  */
1617                 return;
1618         }
1619         if(exact){
1620                 connection->request = strdup("find");
1621         }
1622         else{
1623                 connection->request = strdup("search");
1624         }
1628 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1629         if(connection->request) {
1630                 /* search/find allready in progress */
1631                 /* TODO: set error here?  */
1632                 return;
1633         }
1634         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1635                 /* set error here */
1636                 return;
1637         }
1639         connection->request = malloc(sizeof(char)*(
1640                                 /* length of the field name */
1641                                 strlen(mpdTagItemKeys[field])+
1642                                 /* "list"+space+\0 */
1643                                 6
1644                                 ));
1645         sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1650 void mpd_addConstraintSearch(mpd_Connection *connection,
1651                 int field,
1652                 char *name)
1654         char *arg = NULL;
1655         if(!connection->request){
1656                 return;
1657         }
1658         if(name == NULL) {
1659                 return;
1660         }
1661         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1662                 return;
1663         }
1664         /* clean up the query */
1665         arg = mpd_sanitizeArg(name);
1666         /* create space for the query */
1667         connection->request = realloc(connection->request, (
1668                          /* length of the old string */
1669                          strlen(connection->request)+
1670                          /* space between */
1671                          1+
1672                          /* length of the field name */
1673                          strlen(mpdTagItemKeys[field])+
1674                          /* space plus starting " */
1675                          2+
1676                          /* length of search term */
1677                          strlen(arg)+
1678                          /* closign " +\0 that is added sprintf */
1679                          2
1680                         )*sizeof(char));
1681         /* and form the query */
1682         sprintf(connection->request, "%s %s \"%s\"",
1683                         connection->request,
1684                         mpdTagItemKeys[field],
1685                         arg);
1686         free(arg);
1690 void mpd_commitSearch(mpd_Connection *connection)
1692         if(connection->request)
1693         {
1694                 int length = strlen(connection->request);
1695                 /* fixing up the string for mpd to like */
1696                 connection->request = realloc(connection->request,
1697                                 (length+        /* old length */
1698                                  2              /* closing \n and \0 */
1699                                 )*sizeof(char));
1700                 connection->request[length] = '\n';
1701                 connection->request[length+1] = '\0';
1702                 /* and off we go */
1703                 mpd_sendInfoCommand(connection, connection->request);
1704                 /* clean up a bit */
1705                 free(connection->request);
1706                 connection->request = NULL;
1707         }
1710 /**
1711  * @param connection a MpdConnection
1712  * @param path  the path to the playlist.
1713  *
1714  * List the content, with full metadata, of a stored playlist.
1715  *
1716  */
1717 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1719         char *arg = mpd_sanitizeArg(path);
1720         char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1721         sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1722         mpd_sendInfoCommand(connection, query);
1723         free(arg);
1724         free(query);
1727 /**
1728  * @param connection a MpdConnection
1729  * @param path  the path to the playlist.
1730  *
1731  * List the content of a stored playlist.
1732  *
1733  */
1734 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1736         char *arg = mpd_sanitizeArg(path);
1737         char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1738         sprintf(query, "listplaylist \"%s\"\n",arg);
1739         mpd_sendInfoCommand(connection, query);
1740         free(arg);
1741         free(query);