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