Code

2ab4e28cfb6ef9a064533097a31cc24f9f95b518
[ncmpc.git] / 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.
32 */
34 #include "libmpdclient.h"
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>
49 #ifndef MPD_NO_IPV6
50 #ifdef AF_INET6
51 #define MPD_HAVE_IPV6
52 #endif
53 #endif
55 #ifndef MSG_DONTWAIT
56 #define MSG_DONTWAIT 0
57 #endif
59 #define COMMAND_LIST    1
60 #define COMMAND_LIST_OK 2
62 #ifdef MPD_HAVE_IPV6        
63 int mpd_ipv6Supported() {
64         int s;          
65         s = socket(AF_INET6,SOCK_STREAM,0);
66         if(s == -1) return 0;
67         close(s);       
68         return 1;                       
69 }                       
70 #endif  
73 char * mpd_sanitizeArg(const char * arg) {
74         size_t i;
75         int count=0;
76         char * ret;
78         for(i=0;i<strlen(arg);i++) {
79                 if(arg[i]=='"' || arg[i]=='\\') count++;
80         }
82         ret = malloc(strlen(arg)+count+1);
84         count = 0;
85         for(i=0;i<strlen(arg)+1;i++) {
86                 if(arg[i]=='"' || arg[i]=='\\') {
87                         ret[i+count] = '\\';
88                         count++;
89                 }
90                 ret[i+count] = arg[i];
91         }
93         return ret;
94 }
96 mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
97 {
98         mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
100         ret->name = strdup(name);
101         ret->value = strdup(value);
103         return ret;
106 void mpd_freeReturnElement(mpd_ReturnElement * re) {
107         free(re->name);
108         free(re->value);
109         free(re);
112 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
113                 connection->timeout.tv_sec = (int)timeout;
114                 connection->timeout.tv_usec = (int)(timeout*1e6 -
115                                 connection->timeout.tv_sec*1000000+0.5);
118 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
119         int err;
120         struct hostent * he;
121         struct sockaddr * dest;
122 #ifdef HAVE_SOCKLEN_T
123         socklen_t destlen;
124 #else
125         int destlen;
126 #endif
127         struct sockaddr_in sin;
128         char * rt;
129         char * output;
130         mpd_Connection * connection = malloc(sizeof(mpd_Connection));
131         struct timeval tv;
132         fd_set fds;
133 #ifdef MPD_HAVE_IPV6
134         struct sockaddr_in6 sin6;
135 #endif
136         strcpy(connection->buffer,"");
137         connection->buflen = 0;
138         connection->bufstart = 0;
139         strcpy(connection->errorStr,"");
140         connection->error = 0;
141         connection->doneProcessing = 0;
142         connection->commandList = 0;
143         connection->listOks = 0;
144         connection->doneListOk = 0;
145         connection->returnElement = NULL;
147         if(!(he=gethostbyname(host))) {
148                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
149                                 "host \"%s\" not found",host);
150                 connection->error = MPD_ERROR_UNKHOST;
151                 return connection;
152         }
154         memset(&sin,0,sizeof(struct sockaddr_in));
155         /*dest.sin_family = he->h_addrtype;*/
156         sin.sin_family = AF_INET;
157         sin.sin_port = htons(port);
158 #ifdef MPD_HAVE_IPV6
159         memset(&sin6,0,sizeof(struct sockaddr_in6));
160         sin6.sin6_family = AF_INET6;
161         sin6.sin6_port = htons(port);
162 #endif
163         switch(he->h_addrtype) {
164         case AF_INET:
165                 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
166                                 he->h_length);
167                 dest = (struct sockaddr *)&sin;
168                 destlen = sizeof(struct sockaddr_in);
169                 break;
170 #ifdef MPD_HAVE_IPV6
171         case AF_INET6:
172                 if(!mpd_ipv6Supported()) {
173                         strcpy(connection->errorStr,"no IPv6 suuport but a "
174                                         "IPv6 address found\n");
175                         connection->error = MPD_ERROR_SYSTEM;
176                         return connection;
177                 }
178                 memcpy((char *)&sin6.sin6_addr.s6_addr,(char *)he->h_addr,
179                                 he->h_length);
180                 dest = (struct sockaddr *)&sin6;
181                 destlen = sizeof(struct sockaddr_in6);
182                 break;
183 #endif
184         default:
185                 strcpy(connection->errorStr,"address type is not IPv4 or "
186                                 "IPv6\n");
187                 connection->error = MPD_ERROR_SYSTEM;
188                 return connection;
189                 break;
190         }
191         
192         if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
193                 strcpy(connection->errorStr,"problems creating socket");
194                 connection->error = MPD_ERROR_SYSTEM;
195                 return connection;
196         }
198         mpd_setConnectionTimeout(connection,timeout);
200         /* connect stuff */
201         {
202                 int flags = fcntl(connection->sock, F_GETFL, 0);
203                 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
205                 if(connect(connection->sock,dest,destlen)<0 && 
206                                 errno!=EINPROGRESS) 
207                 {
208                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
209                                         "problems connecting to \"%s\" on port"
210                                         " %i",host,port);
211                         connection->error = MPD_ERROR_CONNPORT;
212                         return connection;
213                 }
214         }
216         while(!(rt = strstr(connection->buffer,"\n"))) {
217                 tv.tv_sec = connection->timeout.tv_sec;
218                 tv.tv_usec = connection->timeout.tv_usec;
219                 FD_ZERO(&fds);
220                 FD_SET(connection->sock,&fds);
221                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
222                         int readed;
223                         readed = recv(connection->sock,
224                                 &(connection->buffer[connection->buflen]),
225                                 MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
226                         if(readed<=0) {
227                                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
228                                         "problems getting a response from"
229                                         " \"%s\" on port %i : %s",host,
230                                         port, strerror(errno));
231                                 connection->error = MPD_ERROR_NORESPONSE;
232                                 return connection;
233                         }
234                         connection->buflen+=readed;
235                         connection->buffer[connection->buflen] = '\0';
236                         tv.tv_sec = connection->timeout.tv_sec;
237                         tv.tv_usec = connection->timeout.tv_usec;
238                 }
239                 else if(err<0) {
240                         switch(errno) {
241                         case EINTR:
242                                 continue;
243                         default:
244                                 snprintf(connection->errorStr,
245                                         MPD_BUFFER_MAX_LENGTH,
246                                         "problems connecting to \"%s\" on port"
247                                         " %i",host,port);
248                                 connection->error = MPD_ERROR_CONNPORT;
249                                 return connection;
250                         }
251                 }
252                 else {
253                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
254                                 "timeout in attempting to get a response from"
255                                  " \"%s\" on port %i",host,port);
256                         connection->error = MPD_ERROR_NORESPONSE;
257                         return connection;
258                 }
259         }
261         *rt = '\0';
262         output = strdup(connection->buffer);
263         strcpy(connection->buffer,rt+1);
264         connection->buflen = strlen(connection->buffer);
266         if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
267                 free(output);
268                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
269                                 "mpd not running on port %i on host \"%s\"",
270                                 port,host);
271                 connection->error = MPD_ERROR_NOTMPD;
272                 return connection;
273         }
275         {
276                 char * test;
277                 char * version[3];
278                 char * tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
279                 char * search = ".";
280                 int i;
282                 for(i=0;i<3;i++) {
283                         char * tok;
284                         if(i==3) search = " ";
285                         version[i] = strtok_r(tmp,search,&tok);
286                         if(!version[i]) {
287                                 free(output);
288                                 snprintf(connection->errorStr,
289                                         MPD_BUFFER_MAX_LENGTH,
290                                         "error parsing version number at "
291                                         "\"%s\"",
292                                         &output[strlen(MPD_WELCOME_MESSAGE)]);
293                                 connection->error = MPD_ERROR_NOTMPD;
294                                 return connection;
295                         }
296                         connection->version[i] = strtol(version[i],&test,10);
297                         if(version[i]==test || *test!='\0') {
298                                 free(output);
299                                 snprintf(connection->errorStr,
300                                         MPD_BUFFER_MAX_LENGTH,
301                                         "error parsing version number at "
302                                         "\"%s\"",
303                                         &output[strlen(MPD_WELCOME_MESSAGE)]);
304                                 connection->error = MPD_ERROR_NOTMPD;
305                                 return connection;
306                         }
307                         tmp = NULL;
308                 }
309         }
311         free(output);
313         connection->doneProcessing = 1;
315         return connection;
318 void mpd_clearError(mpd_Connection * connection) {
319         connection->error = 0;
320         connection->errorStr[0] = '\0';
323 void mpd_closeConnection(mpd_Connection * connection) {
324         close(connection->sock);
325         if(connection->returnElement) free(connection->returnElement);
326         free(connection);
329 void mpd_executeCommand(mpd_Connection * connection, char * command) {
330         int ret;
331         struct timeval tv;
332         fd_set fds;
333         char * commandPtr = command;
334         int commandLen = strlen(command);
336         if(!connection->doneProcessing && !connection->commandList) {
337                 strcpy(connection->errorStr,"not done processing current command");
338                 connection->error = 1;
339                 return;
340         }
342         mpd_clearError(connection);
344         FD_ZERO(&fds);
345         FD_SET(connection->sock,&fds);
346         tv.tv_sec = connection->timeout.tv_sec;
347         tv.tv_usec = connection->timeout.tv_usec;
349         while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) || 
350                         (ret==-1 && errno==EINTR)) {
351                 ret = send(connection->sock,commandPtr,commandLen,
352 #ifdef WIN32
353                            ioctlsocket(connection->sock, commandLen, commandPtr));
354 #endif
355 #ifndef WIN32
356                                 MSG_DONTWAIT);
357 #endif
358                 if(ret<=0)
359                 {
360                         if(ret==EAGAIN || ret==EINTR) continue;
361                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
362                                 "problems giving command \"%s\"",command);
363                         connection->error = MPD_ERROR_SENDING;
364                         return;
365                 }
366                 else {
367                         commandPtr+=ret;
368                         commandLen-=ret;
369                 }
371                 if(commandLen<=0) break;
372         }
374         if(commandLen>0) {
375                 perror("");
376                 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
377                         "timeout sending command \"%s\"",command);
378                 connection->error = MPD_ERROR_TIMEOUT;
379                 return;
380         }
382         if(!connection->commandList) connection->doneProcessing = 0;
383         else if(connection->commandList == COMMAND_LIST_OK) {
384                 connection->listOks++;
385         }
388 void mpd_getNextReturnElement(mpd_Connection * connection) {
389         char * output = NULL;
390         char * rt = NULL;
391         char * name = NULL;
392         char * value = NULL;
393         fd_set fds;
394         struct timeval tv;
395         char * tok = NULL;
396         int readed;
397         char * bufferCheck = NULL;
398         int err;
400         if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
401         connection->returnElement = NULL;
403         if(connection->doneProcessing || (connection->listOks &&
404                         connection->doneListOk)) 
405         {
406                 strcpy(connection->errorStr,"already done processing current command");
407                 connection->error = 1;
408                 return;
409         }
411         bufferCheck = connection->buffer+connection->bufstart;
412         while(connection->bufstart>=connection->buflen || 
413                         !(rt = strstr(bufferCheck,"\n"))) {
414                 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
415                         memmove(connection->buffer,
416                                         connection->buffer+
417                                         connection->bufstart,
418                                         connection->buflen-
419                                         connection->bufstart+1);
420                         bufferCheck-=connection->bufstart;
421                         connection->buflen-=connection->bufstart;
422                         connection->bufstart = 0;
423                 }
424                 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
425                         strcpy(connection->errorStr,"buffer overrun");
426                         connection->error = MPD_ERROR_BUFFEROVERRUN;
427                         connection->doneProcessing = 1;
428                         connection->doneListOk = 0;
429                         return;
430                 }
431                 bufferCheck+=connection->buflen-connection->bufstart;
432                 tv.tv_sec = connection->timeout.tv_sec;
433                 tv.tv_usec = connection->timeout.tv_usec;
434                 FD_ZERO(&fds);
435                 FD_SET(connection->sock,&fds);
436                 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
437                         readed = recv(connection->sock,
438                                 connection->buffer+connection->buflen,
439                                 MPD_BUFFER_MAX_LENGTH-connection->buflen,
440 #ifdef WIN32
441                            ioctlsocket(connection->sock, commandLen, commandPtr));
442 #endif
443 #ifndef WIN32
444                                 MSG_DONTWAIT);
445 #endif
446                         if(readed<0 && (errno==EAGAIN || errno==EINTR)) {
447                                 continue;
448                         }
449                         if(readed<=0) {
450                                 strcpy(connection->errorStr,"connection"
451                                         " closed");
452                                 connection->error = MPD_ERROR_CONNCLOSED;
453                                 connection->doneProcessing = 1;
454                                 connection->doneListOk = 0;
455                                 return;
456                         }
457                         connection->buflen+=readed;
458                         connection->buffer[connection->buflen] = '\0';
459                 }
460                 else if(err<0 && errno==EINTR) continue;
461                 else {
462                         strcpy(connection->errorStr,"connection timeout");
463                         connection->error = MPD_ERROR_TIMEOUT;
464                         connection->doneProcessing = 1;
465                         connection->doneListOk = 0;
466                         return;
467                 }
468         }
470         *rt = '\0';
471         output = connection->buffer+connection->bufstart;
472         connection->bufstart = rt - connection->buffer + 1;
474         if(strcmp(output,"OK")==0) {
475                 if(connection->listOks > 0) {
476                         strcpy(connection->errorStr, "expected more list_OK's");
477                         connection->error = 1;
478                 }
479                 connection->listOks = 0;
480                 connection->doneProcessing = 1;
481                 connection->doneListOk = 0;
482                 return;
483         }
485         if(strcmp(output, "list_OK") == 0) {
486                 if(!connection->listOks) {
487                         strcpy(connection->errorStr, 
488                                         "got an unexpected list_OK");
489                         connection->error = 1;
490                 }
491                 else {
492                         connection->doneListOk = 1;
493                         connection->listOks--;
494                 }
495                 return;
496         }
498         if(strncmp(output,"ACK",strlen("ACK"))==0) {
499                 char * test;
500                 char * needle;
501                 int val;
502         
503                 strcpy(connection->errorStr, output);
504                 connection->error = MPD_ERROR_ACK;
505                 connection->errorCode = MPD_ACK_ERROR_UNK;
506                 connection->errorAt = MPD_ERROR_AT_UNK;
507                 connection->doneProcessing = 1;
508                 connection->doneListOk = 0;
510                 needle = strchr(output, '[');
511                 if(!needle) return;
512                 val = strtol(needle+1, &test, 10);
513                 if(*test != '@') return;
514                 connection->errorCode = val;
515                 val = strtol(test+1, &test, 10);
516                 if(*test != ']') return;
517                 connection->errorAt = val;
518                 return;
519         }
521         name = strtok_r(output,":",&tok);
522         if(name && (value = strtok_r(NULL,"",&tok)) && value[0]==' ') {
523                 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
524         }
525         else {
526                 if(!name || !value) {
527                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
528                                         "error parsing: %s",output);
529                 }
530                 else {
531                         snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
532                                         "error parsing: %s:%s",name,value);
533                 }
534                 connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0';
535                 connection->error = 1;
536         }
539 void mpd_finishCommand(mpd_Connection * connection) {
540         while(!connection->doneProcessing) {
541                 if(connection->doneListOk) connection->doneListOk = 0;
542                 mpd_getNextReturnElement(connection);
543         }
546 void mpd_finishListOkCommand(mpd_Connection * connection) {
547         while(!connection->doneProcessing && connection->listOks && 
548                         !connection->doneListOk ) 
549         {
550                 mpd_getNextReturnElement(connection);
551         }
554 int mpd_nextListOkCommand(mpd_Connection * connection) {
555         mpd_finishListOkCommand(connection);
556         if(!connection->doneProcessing) connection->doneListOk = 0;
557         if(connection->listOks == 0 || connection->doneProcessing) return -1;
558         return 0;
561 void mpd_sendStatusCommand(mpd_Connection * connection) {
562         mpd_executeCommand(connection,"status\n");
565 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
566         mpd_Status * status;
568         /*mpd_executeCommand(connection,"status\n");
569                 
570         if(connection->error) return NULL;*/
572         if(connection->doneProcessing || (connection->listOks && 
573                         connection->doneListOk))
574         {
575                 return NULL;
576         }
578         if(!connection->returnElement) mpd_getNextReturnElement(connection);
580         status = malloc(sizeof(mpd_Status));
581         status->volume = -1;
582         status->repeat = 0;
583         status->random = 0;
584         status->playlist = -1;
585         status->playlistLength = -1;
586         status->state = -1;
587         status->song = 0;
588         status->elapsedTime = 0;
589         status->totalTime = 0;
590         status->bitRate = 0;
591         status->sampleRate = 0;
592         status->bits = 0;
593         status->channels = 0;
594         status->crossfade = -1;
595         status->error = NULL;
596         status->updatingDb = 0;
598         if(connection->error) {
599                 free(status);
600                 return NULL;
601         }
602         while(connection->returnElement) {
603                 mpd_ReturnElement * re = connection->returnElement;
604                 if(strcmp(re->name,"volume")==0) {
605                         status->volume = atoi(re->value);
606                 }
607                 else if(strcmp(re->name,"repeat")==0) {
608                         status->repeat = atoi(re->value);
609                 }
610                 else if(strcmp(re->name,"random")==0) {
611                         status->random = atoi(re->value);
612                 }
613                 else if(strcmp(re->name,"playlist")==0) {
614                         status->playlist = strtol(re->value,NULL,10);
615                 }
616                 else if(strcmp(re->name,"playlistlength")==0) {
617                         status->playlistLength = atoi(re->value);
618                 }
619                 else if(strcmp(re->name,"bitrate")==0) {
620                         status->bitRate = atoi(re->value);
621                 }
622                 else if(strcmp(re->name,"state")==0) {
623                         if(strcmp(re->value,"play")==0) {
624                                 status->state = MPD_STATUS_STATE_PLAY;
625                         }
626                         else if(strcmp(re->value,"stop")==0) {
627                                 status->state = MPD_STATUS_STATE_STOP;
628                         }
629                         else if(strcmp(re->value,"pause")==0) {
630                                 status->state = MPD_STATUS_STATE_PAUSE;
631                         }
632                         else {
633                                 status->state = MPD_STATUS_STATE_UNKNOWN;
634                         }
635                 }
636                 else if(strcmp(re->name,"song")==0) {
637                         status->song = atoi(re->value);
638                 }
639                 else if(strcmp(re->name,"songid")==0) {
640                         status->songid = atoi(re->value);
641                 }
642                 else if(strcmp(re->name,"time")==0) {
643                         char * tok;
644                         char * copy;
645                         char * temp;
646                         copy = strdup(re->value);
647                         temp = strtok_r(copy,":",&tok);
648                         if(temp) {
649                                 status->elapsedTime = atoi(temp);
650                                 temp = strtok_r(NULL,"",&tok);
651                                 if(temp) status->totalTime = atoi(temp);
652                         }
653                         free(copy);
654                 }
655                 else if(strcmp(re->name,"error")==0) {
656                         status->error = strdup(re->value);
657                 }
658                 else if(strcmp(re->name,"xfade")==0) {
659                         status->crossfade = atoi(re->value);
660                 }
661                 else if(strcmp(re->name,"updating_db")==0) {
662                         status->updatingDb = atoi(re->value);
663                 }
664                 else if(strcmp(re->name,"audio")==0) {
665                         char * tok;
666                         char * copy;
667                         char * temp;
668                         copy = strdup(re->value);
669                         temp = strtok_r(copy,":",&tok);
670                         if(temp) {
671                                 status->sampleRate = atoi(temp);
672                                 temp = strtok_r(NULL,":",&tok);
673                                 if(temp) {
674                                         status->bits = atoi(temp);
675                                         temp = strtok_r(NULL,"",&tok);
676                                         if(temp) status->channels = atoi(temp);
677                                 }
678                         }
679                         free(copy);
680                 }
682                 mpd_getNextReturnElement(connection);
683                 if(connection->error) {
684                         free(status);
685                         return NULL;
686                 }
687         }
689         if(connection->error) {
690                 free(status);
691                 return NULL;
692         }
693         else if(status->state<0) {
694                 strcpy(connection->errorStr,"state not found");
695                 connection->error = 1;
696                 free(status);
697                 return NULL;
698         }
700         return status;
703 void mpd_freeStatus(mpd_Status * status) {
704         if(status->error) free(status->error);
705         free(status);
708 void mpd_sendStatsCommand(mpd_Connection * connection) {
709         mpd_executeCommand(connection,"stats\n");
712 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
713         mpd_Stats * stats;
715         /*mpd_executeCommand(connection,"stats\n");
716                 
717         if(connection->error) return NULL;*/
719         if(connection->doneProcessing || (connection->listOks && 
720                         connection->doneListOk))
721         {
722                 return NULL;
723         }
725         if(!connection->returnElement) mpd_getNextReturnElement(connection);
727         stats = malloc(sizeof(mpd_Stats));
728         stats->numberOfArtists = 0;
729         stats->numberOfAlbums = 0;
730         stats->numberOfSongs = 0;
731         stats->uptime = 0;
732         stats->dbUpdateTime = 0;
733         stats->playTime = 0;
734         stats->dbPlayTime = 0;
736         if(connection->error) {
737                 free(stats);
738                 return NULL;
739         }
740         while(connection->returnElement) {
741                 mpd_ReturnElement * re = connection->returnElement;
742                 if(strcmp(re->name,"artists")==0) {
743                         stats->numberOfArtists = atoi(re->value);
744                 }
745                 else if(strcmp(re->name,"albums")==0) {
746                         stats->numberOfAlbums = atoi(re->value);
747                 }
748                 else if(strcmp(re->name,"songs")==0) {
749                         stats->numberOfSongs = atoi(re->value);
750                 }
751                 else if(strcmp(re->name,"uptime")==0) {
752                         stats->uptime = strtol(re->value,NULL,10);
753                 }
754                 else if(strcmp(re->name,"db_update")==0) {
755                         stats->dbUpdateTime = strtol(re->value,NULL,10);
756                 }
757                 else if(strcmp(re->name,"playtime")==0) {
758                         stats->playTime = strtol(re->value,NULL,10);
759                 }
760                 else if(strcmp(re->name,"db_playtime")==0) {
761                         stats->dbPlayTime = strtol(re->value,NULL,10);
762                 }
764                 mpd_getNextReturnElement(connection);
765                 if(connection->error) {
766                         free(stats);
767                         return NULL;
768                 }
769         }
771         if(connection->error) {
772                 free(stats);
773                 return NULL;
774         }
776         return stats;
779 void mpd_freeStats(mpd_Stats * stats) {
780         free(stats);
783 void mpd_initSong(mpd_Song * song) {
784         song->file = NULL;
785         song->artist = NULL;
786         song->album = NULL;
787         song->track = NULL;
788         song->title = NULL;
789         song->name = NULL;
790         song->date = NULL;
791         song->time = MPD_SONG_NO_TIME;
792         song->pos = MPD_SONG_NO_NUM;
793         song->id = MPD_SONG_NO_ID;
796 void mpd_finishSong(mpd_Song * song) {
797         if(song->file) free(song->file);
798         if(song->artist) free(song->artist);
799         if(song->album) free(song->album);
800         if(song->title) free(song->title);
801         if(song->track) free(song->track);
802         if(song->name) free(song->name);
803         if(song->date) free(song->date);
806 mpd_Song * mpd_newSong() {
807         mpd_Song * ret = malloc(sizeof(mpd_Song));
809         mpd_initSong(ret);
811         return ret;
814 void mpd_freeSong(mpd_Song * song) {
815         mpd_finishSong(song);
816         free(song);
819 mpd_Song * mpd_songDup(mpd_Song * song) {
820         mpd_Song * ret = mpd_newSong();
822         if(song->file) ret->file = strdup(song->file);
823         if(song->artist) ret->artist = strdup(song->artist);
824         if(song->album) ret->album = strdup(song->album);
825         if(song->title) ret->title = strdup(song->title);
826         if(song->track) ret->track = strdup(song->track);
827         if(song->name) ret->name = strdup(song->name);
828         if(song->date) ret->date = strdup(song->date);
829         ret->time = song->time;
830         ret->pos = song->pos;
831         ret->id = song->id;
833         return ret;
836 void mpd_initDirectory(mpd_Directory * directory) {
837         directory->path = NULL;
840 void mpd_finishDirectory(mpd_Directory * directory) {
841         if(directory->path) free(directory->path);
844 mpd_Directory * mpd_newDirectory () {
845         mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
847         mpd_initDirectory(directory);
848         
849         return directory;
852 void mpd_freeDirectory(mpd_Directory * directory) {
853         mpd_finishDirectory(directory);
855         free(directory);
858 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
859         mpd_Directory * ret = mpd_newDirectory();
861         if(directory->path) ret->path = strdup(directory->path);
863         return ret;
866 void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
867         playlist->path = NULL;
870 void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
871         if(playlist->path) free(playlist->path);
874 mpd_PlaylistFile * mpd_newPlaylistFile() {
875         mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
877         mpd_initPlaylistFile(playlist);
879         return playlist;
882 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
883         mpd_finishPlaylistFile(playlist);
884         free(playlist);
887 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
888         mpd_PlaylistFile * ret = mpd_newPlaylistFile();
890         if(playlist->path) ret->path = strdup(playlist->path);
892         return ret;
895 void mpd_initInfoEntity(mpd_InfoEntity * entity) {
896         entity->info.directory = NULL;
897
899 void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
900         if(entity->info.directory) {
901                 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
902                         mpd_freeDirectory(entity->info.directory);
903                 }
904                 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
905                         mpd_freeSong(entity->info.song);
906                 }
907                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
908                         mpd_freePlaylistFile(entity->info.playlistFile);
909                 }
910         }
913 mpd_InfoEntity * mpd_newInfoEntity() {
914         mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
915         
916         mpd_initInfoEntity(entity);
918         return entity;
921 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
922         mpd_finishInfoEntity(entity);
923         free(entity);
926 void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
927         mpd_executeCommand(connection,command);
930 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
931         mpd_InfoEntity * entity = NULL;
933         if(connection->doneProcessing || (connection->listOks && 
934                         connection->doneListOk))
935         {
936                 return NULL;
937         }
939         if(!connection->returnElement) mpd_getNextReturnElement(connection);
941         if(connection->returnElement) { 
942                 if(strcmp(connection->returnElement->name,"file")==0) {
943                         entity = mpd_newInfoEntity();
944                         entity->type = MPD_INFO_ENTITY_TYPE_SONG;
945                         entity->info.song = mpd_newSong();
946                         entity->info.song->file = 
947                                 strdup(connection->returnElement->value);
948                 }
949                 else if(strcmp(connection->returnElement->name,
950                                         "directory")==0) {
951                         entity = mpd_newInfoEntity();
952                         entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
953                         entity->info.directory = mpd_newDirectory();
954                         entity->info.directory->path = 
955                                 strdup(connection->returnElement->value);
956                 }
957                 else if(strcmp(connection->returnElement->name,"playlist")==0) {
958                         entity = mpd_newInfoEntity();
959                         entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
960                         entity->info.playlistFile = mpd_newPlaylistFile();
961                         entity->info.playlistFile->path = 
962                                 strdup(connection->returnElement->value);
963                 }
964                 else {
965                         connection->error = 1;
966                         strcpy(connection->errorStr,"problem parsing song info");
967                         return NULL;
968                 }
969         }
970         else return NULL;
972         mpd_getNextReturnElement(connection);
973         while(connection->returnElement) {
974                 mpd_ReturnElement * re = connection->returnElement;
976                 if(strcmp(re->name,"file")==0) return entity;
977                 else if(strcmp(re->name,"directory")==0) return entity;
978                 else if(strcmp(re->name,"playlist")==0) return entity;
980                 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG && 
981                                 strlen(re->value)) {
982                         if(!entity->info.song->artist &&
983                                         strcmp(re->name,"Artist")==0) {
984                                 entity->info.song->artist = strdup(re->value);
985                         }
986                         else if(!entity->info.song->album &&
987                                         strcmp(re->name,"Album")==0) {
988                                 entity->info.song->album = strdup(re->value);
989                         }
990                         else if(!entity->info.song->title &&
991                                         strcmp(re->name,"Title")==0) {
992                                 entity->info.song->title = strdup(re->value);
993                         }
994                         else if(!entity->info.song->track &&
995                                         strcmp(re->name,"Track")==0) {
996                                 entity->info.song->track = strdup(re->value);
997                         }
998                         else if(!entity->info.song->name &&
999                                         strcmp(re->name,"Name")==0) {
1000                                 entity->info.song->name = strdup(re->value);
1001                         }
1002                         else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1003                                         strcmp(re->name,"Time")==0) {
1004                                 entity->info.song->time = atoi(re->value);
1005                         }
1006                         else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1007                                         strcmp(re->name,"Pos")==0) {
1008                                 entity->info.song->pos = atoi(re->value);
1009                         }
1010                         else if(entity->info.song->id==MPD_SONG_NO_ID &&
1011                                         strcmp(re->name,"Id")==0) {
1012                                 entity->info.song->id = atoi(re->value);
1013                         }
1014                         else if(!entity->info.song->date &&
1015                                         strcmp(re->name, "Date") == 0) {
1016                                 entity->info.song->date = strdup(re->value);
1017                         }
1018                 }
1019                 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1020                 }
1021                 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1022                 }
1024                 mpd_getNextReturnElement(connection);
1025         }
1027         return entity;
1030 char * mpd_getNextReturnElementNamed(mpd_Connection * connection, 
1031                 const char * name) 
1033         if(connection->doneProcessing || (connection->listOks && 
1034                         connection->doneListOk)) 
1035         {
1036                 return NULL;
1037         }
1039         mpd_getNextReturnElement(connection);
1040         while(connection->returnElement) {
1041                 mpd_ReturnElement * re = connection->returnElement;
1043                 if(strcmp(re->name,name)==0) return strdup(re->value);
1044                 mpd_getNextReturnElement(connection);
1045         }
1047         return NULL;
1050 char * mpd_getNextArtist(mpd_Connection * connection) {
1051         return mpd_getNextReturnElementNamed(connection,"Artist");
1054 char * mpd_getNextAlbum(mpd_Connection * connection) {
1055         return mpd_getNextReturnElementNamed(connection,"Album");
1058 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1059         char * string = malloc(strlen("playlistinfo")+25);
1060         sprintf(string,"playlistinfo \"%i\"\n",songPos);
1061         mpd_sendInfoCommand(connection,string);
1062         free(string);
1065 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1066         char * string = malloc(strlen("playlistid")+25);
1067         sprintf(string, "playlistid \"%i\"\n", id);
1068         mpd_sendInfoCommand(connection, string);
1069         free(string);
1072 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1073         char * string = malloc(strlen("plchanges")+25);
1074         sprintf(string,"plchanges \"%lld\"\n",playlist);
1075         mpd_sendInfoCommand(connection,string);
1076         free(string);
1079 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1080         char * sDir = mpd_sanitizeArg(dir);
1081         char * string = malloc(strlen("listall")+strlen(sDir)+5);
1082         sprintf(string,"listall \"%s\"\n",sDir);
1083         mpd_sendInfoCommand(connection,string);
1084         free(string);
1085         free(sDir);
1088 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1089         char * sDir = mpd_sanitizeArg(dir);
1090         char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1091         sprintf(string,"listallinfo \"%s\"\n",sDir);
1092         mpd_sendInfoCommand(connection,string);
1093         free(string);
1094         free(sDir);
1097 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1098         char * sDir = mpd_sanitizeArg(dir);
1099         char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1100         sprintf(string,"lsinfo \"%s\"\n",sDir);
1101         mpd_sendInfoCommand(connection,string);
1102         free(string);
1103         free(sDir);
1106 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1107         mpd_executeCommand(connection,"currentsong\n");
1110 void mpd_sendSearchCommand(mpd_Connection * connection, int table, 
1111                 const char * str) 
1113         char st[10];
1114         char * string;
1115         char * sanitStr = mpd_sanitizeArg(str);
1116         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1117         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1118         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1119         else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1120         else {
1121                 connection->error = 1;
1122                 strcpy(connection->errorStr,"unknown table for search");
1123                 return;
1124         }
1125         string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1126         sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1127         mpd_sendInfoCommand(connection,string);
1128         free(string);
1129         free(sanitStr);
1132 void mpd_sendFindCommand(mpd_Connection * connection, int table, 
1133                 const char * str) 
1135         char st[10];
1136         char * string;
1137         char * sanitStr = mpd_sanitizeArg(str);
1138         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1139         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1140         else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1141         else {
1142                 connection->error = 1;
1143                 strcpy(connection->errorStr,"unknown table for find");
1144                 return;
1145         }
1146         string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1147         sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1148         mpd_sendInfoCommand(connection,string);
1149         free(string);
1150         free(sanitStr);
1153 void mpd_sendListCommand(mpd_Connection * connection, int table, 
1154                 const char * arg1) 
1156         char st[10];
1157         char * string;
1158         if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1159         else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1160         else {
1161                 connection->error = 1;
1162                 strcpy(connection->errorStr,"unknown table for list");
1163                 return;
1164         }
1165         if(arg1) {
1166                 char * sanitArg1 = mpd_sanitizeArg(arg1);
1167                 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1168                 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1169                 free(sanitArg1);
1170         }
1171         else {
1172                 string = malloc(strlen("list")+strlen(st)+3);
1173                 sprintf(string,"list %s\n",st);
1174         }
1175         mpd_sendInfoCommand(connection,string);
1176         free(string);
1179 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1180         char * sFile = mpd_sanitizeArg(file);
1181         char * string = malloc(strlen("add")+strlen(sFile)+5);
1182         sprintf(string,"add \"%s\"\n",sFile);
1183         mpd_executeCommand(connection,string);
1184         free(string);
1185         free(sFile);
1188 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1189         char * string = malloc(strlen("delete")+25);
1190         sprintf(string,"delete \"%i\"\n",songPos);
1191         mpd_sendInfoCommand(connection,string);
1192         free(string);
1195 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1196         char * string = malloc(strlen("deleteid")+25);
1197         sprintf(string, "deleteid \"%i\"\n", id);
1198         mpd_sendInfoCommand(connection,string);
1199         free(string);
1202 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1203         char * sName = mpd_sanitizeArg(name);
1204         char * string = malloc(strlen("save")+strlen(sName)+5);
1205         sprintf(string,"save \"%s\"\n",sName);
1206         mpd_executeCommand(connection,string);
1207         free(string);
1208         free(sName);
1211 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1212         char * sName = mpd_sanitizeArg(name);
1213         char * string = malloc(strlen("load")+strlen(sName)+5);
1214         sprintf(string,"load \"%s\"\n",sName);
1215         mpd_executeCommand(connection,string);
1216         free(string);
1217         free(sName);
1220 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1221         char * sName = mpd_sanitizeArg(name);
1222         char * string = malloc(strlen("rm")+strlen(sName)+5);
1223         sprintf(string,"rm \"%s\"\n",sName);
1224         mpd_executeCommand(connection,string);
1225         free(string);
1226         free(sName);
1229 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1230         mpd_executeCommand(connection,"shuffle\n");
1233 void mpd_sendClearCommand(mpd_Connection * connection) {
1234         mpd_executeCommand(connection,"clear\n");
1237 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1238         char * string = malloc(strlen("play")+25);
1239         sprintf(string,"play \"%i\"\n",songPos);
1240         mpd_sendInfoCommand(connection,string);
1241         free(string);
1244 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1245         char * string = malloc(strlen("playid")+25);
1246         sprintf(string,"playid \"%i\"\n",id);
1247         mpd_sendInfoCommand(connection,string);
1248         free(string);
1251 void mpd_sendStopCommand(mpd_Connection * connection) {
1252         mpd_executeCommand(connection,"stop\n");
1255 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1256         char * string = malloc(strlen("pause")+25);
1257         sprintf(string,"pause \"%i\"\n",pauseMode);
1258         mpd_executeCommand(connection,string);
1259         free(string);
1262 void mpd_sendNextCommand(mpd_Connection * connection) {
1263         mpd_executeCommand(connection,"next\n");
1266 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1267         char * string = malloc(strlen("move")+25);
1268         sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1269         mpd_sendInfoCommand(connection,string);
1270         free(string);
1273 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1274         char * string = malloc(strlen("moveid")+25);
1275         sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1276         mpd_sendInfoCommand(connection,string);
1277         free(string);
1280 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1281         char * string = malloc(strlen("swap")+25);
1282         sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1283         mpd_sendInfoCommand(connection,string);
1284         free(string);
1287 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1288         char * string = malloc(strlen("swapid")+25);
1289         sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1290         mpd_sendInfoCommand(connection,string);
1291         free(string);
1294 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1295         char * string = malloc(strlen("seek")+25);
1296         sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1297         mpd_sendInfoCommand(connection,string);
1298         free(string);
1301 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1302         char * string = malloc(strlen("seekid")+25);
1303         sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1304         mpd_sendInfoCommand(connection,string);
1305         free(string);
1308 void mpd_sendUpdateCommand(mpd_Connection * connection, char * path) {
1309         char * sPath = mpd_sanitizeArg(path);
1310         char * string = malloc(strlen("update")+strlen(sPath)+5);
1311         sprintf(string,"update \"%s\"\n",sPath);
1312         mpd_sendInfoCommand(connection,string);
1313         free(string);
1314         free(sPath);
1317 int mpd_getUpdateId(mpd_Connection * connection) {
1318         char * jobid;
1319         int ret = 0;
1321         jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1322         if(jobid) {
1323                 ret = atoi(jobid);
1324                 free(jobid);
1325         }
1327         return ret;
1330 void mpd_sendPrevCommand(mpd_Connection * connection) {
1331         mpd_executeCommand(connection,"previous\n");
1334 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1335         char * string = malloc(strlen("repeat")+25);
1336         sprintf(string,"repeat \"%i\"\n",repeatMode);
1337         mpd_executeCommand(connection,string);
1338         free(string);
1341 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1342         char * string = malloc(strlen("random")+25);
1343         sprintf(string,"random \"%i\"\n",randomMode);
1344         mpd_executeCommand(connection,string);
1345         free(string);
1348 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1349         char * string = malloc(strlen("setvol")+25);
1350         sprintf(string,"setvol \"%i\"\n",volumeChange);
1351         mpd_executeCommand(connection,string);
1352         free(string);
1355 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1356         char * string = malloc(strlen("volume")+25);
1357         sprintf(string,"volume \"%i\"\n",volumeChange);
1358         mpd_executeCommand(connection,string);
1359         free(string);
1362 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1363         char * string = malloc(strlen("crossfade")+25);
1364         sprintf(string,"crossfade \"%i\"\n",seconds);
1365         mpd_executeCommand(connection,string);
1366         free(string);
1369 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1370         char * sPass = mpd_sanitizeArg(pass);
1371         char * string = malloc(strlen("password")+strlen(sPass)+5);
1372         sprintf(string,"password \"%s\"\n",sPass);
1373         mpd_executeCommand(connection,string);
1374         free(string);
1375         free(sPass);
1378 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1379         if(connection->commandList) {
1380                 strcpy(connection->errorStr,"already in command list mode");
1381                 connection->error = 1;
1382                 return;
1383         }
1384         connection->commandList = COMMAND_LIST;
1385         mpd_executeCommand(connection,"command_list_begin\n");
1388 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1389         if(connection->commandList) {
1390                 strcpy(connection->errorStr,"already in command list mode");
1391                 connection->error = 1;
1392                 return;
1393         }
1394         connection->commandList = COMMAND_LIST_OK;
1395         mpd_executeCommand(connection,"command_list_ok_begin\n");
1396         connection->listOks = 0;
1399 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1400         if(!connection->commandList) {
1401                 strcpy(connection->errorStr,"not in command list mode");
1402                 connection->error = 1;
1403                 return;
1404         }
1405         connection->commandList = 0;
1406         mpd_executeCommand(connection,"command_list_end\n");
1409 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1410         mpd_executeCommand(connection,"outputs\n");
1413 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1414         mpd_OutputEntity * output = NULL;
1416         if(connection->doneProcessing || (connection->listOks &&
1417                         connection->doneListOk))
1418         {
1419                 return NULL;
1420         }
1422         if(connection->error) return NULL;
1424         output = malloc(sizeof(mpd_OutputEntity));
1425         output->id = -10;
1426         output->name = NULL;
1427         output->enabled = 0;
1429         if(!connection->returnElement) mpd_getNextReturnElement(connection);
1431         while(connection->returnElement) {
1432                 mpd_ReturnElement * re = connection->returnElement;
1433                 if(strcmp(re->name,"outputid")==0) {
1434                         if(output!=NULL && output->id>=0) return output;
1435                         output->id = atoi(re->value);
1436                 }
1437                 else if(strcmp(re->name,"outputname")==0) {
1438                         output->name = strdup(re->value);
1439                 }
1440                 else if(strcmp(re->name,"outputenabled")==0) {
1441                         output->enabled = atoi(re->value);
1442                 }
1444                 mpd_getNextReturnElement(connection);
1445                 if(connection->error) {
1446                         free(output);
1447                         return NULL;
1448                 }
1449                 
1450         }
1452         return output;
1455 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1456         char * string = malloc(strlen("enableoutput")+25);
1457         sprintf(string,"enableoutput \"%i\"\n",outputId);
1458         mpd_executeCommand(connection,string);
1459         free(string);
1462 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1463         char * string = malloc(strlen("disableoutput")+25);
1464         sprintf(string,"disableoutput \"%i\"\n",outputId);
1465         mpd_executeCommand(connection,string);
1466         free(string);
1469 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1470         free(output->name);
1471         free(output);