- include <signal.h> (needed on *BSD)
[monky] / src / libmpdclient.c
1 /* libmpdclient
2    (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
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 "libmpdclient.h"
35
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netdb.h>
40 #include <stdio.h>
41 #include <sys/param.h>
42 #include <string.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <fcntl.h>
48 #include <sys/signal.h>
49 #include <signal.h>
50
51 #ifndef MPD_NO_IPV6
52 #ifdef AF_INET6
53 #define MPD_HAVE_IPV6
54 #endif
55 #endif
56
57 static char sigpipe = 0;
58
59 #define COMMAND_LIST    1
60 #define COMMAND_LIST_OK 2
61
62 #ifdef MPD_HAVE_IPV6
63 int mpd_ipv6Supported()
64 {
65         int s;
66         s = socket(AF_INET6, SOCK_STREAM, 0);
67         if (s == -1)
68                 return 0;
69         close(s);
70         return 1;
71 }
72 #endif
73
74
75 char *mpd_sanitizeArg(const char *arg)
76 {
77         size_t i;
78         int count = 0;
79         char *ret;
80
81         for (i = 0; i < strlen(arg); i++) {
82                 if (arg[i] == '"' || arg[i] == '\\')
83                         count++;
84         }
85
86         ret = malloc(strlen(arg) + count + 1);
87
88         count = 0;
89         for (i = 0; i < strlen(arg) + 1; i++) {
90                 if (arg[i] == '"' || arg[i] == '\\') {
91                         ret[i + count] = '\\';
92                         count++;
93                 }
94                 ret[i + count] = arg[i];
95         }
96
97         return ret;
98 }
99
100 mpd_ReturnElement *mpd_newReturnElement(const char *name,
101                                         const char *value)
102 {
103         mpd_ReturnElement *ret = malloc(sizeof(mpd_ReturnElement));
104
105         ret->name = strdup(name);
106         ret->value = strdup(value);
107
108         return ret;
109 }
110
111 void mpd_freeReturnElement(mpd_ReturnElement * re)
112 {
113         free(re->name);
114         free(re->value);
115         free(re);
116 }
117
118 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout)
119 {
120         connection->timeout.tv_sec = (int) timeout;
121         connection->timeout.tv_usec = (int) (timeout * 1e6 -
122                                              connection->timeout.tv_sec *
123                                              1000000 + 0.5);
124 }
125
126 mpd_Connection *mpd_newConnection(const char *host, int port,
127                                   float timeout)
128 {
129         int err;
130         struct hostent *he;
131         struct sockaddr *dest;
132 #ifdef HAVE_SOCKLEN_T
133         socklen_t destlen;
134 #else
135         int destlen;
136 #endif
137         struct sockaddr_in sin;
138         char *rt;
139         char *output;
140         mpd_Connection *connection = malloc(sizeof(mpd_Connection));
141         struct timeval tv;
142         fd_set fds;
143         struct sigaction sapipe;           /* definition of signal action */
144
145 #ifdef MPD_HAVE_IPV6
146         struct sockaddr_in6 sin6;
147 #endif
148         strcpy(connection->buffer, "");
149         connection->buflen = 0;
150         connection->bufstart = 0;
151         strcpy(connection->errorStr, "");
152         connection->error = 0;
153         connection->doneProcessing = 0;
154         connection->commandList = 0;
155         connection->listOks = 0;
156         connection->doneListOk = 0;
157         connection->returnElement = NULL;
158         sigpipe = 0;
159
160         if (!(he = gethostbyname(host))) {
161                 snprintf(connection->errorStr, MPD_BUFFER_MAX_LENGTH,
162                          "host \"%s\" not found", host);
163                 connection->error = MPD_ERROR_UNKHOST;
164                 return connection;
165         }
166
167         memset(&sin, 0, sizeof(struct sockaddr_in));
168         /*dest.sin_family = he->h_addrtype; */
169         sin.sin_family = AF_INET;
170         sin.sin_port = htons(port);
171 #ifdef MPD_HAVE_IPV6
172         memset(&sin6, 0, sizeof(struct sockaddr_in6));
173         sin6.sin6_family = AF_INET6;
174         sin6.sin6_port = htons(port);
175 #endif
176         switch (he->h_addrtype) {
177         case AF_INET:
178                 memcpy((char *) &sin.sin_addr.s_addr, (char *) he->h_addr,
179                        he->h_length);
180                 dest = (struct sockaddr *) &sin;
181                 destlen = sizeof(struct sockaddr_in);
182                 break;
183 #ifdef MPD_HAVE_IPV6
184         case AF_INET6:
185                 if (!mpd_ipv6Supported()) {
186                         strcpy(connection->errorStr,
187                                "no IPv6 suuport but a "
188                                "IPv6 address found\n");
189                         connection->error = MPD_ERROR_SYSTEM;
190                         return connection;
191                 }
192                 memcpy((char *) &sin6.sin6_addr.s6_addr,
193                        (char *) he->h_addr, he->h_length);
194                 dest = (struct sockaddr *) &sin6;
195                 destlen = sizeof(struct sockaddr_in6);
196                 break;
197 #endif
198         default:
199                 strcpy(connection->errorStr,
200                        "address type is not IPv4 or " "IPv6\n");
201                 connection->error = MPD_ERROR_SYSTEM;
202                 return connection;
203                 break;
204         }
205
206         if ((connection->sock =
207              socket(dest->sa_family, SOCK_STREAM, 0)) < 0) {
208                 strcpy(connection->errorStr, "problems creating socket");
209                 connection->error = MPD_ERROR_SYSTEM;
210                 return connection;
211         }
212
213         mpd_setConnectionTimeout(connection, timeout);
214         
215         /* install the signal handler */
216         sapipe.sa_handler = mpd_signalHandler;
217 //      sapipe.sa_mask = 0;
218         sapipe.sa_flags = 0;
219 #ifdef __linux__
220         sapipe.sa_restorer = NULL;
221 #endif /* __linux__ */
222         sigaction(SIGPIPE,&sapipe,NULL);
223
224         /* connect stuff */
225         {
226                 int flags = fcntl(connection->sock, F_GETFL, 0);
227                 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
228
229                 if (connect(connection->sock, dest, destlen) < 0
230                     && errno != EINPROGRESS) {
231                         snprintf(connection->errorStr,
232                                  MPD_BUFFER_MAX_LENGTH,
233                                  "problems connecting to \"%s\" on port"
234                                  " %i", host, port);
235                         connection->error = MPD_ERROR_CONNPORT;
236                         return connection;
237                 }
238         }
239
240         while (!(rt = strstr(connection->buffer, "\n"))) {
241                 tv.tv_sec = connection->timeout.tv_sec;
242                 tv.tv_usec = connection->timeout.tv_usec;
243                 FD_ZERO(&fds);
244                 FD_SET(connection->sock, &fds);
245                 if ((err =
246                      select(connection->sock + 1, &fds, NULL, NULL,
247                             &tv)) == 1) {
248                         int readed;
249                         readed = recv(connection->sock,
250                                       &(connection->
251                                         buffer[connection->buflen]),
252                                       MPD_BUFFER_MAX_LENGTH -
253                                       connection->buflen, 0);
254                         if (readed <= 0) {
255                                 snprintf(connection->errorStr,
256                                          MPD_BUFFER_MAX_LENGTH,
257                                          "problems getting a response from"
258                                          " \"%s\" on port %i", host, port);
259                                 connection->error = MPD_ERROR_NORESPONSE;
260                                 return connection;
261                         }
262                         connection->buflen += readed;
263                         connection->buffer[connection->buflen] = '\0';
264                         tv.tv_sec = connection->timeout.tv_sec;
265                         tv.tv_usec = connection->timeout.tv_usec;
266                 } else if (err < 0) {
267                         switch (errno) {
268                         case EINTR:
269                                 continue;
270                         default:
271                                 snprintf(connection->errorStr,
272                                          MPD_BUFFER_MAX_LENGTH,
273                                          "problems connecting to \"%s\" on port"
274                                          " %i", host, port);
275                                 connection->error = MPD_ERROR_CONNPORT;
276                                 return connection;
277                         }
278                 } else {
279                         snprintf(connection->errorStr,
280                                  MPD_BUFFER_MAX_LENGTH,
281                                  "timeout in attempting to get a response from"
282                                  " \"%s\" on port %i", host, port);
283                         connection->error = MPD_ERROR_NORESPONSE;
284                         return connection;
285                 }
286         }
287
288         *rt = '\0';
289         output = strdup(connection->buffer);
290         strcpy(connection->buffer, rt + 1);
291         connection->buflen = strlen(connection->buffer);
292
293         if (strncmp
294             (output, MPD_WELCOME_MESSAGE, strlen(MPD_WELCOME_MESSAGE))) {
295                 free(output);
296                 snprintf(connection->errorStr, MPD_BUFFER_MAX_LENGTH,
297                          "mpd not running on port %i on host \"%s\"", port,
298                          host);
299                 connection->error = MPD_ERROR_NOTMPD;
300                 return connection;
301         }
302
303         {
304                 char *test;
305                 char *version[3];
306                 char *tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
307                 char *search = ".";
308                 int i;
309
310                 for (i = 0; i < 3; i++) {
311                         char *tok;
312                         if (i == 3)
313                                 search = " ";
314                         version[i] = strtok_r(tmp, search, &tok);
315                         if (!version[i]) {
316                                 free(output);
317                                 snprintf(connection->errorStr,
318                                          MPD_BUFFER_MAX_LENGTH,
319                                          "error parsing version number at "
320                                          "\"%s\"",
321                                          &output[strlen
322                                                  (MPD_WELCOME_MESSAGE)]);
323                                 connection->error = MPD_ERROR_NOTMPD;
324                                 return connection;
325                         }
326                         connection->version[i] =
327                             strtol(version[i], &test, 10);
328                         if (version[i] == test || *test != '\0') {
329                                 free(output);
330                                 snprintf(connection->errorStr,
331                                          MPD_BUFFER_MAX_LENGTH,
332                                          "error parsing version number at "
333                                          "\"%s\"",
334                                          &output[strlen
335                                                  (MPD_WELCOME_MESSAGE)]);
336                                 connection->error = MPD_ERROR_NOTMPD;
337                                 return connection;
338                         }
339                         tmp = NULL;
340                 }
341         }
342
343         free(output);
344
345         connection->doneProcessing = 1;
346
347         return connection;
348 }
349
350 void mpd_clearError(mpd_Connection * connection)
351 {
352         connection->error = 0;
353         connection->errorStr[0] = '\0';
354 }
355
356 void mpd_closeConnection(mpd_Connection * connection)
357 {
358         close(connection->sock);
359         if (connection->returnElement)
360                 free(connection->returnElement);
361         free(connection);
362 }
363
364 void mpd_executeCommand(mpd_Connection * connection, char *command)
365 {
366         int ret;
367         struct timeval tv;
368         fd_set fds;
369         char *commandPtr = command;
370         int commandLen = strlen(command);
371
372         if (!connection->doneProcessing && !connection->commandList) {
373                 strcpy(connection->errorStr,
374                        "not done processing current command");
375                 connection->error = 1;
376                 return;
377         }
378
379         mpd_clearError(connection);
380
381         FD_ZERO(&fds);
382         FD_SET(connection->sock, &fds);
383         tv.tv_sec = connection->timeout.tv_sec;
384         tv.tv_usec = connection->timeout.tv_usec;
385
386         if (sigpipe) {
387                 perror("");
388                 snprintf(connection->errorStr, MPD_BUFFER_MAX_LENGTH, "got SIGINT");
389                 connection->error = MPD_ERROR_SENDING;
390                 return;
391         }
392         while ((ret = select(connection->sock + 1, NULL, &fds, NULL, &tv) == 1) || (ret == -1 && errno == EINTR)) {
393                 if (sigpipe) {
394                         perror("");
395                         snprintf(connection->errorStr, MPD_BUFFER_MAX_LENGTH, "got SIGINT");
396                         connection->error = MPD_ERROR_SENDING;
397                         return;
398                 } else {
399                         ret = send(connection->sock, commandPtr, commandLen, MSG_DONTWAIT);
400                         if (ret <= 0) {
401                                 if (ret == EAGAIN || ret == EINTR)
402                                         continue;
403                                 snprintf(connection->errorStr, MPD_BUFFER_MAX_LENGTH, "problems giving command \"%s\"", command);
404                                 connection->error = MPD_ERROR_SENDING;
405                                 return;
406                         } else {
407                                 commandPtr += ret;
408                                 commandLen -= ret;
409                         }
410                         if (commandLen <= 0)
411                                 break;
412                 }
413         }
414
415         if (commandLen > 0) {
416                 perror("");
417                 snprintf(connection->errorStr, MPD_BUFFER_MAX_LENGTH,
418                          "timeout sending command \"%s\"", command);
419                 connection->error = MPD_ERROR_TIMEOUT;
420                 return;
421         }
422
423         if (!connection->commandList)
424                 connection->doneProcessing = 0;
425         else if (connection->commandList == COMMAND_LIST_OK) {
426                 connection->listOks++;
427         }
428 }
429
430 void mpd_getNextReturnElement(mpd_Connection * connection)
431 {
432         char *output = NULL;
433         char *rt = NULL;
434         char *name = NULL;
435         char *value = NULL;
436         fd_set fds;
437         struct timeval tv;
438         char *tok = NULL;
439         int readed;
440         char *bufferCheck = NULL;
441         int err;
442
443         if (connection->returnElement)
444                 mpd_freeReturnElement(connection->returnElement);
445         connection->returnElement = NULL;
446
447         if (connection->doneProcessing || (connection->listOks &&
448                                            connection->doneListOk)) {
449                 strcpy(connection->errorStr,
450                        "already done processing current command");
451                 connection->error = 1;
452                 return;
453         }
454
455         bufferCheck = connection->buffer + connection->bufstart;
456         while (connection->bufstart >= connection->buflen ||
457                !(rt = strstr(bufferCheck, "\n"))) {
458                 if (connection->buflen >= MPD_BUFFER_MAX_LENGTH) {
459                         memmove(connection->buffer,
460                                 connection->buffer +
461                                 connection->bufstart,
462                                 connection->buflen - connection->bufstart +
463                                 1);
464                         bufferCheck -= connection->bufstart;
465                         connection->buflen -= connection->bufstart;
466                         connection->bufstart = 0;
467                 }
468                 if (connection->buflen >= MPD_BUFFER_MAX_LENGTH) {
469                         strcpy(connection->errorStr, "buffer overrun");
470                         connection->error = MPD_ERROR_BUFFEROVERRUN;
471                         connection->doneProcessing = 1;
472                         connection->doneListOk = 0;
473                         return;
474                 }
475                 bufferCheck += connection->buflen - connection->bufstart;
476                 tv.tv_sec = connection->timeout.tv_sec;
477                 tv.tv_usec = connection->timeout.tv_usec;
478                 FD_ZERO(&fds);
479                 FD_SET(connection->sock, &fds);
480                 if ((err =
481                      select(connection->sock + 1, &fds, NULL, NULL,
482                             &tv) == 1)) {
483                         readed =
484                             recv(connection->sock,
485                                  connection->buffer + connection->buflen,
486                                  MPD_BUFFER_MAX_LENGTH -
487                                  connection->buflen, MSG_DONTWAIT);
488                         if (readed < 0
489                             && (errno == EAGAIN || errno == EINTR)) {
490                                 continue;
491                         }
492                         if (readed <= 0) {
493                                 strcpy(connection->errorStr,
494                                        "connection" " closed");
495                                 connection->error = MPD_ERROR_CONNCLOSED;
496                                 connection->doneProcessing = 1;
497                                 connection->doneListOk = 0;
498                                 return;
499                         }
500                         connection->buflen += readed;
501                         connection->buffer[connection->buflen] = '\0';
502                 } else if (err < 0 && errno == EINTR)
503                         continue;
504                 else {
505                         strcpy(connection->errorStr, "connection timeout");
506                         connection->error = MPD_ERROR_TIMEOUT;
507                         connection->doneProcessing = 1;
508                         connection->doneListOk = 0;
509                         return;
510                 }
511         }
512
513         *rt = '\0';
514         output = connection->buffer + connection->bufstart;
515         connection->bufstart = rt - connection->buffer + 1;
516
517         if (strcmp(output, "OK") == 0) {
518                 if (connection->listOks > 0) {
519                         strcpy(connection->errorStr,
520                                "expected more list_OK's");
521                         connection->error = 1;
522                 }
523                 connection->listOks = 0;
524                 connection->doneProcessing = 1;
525                 connection->doneListOk = 0;
526                 return;
527         }
528
529         if (strcmp(output, "list_OK") == 0) {
530                 if (!connection->listOks) {
531                         strcpy(connection->errorStr,
532                                "got an unexpected list_OK");
533                         connection->error = 1;
534                 } else {
535                         connection->doneListOk = 1;
536                         connection->listOks--;
537                 }
538                 return;
539         }
540
541         if (strncmp(output, "ACK", strlen("ACK")) == 0) {
542                 char *test;
543                 char *needle;
544                 int val;
545
546                 strcpy(connection->errorStr, output);
547                 connection->error = MPD_ERROR_ACK;
548                 connection->errorCode = MPD_ACK_ERROR_UNK;
549                 connection->errorAt = MPD_ERROR_AT_UNK;
550                 connection->doneProcessing = 1;
551                 connection->doneListOk = 0;
552
553                 needle = strchr(output, '[');
554                 if (!needle)
555                         return;
556                 val = strtol(needle + 1, &test, 10);
557                 if (*test != '@')
558                         return;
559                 connection->errorCode = val;
560                 val = strtol(test + 1, &test, 10);
561                 if (*test != ']')
562                         return;
563                 connection->errorAt = val;
564                 return;
565         }
566
567         name = strtok_r(output, ":", &tok);
568         if (name && (value = strtok_r(NULL, "", &tok)) && value[0] == ' ') {
569                 connection->returnElement =
570                     mpd_newReturnElement(name, &(value[1]));
571         } else {
572                 if (!name || !value) {
573                         snprintf(connection->errorStr,
574                                  MPD_BUFFER_MAX_LENGTH,
575                                  "error parsing: %s", output);
576                 } else {
577                         snprintf(connection->errorStr,
578                                  MPD_BUFFER_MAX_LENGTH,
579                                  "error parsing: %s:%s", name, value);
580                 }
581                 connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0';
582                 connection->error = 1;
583         }
584 }
585
586 void mpd_finishCommand(mpd_Connection * connection)
587 {
588         while (!connection->doneProcessing) {
589                 if (connection->doneListOk)
590                         connection->doneListOk = 0;
591                 mpd_getNextReturnElement(connection);
592         }
593 }
594
595 void mpd_finishListOkCommand(mpd_Connection * connection)
596 {
597         while (!connection->doneProcessing && connection->listOks &&
598                !connection->doneListOk) {
599                 mpd_getNextReturnElement(connection);
600         }
601 }
602
603 int mpd_nextListOkCommand(mpd_Connection * connection)
604 {
605         mpd_finishListOkCommand(connection);
606         if (!connection->doneProcessing)
607                 connection->doneListOk = 0;
608         if (connection->listOks == 0 || connection->doneProcessing)
609                 return -1;
610         return 0;
611 }
612
613 void mpd_sendStatusCommand(mpd_Connection * connection)
614 {
615         mpd_executeCommand(connection, "status\n");
616 }
617
618 mpd_Status *mpd_getStatus(mpd_Connection * connection)
619 {
620         mpd_Status *status;
621
622         /*mpd_executeCommand(connection,"status\n");
623
624            if(connection->error) return NULL; */
625
626         if (connection->doneProcessing || (connection->listOks &&
627                                            connection->doneListOk)) {
628                 return NULL;
629         }
630
631         if (!connection->returnElement)
632                 mpd_getNextReturnElement(connection);
633
634         status = malloc(sizeof(mpd_Status));
635         status->volume = -1;
636         status->repeat = 0;
637         status->random = 0;
638         status->playlist = -1;
639         status->playlistLength = -1;
640         status->state = -1;
641         status->song = 0;
642         status->elapsedTime = 0;
643         status->totalTime = 0;
644         status->bitRate = 0;
645         status->sampleRate = 0;
646         status->bits = 0;
647         status->channels = 0;
648         status->crossfade = -1;
649         status->error = NULL;
650         status->updatingDb = 0;
651
652         if (connection->error) {
653                 free(status);
654                 return NULL;
655         }
656         while (connection->returnElement) {
657                 mpd_ReturnElement *re = connection->returnElement;
658                 if (strcmp(re->name, "volume") == 0) {
659                         status->volume = atoi(re->value);
660                 } else if (strcmp(re->name, "repeat") == 0) {
661                         status->repeat = atoi(re->value);
662                 } else if (strcmp(re->name, "random") == 0) {
663                         status->random = atoi(re->value);
664                 } else if (strcmp(re->name, "playlist") == 0) {
665                         status->playlist = strtol(re->value, NULL, 10);
666                 } else if (strcmp(re->name, "playlistlength") == 0) {
667                         status->playlistLength = atoi(re->value);
668                 } else if (strcmp(re->name, "bitrate") == 0) {
669                         status->bitRate = atoi(re->value);
670                 } else if (strcmp(re->name, "state") == 0) {
671                         if (strcmp(re->value, "play") == 0) {
672                                 status->state = MPD_STATUS_STATE_PLAY;
673                         } else if (strcmp(re->value, "stop") == 0) {
674                                 status->state = MPD_STATUS_STATE_STOP;
675                         } else if (strcmp(re->value, "pause") == 0) {
676                                 status->state = MPD_STATUS_STATE_PAUSE;
677                         } else {
678                                 status->state = MPD_STATUS_STATE_UNKNOWN;
679                         }
680                 } else if (strcmp(re->name, "song") == 0) {
681                         status->song = atoi(re->value);
682                 } else if (strcmp(re->name, "songid") == 0) {
683                         status->songid = atoi(re->value);
684                 } else if (strcmp(re->name, "time") == 0) {
685                         char *tok;
686                         char *copy;
687                         char *temp;
688                         copy = strdup(re->value);
689                         temp = strtok_r(copy, ":", &tok);
690                         if (temp) {
691                                 status->elapsedTime = atoi(temp);
692                                 temp = strtok_r(NULL, "", &tok);
693                                 if (temp)
694                                         status->totalTime = atoi(temp);
695                         }
696                         free(copy);
697                 } else if (strcmp(re->name, "error") == 0) {
698                         status->error = strdup(re->value);
699                 } else if (strcmp(re->name, "xfade") == 0) {
700                         status->crossfade = atoi(re->value);
701                 } else if (strcmp(re->name, "updating_db") == 0) {
702                         status->updatingDb = atoi(re->value);
703                 } else if (strcmp(re->name, "audio") == 0) {
704                         char *tok;
705                         char *copy;
706                         char *temp;
707                         copy = strdup(re->value);
708                         temp = strtok_r(copy, ":", &tok);
709                         if (temp) {
710                                 status->sampleRate = atoi(temp);
711                                 temp = strtok_r(NULL, ":", &tok);
712                                 if (temp) {
713                                         status->bits = atoi(temp);
714                                         temp = strtok_r(NULL, "", &tok);
715                                         if (temp)
716                                                 status->channels =
717                                                     atoi(temp);
718                                 }
719                         }
720                         free(copy);
721                 }
722
723                 mpd_getNextReturnElement(connection);
724                 if (connection->error) {
725                         free(status);
726                         return NULL;
727                 }
728         }
729
730         if (connection->error) {
731                 free(status);
732                 return NULL;
733         } else if (status->state < 0) {
734                 strcpy(connection->errorStr, "state not found");
735                 connection->error = 1;
736                 free(status);
737                 return NULL;
738         }
739
740         return status;
741 }
742
743 void mpd_freeStatus(mpd_Status * status)
744 {
745         if (status->error)
746                 free(status->error);
747         free(status);
748 }
749
750 void mpd_sendStatsCommand(mpd_Connection * connection)
751 {
752         mpd_executeCommand(connection, "stats\n");
753 }
754
755 mpd_Stats *mpd_getStats(mpd_Connection * connection)
756 {
757         mpd_Stats *stats;
758
759         /*mpd_executeCommand(connection,"stats\n");
760
761            if(connection->error) return NULL; */
762
763         if (connection->doneProcessing || (connection->listOks &&
764                                            connection->doneListOk)) {
765                 return NULL;
766         }
767
768         if (!connection->returnElement)
769                 mpd_getNextReturnElement(connection);
770
771         stats = malloc(sizeof(mpd_Stats));
772         stats->numberOfArtists = 0;
773         stats->numberOfAlbums = 0;
774         stats->numberOfSongs = 0;
775         stats->uptime = 0;
776         stats->dbUpdateTime = 0;
777         stats->playTime = 0;
778         stats->dbPlayTime = 0;
779
780         if (connection->error) {
781                 free(stats);
782                 return NULL;
783         }
784         while (connection->returnElement) {
785                 mpd_ReturnElement *re = connection->returnElement;
786                 if (strcmp(re->name, "artists") == 0) {
787                         stats->numberOfArtists = atoi(re->value);
788                 } else if (strcmp(re->name, "albums") == 0) {
789                         stats->numberOfAlbums = atoi(re->value);
790                 } else if (strcmp(re->name, "songs") == 0) {
791                         stats->numberOfSongs = atoi(re->value);
792                 } else if (strcmp(re->name, "uptime") == 0) {
793                         stats->uptime = strtol(re->value, NULL, 10);
794                 } else if (strcmp(re->name, "db_update") == 0) {
795                         stats->dbUpdateTime = strtol(re->value, NULL, 10);
796                 } else if (strcmp(re->name, "playtime") == 0) {
797                         stats->playTime = strtol(re->value, NULL, 10);
798                 } else if (strcmp(re->name, "db_playtime") == 0) {
799                         stats->dbPlayTime = strtol(re->value, NULL, 10);
800                 }
801
802                 mpd_getNextReturnElement(connection);
803                 if (connection->error) {
804                         free(stats);
805                         return NULL;
806                 }
807         }
808
809         if (connection->error) {
810                 free(stats);
811                 return NULL;
812         }
813
814         return stats;
815 }
816
817 void mpd_freeStats(mpd_Stats * stats)
818 {
819         free(stats);
820 }
821
822 void mpd_initSong(mpd_Song * song)
823 {
824         song->file = NULL;
825         song->artist = NULL;
826         song->album = NULL;
827         song->track = NULL;
828         song->title = NULL;
829         song->name = NULL;
830         song->time = MPD_SONG_NO_TIME;
831         song->pos = MPD_SONG_NO_NUM;
832         song->id = MPD_SONG_NO_ID;
833 }
834
835 void mpd_finishSong(mpd_Song * song)
836 {
837         if (song->file)
838                 free(song->file);
839         if (song->artist)
840                 free(song->artist);
841         if (song->album)
842                 free(song->album);
843         if (song->title)
844                 free(song->title);
845         if (song->track)
846                 free(song->track);
847         if (song->name)
848                 free(song->name);
849 }
850
851 mpd_Song *mpd_newSong()
852 {
853         mpd_Song *ret = malloc(sizeof(mpd_Song));
854
855         mpd_initSong(ret);
856
857         return ret;
858 }
859
860 void mpd_freeSong(mpd_Song * song)
861 {
862         mpd_finishSong(song);
863         free(song);
864 }
865
866 mpd_Song *mpd_songDup(mpd_Song * song)
867 {
868         mpd_Song *ret = mpd_newSong();
869
870         if (song->file)
871                 ret->file = strdup(song->file);
872         if (song->artist)
873                 ret->artist = strdup(song->artist);
874         if (song->album)
875                 ret->album = strdup(song->album);
876         if (song->title)
877                 ret->title = strdup(song->title);
878         if (song->track)
879                 ret->track = strdup(song->track);
880         if (song->name)
881                 ret->name = strdup(song->name);
882         ret->time = song->time;
883         ret->pos = song->pos;
884         ret->id = song->id;
885
886         return ret;
887 }
888
889 void mpd_initDirectory(mpd_Directory * directory)
890 {
891         directory->path = NULL;
892 }
893
894 void mpd_finishDirectory(mpd_Directory * directory)
895 {
896         if (directory->path)
897                 free(directory->path);
898 }
899
900 mpd_Directory *mpd_newDirectory()
901 {
902         mpd_Directory *directory = malloc(sizeof(mpd_Directory));;
903
904         mpd_initDirectory(directory);
905
906         return directory;
907 }
908
909 void mpd_freeDirectory(mpd_Directory * directory)
910 {
911         mpd_finishDirectory(directory);
912
913         free(directory);
914 }
915
916 mpd_Directory *mpd_directoryDup(mpd_Directory * directory)
917 {
918         mpd_Directory *ret = mpd_newDirectory();
919
920         if (directory->path)
921                 ret->path = strdup(directory->path);
922
923         return ret;
924 }
925
926 void mpd_initPlaylistFile(mpd_PlaylistFile * playlist)
927 {
928         playlist->path = NULL;
929 }
930
931 void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist)
932 {
933         if (playlist->path)
934                 free(playlist->path);
935 }
936
937 mpd_PlaylistFile *mpd_newPlaylistFile()
938 {
939         mpd_PlaylistFile *playlist = malloc(sizeof(mpd_PlaylistFile));
940
941         mpd_initPlaylistFile(playlist);
942
943         return playlist;
944 }
945
946 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist)
947 {
948         mpd_finishPlaylistFile(playlist);
949         free(playlist);
950 }
951
952 mpd_PlaylistFile *mpd_playlistFileDup(mpd_PlaylistFile * playlist)
953 {
954         mpd_PlaylistFile *ret = mpd_newPlaylistFile();
955
956         if (playlist->path)
957                 ret->path = strdup(playlist->path);
958
959         return ret;
960 }
961
962 void mpd_initInfoEntity(mpd_InfoEntity * entity)
963 {
964         entity->info.directory = NULL;
965 }
966
967 void mpd_finishInfoEntity(mpd_InfoEntity * entity)
968 {
969         if (entity->info.directory) {
970                 if (entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
971                         mpd_freeDirectory(entity->info.directory);
972                 } else if (entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
973                         mpd_freeSong(entity->info.song);
974                 } else if (entity->type ==
975                            MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
976                         mpd_freePlaylistFile(entity->info.playlistFile);
977                 }
978         }
979 }
980
981 mpd_InfoEntity *mpd_newInfoEntity()
982 {
983         mpd_InfoEntity *entity = malloc(sizeof(mpd_InfoEntity));
984
985         mpd_initInfoEntity(entity);
986
987         return entity;
988 }
989
990 void mpd_freeInfoEntity(mpd_InfoEntity * entity)
991 {
992         mpd_finishInfoEntity(entity);
993         free(entity);
994 }
995
996 void mpd_sendInfoCommand(mpd_Connection * connection, char *command)
997 {
998         mpd_executeCommand(connection, command);
999 }
1000
1001 mpd_InfoEntity *mpd_getNextInfoEntity(mpd_Connection * connection)
1002 {
1003         mpd_InfoEntity *entity = NULL;
1004
1005         if (connection->doneProcessing || (connection->listOks &&
1006                                            connection->doneListOk)) {
1007                 return NULL;
1008         }
1009
1010         if (!connection->returnElement)
1011                 mpd_getNextReturnElement(connection);
1012
1013         if (connection->returnElement) {
1014                 if (strcmp(connection->returnElement->name, "file") == 0) {
1015                         entity = mpd_newInfoEntity();
1016                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1017                         entity->info.song = mpd_newSong();
1018                         entity->info.song->file =
1019                             strdup(connection->returnElement->value);
1020                 } else
1021                     if (strcmp
1022                         (connection->returnElement->name,
1023                          "directory") == 0) {
1024                         entity = mpd_newInfoEntity();
1025                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1026                         entity->info.directory = mpd_newDirectory();
1027                         entity->info.directory->path =
1028                             strdup(connection->returnElement->value);
1029                 } else
1030                     if (strcmp(connection->returnElement->name, "playlist")
1031                         == 0) {
1032                         entity = mpd_newInfoEntity();
1033                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1034                         entity->info.playlistFile = mpd_newPlaylistFile();
1035                         entity->info.playlistFile->path =
1036                             strdup(connection->returnElement->value);
1037                 } else {
1038                         connection->error = 1;
1039                         strcpy(connection->errorStr,
1040                                "problem parsing song info");
1041                         return NULL;
1042                 }
1043         } else
1044                 return NULL;
1045
1046         mpd_getNextReturnElement(connection);
1047         while (connection->returnElement) {
1048                 mpd_ReturnElement *re = connection->returnElement;
1049
1050                 if (strcmp(re->name, "file") == 0)
1051                         return entity;
1052                 else if (strcmp(re->name, "directory") == 0)
1053                         return entity;
1054                 else if (strcmp(re->name, "playlist") == 0)
1055                         return entity;
1056
1057                 if (entity->type == MPD_INFO_ENTITY_TYPE_SONG
1058                     && strlen(re->value)) {
1059                         if (!entity->info.song->artist
1060                             && strcmp(re->name, "Artist") == 0) {
1061                                 entity->info.song->artist =
1062                                     strdup(re->value);
1063                         } else if (!entity->info.song->album
1064                                    && strcmp(re->name, "Album") == 0) {
1065                                 entity->info.song->album =
1066                                     strdup(re->value);
1067                         } else if (!entity->info.song->title
1068                                    && strcmp(re->name, "Title") == 0) {
1069                                 entity->info.song->title =
1070                                     strdup(re->value);
1071                         } else if (!entity->info.song->track
1072                                    && strcmp(re->name, "Track") == 0) {
1073                                 entity->info.song->track =
1074                                     strdup(re->value);
1075                         } else if (!entity->info.song->name
1076                                    && strcmp(re->name, "Name") == 0) {
1077                                 entity->info.song->name =
1078                                     strdup(re->value);
1079                         } else if (entity->info.song->time ==
1080                                    MPD_SONG_NO_TIME
1081                                    && strcmp(re->name, "Time") == 0) {
1082                                 entity->info.song->time = atoi(re->value);
1083                         } else if (entity->info.song->pos ==
1084                                    MPD_SONG_NO_NUM
1085                                    && strcmp(re->name, "Pos") == 0) {
1086                                 entity->info.song->pos = atoi(re->value);
1087                         } else if (entity->info.song->id == MPD_SONG_NO_ID
1088                                    && strcmp(re->name, "Id") == 0) {
1089                                 entity->info.song->id = atoi(re->value);
1090                         }
1091                 } else if (entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1092                 } else if (entity->type ==
1093                            MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1094                 }
1095
1096                 mpd_getNextReturnElement(connection);
1097         }
1098
1099         return entity;
1100 }
1101
1102 char *mpd_getNextReturnElementNamed(mpd_Connection * connection,
1103                                     const char *name)
1104 {
1105         if (connection->doneProcessing || (connection->listOks &&
1106                                            connection->doneListOk)) {
1107                 return NULL;
1108         }
1109
1110         mpd_getNextReturnElement(connection);
1111         while (connection->returnElement) {
1112                 mpd_ReturnElement *re = connection->returnElement;
1113
1114                 if (strcmp(re->name, name) == 0)
1115                         return strdup(re->value);
1116                 mpd_getNextReturnElement(connection);
1117         }
1118
1119         return NULL;
1120 }
1121
1122 char *mpd_getNextArtist(mpd_Connection * connection)
1123 {
1124         return mpd_getNextReturnElementNamed(connection, "Artist");
1125 }
1126
1127 char *mpd_getNextAlbum(mpd_Connection * connection)
1128 {
1129         return mpd_getNextReturnElementNamed(connection, "Album");
1130 }
1131
1132 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos)
1133 {
1134         char *string = malloc(strlen("playlistinfo") + 25);
1135         sprintf(string, "playlistinfo \"%i\"\n", songPos);
1136         mpd_sendInfoCommand(connection, string);
1137         free(string);
1138 }
1139
1140 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id)
1141 {
1142         char *string = malloc(strlen("playlistid") + 25);
1143         sprintf(string, "playlistid \"%i\"\n", id);
1144         mpd_sendInfoCommand(connection, string);
1145         free(string);
1146 }
1147
1148 void
1149 mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist)
1150 {
1151         char *string = malloc(strlen("plchanges") + 25);
1152         sprintf(string, "plchanges \"%lld\"\n", playlist);
1153         mpd_sendInfoCommand(connection, string);
1154         free(string);
1155 }
1156
1157 void mpd_sendListallCommand(mpd_Connection * connection, const char *dir)
1158 {
1159         char *sDir = mpd_sanitizeArg(dir);
1160         char *string = malloc(strlen("listall") + strlen(sDir) + 5);
1161         sprintf(string, "listall \"%s\"\n", sDir);
1162         mpd_sendInfoCommand(connection, string);
1163         free(string);
1164         free(sDir);
1165 }
1166
1167 void
1168 mpd_sendListallInfoCommand(mpd_Connection * connection, const char *dir)
1169 {
1170         char *sDir = mpd_sanitizeArg(dir);
1171         char *string = malloc(strlen("listallinfo") + strlen(sDir) + 5);
1172         sprintf(string, "listallinfo \"%s\"\n", sDir);
1173         mpd_sendInfoCommand(connection, string);
1174         free(string);
1175         free(sDir);
1176 }
1177
1178 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char *dir)
1179 {
1180         char *sDir = mpd_sanitizeArg(dir);
1181         char *string = malloc(strlen("lsinfo") + strlen(sDir) + 5);
1182         sprintf(string, "lsinfo \"%s\"\n", sDir);
1183         mpd_sendInfoCommand(connection, string);
1184         free(string);
1185         free(sDir);
1186 }
1187
1188 void mpd_sendCurrentSongCommand(mpd_Connection * connection)
1189 {
1190         mpd_executeCommand(connection, "currentsong\n");
1191 }
1192
1193 void
1194 mpd_sendSearchCommand(mpd_Connection * connection, int table,
1195                       const char *str)
1196 {
1197         char st[10];
1198         char *string;
1199         char *sanitStr = mpd_sanitizeArg(str);
1200         if (table == MPD_TABLE_ARTIST)
1201                 strcpy(st, "artist");
1202         else if (table == MPD_TABLE_ALBUM)
1203                 strcpy(st, "album");
1204         else if (table == MPD_TABLE_TITLE)
1205                 strcpy(st, "title");
1206         else if (table == MPD_TABLE_FILENAME)
1207                 strcpy(st, "filename");
1208         else {
1209                 connection->error = 1;
1210                 strcpy(connection->errorStr, "unknown table for search");
1211                 return;
1212         }
1213         string =
1214             malloc(strlen("search") + strlen(sanitStr) + strlen(st) + 6);
1215         sprintf(string, "search %s \"%s\"\n", st, sanitStr);
1216         mpd_sendInfoCommand(connection, string);
1217         free(string);
1218         free(sanitStr);
1219 }
1220
1221 void
1222 mpd_sendFindCommand(mpd_Connection * connection, int table,
1223                     const char *str)
1224 {
1225         char st[10];
1226         char *string;
1227         char *sanitStr = mpd_sanitizeArg(str);
1228         if (table == MPD_TABLE_ARTIST)
1229                 strcpy(st, "artist");
1230         else if (table == MPD_TABLE_ALBUM)
1231                 strcpy(st, "album");
1232         else if (table == MPD_TABLE_TITLE)
1233                 strcpy(st, "title");
1234         else {
1235                 connection->error = 1;
1236                 strcpy(connection->errorStr, "unknown table for find");
1237                 return;
1238         }
1239         string =
1240             malloc(strlen("find") + strlen(sanitStr) + strlen(st) + 6);
1241         sprintf(string, "find %s \"%s\"\n", st, sanitStr);
1242         mpd_sendInfoCommand(connection, string);
1243         free(string);
1244         free(sanitStr);
1245 }
1246
1247 void
1248 mpd_sendListCommand(mpd_Connection * connection, int table,
1249                     const char *arg1)
1250 {
1251         char st[10];
1252         char *string;
1253         if (table == MPD_TABLE_ARTIST)
1254                 strcpy(st, "artist");
1255         else if (table == MPD_TABLE_ALBUM)
1256                 strcpy(st, "album");
1257         else {
1258                 connection->error = 1;
1259                 strcpy(connection->errorStr, "unknown table for list");
1260                 return;
1261         }
1262         if (arg1) {
1263                 char *sanitArg1 = mpd_sanitizeArg(arg1);
1264                 string =
1265                     malloc(strlen("list") + strlen(sanitArg1) +
1266                            strlen(st) + 6);
1267                 sprintf(string, "list %s \"%s\"\n", st, sanitArg1);
1268                 free(sanitArg1);
1269         } else {
1270                 string = malloc(strlen("list") + strlen(st) + 3);
1271                 sprintf(string, "list %s\n", st);
1272         }
1273         mpd_sendInfoCommand(connection, string);
1274         free(string);
1275 }
1276
1277 void mpd_sendAddCommand(mpd_Connection * connection, const char *file)
1278 {
1279         char *sFile = mpd_sanitizeArg(file);
1280         char *string = malloc(strlen("add") + strlen(sFile) + 5);
1281         sprintf(string, "add \"%s\"\n", sFile);
1282         mpd_executeCommand(connection, string);
1283         free(string);
1284         free(sFile);
1285 }
1286
1287 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos)
1288 {
1289         char *string = malloc(strlen("delete") + 25);
1290         sprintf(string, "delete \"%i\"\n", songPos);
1291         mpd_sendInfoCommand(connection, string);
1292         free(string);
1293 }
1294
1295 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id)
1296 {
1297         char *string = malloc(strlen("deleteid") + 25);
1298         sprintf(string, "deleteid \"%i\"\n", id);
1299         mpd_sendInfoCommand(connection, string);
1300         free(string);
1301 }
1302
1303 void mpd_sendSaveCommand(mpd_Connection * connection, const char *name)
1304 {
1305         char *sName = mpd_sanitizeArg(name);
1306         char *string = malloc(strlen("save") + strlen(sName) + 5);
1307         sprintf(string, "save \"%s\"\n", sName);
1308         mpd_executeCommand(connection, string);
1309         free(string);
1310         free(sName);
1311 }
1312
1313 void mpd_sendLoadCommand(mpd_Connection * connection, const char *name)
1314 {
1315         char *sName = mpd_sanitizeArg(name);
1316         char *string = malloc(strlen("load") + strlen(sName) + 5);
1317         sprintf(string, "load \"%s\"\n", sName);
1318         mpd_executeCommand(connection, string);
1319         free(string);
1320         free(sName);
1321 }
1322
1323 void mpd_sendRmCommand(mpd_Connection * connection, const char *name)
1324 {
1325         char *sName = mpd_sanitizeArg(name);
1326         char *string = malloc(strlen("rm") + strlen(sName) + 5);
1327         sprintf(string, "rm \"%s\"\n", sName);
1328         mpd_executeCommand(connection, string);
1329         free(string);
1330         free(sName);
1331 }
1332
1333 void mpd_sendShuffleCommand(mpd_Connection * connection)
1334 {
1335         mpd_executeCommand(connection, "shuffle\n");
1336 }
1337
1338 void mpd_sendClearCommand(mpd_Connection * connection)
1339 {
1340         mpd_executeCommand(connection, "clear\n");
1341 }
1342
1343 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos)
1344 {
1345         char *string = malloc(strlen("play") + 25);
1346         sprintf(string, "play \"%i\"\n", songPos);
1347         mpd_sendInfoCommand(connection, string);
1348         free(string);
1349 }
1350
1351 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id)
1352 {
1353         char *string = malloc(strlen("playid") + 25);
1354         sprintf(string, "playid \"%i\"\n", id);
1355         mpd_sendInfoCommand(connection, string);
1356         free(string);
1357 }
1358
1359 void mpd_sendStopCommand(mpd_Connection * connection)
1360 {
1361         mpd_executeCommand(connection, "stop\n");
1362 }
1363
1364 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode)
1365 {
1366         char *string = malloc(strlen("pause") + 25);
1367         sprintf(string, "pause \"%i\"\n", pauseMode);
1368         mpd_executeCommand(connection, string);
1369         free(string);
1370 }
1371
1372 void mpd_sendNextCommand(mpd_Connection * connection)
1373 {
1374         mpd_executeCommand(connection, "next\n");
1375 }
1376
1377 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to)
1378 {
1379         char *string = malloc(strlen("move") + 25);
1380         sprintf(string, "move \"%i\" \"%i\"\n", from, to);
1381         mpd_sendInfoCommand(connection, string);
1382         free(string);
1383 }
1384
1385 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to)
1386 {
1387         char *string = malloc(strlen("moveid") + 25);
1388         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1389         mpd_sendInfoCommand(connection, string);
1390         free(string);
1391 }
1392
1393 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2)
1394 {
1395         char *string = malloc(strlen("swap") + 25);
1396         sprintf(string, "swap \"%i\" \"%i\"\n", song1, song2);
1397         mpd_sendInfoCommand(connection, string);
1398         free(string);
1399 }
1400
1401 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2)
1402 {
1403         char *string = malloc(strlen("swapid") + 25);
1404         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1405         mpd_sendInfoCommand(connection, string);
1406         free(string);
1407 }
1408
1409 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time)
1410 {
1411         char *string = malloc(strlen("seek") + 25);
1412         sprintf(string, "seek \"%i\" \"%i\"\n", song, time);
1413         mpd_sendInfoCommand(connection, string);
1414         free(string);
1415 }
1416
1417 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time)
1418 {
1419         char *string = malloc(strlen("seekid") + 25);
1420         sprintf(string, "seekid \"%i\" \"%i\"\n", id, time);
1421         mpd_sendInfoCommand(connection, string);
1422         free(string);
1423 }
1424
1425 void mpd_sendUpdateCommand(mpd_Connection * connection, char *path)
1426 {
1427         char *sPath = mpd_sanitizeArg(path);
1428         char *string = malloc(strlen("update") + strlen(sPath) + 5);
1429         sprintf(string, "update \"%s\"\n", sPath);
1430         mpd_sendInfoCommand(connection, string);
1431         free(string);
1432         free(sPath);
1433 }
1434
1435 int mpd_getUpdateId(mpd_Connection * connection)
1436 {
1437         char *jobid;
1438         int ret = 0;
1439
1440         jobid = mpd_getNextReturnElementNamed(connection, "updating_db");
1441         if (jobid) {
1442                 ret = atoi(jobid);
1443                 free(jobid);
1444         }
1445
1446         return ret;
1447 }
1448
1449 void mpd_sendPrevCommand(mpd_Connection * connection)
1450 {
1451         mpd_executeCommand(connection, "previous\n");
1452 }
1453
1454 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode)
1455 {
1456         char *string = malloc(strlen("repeat") + 25);
1457         sprintf(string, "repeat \"%i\"\n", repeatMode);
1458         mpd_executeCommand(connection, string);
1459         free(string);
1460 }
1461
1462 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode)
1463 {
1464         char *string = malloc(strlen("random") + 25);
1465         sprintf(string, "random \"%i\"\n", randomMode);
1466         mpd_executeCommand(connection, string);
1467         free(string);
1468 }
1469
1470 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange)
1471 {
1472         char *string = malloc(strlen("setvol") + 25);
1473         sprintf(string, "setvol \"%i\"\n", volumeChange);
1474         mpd_executeCommand(connection, string);
1475         free(string);
1476 }
1477
1478 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange)
1479 {
1480         char *string = malloc(strlen("volume") + 25);
1481         sprintf(string, "volume \"%i\"\n", volumeChange);
1482         mpd_executeCommand(connection, string);
1483         free(string);
1484 }
1485
1486 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds)
1487 {
1488         char *string = malloc(strlen("crossfade") + 25);
1489         sprintf(string, "crossfade \"%i\"\n", seconds);
1490         mpd_executeCommand(connection, string);
1491         free(string);
1492 }
1493
1494 void mpd_sendPasswordCommand(mpd_Connection * connection, const char *pass)
1495 {
1496         char *sPass = mpd_sanitizeArg(pass);
1497         char *string = malloc(strlen("password") + strlen(sPass) + 5);
1498         sprintf(string, "password \"%s\"\n", sPass);
1499         mpd_executeCommand(connection, string);
1500         free(string);
1501         free(sPass);
1502 }
1503
1504 void mpd_sendCommandListBegin(mpd_Connection * connection)
1505 {
1506         if (connection->commandList) {
1507                 strcpy(connection->errorStr,
1508                        "already in command list mode");
1509                 connection->error = 1;
1510                 return;
1511         }
1512         connection->commandList = COMMAND_LIST;
1513         mpd_executeCommand(connection, "command_list_begin\n");
1514 }
1515
1516 void mpd_sendCommandListOkBegin(mpd_Connection * connection)
1517 {
1518         if (connection->commandList) {
1519                 strcpy(connection->errorStr,
1520                        "already in command list mode");
1521                 connection->error = 1;
1522                 return;
1523         }
1524         connection->commandList = COMMAND_LIST_OK;
1525         mpd_executeCommand(connection, "command_list_ok_begin\n");
1526         connection->listOks = 0;
1527 }
1528
1529 void mpd_sendCommandListEnd(mpd_Connection * connection)
1530 {
1531         if (!connection->commandList) {
1532                 strcpy(connection->errorStr, "not in command list mode");
1533                 connection->error = 1;
1534                 return;
1535         }
1536         connection->commandList = 0;
1537         mpd_executeCommand(connection, "command_list_end\n");
1538 }
1539
1540 void mpd_signalHandler(int signal)
1541 {
1542         if (signal == SIGPIPE) {
1543                 fprintf(stderr, "Conky: recieved SIGPIPE from MPD connection\n");
1544                 sigpipe = 1;
1545         }
1546 }
1547