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