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