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