Code

libmpdclient: don't strdup() MPD's welcome message
[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,MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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, MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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,
323                                  MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
384         int err;
385         char * rt;
386         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
387         struct timeval tv;
388         fd_set fds;
389         strcpy(connection->buffer,"");
390         connection->buflen = 0;
391         connection->bufstart = 0;
392         strcpy(connection->errorStr,"");
393         connection->error = 0;
394         connection->doneProcessing = 0;
395         connection->commandList = 0;
396         connection->listOks = 0;
397         connection->doneListOk = 0;
398         connection->returnElement = NULL;
399         connection->request = NULL;
401         if (winsock_dll_error(connection))
402                 return connection;
404 #ifndef WIN32
405         if (host[0] == '/')
406                 err = mpd_connect_un(connection, host, timeout);
407         else
408 #endif
409                 err = mpd_connect(connection, host, port, timeout);
410         if (err < 0)
411                 return connection;
413         while(!(rt = strstr(connection->buffer,"\n"))) {
414                 tv.tv_sec = connection->timeout.tv_sec;
415                 tv.tv_usec = connection->timeout.tv_usec;
416                 FD_ZERO(&fds);
417                 FD_SET(connection->sock,&fds);
418                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
419                         ssize_t readed;
420                         readed = recv(connection->sock,
421                                       &(connection->buffer[connection->buflen]),
422                                       MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
423                         if(readed<=0) {
424                                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
425                                          "problems getting a response from"
426                                          " \"%s\" on port %i : %s",host,
427                                          port, strerror(errno));
428                                 connection->error = MPD_ERROR_NORESPONSE;
429                                 return connection;
430                         }
431                         connection->buflen+=readed;
432                         connection->buffer[connection->buflen] = '\0';
433                 }
434                 else if(err<0) {
435                         if (SELECT_ERRNO_IGNORE)
436                                 continue;
437                         snprintf(connection->errorStr,
438                                  MPD_BUFFER_MAX_LENGTH,
439                                  "problems connecting to \"%s\" on port"
440                                  " %i",host,port);
441                         connection->error = MPD_ERROR_CONNPORT;
442                         return connection;
443                 }
444                 else {
445                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
446                                  "timeout in attempting to get a response from"
447                                  " \"%s\" on port %i",host,port);
448                         connection->error = MPD_ERROR_NORESPONSE;
449                         return connection;
450                 }
451         }
453         *rt = '\0';
454         if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
455                 connection->doneProcessing = 1;
457         strcpy(connection->buffer,rt+1);
458         connection->buflen = strlen(connection->buffer);
460         return connection;
463 void mpd_clearError(mpd_Connection * connection) {
464         connection->error = 0;
465         connection->errorStr[0] = '\0';
468 void mpd_closeConnection(mpd_Connection * connection) {
469         closesocket(connection->sock);
470         if(connection->returnElement) free(connection->returnElement);
471         if(connection->request) free(connection->request);
472         free(connection);
473         WSACleanup();
476 static void mpd_executeCommand(mpd_Connection *connection,
477                                const char *command) {
478         int ret;
479         struct timeval tv;
480         fd_set fds;
481         const char *commandPtr = command;
482         int commandLen = strlen(command);
484         if (!connection->doneProcessing && !connection->commandList) {
485                 strcpy(connection->errorStr,
486                        "not done processing current command");
487                 connection->error = 1;
488                 return;
489         }
491         mpd_clearError(connection);
493         FD_ZERO(&fds);
494         FD_SET(connection->sock,&fds);
495         tv.tv_sec = connection->timeout.tv_sec;
496         tv.tv_usec = connection->timeout.tv_usec;
498         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
499                         (ret==-1 && SELECT_ERRNO_IGNORE)) {
500                 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
501                 if(ret<=0)
502                 {
503                         if (SENDRECV_ERRNO_IGNORE) continue;
504                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
505                                  "problems giving command \"%s\"",command);
506                         connection->error = MPD_ERROR_SENDING;
507                         return;
508                 }
509                 else {
510                         commandPtr+=ret;
511                         commandLen-=ret;
512                 }
514                 if(commandLen<=0) break;
515         }
517         if(commandLen>0) {
518                 perror("");
519                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
520                          "timeout sending command \"%s\"",command);
521                 connection->error = MPD_ERROR_TIMEOUT;
522                 return;
523         }
525         if(!connection->commandList) connection->doneProcessing = 0;
526         else if(connection->commandList == COMMAND_LIST_OK) {
527                 connection->listOks++;
528         }
531 static void mpd_getNextReturnElement(mpd_Connection * connection) {
532         char * output = NULL;
533         char * rt = NULL;
534         char * name = NULL;
535         char * value = NULL;
536         fd_set fds;
537         struct timeval tv;
538         char * tok = NULL;
539         ssize_t readed;
540         char * bufferCheck = NULL;
541         int err;
542         int pos;
544         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
545         connection->returnElement = NULL;
547         if (connection->doneProcessing ||
548             (connection->listOks && connection->doneListOk)) {
549                 strcpy(connection->errorStr,"already done processing current command");
550                 connection->error = 1;
551                 return;
552         }
554         bufferCheck = connection->buffer+connection->bufstart;
555         while (connection->bufstart >= connection->buflen ||
556                !(rt = strchr(bufferCheck, '\n'))) {
557                 if (connection->buflen >= MPD_BUFFER_MAX_LENGTH) {
558                         memmove(connection->buffer,
559                                 connection->buffer + connection->bufstart,
560                                 connection->buflen - connection->bufstart + 1);
561                         connection->buflen -= connection->bufstart;
562                         connection->bufstart = 0;
563                 }
564                 if (connection->buflen >= MPD_BUFFER_MAX_LENGTH) {
565                         strcpy(connection->errorStr,"buffer overrun");
566                         connection->error = MPD_ERROR_BUFFEROVERRUN;
567                         connection->doneProcessing = 1;
568                         connection->doneListOk = 0;
569                         return;
570                 }
571                 bufferCheck = connection->buffer+connection->buflen;
572                 tv.tv_sec = connection->timeout.tv_sec;
573                 tv.tv_usec = connection->timeout.tv_usec;
574                 FD_ZERO(&fds);
575                 FD_SET(connection->sock,&fds);
576                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
577                         readed = recv(connection->sock,
578                                       connection->buffer+connection->buflen,
579                                       MPD_BUFFER_MAX_LENGTH-connection->buflen,
580                                       MSG_DONTWAIT);
581                         if(readed<0 && SENDRECV_ERRNO_IGNORE) {
582                                 continue;
583                         }
584                         if(readed<=0) {
585                                 strcpy(connection->errorStr,"connection"
586                                        " closed");
587                                 connection->error = MPD_ERROR_CONNCLOSED;
588                                 connection->doneProcessing = 1;
589                                 connection->doneListOk = 0;
590                                 return;
591                         }
592                         connection->buflen+=readed;
593                         connection->buffer[connection->buflen] = '\0';
594                 }
595                 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
596                 else {
597                         strcpy(connection->errorStr,"connection timeout");
598                         connection->error = MPD_ERROR_TIMEOUT;
599                         connection->doneProcessing = 1;
600                         connection->doneListOk = 0;
601                         return;
602                 }
603         }
605         *rt = '\0';
606         output = connection->buffer+connection->bufstart;
607         connection->bufstart = rt - connection->buffer + 1;
609         if(strcmp(output,"OK")==0) {
610                 if(connection->listOks > 0) {
611                         strcpy(connection->errorStr, "expected more list_OK's");
612                         connection->error = 1;
613                 }
614                 connection->listOks = 0;
615                 connection->doneProcessing = 1;
616                 connection->doneListOk = 0;
617                 return;
618         }
620         if(strcmp(output, "list_OK") == 0) {
621                 if(!connection->listOks) {
622                         strcpy(connection->errorStr,
623                                         "got an unexpected list_OK");
624                         connection->error = 1;
625                 }
626                 else {
627                         connection->doneListOk = 1;
628                         connection->listOks--;
629                 }
630                 return;
631         }
633         if(strncmp(output,"ACK",strlen("ACK"))==0) {
634                 char * test;
635                 char * needle;
636                 int val;
638                 strcpy(connection->errorStr, output);
639                 connection->error = MPD_ERROR_ACK;
640                 connection->errorCode = MPD_ACK_ERROR_UNK;
641                 connection->errorAt = MPD_ERROR_AT_UNK;
642                 connection->doneProcessing = 1;
643                 connection->doneListOk = 0;
645                 needle = strchr(output, '[');
646                 if(!needle) return;
647                 val = strtol(needle+1, &test, 10);
648                 if(*test != '@') return;
649                 connection->errorCode = val;
650                 val = strtol(test+1, &test, 10);
651                 if(*test != ']') return;
652                 connection->errorAt = val;
653                 return;
654         }
656         tok = strchr(output, ':');
657         if (!tok) return;
658         pos = tok - output;
659         value = ++tok;
660         name = output;
661         name[pos] = '\0';
663         if(value[0]==' ') {
664                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
665         }
666         else {
667                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
668                          "error parsing: %s:%s",name,value);
669                 connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0';
670                 connection->error = 1;
671         }
674 void mpd_finishCommand(mpd_Connection * connection) {
675         while(!connection->doneProcessing) {
676                 if(connection->doneListOk) connection->doneListOk = 0;
677                 mpd_getNextReturnElement(connection);
678         }
681 static void mpd_finishListOkCommand(mpd_Connection * connection) {
682         while(!connection->doneProcessing && connection->listOks &&
683                         !connection->doneListOk)
684         {
685                 mpd_getNextReturnElement(connection);
686         }
689 int mpd_nextListOkCommand(mpd_Connection * connection) {
690         mpd_finishListOkCommand(connection);
691         if(!connection->doneProcessing) connection->doneListOk = 0;
692         if(connection->listOks == 0 || connection->doneProcessing) return -1;
693         return 0;
696 void mpd_sendStatusCommand(mpd_Connection * connection) {
697         mpd_executeCommand(connection,"status\n");
700 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
701         mpd_Status * status;
703         /*mpd_executeCommand(connection,"status\n");
705         if(connection->error) return NULL;*/
707         if(connection->doneProcessing || (connection->listOks &&
708            connection->doneListOk))
709         {
710                 return NULL;
711         }
713         if(!connection->returnElement) mpd_getNextReturnElement(connection);
715         status = malloc(sizeof(mpd_Status));
716         status->volume = -1;
717         status->repeat = 0;
718         status->random = 0;
719         status->playlist = -1;
720         status->playlistLength = -1;
721         status->state = -1;
722         status->song = 0;
723         status->songid = 0;
724         status->elapsedTime = 0;
725         status->totalTime = 0;
726         status->bitRate = 0;
727         status->sampleRate = 0;
728         status->bits = 0;
729         status->channels = 0;
730         status->crossfade = -1;
731         status->error = NULL;
732         status->updatingDb = 0;
734         if(connection->error) {
735                 free(status);
736                 return NULL;
737         }
738         while(connection->returnElement) {
739                 mpd_ReturnElement * re = connection->returnElement;
740                 if(strcmp(re->name,"volume")==0) {
741                         status->volume = atoi(re->value);
742                 }
743                 else if(strcmp(re->name,"repeat")==0) {
744                         status->repeat = atoi(re->value);
745                 }
746                 else if(strcmp(re->name,"random")==0) {
747                         status->random = atoi(re->value);
748                 }
749                 else if(strcmp(re->name,"playlist")==0) {
750                         status->playlist = strtol(re->value,NULL,10);
751                 }
752                 else if(strcmp(re->name,"playlistlength")==0) {
753                         status->playlistLength = atoi(re->value);
754                 }
755                 else if(strcmp(re->name,"bitrate")==0) {
756                         status->bitRate = atoi(re->value);
757                 }
758                 else if(strcmp(re->name,"state")==0) {
759                         if(strcmp(re->value,"play")==0) {
760                                 status->state = MPD_STATUS_STATE_PLAY;
761                         }
762                         else if(strcmp(re->value,"stop")==0) {
763                                 status->state = MPD_STATUS_STATE_STOP;
764                         }
765                         else if(strcmp(re->value,"pause")==0) {
766                                 status->state = MPD_STATUS_STATE_PAUSE;
767                         }
768                         else {
769                                 status->state = MPD_STATUS_STATE_UNKNOWN;
770                         }
771                 }
772                 else if(strcmp(re->name,"song")==0) {
773                         status->song = atoi(re->value);
774                 }
775                 else if(strcmp(re->name,"songid")==0) {
776                         status->songid = atoi(re->value);
777                 }
778                 else if(strcmp(re->name,"time")==0) {
779                         char * tok = strchr(re->value,':');
780                         /* the second strchr below is a safety check */
781                         if (tok && (strchr(tok,0) > (tok+1))) {
782                                 /* atoi stops at the first non-[0-9] char: */
783                                 status->elapsedTime = atoi(re->value);
784                                 status->totalTime = atoi(tok+1);
785                         }
786                 }
787                 else if(strcmp(re->name,"error")==0) {
788                         status->error = strdup(re->value);
789                 }
790                 else if(strcmp(re->name,"xfade")==0) {
791                         status->crossfade = atoi(re->value);
792                 }
793                 else if(strcmp(re->name,"updating_db")==0) {
794                         status->updatingDb = atoi(re->value);
795                 }
796                 else if(strcmp(re->name,"audio")==0) {
797                         char * tok = strchr(re->value,':');
798                         if (tok && (strchr(tok,0) > (tok+1))) {
799                                 status->sampleRate = atoi(re->value);
800                                 status->bits = atoi(++tok);
801                                 tok = strchr(tok,':');
802                                 if (tok && (strchr(tok,0) > (tok+1)))
803                                         status->channels = atoi(tok+1);
804                         }
805                 }
807                 mpd_getNextReturnElement(connection);
808                 if(connection->error) {
809                         free(status);
810                         return NULL;
811                 }
812         }
814         if(connection->error) {
815                 free(status);
816                 return NULL;
817         }
818         else if(status->state<0) {
819                 strcpy(connection->errorStr,"state not found");
820                 connection->error = 1;
821                 free(status);
822                 return NULL;
823         }
825         return status;
828 void mpd_freeStatus(mpd_Status * status) {
829         if(status->error) free(status->error);
830         free(status);
833 void mpd_sendStatsCommand(mpd_Connection * connection) {
834         mpd_executeCommand(connection,"stats\n");
837 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
838         mpd_Stats * stats;
840         /*mpd_executeCommand(connection,"stats\n");
842         if(connection->error) return NULL;*/
844         if(connection->doneProcessing || (connection->listOks &&
845            connection->doneListOk))
846         {
847                 return NULL;
848         }
850         if(!connection->returnElement) mpd_getNextReturnElement(connection);
852         stats = malloc(sizeof(mpd_Stats));
853         stats->numberOfArtists = 0;
854         stats->numberOfAlbums = 0;
855         stats->numberOfSongs = 0;
856         stats->uptime = 0;
857         stats->dbUpdateTime = 0;
858         stats->playTime = 0;
859         stats->dbPlayTime = 0;
861         if(connection->error) {
862                 free(stats);
863                 return NULL;
864         }
865         while(connection->returnElement) {
866                 mpd_ReturnElement * re = connection->returnElement;
867                 if(strcmp(re->name,"artists")==0) {
868                         stats->numberOfArtists = atoi(re->value);
869                 }
870                 else if(strcmp(re->name,"albums")==0) {
871                         stats->numberOfAlbums = atoi(re->value);
872                 }
873                 else if(strcmp(re->name,"songs")==0) {
874                         stats->numberOfSongs = atoi(re->value);
875                 }
876                 else if(strcmp(re->name,"uptime")==0) {
877                         stats->uptime = strtol(re->value,NULL,10);
878                 }
879                 else if(strcmp(re->name,"db_update")==0) {
880                         stats->dbUpdateTime = strtol(re->value,NULL,10);
881                 }
882                 else if(strcmp(re->name,"playtime")==0) {
883                         stats->playTime = strtol(re->value,NULL,10);
884                 }
885                 else if(strcmp(re->name,"db_playtime")==0) {
886                         stats->dbPlayTime = strtol(re->value,NULL,10);
887                 }
889                 mpd_getNextReturnElement(connection);
890                 if(connection->error) {
891                         free(stats);
892                         return NULL;
893                 }
894         }
896         if(connection->error) {
897                 free(stats);
898                 return NULL;
899         }
901         return stats;
904 void mpd_freeStats(mpd_Stats * stats) {
905         free(stats);
908 static void mpd_initDirectory(mpd_Directory * directory) {
909         directory->path = NULL;
912 static void mpd_finishDirectory(mpd_Directory * directory) {
913         if (directory->path)
914                 str_pool_put(directory->path);
917 mpd_Directory * mpd_newDirectory(void) {
918         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
920         mpd_initDirectory(directory);
922         return directory;
925 void mpd_freeDirectory(mpd_Directory * directory) {
926         mpd_finishDirectory(directory);
928         free(directory);
931 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
932         mpd_Directory * ret = mpd_newDirectory();
934         if (directory->path)
935                 ret->path = str_pool_dup(directory->path);
937         return ret;
940 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
941         playlist->path = NULL;
944 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
945         if (playlist->path)
946                 str_pool_put(playlist->path);
949 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
950         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
952         mpd_initPlaylistFile(playlist);
954         return playlist;
957 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
958         mpd_finishPlaylistFile(playlist);
959         free(playlist);
962 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
963         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
965         if (playlist->path)
966                 ret->path = str_pool_dup(playlist->path);
968         return ret;
971 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
972         entity->info.directory = NULL;
975 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
976         if(entity->info.directory) {
977                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
978                         mpd_freeDirectory(entity->info.directory);
979                 }
980                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
981                         mpd_freeSong(entity->info.song);
982                 }
983                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
984                         mpd_freePlaylistFile(entity->info.playlistFile);
985                 }
986         }
989 mpd_InfoEntity * mpd_newInfoEntity(void) {
990         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
992         mpd_initInfoEntity(entity);
994         return entity;
997 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
998         mpd_finishInfoEntity(entity);
999         free(entity);
1002 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1003         mpd_executeCommand(connection,command);
1006 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1007         mpd_InfoEntity * entity = NULL;
1009         if(connection->doneProcessing || (connection->listOks &&
1010            connection->doneListOk)) {
1011                 return NULL;
1012         }
1014         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1016         if(connection->returnElement) {
1017                 if(strcmp(connection->returnElement->name,"file")==0) {
1018                         entity = mpd_newInfoEntity();
1019                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1020                         entity->info.song = mpd_newSong();
1021                         entity->info.song->file =
1022                                 str_pool_dup(connection->returnElement->value);
1023                 }
1024                 else if(strcmp(connection->returnElement->name,
1025                                         "directory")==0) {
1026                         entity = mpd_newInfoEntity();
1027                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1028                         entity->info.directory = mpd_newDirectory();
1029                         entity->info.directory->path =
1030                                 str_pool_dup(connection->returnElement->value);
1031                 }
1032                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1033                         entity = mpd_newInfoEntity();
1034                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1035                         entity->info.playlistFile = mpd_newPlaylistFile();
1036                         entity->info.playlistFile->path =
1037                                 str_pool_dup(connection->returnElement->value);
1038                 }
1039                 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1040                         entity = mpd_newInfoEntity();
1041                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1042                         entity->info.song = mpd_newSong();
1043                         entity->info.song->pos = atoi(connection->returnElement->value);
1044                 }
1045                 else {
1046                         connection->error = 1;
1047                         strcpy(connection->errorStr,"problem parsing song info");
1048                         return NULL;
1049                 }
1050         }
1051         else return NULL;
1053         mpd_getNextReturnElement(connection);
1054         while(connection->returnElement) {
1055                 mpd_ReturnElement * re = connection->returnElement;
1057                 if(strcmp(re->name,"file")==0) return entity;
1058                 else if(strcmp(re->name,"directory")==0) return entity;
1059                 else if(strcmp(re->name,"playlist")==0) return entity;
1060                 else if(strcmp(re->name,"cpos")==0) return entity;
1062                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1063                                 strlen(re->value)) {
1064                         if(!entity->info.song->artist &&
1065                                         strcmp(re->name,"Artist")==0) {
1066                                 entity->info.song->artist = str_pool_dup(re->value);
1067                         }
1068                         else if(!entity->info.song->album &&
1069                                         strcmp(re->name,"Album")==0) {
1070                                 entity->info.song->album = str_pool_dup(re->value);
1071                         }
1072                         else if(!entity->info.song->title &&
1073                                         strcmp(re->name,"Title")==0) {
1074                                 entity->info.song->title = str_pool_dup(re->value);
1075                         }
1076                         else if(!entity->info.song->track &&
1077                                         strcmp(re->name,"Track")==0) {
1078                                 entity->info.song->track = str_pool_dup(re->value);
1079                         }
1080                         else if(!entity->info.song->name &&
1081                                         strcmp(re->name,"Name")==0) {
1082                                 entity->info.song->name = str_pool_dup(re->value);
1083                         }
1084                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1085                                         strcmp(re->name,"Time")==0) {
1086                                 entity->info.song->time = atoi(re->value);
1087                         }
1088                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1089                                         strcmp(re->name,"Pos")==0) {
1090                                 entity->info.song->pos = atoi(re->value);
1091                         }
1092                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
1093                                         strcmp(re->name,"Id")==0) {
1094                                 entity->info.song->id = atoi(re->value);
1095                         }
1096                         else if(!entity->info.song->date &&
1097                                         strcmp(re->name, "Date") == 0) {
1098                                 entity->info.song->date = str_pool_dup(re->value);
1099                         }
1100                         else if(!entity->info.song->genre &&
1101                                         strcmp(re->name, "Genre") == 0) {
1102                                 entity->info.song->genre = str_pool_dup(re->value);
1103                         }
1104                         else if(!entity->info.song->composer &&
1105                                         strcmp(re->name, "Composer") == 0) {
1106                                 entity->info.song->composer = str_pool_dup(re->value);
1107                         }
1108                         else if(!entity->info.song->disc &&
1109                                         strcmp(re->name, "Disc") == 0) {
1110                                 entity->info.song->disc = str_pool_dup(re->value);
1111                         }
1112                         else if(!entity->info.song->comment &&
1113                                         strcmp(re->name, "Comment") == 0) {
1114                                 entity->info.song->comment = str_pool_dup(re->value);
1115                         }
1116                 }
1117                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1118                 }
1119                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1120                 }
1122                 mpd_getNextReturnElement(connection);
1123         }
1125         return entity;
1128 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1129                 const char * name)
1131         if(connection->doneProcessing || (connection->listOks &&
1132                                 connection->doneListOk))
1133         {
1134                 return NULL;
1135         }
1137         mpd_getNextReturnElement(connection);
1138         while(connection->returnElement) {
1139                 mpd_ReturnElement * re = connection->returnElement;
1141                 if(strcmp(re->name,name)==0) return strdup(re->value);
1142                 mpd_getNextReturnElement(connection);
1143         }
1145         return NULL;
1148 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1149         if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1150         {
1151                 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1152         }
1153         return NULL;
1156 char * mpd_getNextArtist(mpd_Connection * connection) {
1157         return mpd_getNextReturnElementNamed(connection,"Artist");
1160 char * mpd_getNextAlbum(mpd_Connection * connection) {
1161         return mpd_getNextReturnElementNamed(connection,"Album");
1164 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1165         char * string = malloc(strlen("playlistinfo")+25);
1166         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1167         mpd_sendInfoCommand(connection,string);
1168         free(string);
1171 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1172         char * string = malloc(strlen("playlistid")+25);
1173         sprintf(string, "playlistid \"%i\"\n", id);
1174         mpd_sendInfoCommand(connection, string);
1175         free(string);
1178 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1179         char * string = malloc(strlen("plchanges")+25);
1180         sprintf(string,"plchanges \"%lld\"\n",playlist);
1181         mpd_sendInfoCommand(connection,string);
1182         free(string);
1185 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1186         char * string = malloc(strlen("plchangesposid")+25);
1187         sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1188         mpd_sendInfoCommand(connection,string);
1189         free(string);
1192 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1193         char * sDir = mpd_sanitizeArg(dir);
1194         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1195         sprintf(string,"listall \"%s\"\n",sDir);
1196         mpd_sendInfoCommand(connection,string);
1197         free(string);
1198         free(sDir);
1201 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1202         char * sDir = mpd_sanitizeArg(dir);
1203         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1204         sprintf(string,"listallinfo \"%s\"\n",sDir);
1205         mpd_sendInfoCommand(connection,string);
1206         free(string);
1207         free(sDir);
1210 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1211         char * sDir = mpd_sanitizeArg(dir);
1212         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1213         sprintf(string,"lsinfo \"%s\"\n",sDir);
1214         mpd_sendInfoCommand(connection,string);
1215         free(string);
1216         free(sDir);
1219 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1220         mpd_executeCommand(connection,"currentsong\n");
1223 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1224                 const char * str)
1226         char st[10];
1227         char * string;
1228         char * sanitStr = mpd_sanitizeArg(str);
1229         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1230         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1231         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1232         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1233         else {
1234                 connection->error = 1;
1235                 strcpy(connection->errorStr,"unknown table for search");
1236                 return;
1237         }
1238         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1239         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1240         mpd_sendInfoCommand(connection,string);
1241         free(string);
1242         free(sanitStr);
1245 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1246                 const char * str)
1248         char st[10];
1249         char * string;
1250         char * sanitStr = mpd_sanitizeArg(str);
1251         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1252         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1253         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1254         else {
1255                 connection->error = 1;
1256                 strcpy(connection->errorStr,"unknown table for find");
1257                 return;
1258         }
1259         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1260         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1261         mpd_sendInfoCommand(connection,string);
1262         free(string);
1263         free(sanitStr);
1266 void mpd_sendListCommand(mpd_Connection * connection, int table,
1267                 const char * arg1)
1269         char st[10];
1270         char * string;
1271         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1272         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1273         else {
1274                 connection->error = 1;
1275                 strcpy(connection->errorStr,"unknown table for list");
1276                 return;
1277         }
1278         if(arg1) {
1279                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1280                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1281                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1282                 free(sanitArg1);
1283         }
1284         else {
1285                 string = malloc(strlen("list")+strlen(st)+3);
1286                 sprintf(string,"list %s\n",st);
1287         }
1288         mpd_sendInfoCommand(connection,string);
1289         free(string);
1292 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1293         char * sFile = mpd_sanitizeArg(file);
1294         char * string = malloc(strlen("add")+strlen(sFile)+5);
1295         sprintf(string,"add \"%s\"\n",sFile);
1296         mpd_executeCommand(connection,string);
1297         free(string);
1298         free(sFile);
1301 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1302         char * string = malloc(strlen("delete")+25);
1303         sprintf(string,"delete \"%i\"\n",songPos);
1304         mpd_sendInfoCommand(connection,string);
1305         free(string);
1308 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1309         char * string = malloc(strlen("deleteid")+25);
1310         sprintf(string, "deleteid \"%i\"\n", id);
1311         mpd_sendInfoCommand(connection,string);
1312         free(string);
1315 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1316         char * sName = mpd_sanitizeArg(name);
1317         char * string = malloc(strlen("save")+strlen(sName)+5);
1318         sprintf(string,"save \"%s\"\n",sName);
1319         mpd_executeCommand(connection,string);
1320         free(string);
1321         free(sName);
1324 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1325         char * sName = mpd_sanitizeArg(name);
1326         char * string = malloc(strlen("load")+strlen(sName)+5);
1327         sprintf(string,"load \"%s\"\n",sName);
1328         mpd_executeCommand(connection,string);
1329         free(string);
1330         free(sName);
1333 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1334         char * sName = mpd_sanitizeArg(name);
1335         char * string = malloc(strlen("rm")+strlen(sName)+5);
1336         sprintf(string,"rm \"%s\"\n",sName);
1337         mpd_executeCommand(connection,string);
1338         free(string);
1339         free(sName);
1342 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1343         mpd_executeCommand(connection,"shuffle\n");
1346 void mpd_sendClearCommand(mpd_Connection * connection) {
1347         mpd_executeCommand(connection,"clear\n");
1350 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1351         char * string = malloc(strlen("play")+25);
1352         sprintf(string,"play \"%i\"\n",songPos);
1353         mpd_sendInfoCommand(connection,string);
1354         free(string);
1357 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1358         char * string = malloc(strlen("playid")+25);
1359         sprintf(string,"playid \"%i\"\n",id);
1360         mpd_sendInfoCommand(connection,string);
1361         free(string);
1364 void mpd_sendStopCommand(mpd_Connection * connection) {
1365         mpd_executeCommand(connection,"stop\n");
1368 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1369         char * string = malloc(strlen("pause")+25);
1370         sprintf(string,"pause \"%i\"\n",pauseMode);
1371         mpd_executeCommand(connection,string);
1372         free(string);
1375 void mpd_sendNextCommand(mpd_Connection * connection) {
1376         mpd_executeCommand(connection,"next\n");
1379 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1380         char * string = malloc(strlen("move")+25);
1381         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1382         mpd_sendInfoCommand(connection,string);
1383         free(string);
1386 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1387         char * string = malloc(strlen("moveid")+25);
1388         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1389         mpd_sendInfoCommand(connection,string);
1390         free(string);
1393 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1394         char * string = malloc(strlen("swap")+25);
1395         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1396         mpd_sendInfoCommand(connection,string);
1397         free(string);
1400 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1401         char * string = malloc(strlen("swapid")+25);
1402         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1403         mpd_sendInfoCommand(connection,string);
1404         free(string);
1407 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1408         char * string = malloc(strlen("seek")+25);
1409         sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1410         mpd_sendInfoCommand(connection,string);
1411         free(string);
1414 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1415         char * string = malloc(strlen("seekid")+25);
1416         sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1417         mpd_sendInfoCommand(connection,string);
1418         free(string);
1421 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1422         char *sPath = mpd_sanitizeArg(path);
1423         char * string = malloc(strlen("update")+strlen(sPath)+5);
1424         sprintf(string,"update \"%s\"\n",sPath);
1425         mpd_sendInfoCommand(connection,string);
1426         free(string);
1427         free(sPath);
1430 int mpd_getUpdateId(mpd_Connection * connection) {
1431         char * jobid;
1432         int ret = 0;
1434         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1435         if(jobid) {
1436                 ret = atoi(jobid);
1437                 free(jobid);
1438         }
1440         return ret;
1443 void mpd_sendPrevCommand(mpd_Connection * connection) {
1444         mpd_executeCommand(connection,"previous\n");
1447 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1448         char * string = malloc(strlen("repeat")+25);
1449         sprintf(string,"repeat \"%i\"\n",repeatMode);
1450         mpd_executeCommand(connection,string);
1451         free(string);
1454 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1455         char * string = malloc(strlen("random")+25);
1456         sprintf(string,"random \"%i\"\n",randomMode);
1457         mpd_executeCommand(connection,string);
1458         free(string);
1461 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1462         char * string = malloc(strlen("setvol")+25);
1463         sprintf(string,"setvol \"%i\"\n",volumeChange);
1464         mpd_executeCommand(connection,string);
1465         free(string);
1468 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1469         char * string = malloc(strlen("volume")+25);
1470         sprintf(string,"volume \"%i\"\n",volumeChange);
1471         mpd_executeCommand(connection,string);
1472         free(string);
1475 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1476         char * string = malloc(strlen("crossfade")+25);
1477         sprintf(string,"crossfade \"%i\"\n",seconds);
1478         mpd_executeCommand(connection,string);
1479         free(string);
1482 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1483         char * sPass = mpd_sanitizeArg(pass);
1484         char * string = malloc(strlen("password")+strlen(sPass)+5);
1485         sprintf(string,"password \"%s\"\n",sPass);
1486         mpd_executeCommand(connection,string);
1487         free(string);
1488         free(sPass);
1491 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1492         if(connection->commandList) {
1493                 strcpy(connection->errorStr,"already in command list mode");
1494                 connection->error = 1;
1495                 return;
1496         }
1497         connection->commandList = COMMAND_LIST;
1498         mpd_executeCommand(connection,"command_list_begin\n");
1501 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1502         if(connection->commandList) {
1503                 strcpy(connection->errorStr,"already in command list mode");
1504                 connection->error = 1;
1505                 return;
1506         }
1507         connection->commandList = COMMAND_LIST_OK;
1508         mpd_executeCommand(connection,"command_list_ok_begin\n");
1509         connection->listOks = 0;
1512 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1513         if(!connection->commandList) {
1514                 strcpy(connection->errorStr,"not in command list mode");
1515                 connection->error = 1;
1516                 return;
1517         }
1518         connection->commandList = 0;
1519         mpd_executeCommand(connection,"command_list_end\n");
1522 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1523         mpd_executeCommand(connection,"outputs\n");
1526 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1527         mpd_OutputEntity * output = NULL;
1529         if(connection->doneProcessing || (connection->listOks &&
1530                                 connection->doneListOk))
1531         {
1532                 return NULL;
1533         }
1535         if(connection->error) return NULL;
1537         output = malloc(sizeof(mpd_OutputEntity));
1538         output->id = -10;
1539         output->name = NULL;
1540         output->enabled = 0;
1542         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1544         while(connection->returnElement) {
1545                 mpd_ReturnElement * re = connection->returnElement;
1546                 if(strcmp(re->name,"outputid")==0) {
1547                         if(output!=NULL && output->id>=0) return output;
1548                         output->id = atoi(re->value);
1549                 }
1550                 else if(strcmp(re->name,"outputname")==0) {
1551                         output->name = strdup(re->value);
1552                 }
1553                 else if(strcmp(re->name,"outputenabled")==0) {
1554                         output->enabled = atoi(re->value);
1555                 }
1557                 mpd_getNextReturnElement(connection);
1558                 if(connection->error) {
1559                         free(output);
1560                         return NULL;
1561                 }
1563         }
1565         return output;
1568 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1569         char * string = malloc(strlen("enableoutput")+25);
1570         sprintf(string,"enableoutput \"%i\"\n",outputId);
1571         mpd_executeCommand(connection,string);
1572         free(string);
1575 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1576         char * string = malloc(strlen("disableoutput")+25);
1577         sprintf(string,"disableoutput \"%i\"\n",outputId);
1578         mpd_executeCommand(connection,string);
1579         free(string);
1582 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1583         free(output->name);
1584         free(output);
1587 /**
1588  * mpd_sendNotCommandsCommand
1589  * odd naming, but it gets the not allowed commands
1590  */
1592 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1593         mpd_executeCommand(connection,"notcommands\n");
1596 /**
1597  * mpd_sendCommandsCommand
1598  * odd naming, but it gets the allowed commands
1599  */
1601 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1602         mpd_executeCommand(connection,"commands\n");
1604 /**
1605  * Get the next returned command
1606  */
1607 char * mpd_getNextCommand(mpd_Connection * connection) {
1608         return mpd_getNextReturnElementNamed(connection,"command");
1611 void mpd_startSearch(mpd_Connection * connection,int exact) {
1612         if(connection->request) {
1613                 /* search/find allready in progress */
1614                 /* TODO: set error here?  */
1615                 return;
1616         }
1617         if(exact){
1618                 connection->request = strdup("find");
1619         }
1620         else{
1621                 connection->request = strdup("search");
1622         }
1626 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1627         if(connection->request) {
1628                 /* search/find allready in progress */
1629                 /* TODO: set error here?  */
1630                 return;
1631         }
1632         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1633                 /* set error here */
1634                 return;
1635         }
1637         connection->request = malloc(sizeof(char)*(
1638                                 /* length of the field name */
1639                                 strlen(mpdTagItemKeys[field])+
1640                                 /* "list"+space+\0 */
1641                                 6
1642                                 ));
1643         sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1648 void mpd_addConstraintSearch(mpd_Connection *connection,
1649                 int field,
1650                 char *name)
1652         char *arg = NULL;
1653         if(!connection->request){
1654                 return;
1655         }
1656         if(name == NULL) {
1657                 return;
1658         }
1659         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1660                 return;
1661         }
1662         /* clean up the query */
1663         arg = mpd_sanitizeArg(name);
1664         /* create space for the query */
1665         connection->request = realloc(connection->request, (
1666                          /* length of the old string */
1667                          strlen(connection->request)+
1668                          /* space between */
1669                          1+
1670                          /* length of the field name */
1671                          strlen(mpdTagItemKeys[field])+
1672                          /* space plus starting " */
1673                          2+
1674                          /* length of search term */
1675                          strlen(arg)+
1676                          /* closign " +\0 that is added sprintf */
1677                          2
1678                         )*sizeof(char));
1679         /* and form the query */
1680         sprintf(connection->request, "%s %s \"%s\"",
1681                         connection->request,
1682                         mpdTagItemKeys[field],
1683                         arg);
1684         free(arg);
1688 void mpd_commitSearch(mpd_Connection *connection)
1690         if(connection->request)
1691         {
1692                 int length = strlen(connection->request);
1693                 /* fixing up the string for mpd to like */
1694                 connection->request = realloc(connection->request,
1695                                 (length+        /* old length */
1696                                  2              /* closing \n and \0 */
1697                                 )*sizeof(char));
1698                 connection->request[length] = '\n';
1699                 connection->request[length+1] = '\0';
1700                 /* and off we go */
1701                 mpd_sendInfoCommand(connection, connection->request);
1702                 /* clean up a bit */
1703                 free(connection->request);
1704                 connection->request = NULL;
1705         }
1708 /**
1709  * @param connection a MpdConnection
1710  * @param path  the path to the playlist.
1711  *
1712  * List the content, with full metadata, of a stored playlist.
1713  *
1714  */
1715 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1717         char *arg = mpd_sanitizeArg(path);
1718         char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1719         sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1720         mpd_sendInfoCommand(connection, query);
1721         free(arg);
1722         free(query);
1725 /**
1726  * @param connection a MpdConnection
1727  * @param path  the path to the playlist.
1728  *
1729  * List the content of a stored playlist.
1730  *
1731  */
1732 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1734         char *arg = mpd_sanitizeArg(path);
1735         char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1736         sprintf(query, "listplaylist \"%s\"\n",arg);
1737         mpd_sendInfoCommand(connection, query);
1738         free(arg);
1739         free(query);