Code

b0498445e0adafd997922e17f950835297da4905
[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         char * output =  NULL;
387         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
388         struct timeval tv;
389         fd_set fds;
390         strcpy(connection->buffer,"");
391         connection->buflen = 0;
392         connection->bufstart = 0;
393         strcpy(connection->errorStr,"");
394         connection->error = 0;
395         connection->doneProcessing = 0;
396         connection->commandList = 0;
397         connection->listOks = 0;
398         connection->doneListOk = 0;
399         connection->returnElement = NULL;
400         connection->request = NULL;
402         if (winsock_dll_error(connection))
403                 return connection;
405 #ifndef WIN32
406         if (host[0] == '/')
407                 err = mpd_connect_un(connection, host, timeout);
408         else
409 #endif
410                 err = mpd_connect(connection, host, port, timeout);
411         if (err < 0)
412                 return connection;
414         while(!(rt = strstr(connection->buffer,"\n"))) {
415                 tv.tv_sec = connection->timeout.tv_sec;
416                 tv.tv_usec = connection->timeout.tv_usec;
417                 FD_ZERO(&fds);
418                 FD_SET(connection->sock,&fds);
419                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
420                         int readed;
421                         readed = recv(connection->sock,
422                                         &(connection->buffer[connection->buflen]),
423                                         MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
424                         if(readed<=0) {
425                                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
426                                                 "problems getting a response from"
427                                                 " \"%s\" on port %i : %s",host,
428                                                 port, strerror(errno));
429                                 connection->error = MPD_ERROR_NORESPONSE;
430                                 return connection;
431                         }
432                         connection->buflen+=readed;
433                         connection->buffer[connection->buflen] = '\0';
434                 }
435                 else if(err<0) {
436                         if (SELECT_ERRNO_IGNORE)
437                                 continue;
438                         snprintf(connection->errorStr,
439                                         MPD_BUFFER_MAX_LENGTH,
440                                         "problems connecting to \"%s\" on port"
441                                         " %i",host,port);
442                         connection->error = MPD_ERROR_CONNPORT;
443                         return connection;
444                 }
445                 else {
446                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
447                                         "timeout in attempting to get a response from"
448                                         " \"%s\" on port %i",host,port);
449                         connection->error = MPD_ERROR_NORESPONSE;
450                         return connection;
451                 }
452         }
454         *rt = '\0';
455         output = strdup(connection->buffer);
456         strcpy(connection->buffer,rt+1);
457         connection->buflen = strlen(connection->buffer);
459         if(mpd_parseWelcome(connection,host,port,output) == 0)
460                 connection->doneProcessing = 1;
462         free(output);
464         return connection;
467 void mpd_clearError(mpd_Connection * connection) {
468         connection->error = 0;
469         connection->errorStr[0] = '\0';
472 void mpd_closeConnection(mpd_Connection * connection) {
473         closesocket(connection->sock);
474         if(connection->returnElement) free(connection->returnElement);
475         if(connection->request) free(connection->request);
476         free(connection);
477         WSACleanup();
480 static void mpd_executeCommand(mpd_Connection *connection,
481                                const char *command) {
482         int ret;
483         struct timeval tv;
484         fd_set fds;
485         const char *commandPtr = command;
486         int commandLen = strlen(command);
488         if(!connection->doneProcessing && !connection->commandList) {
489                 strcpy(connection->errorStr,"not done processing current command");
490                 connection->error = 1;
491                 return;
492         }
494         mpd_clearError(connection);
496         FD_ZERO(&fds);
497         FD_SET(connection->sock,&fds);
498         tv.tv_sec = connection->timeout.tv_sec;
499         tv.tv_usec = connection->timeout.tv_usec;
501         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
502                         (ret==-1 && SELECT_ERRNO_IGNORE)) {
503                 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
504                 if(ret<=0)
505                 {
506                         if (SENDRECV_ERRNO_IGNORE) continue;
507                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
508                                  "problems giving command \"%s\"",command);
509                         connection->error = MPD_ERROR_SENDING;
510                         return;
511                 }
512                 else {
513                         commandPtr+=ret;
514                         commandLen-=ret;
515                 }
517                 if(commandLen<=0) break;
518         }
520         if(commandLen>0) {
521                 perror("");
522                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
523                          "timeout sending command \"%s\"",command);
524                 connection->error = MPD_ERROR_TIMEOUT;
525                 return;
526         }
528         if(!connection->commandList) connection->doneProcessing = 0;
529         else if(connection->commandList == COMMAND_LIST_OK) {
530                 connection->listOks++;
531         }
534 static void mpd_getNextReturnElement(mpd_Connection * connection) {
535         char * output = NULL;
536         char * rt = NULL;
537         char * name = NULL;
538         char * value = NULL;
539         fd_set fds;
540         struct timeval tv;
541         char * tok = NULL;
542         int readed;
543         char * bufferCheck = NULL;
544         int err;
545         int pos;
547         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
548         connection->returnElement = NULL;
550         if(connection->doneProcessing || (connection->listOks &&
551            connection->doneListOk))
552         {
553                 strcpy(connection->errorStr,"already done processing current command");
554                 connection->error = 1;
555                 return;
556         }
558         bufferCheck = connection->buffer+connection->bufstart;
559         while(connection->bufstart>=connection->buflen ||
560                         !(rt = strchr(bufferCheck,'\n'))) {
561                 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
562                         memmove(connection->buffer,
563                                         connection->buffer+
564                                         connection->bufstart,
565                                         connection->buflen-
566                                         connection->bufstart+1);
567                         connection->buflen-=connection->bufstart;
568                         connection->bufstart = 0;
569                 }
570                 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
571                         strcpy(connection->errorStr,"buffer overrun");
572                         connection->error = MPD_ERROR_BUFFEROVERRUN;
573                         connection->doneProcessing = 1;
574                         connection->doneListOk = 0;
575                         return;
576                 }
577                 bufferCheck = connection->buffer+connection->buflen;
578                 tv.tv_sec = connection->timeout.tv_sec;
579                 tv.tv_usec = connection->timeout.tv_usec;
580                 FD_ZERO(&fds);
581                 FD_SET(connection->sock,&fds);
582                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
583                         readed = recv(connection->sock,
584                                         connection->buffer+connection->buflen,
585                                         MPD_BUFFER_MAX_LENGTH-connection->buflen,
586                                         MSG_DONTWAIT);
587                         if(readed<0 && SENDRECV_ERRNO_IGNORE) {
588                                 continue;
589                         }
590                         if(readed<=0) {
591                                 strcpy(connection->errorStr,"connection"
592                                        " closed");
593                                 connection->error = MPD_ERROR_CONNCLOSED;
594                                 connection->doneProcessing = 1;
595                                 connection->doneListOk = 0;
596                                 return;
597                         }
598                         connection->buflen+=readed;
599                         connection->buffer[connection->buflen] = '\0';
600                 }
601                 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
602                 else {
603                         strcpy(connection->errorStr,"connection timeout");
604                         connection->error = MPD_ERROR_TIMEOUT;
605                         connection->doneProcessing = 1;
606                         connection->doneListOk = 0;
607                         return;
608                 }
609         }
611         *rt = '\0';
612         output = connection->buffer+connection->bufstart;
613         connection->bufstart = rt - connection->buffer + 1;
615         if(strcmp(output,"OK")==0) {
616                 if(connection->listOks > 0) {
617                         strcpy(connection->errorStr, "expected more list_OK's");
618                         connection->error = 1;
619                 }
620                 connection->listOks = 0;
621                 connection->doneProcessing = 1;
622                 connection->doneListOk = 0;
623                 return;
624         }
626         if(strcmp(output, "list_OK") == 0) {
627                 if(!connection->listOks) {
628                         strcpy(connection->errorStr,
629                                         "got an unexpected list_OK");
630                         connection->error = 1;
631                 }
632                 else {
633                         connection->doneListOk = 1;
634                         connection->listOks--;
635                 }
636                 return;
637         }
639         if(strncmp(output,"ACK",strlen("ACK"))==0) {
640                 char * test;
641                 char * needle;
642                 int val;
644                 strcpy(connection->errorStr, output);
645                 connection->error = MPD_ERROR_ACK;
646                 connection->errorCode = MPD_ACK_ERROR_UNK;
647                 connection->errorAt = MPD_ERROR_AT_UNK;
648                 connection->doneProcessing = 1;
649                 connection->doneListOk = 0;
651                 needle = strchr(output, '[');
652                 if(!needle) return;
653                 val = strtol(needle+1, &test, 10);
654                 if(*test != '@') return;
655                 connection->errorCode = val;
656                 val = strtol(test+1, &test, 10);
657                 if(*test != ']') return;
658                 connection->errorAt = val;
659                 return;
660         }
662         tok = strchr(output, ':');
663         if (!tok) return;
664         pos = tok - output;
665         value = ++tok;
666         name = output;
667         name[pos] = '\0';
669         if(value[0]==' ') {
670                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
671         }
672         else {
673                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
674                                         "error parsing: %s:%s",name,value);
675                 connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0';
676                 connection->error = 1;
677         }
680 void mpd_finishCommand(mpd_Connection * connection) {
681         while(!connection->doneProcessing) {
682                 if(connection->doneListOk) connection->doneListOk = 0;
683                 mpd_getNextReturnElement(connection);
684         }
687 static void mpd_finishListOkCommand(mpd_Connection * connection) {
688         while(!connection->doneProcessing && connection->listOks &&
689                         !connection->doneListOk)
690         {
691                 mpd_getNextReturnElement(connection);
692         }
695 int mpd_nextListOkCommand(mpd_Connection * connection) {
696         mpd_finishListOkCommand(connection);
697         if(!connection->doneProcessing) connection->doneListOk = 0;
698         if(connection->listOks == 0 || connection->doneProcessing) return -1;
699         return 0;
702 void mpd_sendStatusCommand(mpd_Connection * connection) {
703         mpd_executeCommand(connection,"status\n");
706 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
707         mpd_Status * status;
709         /*mpd_executeCommand(connection,"status\n");
711         if(connection->error) return NULL;*/
713         if(connection->doneProcessing || (connection->listOks &&
714            connection->doneListOk))
715         {
716                 return NULL;
717         }
719         if(!connection->returnElement) mpd_getNextReturnElement(connection);
721         status = malloc(sizeof(mpd_Status));
722         status->volume = -1;
723         status->repeat = 0;
724         status->random = 0;
725         status->playlist = -1;
726         status->playlistLength = -1;
727         status->state = -1;
728         status->song = 0;
729         status->songid = 0;
730         status->elapsedTime = 0;
731         status->totalTime = 0;
732         status->bitRate = 0;
733         status->sampleRate = 0;
734         status->bits = 0;
735         status->channels = 0;
736         status->crossfade = -1;
737         status->error = NULL;
738         status->updatingDb = 0;
740         if(connection->error) {
741                 free(status);
742                 return NULL;
743         }
744         while(connection->returnElement) {
745                 mpd_ReturnElement * re = connection->returnElement;
746                 if(strcmp(re->name,"volume")==0) {
747                         status->volume = atoi(re->value);
748                 }
749                 else if(strcmp(re->name,"repeat")==0) {
750                         status->repeat = atoi(re->value);
751                 }
752                 else if(strcmp(re->name,"random")==0) {
753                         status->random = atoi(re->value);
754                 }
755                 else if(strcmp(re->name,"playlist")==0) {
756                         status->playlist = strtol(re->value,NULL,10);
757                 }
758                 else if(strcmp(re->name,"playlistlength")==0) {
759                         status->playlistLength = atoi(re->value);
760                 }
761                 else if(strcmp(re->name,"bitrate")==0) {
762                         status->bitRate = atoi(re->value);
763                 }
764                 else if(strcmp(re->name,"state")==0) {
765                         if(strcmp(re->value,"play")==0) {
766                                 status->state = MPD_STATUS_STATE_PLAY;
767                         }
768                         else if(strcmp(re->value,"stop")==0) {
769                                 status->state = MPD_STATUS_STATE_STOP;
770                         }
771                         else if(strcmp(re->value,"pause")==0) {
772                                 status->state = MPD_STATUS_STATE_PAUSE;
773                         }
774                         else {
775                                 status->state = MPD_STATUS_STATE_UNKNOWN;
776                         }
777                 }
778                 else if(strcmp(re->name,"song")==0) {
779                         status->song = atoi(re->value);
780                 }
781                 else if(strcmp(re->name,"songid")==0) {
782                         status->songid = atoi(re->value);
783                 }
784                 else if(strcmp(re->name,"time")==0) {
785                         char * tok = strchr(re->value,':');
786                         /* the second strchr below is a safety check */
787                         if (tok && (strchr(tok,0) > (tok+1))) {
788                                 /* atoi stops at the first non-[0-9] char: */
789                                 status->elapsedTime = atoi(re->value);
790                                 status->totalTime = atoi(tok+1);
791                         }
792                 }
793                 else if(strcmp(re->name,"error")==0) {
794                         status->error = strdup(re->value);
795                 }
796                 else if(strcmp(re->name,"xfade")==0) {
797                         status->crossfade = atoi(re->value);
798                 }
799                 else if(strcmp(re->name,"updating_db")==0) {
800                         status->updatingDb = atoi(re->value);
801                 }
802                 else if(strcmp(re->name,"audio")==0) {
803                         char * tok = strchr(re->value,':');
804                         if (tok && (strchr(tok,0) > (tok+1))) {
805                                 status->sampleRate = atoi(re->value);
806                                 status->bits = atoi(++tok);
807                                 tok = strchr(tok,':');
808                                 if (tok && (strchr(tok,0) > (tok+1)))
809                                         status->channels = atoi(tok+1);
810                         }
811                 }
813                 mpd_getNextReturnElement(connection);
814                 if(connection->error) {
815                         free(status);
816                         return NULL;
817                 }
818         }
820         if(connection->error) {
821                 free(status);
822                 return NULL;
823         }
824         else if(status->state<0) {
825                 strcpy(connection->errorStr,"state not found");
826                 connection->error = 1;
827                 free(status);
828                 return NULL;
829         }
831         return status;
834 void mpd_freeStatus(mpd_Status * status) {
835         if(status->error) free(status->error);
836         free(status);
839 void mpd_sendStatsCommand(mpd_Connection * connection) {
840         mpd_executeCommand(connection,"stats\n");
843 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
844         mpd_Stats * stats;
846         /*mpd_executeCommand(connection,"stats\n");
848         if(connection->error) return NULL;*/
850         if(connection->doneProcessing || (connection->listOks &&
851            connection->doneListOk))
852         {
853                 return NULL;
854         }
856         if(!connection->returnElement) mpd_getNextReturnElement(connection);
858         stats = malloc(sizeof(mpd_Stats));
859         stats->numberOfArtists = 0;
860         stats->numberOfAlbums = 0;
861         stats->numberOfSongs = 0;
862         stats->uptime = 0;
863         stats->dbUpdateTime = 0;
864         stats->playTime = 0;
865         stats->dbPlayTime = 0;
867         if(connection->error) {
868                 free(stats);
869                 return NULL;
870         }
871         while(connection->returnElement) {
872                 mpd_ReturnElement * re = connection->returnElement;
873                 if(strcmp(re->name,"artists")==0) {
874                         stats->numberOfArtists = atoi(re->value);
875                 }
876                 else if(strcmp(re->name,"albums")==0) {
877                         stats->numberOfAlbums = atoi(re->value);
878                 }
879                 else if(strcmp(re->name,"songs")==0) {
880                         stats->numberOfSongs = atoi(re->value);
881                 }
882                 else if(strcmp(re->name,"uptime")==0) {
883                         stats->uptime = strtol(re->value,NULL,10);
884                 }
885                 else if(strcmp(re->name,"db_update")==0) {
886                         stats->dbUpdateTime = strtol(re->value,NULL,10);
887                 }
888                 else if(strcmp(re->name,"playtime")==0) {
889                         stats->playTime = strtol(re->value,NULL,10);
890                 }
891                 else if(strcmp(re->name,"db_playtime")==0) {
892                         stats->dbPlayTime = strtol(re->value,NULL,10);
893                 }
895                 mpd_getNextReturnElement(connection);
896                 if(connection->error) {
897                         free(stats);
898                         return NULL;
899                 }
900         }
902         if(connection->error) {
903                 free(stats);
904                 return NULL;
905         }
907         return stats;
910 void mpd_freeStats(mpd_Stats * stats) {
911         free(stats);
914 static void mpd_initSong(mpd_Song * song) {
915         song->file = NULL;
916         song->artist = NULL;
917         song->album = NULL;
918         song->track = NULL;
919         song->title = NULL;
920         song->name = NULL;
921         song->date = NULL;
922         /* added by Qball */
923         song->genre = NULL;
924         song->composer = NULL;
925         song->disc = NULL;
926         song->comment = NULL;
928         song->time = MPD_SONG_NO_TIME;
929         song->pos = MPD_SONG_NO_NUM;
930         song->id = MPD_SONG_NO_ID;
933 static void mpd_finishSong(mpd_Song * song) {
934         if(song->file) str_pool_put(song->file);
935         if(song->artist) str_pool_put(song->artist);
936         if(song->album) str_pool_put(song->album);
937         if(song->title) str_pool_put(song->title);
938         if(song->track) str_pool_put(song->track);
939         if(song->name) str_pool_put(song->name);
940         if(song->date) str_pool_put(song->date);
941         if(song->genre) str_pool_put(song->genre);
942         if(song->composer) str_pool_put(song->composer);
943         if(song->disc) str_pool_put(song->disc);
944         if(song->comment) str_pool_put(song->comment);
947 mpd_Song * mpd_newSong(void) {
948         mpd_Song * ret = malloc(sizeof(mpd_Song));
950         mpd_initSong(ret);
952         return ret;
955 void mpd_freeSong(mpd_Song * song) {
956         mpd_finishSong(song);
957         free(song);
960 mpd_Song * mpd_songDup(mpd_Song * song) {
961         mpd_Song * ret = mpd_newSong();
963         if(song->file) ret->file = str_pool_dup(song->file);
964         if(song->artist) ret->artist = str_pool_dup(song->artist);
965         if(song->album) ret->album = str_pool_dup(song->album);
966         if(song->title) ret->title = str_pool_dup(song->title);
967         if(song->track) ret->track = str_pool_dup(song->track);
968         if(song->name) ret->name = str_pool_dup(song->name);
969         if(song->date) ret->date = str_pool_dup(song->date);
970         if(song->genre) ret->genre= str_pool_dup(song->genre);
971         if(song->composer) ret->composer= str_pool_dup(song->composer);
972         if(song->disc) ret->disc = str_pool_dup(song->disc);
973         if(song->comment) ret->comment = str_pool_dup(song->comment);
974         ret->time = song->time;
975         ret->pos = song->pos;
976         ret->id = song->id;
978         return ret;
981 static void mpd_initDirectory(mpd_Directory * directory) {
982         directory->path = NULL;
985 static void mpd_finishDirectory(mpd_Directory * directory) {
986         if (directory->path)
987                 str_pool_put(directory->path);
990 mpd_Directory * mpd_newDirectory(void) {
991         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
993         mpd_initDirectory(directory);
995         return directory;
998 void mpd_freeDirectory(mpd_Directory * directory) {
999         mpd_finishDirectory(directory);
1001         free(directory);
1004 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
1005         mpd_Directory * ret = mpd_newDirectory();
1007         if (directory->path)
1008                 ret->path = str_pool_dup(directory->path);
1010         return ret;
1013 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
1014         playlist->path = NULL;
1017 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
1018         if (playlist->path)
1019                 str_pool_put(playlist->path);
1022 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
1023         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
1025         mpd_initPlaylistFile(playlist);
1027         return playlist;
1030 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
1031         mpd_finishPlaylistFile(playlist);
1032         free(playlist);
1035 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1036         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1038         if (playlist->path)
1039                 ret->path = str_pool_dup(playlist->path);
1041         return ret;
1044 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
1045         entity->info.directory = NULL;
1048 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1049         if(entity->info.directory) {
1050                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1051                         mpd_freeDirectory(entity->info.directory);
1052                 }
1053                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1054                         mpd_freeSong(entity->info.song);
1055                 }
1056                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1057                         mpd_freePlaylistFile(entity->info.playlistFile);
1058                 }
1059         }
1062 mpd_InfoEntity * mpd_newInfoEntity(void) {
1063         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
1065         mpd_initInfoEntity(entity);
1067         return entity;
1070 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1071         mpd_finishInfoEntity(entity);
1072         free(entity);
1075 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1076         mpd_executeCommand(connection,command);
1079 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1080         mpd_InfoEntity * entity = NULL;
1082         if(connection->doneProcessing || (connection->listOks &&
1083            connection->doneListOk)) {
1084                 return NULL;
1085         }
1087         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1089         if(connection->returnElement) {
1090                 if(strcmp(connection->returnElement->name,"file")==0) {
1091                         entity = mpd_newInfoEntity();
1092                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1093                         entity->info.song = mpd_newSong();
1094                         entity->info.song->file =
1095                                 str_pool_dup(connection->returnElement->value);
1096                 }
1097                 else if(strcmp(connection->returnElement->name,
1098                                         "directory")==0) {
1099                         entity = mpd_newInfoEntity();
1100                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1101                         entity->info.directory = mpd_newDirectory();
1102                         entity->info.directory->path =
1103                                 str_pool_dup(connection->returnElement->value);
1104                 }
1105                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1106                         entity = mpd_newInfoEntity();
1107                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1108                         entity->info.playlistFile = mpd_newPlaylistFile();
1109                         entity->info.playlistFile->path =
1110                                 str_pool_dup(connection->returnElement->value);
1111                 }
1112                 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1113                         entity = mpd_newInfoEntity();
1114                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1115                         entity->info.song = mpd_newSong();
1116                         entity->info.song->pos = atoi(connection->returnElement->value);
1117                 }
1118                 else {
1119                         connection->error = 1;
1120                         strcpy(connection->errorStr,"problem parsing song info");
1121                         return NULL;
1122                 }
1123         }
1124         else return NULL;
1126         mpd_getNextReturnElement(connection);
1127         while(connection->returnElement) {
1128                 mpd_ReturnElement * re = connection->returnElement;
1130                 if(strcmp(re->name,"file")==0) return entity;
1131                 else if(strcmp(re->name,"directory")==0) return entity;
1132                 else if(strcmp(re->name,"playlist")==0) return entity;
1133                 else if(strcmp(re->name,"cpos")==0) return entity;
1135                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1136                                 strlen(re->value)) {
1137                         if(!entity->info.song->artist &&
1138                                         strcmp(re->name,"Artist")==0) {
1139                                 entity->info.song->artist = str_pool_dup(re->value);
1140                         }
1141                         else if(!entity->info.song->album &&
1142                                         strcmp(re->name,"Album")==0) {
1143                                 entity->info.song->album = str_pool_dup(re->value);
1144                         }
1145                         else if(!entity->info.song->title &&
1146                                         strcmp(re->name,"Title")==0) {
1147                                 entity->info.song->title = str_pool_dup(re->value);
1148                         }
1149                         else if(!entity->info.song->track &&
1150                                         strcmp(re->name,"Track")==0) {
1151                                 entity->info.song->track = str_pool_dup(re->value);
1152                         }
1153                         else if(!entity->info.song->name &&
1154                                         strcmp(re->name,"Name")==0) {
1155                                 entity->info.song->name = str_pool_dup(re->value);
1156                         }
1157                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1158                                         strcmp(re->name,"Time")==0) {
1159                                 entity->info.song->time = atoi(re->value);
1160                         }
1161                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1162                                         strcmp(re->name,"Pos")==0) {
1163                                 entity->info.song->pos = atoi(re->value);
1164                         }
1165                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
1166                                         strcmp(re->name,"Id")==0) {
1167                                 entity->info.song->id = atoi(re->value);
1168                         }
1169                         else if(!entity->info.song->date &&
1170                                         strcmp(re->name, "Date") == 0) {
1171                                 entity->info.song->date = str_pool_dup(re->value);
1172                         }
1173                         else if(!entity->info.song->genre &&
1174                                         strcmp(re->name, "Genre") == 0) {
1175                                 entity->info.song->genre = str_pool_dup(re->value);
1176                         }
1177                         else if(!entity->info.song->composer &&
1178                                         strcmp(re->name, "Composer") == 0) {
1179                                 entity->info.song->composer = str_pool_dup(re->value);
1180                         }
1181                         else if(!entity->info.song->disc &&
1182                                         strcmp(re->name, "Disc") == 0) {
1183                                 entity->info.song->disc = str_pool_dup(re->value);
1184                         }
1185                         else if(!entity->info.song->comment &&
1186                                         strcmp(re->name, "Comment") == 0) {
1187                                 entity->info.song->comment = str_pool_dup(re->value);
1188                         }
1189                 }
1190                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1191                 }
1192                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1193                 }
1195                 mpd_getNextReturnElement(connection);
1196         }
1198         return entity;
1201 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1202                 const char * name)
1204         if(connection->doneProcessing || (connection->listOks &&
1205                                 connection->doneListOk))
1206         {
1207                 return NULL;
1208         }
1210         mpd_getNextReturnElement(connection);
1211         while(connection->returnElement) {
1212                 mpd_ReturnElement * re = connection->returnElement;
1214                 if(strcmp(re->name,name)==0) return strdup(re->value);
1215                 mpd_getNextReturnElement(connection);
1216         }
1218         return NULL;
1221 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1222         if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1223         {
1224                 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1225         }
1226         return NULL;
1229 char * mpd_getNextArtist(mpd_Connection * connection) {
1230         return mpd_getNextReturnElementNamed(connection,"Artist");
1233 char * mpd_getNextAlbum(mpd_Connection * connection) {
1234         return mpd_getNextReturnElementNamed(connection,"Album");
1237 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1238         char * string = malloc(strlen("playlistinfo")+25);
1239         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1240         mpd_sendInfoCommand(connection,string);
1241         free(string);
1244 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1245         char * string = malloc(strlen("playlistid")+25);
1246         sprintf(string, "playlistid \"%i\"\n", id);
1247         mpd_sendInfoCommand(connection, string);
1248         free(string);
1251 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1252         char * string = malloc(strlen("plchanges")+25);
1253         sprintf(string,"plchanges \"%lld\"\n",playlist);
1254         mpd_sendInfoCommand(connection,string);
1255         free(string);
1258 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1259         char * string = malloc(strlen("plchangesposid")+25);
1260         sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1261         mpd_sendInfoCommand(connection,string);
1262         free(string);
1265 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1266         char * sDir = mpd_sanitizeArg(dir);
1267         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1268         sprintf(string,"listall \"%s\"\n",sDir);
1269         mpd_sendInfoCommand(connection,string);
1270         free(string);
1271         free(sDir);
1274 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1275         char * sDir = mpd_sanitizeArg(dir);
1276         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1277         sprintf(string,"listallinfo \"%s\"\n",sDir);
1278         mpd_sendInfoCommand(connection,string);
1279         free(string);
1280         free(sDir);
1283 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1284         char * sDir = mpd_sanitizeArg(dir);
1285         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1286         sprintf(string,"lsinfo \"%s\"\n",sDir);
1287         mpd_sendInfoCommand(connection,string);
1288         free(string);
1289         free(sDir);
1292 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1293         mpd_executeCommand(connection,"currentsong\n");
1296 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1297                 const char * str)
1299         char st[10];
1300         char * string;
1301         char * sanitStr = mpd_sanitizeArg(str);
1302         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1303         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1304         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1305         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1306         else {
1307                 connection->error = 1;
1308                 strcpy(connection->errorStr,"unknown table for search");
1309                 return;
1310         }
1311         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1312         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1313         mpd_sendInfoCommand(connection,string);
1314         free(string);
1315         free(sanitStr);
1318 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1319                 const char * str)
1321         char st[10];
1322         char * string;
1323         char * sanitStr = mpd_sanitizeArg(str);
1324         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1325         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1326         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1327         else {
1328                 connection->error = 1;
1329                 strcpy(connection->errorStr,"unknown table for find");
1330                 return;
1331         }
1332         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1333         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1334         mpd_sendInfoCommand(connection,string);
1335         free(string);
1336         free(sanitStr);
1339 void mpd_sendListCommand(mpd_Connection * connection, int table,
1340                 const char * arg1)
1342         char st[10];
1343         char * string;
1344         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1345         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1346         else {
1347                 connection->error = 1;
1348                 strcpy(connection->errorStr,"unknown table for list");
1349                 return;
1350         }
1351         if(arg1) {
1352                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1353                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1354                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1355                 free(sanitArg1);
1356         }
1357         else {
1358                 string = malloc(strlen("list")+strlen(st)+3);
1359                 sprintf(string,"list %s\n",st);
1360         }
1361         mpd_sendInfoCommand(connection,string);
1362         free(string);
1365 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1366         char * sFile = mpd_sanitizeArg(file);
1367         char * string = malloc(strlen("add")+strlen(sFile)+5);
1368         sprintf(string,"add \"%s\"\n",sFile);
1369         mpd_executeCommand(connection,string);
1370         free(string);
1371         free(sFile);
1374 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1375         char * string = malloc(strlen("delete")+25);
1376         sprintf(string,"delete \"%i\"\n",songPos);
1377         mpd_sendInfoCommand(connection,string);
1378         free(string);
1381 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1382         char * string = malloc(strlen("deleteid")+25);
1383         sprintf(string, "deleteid \"%i\"\n", id);
1384         mpd_sendInfoCommand(connection,string);
1385         free(string);
1388 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1389         char * sName = mpd_sanitizeArg(name);
1390         char * string = malloc(strlen("save")+strlen(sName)+5);
1391         sprintf(string,"save \"%s\"\n",sName);
1392         mpd_executeCommand(connection,string);
1393         free(string);
1394         free(sName);
1397 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1398         char * sName = mpd_sanitizeArg(name);
1399         char * string = malloc(strlen("load")+strlen(sName)+5);
1400         sprintf(string,"load \"%s\"\n",sName);
1401         mpd_executeCommand(connection,string);
1402         free(string);
1403         free(sName);
1406 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1407         char * sName = mpd_sanitizeArg(name);
1408         char * string = malloc(strlen("rm")+strlen(sName)+5);
1409         sprintf(string,"rm \"%s\"\n",sName);
1410         mpd_executeCommand(connection,string);
1411         free(string);
1412         free(sName);
1415 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1416         mpd_executeCommand(connection,"shuffle\n");
1419 void mpd_sendClearCommand(mpd_Connection * connection) {
1420         mpd_executeCommand(connection,"clear\n");
1423 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1424         char * string = malloc(strlen("play")+25);
1425         sprintf(string,"play \"%i\"\n",songPos);
1426         mpd_sendInfoCommand(connection,string);
1427         free(string);
1430 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1431         char * string = malloc(strlen("playid")+25);
1432         sprintf(string,"playid \"%i\"\n",id);
1433         mpd_sendInfoCommand(connection,string);
1434         free(string);
1437 void mpd_sendStopCommand(mpd_Connection * connection) {
1438         mpd_executeCommand(connection,"stop\n");
1441 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1442         char * string = malloc(strlen("pause")+25);
1443         sprintf(string,"pause \"%i\"\n",pauseMode);
1444         mpd_executeCommand(connection,string);
1445         free(string);
1448 void mpd_sendNextCommand(mpd_Connection * connection) {
1449         mpd_executeCommand(connection,"next\n");
1452 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1453         char * string = malloc(strlen("move")+25);
1454         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1455         mpd_sendInfoCommand(connection,string);
1456         free(string);
1459 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1460         char * string = malloc(strlen("moveid")+25);
1461         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1462         mpd_sendInfoCommand(connection,string);
1463         free(string);
1466 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1467         char * string = malloc(strlen("swap")+25);
1468         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1469         mpd_sendInfoCommand(connection,string);
1470         free(string);
1473 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1474         char * string = malloc(strlen("swapid")+25);
1475         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1476         mpd_sendInfoCommand(connection,string);
1477         free(string);
1480 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1481         char * string = malloc(strlen("seek")+25);
1482         sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1483         mpd_sendInfoCommand(connection,string);
1484         free(string);
1487 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1488         char * string = malloc(strlen("seekid")+25);
1489         sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1490         mpd_sendInfoCommand(connection,string);
1491         free(string);
1494 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1495         char *sPath = mpd_sanitizeArg(path);
1496         char * string = malloc(strlen("update")+strlen(sPath)+5);
1497         sprintf(string,"update \"%s\"\n",sPath);
1498         mpd_sendInfoCommand(connection,string);
1499         free(string);
1500         free(sPath);
1503 int mpd_getUpdateId(mpd_Connection * connection) {
1504         char * jobid;
1505         int ret = 0;
1507         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1508         if(jobid) {
1509                 ret = atoi(jobid);
1510                 free(jobid);
1511         }
1513         return ret;
1516 void mpd_sendPrevCommand(mpd_Connection * connection) {
1517         mpd_executeCommand(connection,"previous\n");
1520 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1521         char * string = malloc(strlen("repeat")+25);
1522         sprintf(string,"repeat \"%i\"\n",repeatMode);
1523         mpd_executeCommand(connection,string);
1524         free(string);
1527 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1528         char * string = malloc(strlen("random")+25);
1529         sprintf(string,"random \"%i\"\n",randomMode);
1530         mpd_executeCommand(connection,string);
1531         free(string);
1534 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1535         char * string = malloc(strlen("setvol")+25);
1536         sprintf(string,"setvol \"%i\"\n",volumeChange);
1537         mpd_executeCommand(connection,string);
1538         free(string);
1541 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1542         char * string = malloc(strlen("volume")+25);
1543         sprintf(string,"volume \"%i\"\n",volumeChange);
1544         mpd_executeCommand(connection,string);
1545         free(string);
1548 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1549         char * string = malloc(strlen("crossfade")+25);
1550         sprintf(string,"crossfade \"%i\"\n",seconds);
1551         mpd_executeCommand(connection,string);
1552         free(string);
1555 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1556         char * sPass = mpd_sanitizeArg(pass);
1557         char * string = malloc(strlen("password")+strlen(sPass)+5);
1558         sprintf(string,"password \"%s\"\n",sPass);
1559         mpd_executeCommand(connection,string);
1560         free(string);
1561         free(sPass);
1564 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1565         if(connection->commandList) {
1566                 strcpy(connection->errorStr,"already in command list mode");
1567                 connection->error = 1;
1568                 return;
1569         }
1570         connection->commandList = COMMAND_LIST;
1571         mpd_executeCommand(connection,"command_list_begin\n");
1574 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1575         if(connection->commandList) {
1576                 strcpy(connection->errorStr,"already in command list mode");
1577                 connection->error = 1;
1578                 return;
1579         }
1580         connection->commandList = COMMAND_LIST_OK;
1581         mpd_executeCommand(connection,"command_list_ok_begin\n");
1582         connection->listOks = 0;
1585 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1586         if(!connection->commandList) {
1587                 strcpy(connection->errorStr,"not in command list mode");
1588                 connection->error = 1;
1589                 return;
1590         }
1591         connection->commandList = 0;
1592         mpd_executeCommand(connection,"command_list_end\n");
1595 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1596         mpd_executeCommand(connection,"outputs\n");
1599 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1600         mpd_OutputEntity * output = NULL;
1602         if(connection->doneProcessing || (connection->listOks &&
1603                                 connection->doneListOk))
1604         {
1605                 return NULL;
1606         }
1608         if(connection->error) return NULL;
1610         output = malloc(sizeof(mpd_OutputEntity));
1611         output->id = -10;
1612         output->name = NULL;
1613         output->enabled = 0;
1615         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1617         while(connection->returnElement) {
1618                 mpd_ReturnElement * re = connection->returnElement;
1619                 if(strcmp(re->name,"outputid")==0) {
1620                         if(output!=NULL && output->id>=0) return output;
1621                         output->id = atoi(re->value);
1622                 }
1623                 else if(strcmp(re->name,"outputname")==0) {
1624                         output->name = strdup(re->value);
1625                 }
1626                 else if(strcmp(re->name,"outputenabled")==0) {
1627                         output->enabled = atoi(re->value);
1628                 }
1630                 mpd_getNextReturnElement(connection);
1631                 if(connection->error) {
1632                         free(output);
1633                         return NULL;
1634                 }
1636         }
1638         return output;
1641 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1642         char * string = malloc(strlen("enableoutput")+25);
1643         sprintf(string,"enableoutput \"%i\"\n",outputId);
1644         mpd_executeCommand(connection,string);
1645         free(string);
1648 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1649         char * string = malloc(strlen("disableoutput")+25);
1650         sprintf(string,"disableoutput \"%i\"\n",outputId);
1651         mpd_executeCommand(connection,string);
1652         free(string);
1655 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1656         free(output->name);
1657         free(output);
1660 /**
1661  * mpd_sendNotCommandsCommand
1662  * odd naming, but it gets the not allowed commands
1663  */
1665 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1666         mpd_executeCommand(connection,"notcommands\n");
1669 /**
1670  * mpd_sendCommandsCommand
1671  * odd naming, but it gets the allowed commands
1672  */
1674 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1675         mpd_executeCommand(connection,"commands\n");
1677 /**
1678  * Get the next returned command
1679  */
1680 char * mpd_getNextCommand(mpd_Connection * connection) {
1681         return mpd_getNextReturnElementNamed(connection,"command");
1684 void mpd_startSearch(mpd_Connection * connection,int exact) {
1685         if(connection->request) {
1686                 /* search/find allready in progress */
1687                 /* TODO: set error here?  */
1688                 return;
1689         }
1690         if(exact){
1691                 connection->request = strdup("find");
1692         }
1693         else{
1694                 connection->request = strdup("search");
1695         }
1699 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1700         if(connection->request) {
1701                 /* search/find allready in progress */
1702                 /* TODO: set error here?  */
1703                 return;
1704         }
1705         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1706                 /* set error here */
1707                 return;
1708         }
1710         connection->request = malloc(sizeof(char)*(
1711                                 /* length of the field name */
1712                                 strlen(mpdTagItemKeys[field])+
1713                                 /* "list"+space+\0 */
1714                                 6
1715                                 ));
1716         sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1721 void mpd_addConstraintSearch(mpd_Connection *connection,
1722                 int field,
1723                 char *name)
1725         char *arg = NULL;
1726         if(!connection->request){
1727                 return;
1728         }
1729         if(name == NULL) {
1730                 return;
1731         }
1732         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1733                 return;
1734         }
1735         /* clean up the query */
1736         arg = mpd_sanitizeArg(name);
1737         /* create space for the query */
1738         connection->request = realloc(connection->request, (
1739                          /* length of the old string */
1740                          strlen(connection->request)+
1741                          /* space between */
1742                          1+
1743                          /* length of the field name */
1744                          strlen(mpdTagItemKeys[field])+
1745                          /* space plus starting " */
1746                          2+
1747                          /* length of search term */
1748                          strlen(arg)+
1749                          /* closign " +\0 that is added sprintf */
1750                          2
1751                         )*sizeof(char));
1752         /* and form the query */
1753         sprintf(connection->request, "%s %s \"%s\"",
1754                         connection->request,
1755                         mpdTagItemKeys[field],
1756                         arg);
1757         free(arg);
1761 void mpd_commitSearch(mpd_Connection *connection)
1763         if(connection->request)
1764         {
1765                 int length = strlen(connection->request);
1766                 /* fixing up the string for mpd to like */
1767                 connection->request = realloc(connection->request,
1768                                 (length+        /* old length */
1769                                  2              /* closing \n and \0 */
1770                                 )*sizeof(char));
1771                 connection->request[length] = '\n';
1772                 connection->request[length+1] = '\0';
1773                 /* and off we go */
1774                 mpd_sendInfoCommand(connection, connection->request);
1775                 /* clean up a bit */
1776                 free(connection->request);
1777                 connection->request = NULL;
1778         }
1781 /**
1782  * @param connection a MpdConnection
1783  * @param path  the path to the playlist.
1784  *
1785  * List the content, with full metadata, of a stored playlist.
1786  *
1787  */
1788 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1790         char *arg = mpd_sanitizeArg(path);
1791         char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1792         sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1793         mpd_sendInfoCommand(connection, query);
1794         free(arg);
1795         free(query);
1798 /**
1799  * @param connection a MpdConnection
1800  * @param path  the path to the playlist.
1801  *
1802  * List the content of a stored playlist.
1803  *
1804  */
1805 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1807         char *arg = mpd_sanitizeArg(path);
1808         char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1809         sprintf(query, "listplaylist \"%s\"\n",arg);
1810         mpd_sendInfoCommand(connection, query);
1811         free(arg);
1812         free(query);