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 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
20 #include "libmpdclient.h"
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netdb.h>
26 #include <stdio.h>
27 #include <sys/param.h>
28 #include <string.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <fcntl.h>
35 #ifndef MPD_NO_IPV6
36 #ifdef AF_INET6
37 #define MPD_HAVE_IPV6
38 #endif
39 #endif
41 #define COMMAND_LIST 1
42 #define COMMAND_LIST_OK 2
44 #ifdef MPD_HAVE_IPV6
45 int mpd_ipv6Supported() {
46 int s;
47 s = socket(AF_INET6,SOCK_STREAM,0);
48 if(s == -1) return 0;
49 close(s);
50 return 1;
51 }
52 #endif
55 char * mpd_sanitizeArg(const char * arg) {
56 size_t i;
57 int count=0;
58 char * ret;
60 for(i=0;i<strlen(arg);i++) {
61 if(arg[i]=='"' || arg[i]=='\\') count++;
62 }
64 ret = malloc(strlen(arg)+count+1);
66 count = 0;
67 for(i=0;i<strlen(arg)+1;i++) {
68 if(arg[i]=='"' || arg[i]=='\\') {
69 ret[i+count] = '\\';
70 count++;
71 }
72 ret[i+count] = arg[i];
73 }
75 return ret;
76 }
78 mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
79 {
80 mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
82 ret->name = strdup(name);
83 ret->value = strdup(value);
85 return ret;
86 }
88 void mpd_freeReturnElement(mpd_ReturnElement * re) {
89 free(re->name);
90 free(re->value);
91 free(re);
92 }
94 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
95 connection->timeout.tv_sec = (int)timeout;
96 connection->timeout.tv_usec = (int)(timeout*1e6 -
97 connection->timeout.tv_sec*1000000+0.5);
98 }
100 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
101 int err;
102 struct hostent * he;
103 struct sockaddr * dest;
104 #ifdef MPD_HAVE_SOCKLEN_T
105 socklen_t destlen;
106 #else
107 int destlen;
108 #endif
109 struct sockaddr_in sin;
110 char * rt;
111 char * output;
112 mpd_Connection * connection = malloc(sizeof(mpd_Connection));
113 struct timeval tv;
114 fd_set fds;
115 #ifdef MPD_HAVE_IPV6
116 struct sockaddr_in6 sin6;
117 #endif
118 strcpy(connection->buffer,"");
119 connection->buflen = 0;
120 connection->bufstart = 0;
121 strcpy(connection->errorStr,"");
122 connection->error = 0;
123 connection->doneProcessing = 0;
124 connection->commandList = 0;
125 connection->listOks = 0;
126 connection->doneListOk = 0;
127 connection->returnElement = NULL;
129 if(!(he=gethostbyname(host))) {
130 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
131 "host \"%s\" not found",host);
132 connection->error = MPD_ERROR_UNKHOST;
133 return connection;
134 }
136 memset(&sin,0,sizeof(struct sockaddr_in));
137 /*dest.sin_family = he->h_addrtype;*/
138 sin.sin_family = AF_INET;
139 sin.sin_port = htons(port);
140 #ifdef MPD_HAVE_IPV6
141 memset(&sin6,0,sizeof(struct sockaddr_in6));
142 sin6.sin6_family = AF_INET6;
143 sin6.sin6_port = htons(port);
144 #endif
145 switch(he->h_addrtype) {
146 case AF_INET:
147 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
148 he->h_length);
149 dest = (struct sockaddr *)&sin;
150 destlen = sizeof(struct sockaddr_in);
151 break;
152 #ifdef MPD_HAVE_IPV6
153 case AF_INET6:
154 if(!mpd_ipv6Supported()) {
155 strcpy(connection->errorStr,"no IPv6 suuport but a "
156 "IPv6 address found\n");
157 connection->error = MPD_ERROR_SYSTEM;
158 return connection;
159 }
160 memcpy((char *)&sin6.sin6_addr.s6_addr,(char *)he->h_addr,
161 he->h_length);
162 dest = (struct sockaddr *)&sin6;
163 destlen = sizeof(struct sockaddr_in6);
164 break;
165 #endif
166 default:
167 strcpy(connection->errorStr,"address type is not IPv4 or "
168 "IPv6\n");
169 connection->error = MPD_ERROR_SYSTEM;
170 return connection;
171 break;
172 }
174 if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
175 strcpy(connection->errorStr,"problems creating socket");
176 connection->error = MPD_ERROR_SYSTEM;
177 return connection;
178 }
180 mpd_setConnectionTimeout(connection,timeout);
182 /* connect stuff */
183 {
184 int flags = fcntl(connection->sock, F_GETFL, 0);
185 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
187 if(connect(connection->sock,dest,destlen)<0 &&
188 errno!=EINPROGRESS)
189 {
190 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
191 "problems connecting to \"%s\" on port"
192 " %i",host,port);
193 connection->error = MPD_ERROR_CONNPORT;
194 return connection;
195 }
196 }
198 while(!(rt = strstr(connection->buffer,"\n"))) {
199 tv.tv_sec = connection->timeout.tv_sec;
200 tv.tv_usec = connection->timeout.tv_usec;
201 FD_ZERO(&fds);
202 FD_SET(connection->sock,&fds);
203 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
204 int readed;
205 readed = recv(connection->sock,
206 &(connection->buffer[connection->buflen]),
207 MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
208 if(readed<=0) {
209 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
210 "problems getting a response from"
211 " \"%s\" on port %i",host,
212 port);
213 connection->error = MPD_ERROR_NORESPONSE;
214 return connection;
215 }
216 connection->buflen+=readed;
217 connection->buffer[connection->buflen] = '\0';
218 tv.tv_sec = connection->timeout.tv_sec;
219 tv.tv_usec = connection->timeout.tv_usec;
220 }
221 else if(err<0) {
222 switch(errno) {
223 case EINTR:
224 continue;
225 default:
226 snprintf(connection->errorStr,
227 MPD_BUFFER_MAX_LENGTH,
228 "problems connecting to \"%s\" on port"
229 " %i",host,port);
230 connection->error = MPD_ERROR_CONNPORT;
231 return connection;
232 }
233 }
234 else {
235 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
236 "timeout in attempting to get a response from"
237 " \"%s\" on port %i",host,port);
238 connection->error = MPD_ERROR_NORESPONSE;
239 return connection;
240 }
241 }
243 *rt = '\0';
244 output = strdup(connection->buffer);
245 strcpy(connection->buffer,rt+1);
246 connection->buflen = strlen(connection->buffer);
248 if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
249 free(output);
250 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
251 "mpd not running on port %i on host \"%s\"",
252 port,host);
253 connection->error = MPD_ERROR_NOTMPD;
254 return connection;
255 }
257 {
258 char * test;
259 char * version[3];
260 char * tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
261 char * search = ".";
262 int i;
264 for(i=0;i<3;i++) {
265 char * tok;
266 if(i==3) search = " ";
267 version[i] = strtok_r(tmp,search,&tok);
268 if(!version[i]) {
269 free(output);
270 snprintf(connection->errorStr,
271 MPD_BUFFER_MAX_LENGTH,
272 "error parsing version number at "
273 "\"%s\"",
274 &output[strlen(MPD_WELCOME_MESSAGE)]);
275 connection->error = MPD_ERROR_NOTMPD;
276 return connection;
277 }
278 connection->version[i] = strtol(version[i],&test,10);
279 if(version[i]==test || *test!='\0') {
280 free(output);
281 snprintf(connection->errorStr,
282 MPD_BUFFER_MAX_LENGTH,
283 "error parsing version number at "
284 "\"%s\"",
285 &output[strlen(MPD_WELCOME_MESSAGE)]);
286 connection->error = MPD_ERROR_NOTMPD;
287 return connection;
288 }
289 tmp = NULL;
290 }
291 }
293 free(output);
295 connection->doneProcessing = 1;
297 return connection;
298 }
300 void mpd_clearError(mpd_Connection * connection) {
301 connection->error = 0;
302 connection->errorStr[0] = '\0';
303 }
305 void mpd_closeConnection(mpd_Connection * connection) {
306 close(connection->sock);
307 if(connection->returnElement) free(connection->returnElement);
308 free(connection);
309 }
311 void mpd_executeCommand(mpd_Connection * connection, char * command) {
312 int ret;
313 struct timeval tv;
314 fd_set fds;
315 char * commandPtr = command;
316 int commandLen = strlen(command);
318 if(!connection->doneProcessing && !connection->commandList) {
319 strcpy(connection->errorStr,"not done processing current command");
320 connection->error = 1;
321 return;
322 }
324 mpd_clearError(connection);
326 FD_ZERO(&fds);
327 FD_SET(connection->sock,&fds);
328 tv.tv_sec = connection->timeout.tv_sec;
329 tv.tv_usec = connection->timeout.tv_usec;
331 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
332 (ret==-1 && errno==EINTR)) {
333 ret = send(connection->sock,commandPtr,commandLen,
334 MSG_DONTWAIT);
335 if(ret<=0)
336 {
337 if(ret==EAGAIN || ret==EINTR) continue;
338 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
339 "problems giving command \"%s\"",command);
340 connection->error = MPD_ERROR_SENDING;
341 return;
342 }
343 else {
344 commandPtr+=ret;
345 commandLen-=ret;
346 }
348 if(commandLen<=0) break;
349 }
351 if(commandLen>0) {
352 perror("");
353 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
354 "timeout sending command \"%s\"",command);
355 connection->error = MPD_ERROR_TIMEOUT;
356 return;
357 }
359 if(!connection->commandList) connection->doneProcessing = 0;
360 else if(connection->commandList == COMMAND_LIST_OK) {
361 connection->listOks++;
362 }
363 }
365 void mpd_getNextReturnElement(mpd_Connection * connection) {
366 char * output = NULL;
367 char * rt = NULL;
368 char * name = NULL;
369 char * value = NULL;
370 fd_set fds;
371 struct timeval tv;
372 char * tok = NULL;
373 int readed;
374 char * bufferCheck = NULL;
375 int err;
377 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
378 connection->returnElement = NULL;
380 if(connection->doneProcessing || (connection->listOks &&
381 connection->doneListOk))
382 {
383 strcpy(connection->errorStr,"already done processing current command");
384 connection->error = 1;
385 return;
386 }
388 bufferCheck = connection->buffer+connection->bufstart;
389 while(connection->bufstart>=connection->buflen ||
390 !(rt = strstr(bufferCheck,"\n"))) {
391 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
392 memmove(connection->buffer,
393 connection->buffer+
394 connection->bufstart,
395 connection->buflen-
396 connection->bufstart+1);
397 bufferCheck-=connection->bufstart;
398 connection->buflen-=connection->bufstart;
399 connection->bufstart = 0;
400 }
401 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
402 strcpy(connection->errorStr,"buffer overrun");
403 connection->error = MPD_ERROR_BUFFEROVERRUN;
404 connection->doneProcessing = 1;
405 return;
406 }
407 bufferCheck+=connection->buflen-connection->bufstart;
408 tv.tv_sec = connection->timeout.tv_sec;
409 tv.tv_usec = connection->timeout.tv_usec;
410 FD_ZERO(&fds);
411 FD_SET(connection->sock,&fds);
412 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
413 readed = recv(connection->sock,
414 connection->buffer+connection->buflen,
415 MPD_BUFFER_MAX_LENGTH-connection->buflen,
416 MSG_DONTWAIT);
417 if(readed<0 && (errno==EAGAIN || errno==EINTR)) {
418 continue;
419 }
420 if(readed<=0) {
421 strcpy(connection->errorStr,"connection"
422 " closed");
423 connection->error = MPD_ERROR_CONNCLOSED;
424 connection->doneProcessing = 1;
425 return;
426 }
427 connection->buflen+=readed;
428 connection->buffer[connection->buflen] = '\0';
429 }
430 else if(err<0 && errno==EINTR) continue;
431 else {
432 strcpy(connection->errorStr,"connection timeout");
433 connection->error = MPD_ERROR_TIMEOUT;
434 connection->doneProcessing = 1;
435 return;
436 }
437 }
439 *rt = '\0';
440 output = connection->buffer+connection->bufstart;
441 connection->bufstart = rt - connection->buffer + 1;
443 if(strcmp(output,"OK")==0) {
444 if(connection->listOks > 0) {
445 strcpy(connection->errorStr, "expected more list_OK's");
446 connection->error = 1;
447 }
448 connection->listOks = 0;
449 connection->doneProcessing = 1;
450 return;
451 }
453 if(strcmp(output, "list_OK") == 0) {
454 if(!connection->listOks) {
455 strcpy(connection->errorStr,
456 "got an unexpected list_OK");
457 connection->error = 1;
458 }
459 else {
460 connection->doneListOk = 1;
461 connection->listOks--;
462 }
463 return;
464 }
466 if(strncmp(output,"ACK",strlen("ACK"))==0) {
467 char * test;
468 char * needle;
469 int val;
471 strcpy(connection->errorStr, output);
472 connection->error = MPD_ERROR_ACK;
473 connection->errorCode = MPD_ACK_ERROR_UNK;
474 connection->errorAt = MPD_ERROR_AT_UNK;
475 connection->doneProcessing = 1;
477 needle = strchr(output, '[');
478 if(!needle) return;
479 val = strtol(needle+1, &test, 10);
480 if(*test != '@') return;
481 connection->errorCode = val;
482 val = strtol(test+1, &test, 10);
483 if(*test != ']') return;
484 connection->errorAt = val;
485 return;
486 }
488 name = strtok_r(output,":",&tok);
489 if(name && (value = strtok_r(NULL,"",&tok)) && value[0]==' ') {
490 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
491 }
492 else {
493 if(!name || !value) {
494 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
495 "error parsing: %s",output);
496 }
497 else {
498 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
499 "error parsing: %s:%s",name,value);
500 }
501 connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0';
502 connection->error = 1;
503 }
504 }
506 void mpd_finishCommand(mpd_Connection * connection) {
507 while(!connection->doneProcessing) {
508 if(connection->doneListOk) connection->doneListOk = 0;
509 mpd_getNextReturnElement(connection);
510 }
511 }
513 void mpd_finishListOkCommand(mpd_Connection * connection) {
514 while(!connection->doneProcessing && connection->listOks &&
515 !connection->doneListOk )
516 {
517 mpd_getNextReturnElement(connection);
518 }
519 }
521 int mpd_nextListOkCommand(mpd_Connection * connection) {
522 mpd_finishListOkCommand(connection);
523 if(!connection->doneProcessing) connection->doneListOk = 0;
524 if(connection->listOks == 0 || connection->doneProcessing) return -1;
525 return 0;
526 }
528 void mpd_sendStatusCommand(mpd_Connection * connection) {
529 mpd_executeCommand(connection,"status\n");
530 }
532 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
533 mpd_Status * status;
535 /*mpd_executeCommand(connection,"status\n");
537 if(connection->error) return NULL;*/
539 if(connection->doneProcessing || (connection->listOks &&
540 connection->doneListOk))
541 {
542 return NULL;
543 }
545 if(!connection->returnElement) mpd_getNextReturnElement(connection);
547 status = malloc(sizeof(mpd_Status));
548 status->volume = -1;
549 status->repeat = 0;
550 status->random = 0;
551 status->playlist = -1;
552 status->playlistLength = -1;
553 status->state = -1;
554 status->song = 0;
555 status->elapsedTime = 0;
556 status->totalTime = 0;
557 status->bitRate = 0;
558 status->sampleRate = 0;
559 status->bits = 0;
560 status->channels = 0;
561 status->crossfade = -1;
562 status->error = NULL;
563 status->updatingDb = 0;
565 if(connection->error) {
566 free(status);
567 return NULL;
568 }
569 while(connection->returnElement) {
570 mpd_ReturnElement * re = connection->returnElement;
571 if(strcmp(re->name,"volume")==0) {
572 status->volume = atoi(re->value);
573 }
574 else if(strcmp(re->name,"repeat")==0) {
575 status->repeat = atoi(re->value);
576 }
577 else if(strcmp(re->name,"random")==0) {
578 status->random = atoi(re->value);
579 }
580 else if(strcmp(re->name,"playlist")==0) {
581 status->playlist = strtol(re->value,NULL,10);
582 }
583 else if(strcmp(re->name,"playlistlength")==0) {
584 status->playlistLength = atoi(re->value);
585 }
586 else if(strcmp(re->name,"bitrate")==0) {
587 status->bitRate = atoi(re->value);
588 }
589 else if(strcmp(re->name,"state")==0) {
590 if(strcmp(re->value,"play")==0) {
591 status->state = MPD_STATUS_STATE_PLAY;
592 }
593 else if(strcmp(re->value,"stop")==0) {
594 status->state = MPD_STATUS_STATE_STOP;
595 }
596 else if(strcmp(re->value,"pause")==0) {
597 status->state = MPD_STATUS_STATE_PAUSE;
598 }
599 else {
600 status->state = MPD_STATUS_STATE_UNKNOWN;
601 }
602 }
603 else if(strcmp(re->name,"song")==0) {
604 status->song = atoi(re->value);
605 }
606 else if(strcmp(re->name,"songid")==0) {
607 status->songid = atoi(re->value);
608 }
609 else if(strcmp(re->name,"time")==0) {
610 char * tok;
611 char * copy;
612 char * temp;
613 copy = strdup(re->value);
614 temp = strtok_r(copy,":",&tok);
615 if(temp) {
616 status->elapsedTime = atoi(temp);
617 temp = strtok_r(NULL,"",&tok);
618 if(temp) status->totalTime = atoi(temp);
619 }
620 free(copy);
621 }
622 else if(strcmp(re->name,"error")==0) {
623 status->error = strdup(re->value);
624 }
625 else if(strcmp(re->name,"xfade")==0) {
626 status->crossfade = atoi(re->value);
627 }
628 else if(strcmp(re->name,"updating_db")==0) {
629 status->updatingDb = atoi(re->value);
630 }
631 else if(strcmp(re->name,"audio")==0) {
632 char * tok;
633 char * copy;
634 char * temp;
635 copy = strdup(re->value);
636 temp = strtok_r(copy,":",&tok);
637 if(temp) {
638 status->sampleRate = atoi(temp);
639 temp = strtok_r(NULL,":",&tok);
640 if(temp) {
641 status->bits = atoi(temp);
642 temp = strtok_r(NULL,"",&tok);
643 if(temp) status->channels = atoi(temp);
644 }
645 }
646 free(copy);
647 }
649 mpd_getNextReturnElement(connection);
650 if(connection->error) {
651 free(status);
652 return NULL;
653 }
654 }
656 if(connection->error) {
657 free(status);
658 return NULL;
659 }
660 else if(status->state<0) {
661 strcpy(connection->errorStr,"state not found");
662 connection->error = 1;
663 free(status);
664 return NULL;
665 }
667 return status;
668 }
670 void mpd_freeStatus(mpd_Status * status) {
671 if(status->error) free(status->error);
672 free(status);
673 }
675 void mpd_sendStatsCommand(mpd_Connection * connection) {
676 mpd_executeCommand(connection,"stats\n");
677 }
679 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
680 mpd_Stats * stats;
682 /*mpd_executeCommand(connection,"stats\n");
684 if(connection->error) return NULL;*/
686 if(connection->doneProcessing || (connection->listOks &&
687 connection->doneListOk))
688 {
689 return NULL;
690 }
692 if(!connection->returnElement) mpd_getNextReturnElement(connection);
694 stats = malloc(sizeof(mpd_Stats));
695 stats->numberOfArtists = 0;
696 stats->numberOfAlbums = 0;
697 stats->numberOfSongs = 0;
698 stats->uptime = 0;
699 stats->dbUpdateTime = 0;
700 stats->playTime = 0;
701 stats->dbPlayTime = 0;
703 if(connection->error) {
704 free(stats);
705 return NULL;
706 }
707 while(connection->returnElement) {
708 mpd_ReturnElement * re = connection->returnElement;
709 if(strcmp(re->name,"artists")==0) {
710 stats->numberOfArtists = atoi(re->value);
711 }
712 else if(strcmp(re->name,"albums")==0) {
713 stats->numberOfAlbums = atoi(re->value);
714 }
715 else if(strcmp(re->name,"songs")==0) {
716 stats->numberOfSongs = atoi(re->value);
717 }
718 else if(strcmp(re->name,"uptime")==0) {
719 stats->uptime = strtol(re->value,NULL,10);
720 }
721 else if(strcmp(re->name,"db_update")==0) {
722 stats->dbUpdateTime = strtol(re->value,NULL,10);
723 }
724 else if(strcmp(re->name,"playtime")==0) {
725 stats->playTime = strtol(re->value,NULL,10);
726 }
727 else if(strcmp(re->name,"db_playtime")==0) {
728 stats->dbPlayTime = strtol(re->value,NULL,10);
729 }
731 mpd_getNextReturnElement(connection);
732 if(connection->error) {
733 free(stats);
734 return NULL;
735 }
736 }
738 if(connection->error) {
739 free(stats);
740 return NULL;
741 }
743 return stats;
744 }
746 void mpd_freeStats(mpd_Stats * stats) {
747 free(stats);
748 }
750 void mpd_initSong(mpd_Song * song) {
751 song->file = NULL;
752 song->artist = NULL;
753 song->album = NULL;
754 song->track = NULL;
755 song->title = NULL;
756 song->name = NULL;
757 song->time = MPD_SONG_NO_TIME;
758 song->pos = MPD_SONG_NO_NUM;
759 song->id = MPD_SONG_NO_ID;
760 }
762 void mpd_finishSong(mpd_Song * song) {
763 if(song->file) free(song->file);
764 if(song->artist) free(song->artist);
765 if(song->album) free(song->album);
766 if(song->title) free(song->title);
767 if(song->track) free(song->track);
768 if(song->name) free(song->name);
769 }
771 mpd_Song * mpd_newSong() {
772 mpd_Song * ret = malloc(sizeof(mpd_Song));
774 mpd_initSong(ret);
776 return ret;
777 }
779 void mpd_freeSong(mpd_Song * song) {
780 mpd_finishSong(song);
781 free(song);
782 }
784 mpd_Song * mpd_songDup(mpd_Song * song) {
785 mpd_Song * ret = mpd_newSong();
787 if(song->file) ret->file = strdup(song->file);
788 if(song->artist) ret->artist = strdup(song->artist);
789 if(song->album) ret->album = strdup(song->album);
790 if(song->title) ret->title = strdup(song->title);
791 if(song->track) ret->track = strdup(song->track);
792 if(song->name) ret->name = strdup(song->name);
793 ret->time = song->time;
794 ret->pos = song->pos;
795 ret->id = song->id;
797 return ret;
798 }
800 void mpd_initDirectory(mpd_Directory * directory) {
801 directory->path = NULL;
802 }
804 void mpd_finishDirectory(mpd_Directory * directory) {
805 if(directory->path) free(directory->path);
806 }
808 mpd_Directory * mpd_newDirectory () {
809 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
811 mpd_initDirectory(directory);
813 return directory;
814 }
816 void mpd_freeDirectory(mpd_Directory * directory) {
817 mpd_finishDirectory(directory);
819 free(directory);
820 }
822 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
823 mpd_Directory * ret = mpd_newDirectory();
825 if(directory->path) ret->path = strdup(directory->path);
827 return ret;
828 }
830 void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
831 playlist->path = NULL;
832 }
834 void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
835 if(playlist->path) free(playlist->path);
836 }
838 mpd_PlaylistFile * mpd_newPlaylistFile() {
839 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
841 mpd_initPlaylistFile(playlist);
843 return playlist;
844 }
846 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
847 mpd_finishPlaylistFile(playlist);
848 free(playlist);
849 }
851 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
852 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
854 if(playlist->path) ret->path = strdup(playlist->path);
856 return ret;
857 }
859 void mpd_initInfoEntity(mpd_InfoEntity * entity) {
860 entity->info.directory = NULL;
861 }
863 void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
864 if(entity->info.directory) {
865 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
866 mpd_freeDirectory(entity->info.directory);
867 }
868 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
869 mpd_freeSong(entity->info.song);
870 }
871 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
872 mpd_freePlaylistFile(entity->info.playlistFile);
873 }
874 }
875 }
877 mpd_InfoEntity * mpd_newInfoEntity() {
878 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
880 mpd_initInfoEntity(entity);
882 return entity;
883 }
885 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
886 mpd_finishInfoEntity(entity);
887 free(entity);
888 }
890 void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
891 mpd_executeCommand(connection,command);
892 }
894 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
895 mpd_InfoEntity * entity = NULL;
897 if(connection->doneProcessing || (connection->listOks &&
898 connection->doneListOk))
899 {
900 return NULL;
901 }
903 if(!connection->returnElement) mpd_getNextReturnElement(connection);
905 if(connection->returnElement) {
906 if(strcmp(connection->returnElement->name,"file")==0) {
907 entity = mpd_newInfoEntity();
908 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
909 entity->info.song = mpd_newSong();
910 entity->info.song->file =
911 strdup(connection->returnElement->value);
912 }
913 else if(strcmp(connection->returnElement->name,
914 "directory")==0) {
915 entity = mpd_newInfoEntity();
916 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
917 entity->info.directory = mpd_newDirectory();
918 entity->info.directory->path =
919 strdup(connection->returnElement->value);
920 }
921 else if(strcmp(connection->returnElement->name,"playlist")==0) {
922 entity = mpd_newInfoEntity();
923 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
924 entity->info.playlistFile = mpd_newPlaylistFile();
925 entity->info.playlistFile->path =
926 strdup(connection->returnElement->value);
927 }
928 else {
929 connection->error = 1;
930 strcpy(connection->errorStr,"problem parsing song info");
931 return NULL;
932 }
933 }
934 else return NULL;
936 mpd_getNextReturnElement(connection);
937 while(connection->returnElement) {
938 mpd_ReturnElement * re = connection->returnElement;
940 if(strcmp(re->name,"file")==0) return entity;
941 else if(strcmp(re->name,"directory")==0) return entity;
942 else if(strcmp(re->name,"playlist")==0) return entity;
944 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
945 strlen(re->value)) {
946 if(!entity->info.song->artist &&
947 strcmp(re->name,"Artist")==0) {
948 entity->info.song->artist = strdup(re->value);
949 }
950 else if(!entity->info.song->album &&
951 strcmp(re->name,"Album")==0) {
952 entity->info.song->album = strdup(re->value);
953 }
954 else if(!entity->info.song->title &&
955 strcmp(re->name,"Title")==0) {
956 entity->info.song->title = strdup(re->value);
957 }
958 else if(!entity->info.song->track &&
959 strcmp(re->name,"Track")==0) {
960 entity->info.song->track = strdup(re->value);
961 }
962 else if(!entity->info.song->name &&
963 strcmp(re->name,"Name")==0) {
964 entity->info.song->name = strdup(re->value);
965 }
966 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
967 strcmp(re->name,"Time")==0) {
968 entity->info.song->time = atoi(re->value);
969 }
970 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
971 strcmp(re->name,"Pos")==0) {
972 entity->info.song->pos = atoi(re->value);
973 }
974 else if(entity->info.song->id==MPD_SONG_NO_ID &&
975 strcmp(re->name,"Id")==0) {
976 entity->info.song->id = atoi(re->value);
977 }
978 }
979 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
980 }
981 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
982 }
984 mpd_getNextReturnElement(connection);
985 }
987 return entity;
988 }
990 char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
991 const char * name)
992 {
993 if(connection->doneProcessing || (connection->listOks &&
994 connection->doneListOk))
995 {
996 return NULL;
997 }
999 mpd_getNextReturnElement(connection);
1000 while(connection->returnElement) {
1001 mpd_ReturnElement * re = connection->returnElement;
1003 if(strcmp(re->name,name)==0) return strdup(re->value);
1004 mpd_getNextReturnElement(connection);
1005 }
1007 return NULL;
1008 }
1010 char * mpd_getNextArtist(mpd_Connection * connection) {
1011 return mpd_getNextReturnElementNamed(connection,"Artist");
1012 }
1014 char * mpd_getNextAlbum(mpd_Connection * connection) {
1015 return mpd_getNextReturnElementNamed(connection,"Album");
1016 }
1018 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1019 char * string = malloc(strlen("playlistinfo")+25);
1020 sprintf(string,"playlistinfo \"%i\"\n",songPos);
1021 mpd_sendInfoCommand(connection,string);
1022 free(string);
1023 }
1025 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1026 char * string = malloc(strlen("playlistid")+25);
1027 sprintf(string, "playlistid \"%i\"\n", id);
1028 mpd_sendInfoCommand(connection, string);
1029 free(string);
1030 }
1032 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1033 char * string = malloc(strlen("plchanges")+25);
1034 sprintf(string,"plchanges \"%lld\"\n",playlist);
1035 mpd_sendInfoCommand(connection,string);
1036 free(string);
1037 }
1039 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1040 char * sDir = mpd_sanitizeArg(dir);
1041 char * string = malloc(strlen("listall")+strlen(sDir)+5);
1042 sprintf(string,"listall \"%s\"\n",sDir);
1043 mpd_sendInfoCommand(connection,string);
1044 free(string);
1045 free(sDir);
1046 }
1048 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1049 char * sDir = mpd_sanitizeArg(dir);
1050 char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1051 sprintf(string,"listallinfo \"%s\"\n",sDir);
1052 mpd_sendInfoCommand(connection,string);
1053 free(string);
1054 free(sDir);
1055 }
1057 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1058 char * sDir = mpd_sanitizeArg(dir);
1059 char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1060 sprintf(string,"lsinfo \"%s\"\n",sDir);
1061 mpd_sendInfoCommand(connection,string);
1062 free(string);
1063 free(sDir);
1064 }
1066 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1067 mpd_executeCommand(connection,"currentsong\n");
1068 }
1070 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1071 const char * str)
1072 {
1073 char st[10];
1074 char * string;
1075 char * sanitStr = mpd_sanitizeArg(str);
1076 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1077 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1078 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1079 else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1080 else {
1081 connection->error = 1;
1082 strcpy(connection->errorStr,"unknown table for search");
1083 return;
1084 }
1085 string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1086 sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1087 mpd_sendInfoCommand(connection,string);
1088 free(string);
1089 free(sanitStr);
1090 }
1092 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1093 const char * str)
1094 {
1095 char st[10];
1096 char * string;
1097 char * sanitStr = mpd_sanitizeArg(str);
1098 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1099 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1100 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1101 else {
1102 connection->error = 1;
1103 strcpy(connection->errorStr,"unknown table for find");
1104 return;
1105 }
1106 string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1107 sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1108 mpd_sendInfoCommand(connection,string);
1109 free(string);
1110 free(sanitStr);
1111 }
1113 void mpd_sendListCommand(mpd_Connection * connection, int table,
1114 const char * arg1)
1115 {
1116 char st[10];
1117 char * string;
1118 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1119 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1120 else {
1121 connection->error = 1;
1122 strcpy(connection->errorStr,"unknown table for list");
1123 return;
1124 }
1125 if(arg1) {
1126 char * sanitArg1 = mpd_sanitizeArg(arg1);
1127 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1128 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1129 free(sanitArg1);
1130 }
1131 else {
1132 string = malloc(strlen("list")+strlen(st)+3);
1133 sprintf(string,"list %s\n",st);
1134 }
1135 mpd_sendInfoCommand(connection,string);
1136 free(string);
1137 }
1139 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1140 char * sFile = mpd_sanitizeArg(file);
1141 char * string = malloc(strlen("add")+strlen(sFile)+5);
1142 sprintf(string,"add \"%s\"\n",sFile);
1143 mpd_executeCommand(connection,string);
1144 free(string);
1145 free(sFile);
1146 }
1148 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1149 char * string = malloc(strlen("delete")+25);
1150 sprintf(string,"delete \"%i\"\n",songPos);
1151 mpd_sendInfoCommand(connection,string);
1152 free(string);
1153 }
1155 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1156 char * string = malloc(strlen("deleteid")+25);
1157 sprintf(string, "deleteid \"%i\"\n", id);
1158 mpd_sendInfoCommand(connection,string);
1159 free(string);
1160 }
1162 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1163 char * sName = mpd_sanitizeArg(name);
1164 char * string = malloc(strlen("save")+strlen(sName)+5);
1165 sprintf(string,"save \"%s\"\n",sName);
1166 mpd_executeCommand(connection,string);
1167 free(string);
1168 free(sName);
1169 }
1171 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1172 char * sName = mpd_sanitizeArg(name);
1173 char * string = malloc(strlen("load")+strlen(sName)+5);
1174 sprintf(string,"load \"%s\"\n",sName);
1175 mpd_executeCommand(connection,string);
1176 free(string);
1177 free(sName);
1178 }
1180 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1181 char * sName = mpd_sanitizeArg(name);
1182 char * string = malloc(strlen("rm")+strlen(sName)+5);
1183 sprintf(string,"rm \"%s\"\n",sName);
1184 mpd_executeCommand(connection,string);
1185 free(string);
1186 free(sName);
1187 }
1189 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1190 mpd_executeCommand(connection,"shuffle\n");
1191 }
1193 void mpd_sendClearCommand(mpd_Connection * connection) {
1194 mpd_executeCommand(connection,"clear\n");
1195 }
1197 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1198 char * string = malloc(strlen("play")+25);
1199 sprintf(string,"play \"%i\"\n",songPos);
1200 mpd_sendInfoCommand(connection,string);
1201 free(string);
1202 }
1204 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1205 char * string = malloc(strlen("playid")+25);
1206 sprintf(string,"playid \"%i\"\n",id);
1207 mpd_sendInfoCommand(connection,string);
1208 free(string);
1209 }
1211 void mpd_sendStopCommand(mpd_Connection * connection) {
1212 mpd_executeCommand(connection,"stop\n");
1213 }
1215 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1216 char * string = malloc(strlen("pause")+25);
1217 sprintf(string,"pause \"%i\"\n",pauseMode);
1218 mpd_executeCommand(connection,string);
1219 free(string);
1220 }
1222 void mpd_sendNextCommand(mpd_Connection * connection) {
1223 mpd_executeCommand(connection,"next\n");
1224 }
1226 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1227 char * string = malloc(strlen("move")+25);
1228 sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1229 mpd_sendInfoCommand(connection,string);
1230 free(string);
1231 }
1233 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1234 char * string = malloc(strlen("moveid")+25);
1235 sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1236 mpd_sendInfoCommand(connection,string);
1237 free(string);
1238 }
1240 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1241 char * string = malloc(strlen("swap")+25);
1242 sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1243 mpd_sendInfoCommand(connection,string);
1244 free(string);
1245 }
1247 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1248 char * string = malloc(strlen("swapid")+25);
1249 sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1250 mpd_sendInfoCommand(connection,string);
1251 free(string);
1252 }
1254 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1255 char * string = malloc(strlen("seek")+25);
1256 sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1257 mpd_sendInfoCommand(connection,string);
1258 free(string);
1259 }
1261 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1262 char * string = malloc(strlen("seekid")+25);
1263 sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1264 mpd_sendInfoCommand(connection,string);
1265 free(string);
1266 }
1268 void mpd_sendUpdateCommand(mpd_Connection * connection) {
1269 mpd_executeCommand(connection,"update\n");
1270 }
1272 int mpd_getUpdateId(mpd_Connection * connection) {
1273 char * jobid;
1274 int ret = 0;
1276 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1277 if(jobid) {
1278 ret = atoi(jobid);
1279 free(jobid);
1280 }
1282 return ret;
1283 }
1285 void mpd_sendPrevCommand(mpd_Connection * connection) {
1286 mpd_executeCommand(connection,"previous\n");
1287 }
1289 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1290 char * string = malloc(strlen("repeat")+25);
1291 sprintf(string,"repeat \"%i\"\n",repeatMode);
1292 mpd_executeCommand(connection,string);
1293 free(string);
1294 }
1296 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1297 char * string = malloc(strlen("random")+25);
1298 sprintf(string,"random \"%i\"\n",randomMode);
1299 mpd_executeCommand(connection,string);
1300 free(string);
1301 }
1303 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1304 char * string = malloc(strlen("setvol")+25);
1305 sprintf(string,"setvol \"%i\"\n",volumeChange);
1306 mpd_executeCommand(connection,string);
1307 free(string);
1308 }
1310 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1311 char * string = malloc(strlen("volume")+25);
1312 sprintf(string,"volume \"%i\"\n",volumeChange);
1313 mpd_executeCommand(connection,string);
1314 free(string);
1315 }
1317 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1318 char * string = malloc(strlen("crossfade")+25);
1319 sprintf(string,"crossfade \"%i\"\n",seconds);
1320 mpd_executeCommand(connection,string);
1321 free(string);
1322 }
1324 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1325 char * sPass = mpd_sanitizeArg(pass);
1326 char * string = malloc(strlen("password")+strlen(sPass)+5);
1327 sprintf(string,"password \"%s\"\n",sPass);
1328 mpd_executeCommand(connection,string);
1329 free(string);
1330 free(sPass);
1331 }
1333 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1334 if(connection->commandList) {
1335 strcpy(connection->errorStr,"already in command list mode");
1336 connection->error = 1;
1337 return;
1338 }
1339 connection->commandList = COMMAND_LIST;
1340 mpd_executeCommand(connection,"command_list_begin\n");
1341 }
1343 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1344 if(connection->commandList) {
1345 strcpy(connection->errorStr,"already in command list mode");
1346 connection->error = 1;
1347 return;
1348 }
1349 connection->commandList = COMMAND_LIST_OK;
1350 mpd_executeCommand(connection,"command_list_ok_begin\n");
1351 connection->listOks = 0;
1352 }
1354 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1355 if(!connection->commandList) {
1356 strcpy(connection->errorStr,"not in command list mode");
1357 connection->error = 1;
1358 return;
1359 }
1360 connection->commandList = 0;
1361 mpd_executeCommand(connection,"command_list_end\n");
1362 }