Code

libmpdclient: check connection->sock >= 0
[ncmpc.git] / src / libmpdclient.c
1 /* libmpdclient
2    (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
3    This project's homepage is: http://www.musicpd.org
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
9    - Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
12    - Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
16    - Neither the name of the Music Player Daemon nor the names of its
17    contributors may be used to endorse or promote products derived from
18    this software without specific prior written permission.
20    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
24    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
33 #include "libmpdclient.h"
34 #include "resolver.h"
35 #include "str_pool.h"
37 #include <assert.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <stdio.h>
41 #include <sys/param.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45 #include <fcntl.h>
47 #ifdef WIN32
48 #  include <ws2tcpip.h>
49 #  include <winsock.h>
50 #else
51 #  include <netinet/in.h>
52 #  include <arpa/inet.h>
53 #  include <sys/socket.h>
54 #  include <netdb.h>
55 #endif
57 #ifndef WIN32
58 #include <sys/un.h>
59 #endif
61 #ifndef MSG_DONTWAIT
62 #  define MSG_DONTWAIT 0
63 #endif
65 #define COMMAND_LIST    1
66 #define COMMAND_LIST_OK 2
68 #ifdef WIN32
69 #  define SELECT_ERRNO_IGNORE   (errno == WSAEINTR || errno == WSAEINPROGRESS)
70 #  define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
71 #else
72 #  define SELECT_ERRNO_IGNORE   (errno == EINTR)
73 #  define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
74 #  define winsock_dll_error(c)  0
75 #  define closesocket(s)        close(s)
76 #  define WSACleanup()          do { /* nothing */ } while (0)
77 #endif
79 #ifdef WIN32
80 static int winsock_dll_error(mpd_Connection *connection)
81 {
82         WSADATA wsaData;
83         if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
84                         LOBYTE(wsaData.wVersion) != 2 ||
85                         HIBYTE(wsaData.wVersion) != 2 ) {
86                 snprintf(connection->errorStr, sizeof(connection->errorStr),
87                          "Could not find usable WinSock DLL.");
88                 connection->error = MPD_ERROR_SYSTEM;
89                 return 1;
90         }
91         return 0;
92 }
94 static int do_connect_fail(mpd_Connection *connection,
95                            const struct sockaddr *serv_addr, int addrlen)
96 {
97         int iMode = 1; /* 0 = blocking, else non-blocking */
98         ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
99         return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR
100                         && WSAGetLastError() != WSAEWOULDBLOCK);
102 #else /* !WIN32 (sane operating systems) */
103 static int do_connect_fail(mpd_Connection *connection,
104                            const struct sockaddr *serv_addr, int addrlen)
106         int flags = fcntl(connection->sock, F_GETFL, 0);
107         fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
108         return (connect(connection->sock,serv_addr,addrlen)<0 &&
109                                 errno!=EINPROGRESS);
111 #endif /* !WIN32 */
113 const char *const mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
115         "Artist",
116         "Album",
117         "Title",
118         "Track",
119         "Name",
120         "Genre",
121         "Date",
122         "Composer",
123         "Performer",
124         "Comment",
125         "Disc",
126         "filename"
127 };
129 static char * mpd_sanitizeArg(const char * arg) {
130         size_t i;
131         char * ret;
132         register const char *c;
133         register char *rc;
135         /* instead of counting in that loop above, just
136          * use a bit more memory and half running time
137          */
138         ret = malloc(strlen(arg) * 2 + 1);
140         c = arg;
141         rc = ret;
142         for(i = strlen(arg)+1; i != 0; --i) {
143                 if(*c=='"' || *c=='\\')
144                         *rc++ = '\\';
145                 *(rc++) = *(c++);
146         }
148         return ret;
151 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
153         mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
155         ret->name = str_pool_get(name);
156         ret->value = str_pool_get(value);
158         return ret;
161 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
162         str_pool_put(re->name);
163         str_pool_put(re->value);
164         free(re);
167 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
168         connection->timeout.tv_sec = (int)timeout;
169         connection->timeout.tv_usec = (int)(timeout*1e6 -
170                                             connection->timeout.tv_sec*1000000 +
171                                             0.5);
174 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
175                             char * output) {
176         char * tmp;
177         char * test;
178         int i;
180         if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
181                 snprintf(connection->errorStr, sizeof(connection->errorStr),
182                          "mpd not running on port %i on host \"%s\"",
183                          port,host);
184                 connection->error = MPD_ERROR_NOTMPD;
185                 return 1;
186         }
188         tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
190         for(i=0;i<3;i++) {
191                 if(tmp) connection->version[i] = strtol(tmp,&test,10);
193                 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
194                         snprintf(connection->errorStr, sizeof(connection->errorStr),
195                                  "error parsing version number at "
196                                  "\"%s\"",
197                                  &output[strlen(MPD_WELCOME_MESSAGE)]);
198                         connection->error = MPD_ERROR_NOTMPD;
199                         return 1;
200                 }
201                 tmp = ++test;
202         }
204         return 0;
207 /**
208  * Wait for the socket to become readable.
209  */
210 static int mpd_wait(mpd_Connection *connection)
212         struct timeval tv;
213         fd_set fds;
214         int ret;
216         assert(connection->sock >= 0);
218         while (1) {
219                 tv = connection->timeout;
220                 FD_ZERO(&fds);
221                 FD_SET(connection->sock, &fds);
223                 ret = select(connection->sock + 1, &fds, NULL, NULL, &tv);
224                 if (ret > 0)
225                         return 0;
227                 if (ret == 0 || !SELECT_ERRNO_IGNORE)
228                         return -1;
229         }
232 /**
233  * Wait until the socket is connected and check its result.  Returns 1
234  * on success, 0 on timeout, -errno on error.
235  */
236 static int mpd_wait_connected(mpd_Connection *connection)
238         int ret;
239         int s_err = 0;
240         socklen_t s_err_size = sizeof(s_err);
242         ret = mpd_wait(connection);
243         if (ret < 0)
244                 return 0;
246         ret = getsockopt(connection->sock, SOL_SOCKET, SO_ERROR,
247                          (char*)&s_err, &s_err_size);
248         if (ret < 0)
249                 return -errno;
251         if (s_err != 0)
252                 return -s_err;
254         return 1;
257 /**
258  * Attempt to read data from the socket into the input buffer.
259  * Returns 0 on success, -1 on error.
260  */
261 static int mpd_recv(mpd_Connection *connection)
263         int ret;
264         ssize_t nbytes;
266         assert(connection != NULL);
267         assert(connection->buflen <= sizeof(connection->buffer));
268         assert(connection->bufstart <= connection->buflen);
270         if (connection->sock < 0) {
271                 strcpy(connection->errorStr, "not connected");
272                 connection->error = MPD_ERROR_CONNCLOSED;
273                 connection->doneProcessing = 1;
274                 connection->doneListOk = 0;
275                 return -1;
276         }
278         if (connection->buflen >= sizeof(connection->buffer)) {
279                 /* delete consumed data from beginning of buffer */
280                 connection->buflen -= connection->bufstart;
281                 memmove(connection->buffer,
282                         connection->buffer + connection->bufstart,
283                         connection->buflen);
284                 connection->bufstart = 0;
285         }
287         if (connection->buflen >= sizeof(connection->buffer)) {
288                 strcpy(connection->errorStr, "buffer overrun");
289                 connection->error = MPD_ERROR_BUFFEROVERRUN;
290                 connection->doneProcessing = 1;
291                 connection->doneListOk = 0;
292                 return -1;
293         }
295         while (1) {
296                 ret = mpd_wait(connection);
297                 if (ret < 0) {
298                         strcpy(connection->errorStr, "connection timeout");
299                         connection->error = MPD_ERROR_TIMEOUT;
300                         connection->doneProcessing = 1;
301                         connection->doneListOk = 0;
302                         return -1;
303                 }
305                 nbytes = read(connection->sock,
306                               connection->buffer + connection->buflen,
307                               sizeof(connection->buffer) - connection->buflen);
308                 if (nbytes > 0) {
309                         connection->buflen += nbytes;
310                         return 0;
311                 }
313                 if (nbytes == 0 || !SENDRECV_ERRNO_IGNORE) {
314                         strcpy(connection->errorStr, "connection closed");
315                         connection->error = MPD_ERROR_CONNCLOSED;
316                         connection->doneProcessing = 1;
317                         connection->doneListOk = 0;
318                         return -1;
319                 }
320         }
323 static int
324 mpd_connect(mpd_Connection *connection, const char * host, int port)
326         struct resolver *resolver;
327         const struct resolver_address *address;
328         int ret;
330         resolver = resolver_new(host, port);
331         if (resolver == NULL) {
332                 snprintf(connection->errorStr, sizeof(connection->errorStr),
333                          "host \"%s\" not found", host);
334                 connection->error = MPD_ERROR_UNKHOST;
335                 return -1;
336         }
338         while ((address = resolver_next(resolver)) != NULL) {
339                 connection->sock = socket(address->family, SOCK_STREAM,
340                                           address->protocol);
341                 if (connection->sock < 0) {
342                         snprintf(connection->errorStr,
343                                  sizeof(connection->errorStr),
344                                  "problems creating socket: %s",
345                                  strerror(errno));
346                         connection->error = MPD_ERROR_SYSTEM;
347                         continue;
348                 }
350                 ret = do_connect_fail(connection,
351                                       address->addr, address->addrlen);
352                 if (ret != 0) {
353                         snprintf(connection->errorStr,
354                                  sizeof(connection->errorStr),
355                                  "problems connecting to \"%s\" on port"
356                                  " %i: %s", host, port, strerror(errno));
357                         connection->error = MPD_ERROR_CONNPORT;
359                         closesocket(connection->sock);
360                         connection->sock = -1;
361                         continue;
362                 }
364                 ret = mpd_wait_connected(connection);
365                 if (ret > 0) {
366                         resolver_free(resolver);
367                         mpd_clearError(connection);
368                         return 0;
369                 }
371                 if (ret == 0) {
372                         snprintf(connection->errorStr,
373                                  sizeof(connection->errorStr),
374                                  "timeout in attempting to get a response from"
375                                  " \"%s\" on port %i", host, port);
376                         connection->error = MPD_ERROR_NORESPONSE;
377                 } else if (ret < 0) {
378                         snprintf(connection->errorStr,
379                                  sizeof(connection->errorStr),
380                                  "problems connecting to \"%s\" on port %i: %s",
381                                  host, port, strerror(-ret));
382                         connection->error = MPD_ERROR_CONNPORT;
383                 }
385                 closesocket(connection->sock);
386                 connection->sock = -1;
387         }
389         resolver_free(resolver);
390         return -1;
393 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
394         int err;
395         char * rt;
396         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
398         connection->buflen = 0;
399         connection->bufstart = 0;
400         mpd_clearError(connection);
401         connection->doneProcessing = 0;
402         connection->commandList = 0;
403         connection->listOks = 0;
404         connection->doneListOk = 0;
405         connection->returnElement = NULL;
406         connection->request = NULL;
408         if (winsock_dll_error(connection))
409                 return connection;
411         mpd_setConnectionTimeout(connection,timeout);
413         err = mpd_connect(connection, host, port);
414         if (err < 0)
415                 return connection;
417         while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
418                 err = mpd_recv(connection);
419                 if (err < 0)
420                         return connection;
421         }
423         *rt = '\0';
424         if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
425                 connection->doneProcessing = 1;
427         connection->buflen -= rt + 1 - connection->buffer;
428         memmove(connection->buffer, rt + 1, connection->buflen);
430         return connection;
433 void mpd_clearError(mpd_Connection * connection) {
434         connection->error = 0;
435         connection->errorStr[0] = '\0';
438 void mpd_closeConnection(mpd_Connection * connection) {
439         closesocket(connection->sock);
440         if(connection->returnElement) free(connection->returnElement);
441         if(connection->request) free(connection->request);
442         free(connection);
443         WSACleanup();
446 static void mpd_executeCommand(mpd_Connection *connection,
447                                const char *command) {
448         int ret;
449         struct timeval tv;
450         fd_set fds;
451         const char *commandPtr = command;
452         int commandLen = strlen(command);
454         if (!connection->doneProcessing && !connection->commandList) {
455                 strcpy(connection->errorStr,
456                        "not done processing current command");
457                 connection->error = 1;
458                 return;
459         }
461         mpd_clearError(connection);
463         FD_ZERO(&fds);
464         FD_SET(connection->sock,&fds);
465         tv.tv_sec = connection->timeout.tv_sec;
466         tv.tv_usec = connection->timeout.tv_usec;
468         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
469                         (ret==-1 && SELECT_ERRNO_IGNORE)) {
470                 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
471                 if(ret<=0)
472                 {
473                         if (SENDRECV_ERRNO_IGNORE) continue;
474                         snprintf(connection->errorStr, sizeof(connection->errorStr),
475                                  "problems giving command \"%s\"",command);
476                         connection->error = MPD_ERROR_SENDING;
477                         return;
478                 }
479                 else {
480                         commandPtr+=ret;
481                         commandLen-=ret;
482                 }
484                 if(commandLen<=0) break;
485         }
487         if(commandLen>0) {
488                 perror("");
489                 snprintf(connection->errorStr, sizeof(connection->errorStr),
490                          "timeout sending command \"%s\"",command);
491                 connection->error = MPD_ERROR_TIMEOUT;
492                 return;
493         }
495         if(!connection->commandList) connection->doneProcessing = 0;
496         else if(connection->commandList == COMMAND_LIST_OK) {
497                 connection->listOks++;
498         }
501 static void mpd_getNextReturnElement(mpd_Connection * connection) {
502         char * output = NULL;
503         char * rt = NULL;
504         char * name = NULL;
505         char * value = NULL;
506         char * tok = NULL;
507         int err;
508         int pos;
510         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
511         connection->returnElement = NULL;
513         if (connection->doneProcessing ||
514             (connection->listOks && connection->doneListOk)) {
515                 strcpy(connection->errorStr,"already done processing current command");
516                 connection->error = 1;
517                 return;
518         }
520         while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
521                              connection->buflen - connection->bufstart))) {
522                 err = mpd_recv(connection);
523                 if (err < 0)
524                         return;
525         }
527         *rt = '\0';
528         output = connection->buffer+connection->bufstart;
529         connection->bufstart = rt - connection->buffer + 1;
531         if(strcmp(output,"OK")==0) {
532                 if(connection->listOks > 0) {
533                         strcpy(connection->errorStr, "expected more list_OK's");
534                         connection->error = 1;
535                 }
536                 connection->listOks = 0;
537                 connection->doneProcessing = 1;
538                 connection->doneListOk = 0;
539                 return;
540         }
542         if(strcmp(output, "list_OK") == 0) {
543                 if(!connection->listOks) {
544                         strcpy(connection->errorStr,
545                                         "got an unexpected list_OK");
546                         connection->error = 1;
547                 }
548                 else {
549                         connection->doneListOk = 1;
550                         connection->listOks--;
551                 }
552                 return;
553         }
555         if(strncmp(output,"ACK",strlen("ACK"))==0) {
556                 size_t length = strlen(output);
557                 char * test;
558                 char * needle;
559                 int val;
561                 if (length >= sizeof(connection->errorStr))
562                         length = sizeof(connection->errorStr) - 1;
564                 memcpy(connection->errorStr, output, length);
565                 connection->errorStr[length] = 0;
566                 connection->error = MPD_ERROR_ACK;
567                 connection->errorCode = MPD_ACK_ERROR_UNK;
568                 connection->errorAt = MPD_ERROR_AT_UNK;
569                 connection->doneProcessing = 1;
570                 connection->doneListOk = 0;
572                 needle = strchr(output, '[');
573                 if(!needle) return;
574                 val = strtol(needle+1, &test, 10);
575                 if(*test != '@') return;
576                 connection->errorCode = val;
577                 val = strtol(test+1, &test, 10);
578                 if(*test != ']') return;
579                 connection->errorAt = val;
580                 return;
581         }
583         tok = strchr(output, ':');
584         if (!tok) return;
585         pos = tok - output;
586         value = ++tok;
587         name = output;
588         name[pos] = '\0';
590         if(value[0]==' ') {
591                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
592         }
593         else {
594                 snprintf(connection->errorStr, sizeof(connection->errorStr),
595                          "error parsing: %s:%s",name,value);
596                 connection->error = 1;
597         }
600 void mpd_finishCommand(mpd_Connection * connection) {
601         while(!connection->doneProcessing) {
602                 if(connection->doneListOk) connection->doneListOk = 0;
603                 mpd_getNextReturnElement(connection);
604         }
607 static void mpd_finishListOkCommand(mpd_Connection * connection) {
608         while(!connection->doneProcessing && connection->listOks &&
609                         !connection->doneListOk)
610         {
611                 mpd_getNextReturnElement(connection);
612         }
615 int mpd_nextListOkCommand(mpd_Connection * connection) {
616         mpd_finishListOkCommand(connection);
617         if(!connection->doneProcessing) connection->doneListOk = 0;
618         if(connection->listOks == 0 || connection->doneProcessing) return -1;
619         return 0;
622 void mpd_sendStatusCommand(mpd_Connection * connection) {
623         mpd_executeCommand(connection,"status\n");
626 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
627         mpd_Status * status;
629         /*mpd_executeCommand(connection,"status\n");
631         if(connection->error) return NULL;*/
633         if(connection->doneProcessing || (connection->listOks &&
634            connection->doneListOk))
635         {
636                 return NULL;
637         }
639         if(!connection->returnElement) mpd_getNextReturnElement(connection);
641         status = malloc(sizeof(mpd_Status));
642         status->volume = -1;
643         status->repeat = 0;
644         status->random = 0;
645         status->playlist = -1;
646         status->playlistLength = -1;
647         status->state = -1;
648         status->song = 0;
649         status->songid = 0;
650         status->elapsedTime = 0;
651         status->totalTime = 0;
652         status->bitRate = 0;
653         status->sampleRate = 0;
654         status->bits = 0;
655         status->channels = 0;
656         status->crossfade = -1;
657         status->error = NULL;
658         status->updatingDb = 0;
660         if(connection->error) {
661                 free(status);
662                 return NULL;
663         }
664         while(connection->returnElement) {
665                 mpd_ReturnElement * re = connection->returnElement;
666                 if(strcmp(re->name,"volume")==0) {
667                         status->volume = atoi(re->value);
668                 }
669                 else if(strcmp(re->name,"repeat")==0) {
670                         status->repeat = atoi(re->value);
671                 }
672                 else if(strcmp(re->name,"random")==0) {
673                         status->random = atoi(re->value);
674                 }
675                 else if(strcmp(re->name,"playlist")==0) {
676                         status->playlist = strtol(re->value,NULL,10);
677                 }
678                 else if(strcmp(re->name,"playlistlength")==0) {
679                         status->playlistLength = atoi(re->value);
680                 }
681                 else if(strcmp(re->name,"bitrate")==0) {
682                         status->bitRate = atoi(re->value);
683                 }
684                 else if(strcmp(re->name,"state")==0) {
685                         if(strcmp(re->value,"play")==0) {
686                                 status->state = MPD_STATUS_STATE_PLAY;
687                         }
688                         else if(strcmp(re->value,"stop")==0) {
689                                 status->state = MPD_STATUS_STATE_STOP;
690                         }
691                         else if(strcmp(re->value,"pause")==0) {
692                                 status->state = MPD_STATUS_STATE_PAUSE;
693                         }
694                         else {
695                                 status->state = MPD_STATUS_STATE_UNKNOWN;
696                         }
697                 }
698                 else if(strcmp(re->name,"song")==0) {
699                         status->song = atoi(re->value);
700                 }
701                 else if(strcmp(re->name,"songid")==0) {
702                         status->songid = atoi(re->value);
703                 }
704                 else if(strcmp(re->name,"time")==0) {
705                         char * tok = strchr(re->value,':');
706                         /* the second strchr below is a safety check */
707                         if (tok && (strchr(tok,0) > (tok+1))) {
708                                 /* atoi stops at the first non-[0-9] char: */
709                                 status->elapsedTime = atoi(re->value);
710                                 status->totalTime = atoi(tok+1);
711                         }
712                 }
713                 else if(strcmp(re->name,"error")==0) {
714                         status->error = strdup(re->value);
715                 }
716                 else if(strcmp(re->name,"xfade")==0) {
717                         status->crossfade = atoi(re->value);
718                 }
719                 else if(strcmp(re->name,"updating_db")==0) {
720                         status->updatingDb = atoi(re->value);
721                 }
722                 else if(strcmp(re->name,"audio")==0) {
723                         char * tok = strchr(re->value,':');
724                         if (tok && (strchr(tok,0) > (tok+1))) {
725                                 status->sampleRate = atoi(re->value);
726                                 status->bits = atoi(++tok);
727                                 tok = strchr(tok,':');
728                                 if (tok && (strchr(tok,0) > (tok+1)))
729                                         status->channels = atoi(tok+1);
730                         }
731                 }
733                 mpd_getNextReturnElement(connection);
734                 if(connection->error) {
735                         free(status);
736                         return NULL;
737                 }
738         }
740         if(connection->error) {
741                 free(status);
742                 return NULL;
743         }
744         else if(status->state<0) {
745                 strcpy(connection->errorStr,"state not found");
746                 connection->error = 1;
747                 free(status);
748                 return NULL;
749         }
751         return status;
754 void mpd_freeStatus(mpd_Status * status) {
755         if(status->error) free(status->error);
756         free(status);
759 void mpd_sendStatsCommand(mpd_Connection * connection) {
760         mpd_executeCommand(connection,"stats\n");
763 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
764         mpd_Stats * stats;
766         /*mpd_executeCommand(connection,"stats\n");
768         if(connection->error) return NULL;*/
770         if(connection->doneProcessing || (connection->listOks &&
771            connection->doneListOk))
772         {
773                 return NULL;
774         }
776         if(!connection->returnElement) mpd_getNextReturnElement(connection);
778         stats = malloc(sizeof(mpd_Stats));
779         stats->numberOfArtists = 0;
780         stats->numberOfAlbums = 0;
781         stats->numberOfSongs = 0;
782         stats->uptime = 0;
783         stats->dbUpdateTime = 0;
784         stats->playTime = 0;
785         stats->dbPlayTime = 0;
787         if(connection->error) {
788                 free(stats);
789                 return NULL;
790         }
791         while(connection->returnElement) {
792                 mpd_ReturnElement * re = connection->returnElement;
793                 if(strcmp(re->name,"artists")==0) {
794                         stats->numberOfArtists = atoi(re->value);
795                 }
796                 else if(strcmp(re->name,"albums")==0) {
797                         stats->numberOfAlbums = atoi(re->value);
798                 }
799                 else if(strcmp(re->name,"songs")==0) {
800                         stats->numberOfSongs = atoi(re->value);
801                 }
802                 else if(strcmp(re->name,"uptime")==0) {
803                         stats->uptime = strtol(re->value,NULL,10);
804                 }
805                 else if(strcmp(re->name,"db_update")==0) {
806                         stats->dbUpdateTime = strtol(re->value,NULL,10);
807                 }
808                 else if(strcmp(re->name,"playtime")==0) {
809                         stats->playTime = strtol(re->value,NULL,10);
810                 }
811                 else if(strcmp(re->name,"db_playtime")==0) {
812                         stats->dbPlayTime = strtol(re->value,NULL,10);
813                 }
815                 mpd_getNextReturnElement(connection);
816                 if(connection->error) {
817                         free(stats);
818                         return NULL;
819                 }
820         }
822         if(connection->error) {
823                 free(stats);
824                 return NULL;
825         }
827         return stats;
830 void mpd_freeStats(mpd_Stats * stats) {
831         free(stats);
834 static void mpd_initDirectory(mpd_Directory * directory) {
835         directory->path = NULL;
838 static void mpd_finishDirectory(mpd_Directory * directory) {
839         if (directory->path)
840                 str_pool_put(directory->path);
843 mpd_Directory * mpd_newDirectory(void) {
844         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
846         mpd_initDirectory(directory);
848         return directory;
851 void mpd_freeDirectory(mpd_Directory * directory) {
852         mpd_finishDirectory(directory);
854         free(directory);
857 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
858         mpd_Directory * ret = mpd_newDirectory();
860         if (directory->path)
861                 ret->path = str_pool_dup(directory->path);
863         return ret;
866 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
867         playlist->path = NULL;
870 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
871         if (playlist->path)
872                 str_pool_put(playlist->path);
875 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
876         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
878         mpd_initPlaylistFile(playlist);
880         return playlist;
883 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
884         mpd_finishPlaylistFile(playlist);
885         free(playlist);
888 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
889         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
891         if (playlist->path)
892                 ret->path = str_pool_dup(playlist->path);
894         return ret;
897 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
898         entity->info.directory = NULL;
901 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
902         if(entity->info.directory) {
903                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
904                         mpd_freeDirectory(entity->info.directory);
905                 }
906                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
907                         mpd_freeSong(entity->info.song);
908                 }
909                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
910                         mpd_freePlaylistFile(entity->info.playlistFile);
911                 }
912         }
915 mpd_InfoEntity * mpd_newInfoEntity(void) {
916         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
918         mpd_initInfoEntity(entity);
920         return entity;
923 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
924         mpd_finishInfoEntity(entity);
925         free(entity);
928 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
929         mpd_executeCommand(connection,command);
932 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
933         mpd_InfoEntity * entity = NULL;
935         if(connection->doneProcessing || (connection->listOks &&
936            connection->doneListOk)) {
937                 return NULL;
938         }
940         if(!connection->returnElement) mpd_getNextReturnElement(connection);
942         if(connection->returnElement) {
943                 if(strcmp(connection->returnElement->name,"file")==0) {
944                         entity = mpd_newInfoEntity();
945                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
946                         entity->info.song = mpd_newSong();
947                         entity->info.song->file =
948                                 str_pool_dup(connection->returnElement->value);
949                 }
950                 else if(strcmp(connection->returnElement->name,
951                                         "directory")==0) {
952                         entity = mpd_newInfoEntity();
953                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
954                         entity->info.directory = mpd_newDirectory();
955                         entity->info.directory->path =
956                                 str_pool_dup(connection->returnElement->value);
957                 }
958                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
959                         entity = mpd_newInfoEntity();
960                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
961                         entity->info.playlistFile = mpd_newPlaylistFile();
962                         entity->info.playlistFile->path =
963                                 str_pool_dup(connection->returnElement->value);
964                 }
965                 else if(strcmp(connection->returnElement->name, "cpos") == 0){
966                         entity = mpd_newInfoEntity();
967                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
968                         entity->info.song = mpd_newSong();
969                         entity->info.song->pos = atoi(connection->returnElement->value);
970                 }
971                 else {
972                         connection->error = 1;
973                         strcpy(connection->errorStr,"problem parsing song info");
974                         return NULL;
975                 }
976         }
977         else return NULL;
979         mpd_getNextReturnElement(connection);
980         while(connection->returnElement) {
981                 mpd_ReturnElement * re = connection->returnElement;
983                 if(strcmp(re->name,"file")==0) return entity;
984                 else if(strcmp(re->name,"directory")==0) return entity;
985                 else if(strcmp(re->name,"playlist")==0) return entity;
986                 else if(strcmp(re->name,"cpos")==0) return entity;
988                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
989                                 strlen(re->value)) {
990                         if(!entity->info.song->artist &&
991                                         strcmp(re->name,"Artist")==0) {
992                                 entity->info.song->artist = str_pool_dup(re->value);
993                         }
994                         else if(!entity->info.song->album &&
995                                         strcmp(re->name,"Album")==0) {
996                                 entity->info.song->album = str_pool_dup(re->value);
997                         }
998                         else if(!entity->info.song->title &&
999                                         strcmp(re->name,"Title")==0) {
1000                                 entity->info.song->title = str_pool_dup(re->value);
1001                         }
1002                         else if(!entity->info.song->track &&
1003                                         strcmp(re->name,"Track")==0) {
1004                                 entity->info.song->track = str_pool_dup(re->value);
1005                         }
1006                         else if(!entity->info.song->name &&
1007                                         strcmp(re->name,"Name")==0) {
1008                                 entity->info.song->name = str_pool_dup(re->value);
1009                         }
1010                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1011                                         strcmp(re->name,"Time")==0) {
1012                                 entity->info.song->time = atoi(re->value);
1013                         }
1014                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1015                                         strcmp(re->name,"Pos")==0) {
1016                                 entity->info.song->pos = atoi(re->value);
1017                         }
1018                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
1019                                         strcmp(re->name,"Id")==0) {
1020                                 entity->info.song->id = atoi(re->value);
1021                         }
1022                         else if(!entity->info.song->date &&
1023                                         strcmp(re->name, "Date") == 0) {
1024                                 entity->info.song->date = str_pool_dup(re->value);
1025                         }
1026                         else if(!entity->info.song->genre &&
1027                                         strcmp(re->name, "Genre") == 0) {
1028                                 entity->info.song->genre = str_pool_dup(re->value);
1029                         }
1030                         else if(!entity->info.song->composer &&
1031                                         strcmp(re->name, "Composer") == 0) {
1032                                 entity->info.song->composer = str_pool_dup(re->value);
1033                         }
1034                         else if(!entity->info.song->disc &&
1035                                         strcmp(re->name, "Disc") == 0) {
1036                                 entity->info.song->disc = str_pool_dup(re->value);
1037                         }
1038                         else if(!entity->info.song->comment &&
1039                                         strcmp(re->name, "Comment") == 0) {
1040                                 entity->info.song->comment = str_pool_dup(re->value);
1041                         }
1042                 }
1043                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1044                 }
1045                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1046                 }
1048                 mpd_getNextReturnElement(connection);
1049         }
1051         return entity;
1054 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1055                 const char * name)
1057         if(connection->doneProcessing || (connection->listOks &&
1058                                 connection->doneListOk))
1059         {
1060                 return NULL;
1061         }
1063         mpd_getNextReturnElement(connection);
1064         while(connection->returnElement) {
1065                 mpd_ReturnElement * re = connection->returnElement;
1067                 if(strcmp(re->name,name)==0) return strdup(re->value);
1068                 mpd_getNextReturnElement(connection);
1069         }
1071         return NULL;
1074 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1075         if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1076         {
1077                 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1078         }
1079         return NULL;
1082 char * mpd_getNextArtist(mpd_Connection * connection) {
1083         return mpd_getNextReturnElementNamed(connection,"Artist");
1086 char * mpd_getNextAlbum(mpd_Connection * connection) {
1087         return mpd_getNextReturnElementNamed(connection,"Album");
1090 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1091         char * string = malloc(strlen("playlistinfo")+25);
1092         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1093         mpd_sendInfoCommand(connection,string);
1094         free(string);
1097 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1098         char * string = malloc(strlen("playlistid")+25);
1099         sprintf(string, "playlistid \"%i\"\n", id);
1100         mpd_sendInfoCommand(connection, string);
1101         free(string);
1104 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1105         char * string = malloc(strlen("plchanges")+25);
1106         sprintf(string,"plchanges \"%lld\"\n",playlist);
1107         mpd_sendInfoCommand(connection,string);
1108         free(string);
1111 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1112         char * string = malloc(strlen("plchangesposid")+25);
1113         sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1114         mpd_sendInfoCommand(connection,string);
1115         free(string);
1118 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1119         char * sDir = mpd_sanitizeArg(dir);
1120         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1121         sprintf(string,"listall \"%s\"\n",sDir);
1122         mpd_sendInfoCommand(connection,string);
1123         free(string);
1124         free(sDir);
1127 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1128         char * sDir = mpd_sanitizeArg(dir);
1129         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1130         sprintf(string,"listallinfo \"%s\"\n",sDir);
1131         mpd_sendInfoCommand(connection,string);
1132         free(string);
1133         free(sDir);
1136 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1137         char * sDir = mpd_sanitizeArg(dir);
1138         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1139         sprintf(string,"lsinfo \"%s\"\n",sDir);
1140         mpd_sendInfoCommand(connection,string);
1141         free(string);
1142         free(sDir);
1145 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1146         mpd_executeCommand(connection,"currentsong\n");
1149 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1150                 const char * str)
1152         char st[10];
1153         char * string;
1154         char * sanitStr = mpd_sanitizeArg(str);
1155         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1156         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1157         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1158         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1159         else {
1160                 connection->error = 1;
1161                 strcpy(connection->errorStr,"unknown table for search");
1162                 return;
1163         }
1164         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1165         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1166         mpd_sendInfoCommand(connection,string);
1167         free(string);
1168         free(sanitStr);
1171 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1172                 const char * str)
1174         char st[10];
1175         char * string;
1176         char * sanitStr = mpd_sanitizeArg(str);
1177         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1178         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1179         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1180         else {
1181                 connection->error = 1;
1182                 strcpy(connection->errorStr,"unknown table for find");
1183                 return;
1184         }
1185         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1186         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1187         mpd_sendInfoCommand(connection,string);
1188         free(string);
1189         free(sanitStr);
1192 void mpd_sendListCommand(mpd_Connection * connection, int table,
1193                 const char * arg1)
1195         char st[10];
1196         char * string;
1197         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1198         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1199         else {
1200                 connection->error = 1;
1201                 strcpy(connection->errorStr,"unknown table for list");
1202                 return;
1203         }
1204         if(arg1) {
1205                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1206                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1207                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1208                 free(sanitArg1);
1209         }
1210         else {
1211                 string = malloc(strlen("list")+strlen(st)+3);
1212                 sprintf(string,"list %s\n",st);
1213         }
1214         mpd_sendInfoCommand(connection,string);
1215         free(string);
1218 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1219         char * sFile = mpd_sanitizeArg(file);
1220         char * string = malloc(strlen("add")+strlen(sFile)+5);
1221         sprintf(string,"add \"%s\"\n",sFile);
1222         mpd_executeCommand(connection,string);
1223         free(string);
1224         free(sFile);
1227 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1228         char * string = malloc(strlen("delete")+25);
1229         sprintf(string,"delete \"%i\"\n",songPos);
1230         mpd_sendInfoCommand(connection,string);
1231         free(string);
1234 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1235         char * string = malloc(strlen("deleteid")+25);
1236         sprintf(string, "deleteid \"%i\"\n", id);
1237         mpd_sendInfoCommand(connection,string);
1238         free(string);
1241 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1242         char * sName = mpd_sanitizeArg(name);
1243         char * string = malloc(strlen("save")+strlen(sName)+5);
1244         sprintf(string,"save \"%s\"\n",sName);
1245         mpd_executeCommand(connection,string);
1246         free(string);
1247         free(sName);
1250 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1251         char * sName = mpd_sanitizeArg(name);
1252         char * string = malloc(strlen("load")+strlen(sName)+5);
1253         sprintf(string,"load \"%s\"\n",sName);
1254         mpd_executeCommand(connection,string);
1255         free(string);
1256         free(sName);
1259 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1260         char * sName = mpd_sanitizeArg(name);
1261         char * string = malloc(strlen("rm")+strlen(sName)+5);
1262         sprintf(string,"rm \"%s\"\n",sName);
1263         mpd_executeCommand(connection,string);
1264         free(string);
1265         free(sName);
1268 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1269         mpd_executeCommand(connection,"shuffle\n");
1272 void mpd_sendClearCommand(mpd_Connection * connection) {
1273         mpd_executeCommand(connection,"clear\n");
1276 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1277         char * string = malloc(strlen("play")+25);
1278         sprintf(string,"play \"%i\"\n",songPos);
1279         mpd_sendInfoCommand(connection,string);
1280         free(string);
1283 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1284         char * string = malloc(strlen("playid")+25);
1285         sprintf(string,"playid \"%i\"\n",id);
1286         mpd_sendInfoCommand(connection,string);
1287         free(string);
1290 void mpd_sendStopCommand(mpd_Connection * connection) {
1291         mpd_executeCommand(connection,"stop\n");
1294 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1295         char * string = malloc(strlen("pause")+25);
1296         sprintf(string,"pause \"%i\"\n",pauseMode);
1297         mpd_executeCommand(connection,string);
1298         free(string);
1301 void mpd_sendNextCommand(mpd_Connection * connection) {
1302         mpd_executeCommand(connection,"next\n");
1305 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1306         char * string = malloc(strlen("move")+25);
1307         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1308         mpd_sendInfoCommand(connection,string);
1309         free(string);
1312 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1313         char * string = malloc(strlen("moveid")+25);
1314         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1315         mpd_sendInfoCommand(connection,string);
1316         free(string);
1319 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1320         char * string = malloc(strlen("swap")+25);
1321         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1322         mpd_sendInfoCommand(connection,string);
1323         free(string);
1326 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1327         char * string = malloc(strlen("swapid")+25);
1328         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1329         mpd_sendInfoCommand(connection,string);
1330         free(string);
1333 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1334         char * string = malloc(strlen("seek")+25);
1335         sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1336         mpd_sendInfoCommand(connection,string);
1337         free(string);
1340 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1341         char * string = malloc(strlen("seekid")+25);
1342         sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1343         mpd_sendInfoCommand(connection,string);
1344         free(string);
1347 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1348         char *sPath = mpd_sanitizeArg(path);
1349         char * string = malloc(strlen("update")+strlen(sPath)+5);
1350         sprintf(string,"update \"%s\"\n",sPath);
1351         mpd_sendInfoCommand(connection,string);
1352         free(string);
1353         free(sPath);
1356 int mpd_getUpdateId(mpd_Connection * connection) {
1357         char * jobid;
1358         int ret = 0;
1360         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1361         if(jobid) {
1362                 ret = atoi(jobid);
1363                 free(jobid);
1364         }
1366         return ret;
1369 void mpd_sendPrevCommand(mpd_Connection * connection) {
1370         mpd_executeCommand(connection,"previous\n");
1373 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1374         char * string = malloc(strlen("repeat")+25);
1375         sprintf(string,"repeat \"%i\"\n",repeatMode);
1376         mpd_executeCommand(connection,string);
1377         free(string);
1380 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1381         char * string = malloc(strlen("random")+25);
1382         sprintf(string,"random \"%i\"\n",randomMode);
1383         mpd_executeCommand(connection,string);
1384         free(string);
1387 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1388         char * string = malloc(strlen("setvol")+25);
1389         sprintf(string,"setvol \"%i\"\n",volumeChange);
1390         mpd_executeCommand(connection,string);
1391         free(string);
1394 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1395         char * string = malloc(strlen("volume")+25);
1396         sprintf(string,"volume \"%i\"\n",volumeChange);
1397         mpd_executeCommand(connection,string);
1398         free(string);
1401 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1402         char * string = malloc(strlen("crossfade")+25);
1403         sprintf(string,"crossfade \"%i\"\n",seconds);
1404         mpd_executeCommand(connection,string);
1405         free(string);
1408 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1409         char * sPass = mpd_sanitizeArg(pass);
1410         char * string = malloc(strlen("password")+strlen(sPass)+5);
1411         sprintf(string,"password \"%s\"\n",sPass);
1412         mpd_executeCommand(connection,string);
1413         free(string);
1414         free(sPass);
1417 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1418         if(connection->commandList) {
1419                 strcpy(connection->errorStr,"already in command list mode");
1420                 connection->error = 1;
1421                 return;
1422         }
1423         connection->commandList = COMMAND_LIST;
1424         mpd_executeCommand(connection,"command_list_begin\n");
1427 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1428         if(connection->commandList) {
1429                 strcpy(connection->errorStr,"already in command list mode");
1430                 connection->error = 1;
1431                 return;
1432         }
1433         connection->commandList = COMMAND_LIST_OK;
1434         mpd_executeCommand(connection,"command_list_ok_begin\n");
1435         connection->listOks = 0;
1438 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1439         if(!connection->commandList) {
1440                 strcpy(connection->errorStr,"not in command list mode");
1441                 connection->error = 1;
1442                 return;
1443         }
1444         connection->commandList = 0;
1445         mpd_executeCommand(connection,"command_list_end\n");
1448 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1449         mpd_executeCommand(connection,"outputs\n");
1452 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1453         mpd_OutputEntity * output = NULL;
1455         if(connection->doneProcessing || (connection->listOks &&
1456                                 connection->doneListOk))
1457         {
1458                 return NULL;
1459         }
1461         if(connection->error) return NULL;
1463         output = malloc(sizeof(mpd_OutputEntity));
1464         output->id = -10;
1465         output->name = NULL;
1466         output->enabled = 0;
1468         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1470         while(connection->returnElement) {
1471                 mpd_ReturnElement * re = connection->returnElement;
1472                 if(strcmp(re->name,"outputid")==0) {
1473                         if(output!=NULL && output->id>=0) return output;
1474                         output->id = atoi(re->value);
1475                 }
1476                 else if(strcmp(re->name,"outputname")==0) {
1477                         output->name = strdup(re->value);
1478                 }
1479                 else if(strcmp(re->name,"outputenabled")==0) {
1480                         output->enabled = atoi(re->value);
1481                 }
1483                 mpd_getNextReturnElement(connection);
1484                 if(connection->error) {
1485                         free(output);
1486                         return NULL;
1487                 }
1489         }
1491         return output;
1494 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1495         char * string = malloc(strlen("enableoutput")+25);
1496         sprintf(string,"enableoutput \"%i\"\n",outputId);
1497         mpd_executeCommand(connection,string);
1498         free(string);
1501 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1502         char * string = malloc(strlen("disableoutput")+25);
1503         sprintf(string,"disableoutput \"%i\"\n",outputId);
1504         mpd_executeCommand(connection,string);
1505         free(string);
1508 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1509         free(output->name);
1510         free(output);
1513 /**
1514  * mpd_sendNotCommandsCommand
1515  * odd naming, but it gets the not allowed commands
1516  */
1518 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1519         mpd_executeCommand(connection,"notcommands\n");
1522 /**
1523  * mpd_sendCommandsCommand
1524  * odd naming, but it gets the allowed commands
1525  */
1527 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1528         mpd_executeCommand(connection,"commands\n");
1530 /**
1531  * Get the next returned command
1532  */
1533 char * mpd_getNextCommand(mpd_Connection * connection) {
1534         return mpd_getNextReturnElementNamed(connection,"command");
1537 void mpd_startSearch(mpd_Connection * connection,int exact) {
1538         if(connection->request) {
1539                 /* search/find allready in progress */
1540                 /* TODO: set error here?  */
1541                 return;
1542         }
1543         if(exact){
1544                 connection->request = strdup("find");
1545         }
1546         else{
1547                 connection->request = strdup("search");
1548         }
1552 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1553         if(connection->request) {
1554                 /* search/find allready in progress */
1555                 /* TODO: set error here?  */
1556                 return;
1557         }
1558         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1559                 /* set error here */
1560                 return;
1561         }
1563         connection->request = malloc(sizeof(char)*(
1564                                 /* length of the field name */
1565                                 strlen(mpdTagItemKeys[field])+
1566                                 /* "list"+space+\0 */
1567                                 6
1568                                 ));
1569         sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1574 void mpd_addConstraintSearch(mpd_Connection *connection,
1575                 int field,
1576                 char *name)
1578         char *arg = NULL;
1579         if(!connection->request){
1580                 return;
1581         }
1582         if(name == NULL) {
1583                 return;
1584         }
1585         if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1586                 return;
1587         }
1588         /* clean up the query */
1589         arg = mpd_sanitizeArg(name);
1590         /* create space for the query */
1591         connection->request = realloc(connection->request, (
1592                          /* length of the old string */
1593                          strlen(connection->request)+
1594                          /* space between */
1595                          1+
1596                          /* length of the field name */
1597                          strlen(mpdTagItemKeys[field])+
1598                          /* space plus starting " */
1599                          2+
1600                          /* length of search term */
1601                          strlen(arg)+
1602                          /* closign " +\0 that is added sprintf */
1603                          2
1604                         )*sizeof(char));
1605         /* and form the query */
1606         sprintf(connection->request, "%s %s \"%s\"",
1607                         connection->request,
1608                         mpdTagItemKeys[field],
1609                         arg);
1610         free(arg);
1614 void mpd_commitSearch(mpd_Connection *connection)
1616         if(connection->request)
1617         {
1618                 int length = strlen(connection->request);
1619                 /* fixing up the string for mpd to like */
1620                 connection->request = realloc(connection->request,
1621                                 (length+        /* old length */
1622                                  2              /* closing \n and \0 */
1623                                 )*sizeof(char));
1624                 connection->request[length] = '\n';
1625                 connection->request[length+1] = '\0';
1626                 /* and off we go */
1627                 mpd_sendInfoCommand(connection, connection->request);
1628                 /* clean up a bit */
1629                 free(connection->request);
1630                 connection->request = NULL;
1631         }
1634 /**
1635  * @param connection a MpdConnection
1636  * @param path  the path to the playlist.
1637  *
1638  * List the content, with full metadata, of a stored playlist.
1639  *
1640  */
1641 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1643         char *arg = mpd_sanitizeArg(path);
1644         char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1645         sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1646         mpd_sendInfoCommand(connection, query);
1647         free(arg);
1648         free(query);
1651 /**
1652  * @param connection a MpdConnection
1653  * @param path  the path to the playlist.
1654  *
1655  * List the content of a stored playlist.
1656  *
1657  */
1658 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1660         char *arg = mpd_sanitizeArg(path);
1661         char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1662         sprintf(query, "listplaylist \"%s\"\n",arg);
1663         mpd_sendInfoCommand(connection, query);
1664         free(arg);
1665         free(query);