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