2ab4e28cfb6ef9a064533097a31cc24f9f95b518
1 /* libmpdclient
2 (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
3 This project's homepage is: http://www.musicpd.org
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
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.
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.
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;
104 }
106 void mpd_freeReturnElement(mpd_ReturnElement * re) {
107 free(re->name);
108 free(re->value);
109 free(re);
110 }
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);
116 }
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 }
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;
316 }
318 void mpd_clearError(mpd_Connection * connection) {
319 connection->error = 0;
320 connection->errorStr[0] = '\0';
321 }
323 void mpd_closeConnection(mpd_Connection * connection) {
324 close(connection->sock);
325 if(connection->returnElement) free(connection->returnElement);
326 free(connection);
327 }
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 }
386 }
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;
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 }
537 }
539 void mpd_finishCommand(mpd_Connection * connection) {
540 while(!connection->doneProcessing) {
541 if(connection->doneListOk) connection->doneListOk = 0;
542 mpd_getNextReturnElement(connection);
543 }
544 }
546 void mpd_finishListOkCommand(mpd_Connection * connection) {
547 while(!connection->doneProcessing && connection->listOks &&
548 !connection->doneListOk )
549 {
550 mpd_getNextReturnElement(connection);
551 }
552 }
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;
559 }
561 void mpd_sendStatusCommand(mpd_Connection * connection) {
562 mpd_executeCommand(connection,"status\n");
563 }
565 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
566 mpd_Status * status;
568 /*mpd_executeCommand(connection,"status\n");
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;
701 }
703 void mpd_freeStatus(mpd_Status * status) {
704 if(status->error) free(status->error);
705 free(status);
706 }
708 void mpd_sendStatsCommand(mpd_Connection * connection) {
709 mpd_executeCommand(connection,"stats\n");
710 }
712 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
713 mpd_Stats * stats;
715 /*mpd_executeCommand(connection,"stats\n");
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;
777 }
779 void mpd_freeStats(mpd_Stats * stats) {
780 free(stats);
781 }
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;
794 }
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);
804 }
806 mpd_Song * mpd_newSong() {
807 mpd_Song * ret = malloc(sizeof(mpd_Song));
809 mpd_initSong(ret);
811 return ret;
812 }
814 void mpd_freeSong(mpd_Song * song) {
815 mpd_finishSong(song);
816 free(song);
817 }
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;
834 }
836 void mpd_initDirectory(mpd_Directory * directory) {
837 directory->path = NULL;
838 }
840 void mpd_finishDirectory(mpd_Directory * directory) {
841 if(directory->path) free(directory->path);
842 }
844 mpd_Directory * mpd_newDirectory () {
845 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
847 mpd_initDirectory(directory);
849 return directory;
850 }
852 void mpd_freeDirectory(mpd_Directory * directory) {
853 mpd_finishDirectory(directory);
855 free(directory);
856 }
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;
864 }
866 void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
867 playlist->path = NULL;
868 }
870 void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
871 if(playlist->path) free(playlist->path);
872 }
874 mpd_PlaylistFile * mpd_newPlaylistFile() {
875 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
877 mpd_initPlaylistFile(playlist);
879 return playlist;
880 }
882 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
883 mpd_finishPlaylistFile(playlist);
884 free(playlist);
885 }
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;
893 }
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 }
911 }
913 mpd_InfoEntity * mpd_newInfoEntity() {
914 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
916 mpd_initInfoEntity(entity);
918 return entity;
919 }
921 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
922 mpd_finishInfoEntity(entity);
923 free(entity);
924 }
926 void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
927 mpd_executeCommand(connection,command);
928 }
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;
1028 }
1030 char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1031 const char * name)
1032 {
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;
1048 }
1050 char * mpd_getNextArtist(mpd_Connection * connection) {
1051 return mpd_getNextReturnElementNamed(connection,"Artist");
1052 }
1054 char * mpd_getNextAlbum(mpd_Connection * connection) {
1055 return mpd_getNextReturnElementNamed(connection,"Album");
1056 }
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);
1063 }
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);
1070 }
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);
1077 }
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);
1086 }
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);
1095 }
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);
1104 }
1106 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1107 mpd_executeCommand(connection,"currentsong\n");
1108 }
1110 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1111 const char * str)
1112 {
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);
1130 }
1132 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1133 const char * str)
1134 {
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);
1151 }
1153 void mpd_sendListCommand(mpd_Connection * connection, int table,
1154 const char * arg1)
1155 {
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);
1177 }
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);
1186 }
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);
1193 }
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);
1200 }
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);
1209 }
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);
1218 }
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);
1227 }
1229 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1230 mpd_executeCommand(connection,"shuffle\n");
1231 }
1233 void mpd_sendClearCommand(mpd_Connection * connection) {
1234 mpd_executeCommand(connection,"clear\n");
1235 }
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);
1242 }
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);
1249 }
1251 void mpd_sendStopCommand(mpd_Connection * connection) {
1252 mpd_executeCommand(connection,"stop\n");
1253 }
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);
1260 }
1262 void mpd_sendNextCommand(mpd_Connection * connection) {
1263 mpd_executeCommand(connection,"next\n");
1264 }
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);
1271 }
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);
1278 }
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);
1285 }
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);
1292 }
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);
1299 }
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);
1306 }
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);
1315 }
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;
1328 }
1330 void mpd_sendPrevCommand(mpd_Connection * connection) {
1331 mpd_executeCommand(connection,"previous\n");
1332 }
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);
1339 }
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);
1346 }
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);
1353 }
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);
1360 }
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);
1367 }
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);
1376 }
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");
1386 }
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;
1397 }
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");
1407 }
1409 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1410 mpd_executeCommand(connection,"outputs\n");
1411 }
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 }
1450 }
1452 return output;
1453 }
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);
1460 }
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);
1467 }
1469 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1470 free(output->name);
1471 free(output);
1472 }