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