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