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