27bd9ad525fec8849c3d9469fcf538df317f0a2d
1 /* libmpdclient
2 (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
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.
31 */
33 #include "libmpdclient.h"
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <stdio.h>
38 #include <sys/param.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <fcntl.h>
44 #ifdef WIN32
45 # include <ws2tcpip.h>
46 # include <winsock.h>
47 #else
48 # include <netinet/in.h>
49 # include <arpa/inet.h>
50 # include <sys/socket.h>
51 # include <netdb.h>
52 #endif
54 #ifndef MSG_DONTWAIT
55 # define MSG_DONTWAIT 0
56 #endif
58 #ifndef MPD_NO_GAI
59 # ifdef AI_ADDRCONFIG
60 # define MPD_HAVE_GAI
61 # endif
62 #endif
64 #define COMMAND_LIST 1
65 #define COMMAND_LIST_OK 2
67 #ifdef WIN32
68 # define SELECT_ERRNO_IGNORE (errno == WSAEINTR || errno == WSAEINPROGRESS)
69 # define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
70 #else
71 # define SELECT_ERRNO_IGNORE (errno == EINTR)
72 # define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
73 # define winsock_dll_error(c) 0
74 # define closesocket(s) close(s)
75 # define WSACleanup() do { /* nothing */ } while (0)
76 #endif
78 #ifdef WIN32
79 static int winsock_dll_error(mpd_Connection *connection)
80 {
81 WSADATA wsaData;
82 if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
83 LOBYTE(wsaData.wVersion) != 2 ||
84 HIBYTE(wsaData.wVersion) != 2 ) {
85 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
86 "Could not find usable WinSock DLL.");
87 connection->error = MPD_ERROR_SYSTEM;
88 return 1;
89 }
90 return 0;
91 }
93 static int do_connect_fail(mpd_Connection *connection,
94 const struct sockaddr *serv_addr, int addrlen)
95 {
96 int iMode = 1; /* 0 = blocking, else non-blocking */
97 ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
98 return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR
99 && WSAGetLastError() != WSAEWOULDBLOCK);
100 }
101 #else /* !WIN32 (sane operating systems) */
102 static int do_connect_fail(mpd_Connection *connection,
103 const struct sockaddr *serv_addr, int addrlen)
104 {
105 int flags = fcntl(connection->sock, F_GETFL, 0);
106 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
107 return (connect(connection->sock,serv_addr,addrlen)<0 &&
108 errno!=EINPROGRESS);
109 }
110 #endif /* !WIN32 */
112 #ifdef MPD_HAVE_GAI
113 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
114 float timeout)
115 {
116 int error;
117 char service[20];
118 struct addrinfo hints;
119 struct addrinfo *res = NULL;
120 struct addrinfo *addrinfo = NULL;
122 /**
123 * Setup hints
124 */
125 hints.ai_flags = AI_ADDRCONFIG;
126 hints.ai_family = PF_UNSPEC;
127 hints.ai_socktype = SOCK_STREAM;
128 hints.ai_protocol = IPPROTO_TCP;
129 hints.ai_addrlen = 0;
130 hints.ai_addr = NULL;
131 hints.ai_canonname = NULL;
132 hints.ai_next = NULL;
134 snprintf(service, sizeof(service), "%d", port);
136 error = getaddrinfo(host, service, &hints, &addrinfo);
138 if (error) {
139 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
140 "host \"%s\" not found: %s",host, gai_strerror(error));
141 connection->error = MPD_ERROR_UNKHOST;
142 return -1;
143 }
145 for (res = addrinfo; res; res = res->ai_next) {
146 /* create socket */
147 connection->sock = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
148 if (connection->sock < 0) {
149 snprintf(connection->errorStr, MPD_BUFFER_MAX_LENGTH,
150 "problems creating socket: %s",
151 strerror(errno));
152 connection->error = MPD_ERROR_SYSTEM;
153 freeaddrinfo(addrinfo);
154 return -1;
155 }
157 mpd_setConnectionTimeout(connection,timeout);
159 /* connect stuff */
160 if (do_connect_fail(connection, res->ai_addr, res->ai_addrlen)) {
161 /* try the next address family */
162 closesocket(connection->sock);
163 connection->sock = -1;
164 continue;
165 }
166 }
167 freeaddrinfo(addrinfo);
169 if (connection->sock < 0) {
170 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
171 "problems connecting to \"%s\" on port"
172 " %i: %s",host,port, strerror(errno));
173 connection->error = MPD_ERROR_CONNPORT;
175 return -1;
176 }
178 return 0;
179 }
180 #else /* !MPD_HAVE_GAI */
181 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
182 float timeout)
183 {
184 struct hostent * he;
185 struct sockaddr * dest;
186 int destlen;
187 struct sockaddr_in sin;
189 if(!(he=gethostbyname(host))) {
190 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
191 "host \"%s\" not found",host);
192 connection->error = MPD_ERROR_UNKHOST;
193 return -1;
194 }
196 memset(&sin,0,sizeof(struct sockaddr_in));
197 /*dest.sin_family = he->h_addrtype;*/
198 sin.sin_family = AF_INET;
199 sin.sin_port = htons(port);
201 switch(he->h_addrtype) {
202 case AF_INET:
203 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
204 he->h_length);
205 dest = (struct sockaddr *)&sin;
206 destlen = sizeof(struct sockaddr_in);
207 break;
208 default:
209 strcpy(connection->errorStr,"address type is not IPv4\n");
210 connection->error = MPD_ERROR_SYSTEM;
211 return -1;
212 break;
213 }
215 if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
216 strcpy(connection->errorStr,"problems creating socket");
217 connection->error = MPD_ERROR_SYSTEM;
218 return -1;
219 }
221 mpd_setConnectionTimeout(connection,timeout);
223 /* connect stuff */
224 if (do_connect_fail(connection, dest, destlen)) {
225 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
226 "problems connecting to \"%s\" on port"
227 " %i",host,port);
228 connection->error = MPD_ERROR_CONNPORT;
229 return -1;
230 }
232 return 0;
233 }
234 #endif /* !MPD_HAVE_GAI */
236 char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
237 {
238 "Artist",
239 "Album",
240 "Title",
241 "Track",
242 "Name",
243 "Genre",
244 "Date",
245 "Composer",
246 "Performer",
247 "Comment",
248 "Disc",
249 "filename"
250 };
252 static char * mpd_sanitizeArg(const char * arg) {
253 size_t i;
254 char * ret;
255 register const char *c;
256 register char *rc;
258 /* instead of counting in that loop above, just
259 * use a bit more memory and half running time
260 */
261 ret = malloc(strlen(arg) * 2 + 1);
263 c = arg;
264 rc = ret;
265 for(i = strlen(arg)+1; i != 0; --i) {
266 if(*c=='"' || *c=='\\')
267 *rc++ = '\\';
268 *(rc++) = *(c++);
269 }
271 return ret;
272 }
274 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
275 {
276 mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
278 ret->name = strdup(name);
279 ret->value = strdup(value);
281 return ret;
282 }
284 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
285 free(re->name);
286 free(re->value);
287 free(re);
288 }
290 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
291 connection->timeout.tv_sec = (int)timeout;
292 connection->timeout.tv_usec = (int)(timeout*1e6 -
293 connection->timeout.tv_sec*1000000 +
294 0.5);
295 }
297 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
298 char * rt, char * output) {
299 char * tmp;
300 char * test;
301 int i;
303 if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
304 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
305 "mpd not running on port %i on host \"%s\"",
306 port,host);
307 connection->error = MPD_ERROR_NOTMPD;
308 return 1;
309 }
311 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
313 for(i=0;i<3;i++) {
314 if(tmp) connection->version[i] = strtol(tmp,&test,10);
316 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
317 snprintf(connection->errorStr,
318 MPD_BUFFER_MAX_LENGTH,
319 "error parsing version number at "
320 "\"%s\"",
321 &output[strlen(MPD_WELCOME_MESSAGE)]);
322 connection->error = MPD_ERROR_NOTMPD;
323 return 1;
324 }
325 tmp = ++test;
326 }
328 return 0;
329 }
331 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
332 int err;
333 char * rt;
334 char * output = NULL;
335 mpd_Connection * connection = malloc(sizeof(mpd_Connection));
336 struct timeval tv;
337 fd_set fds;
338 strcpy(connection->buffer,"");
339 connection->buflen = 0;
340 connection->bufstart = 0;
341 strcpy(connection->errorStr,"");
342 connection->error = 0;
343 connection->doneProcessing = 0;
344 connection->commandList = 0;
345 connection->listOks = 0;
346 connection->doneListOk = 0;
347 connection->returnElement = NULL;
348 connection->request = NULL;
350 if (winsock_dll_error(connection))
351 return connection;
353 if (mpd_connect(connection, host, port, timeout) < 0)
354 return connection;
356 while(!(rt = strstr(connection->buffer,"\n"))) {
357 tv.tv_sec = connection->timeout.tv_sec;
358 tv.tv_usec = connection->timeout.tv_usec;
359 FD_ZERO(&fds);
360 FD_SET(connection->sock,&fds);
361 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
362 int readed;
363 readed = recv(connection->sock,
364 &(connection->buffer[connection->buflen]),
365 MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
366 if(readed<=0) {
367 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
368 "problems getting a response from"
369 " \"%s\" on port %i : %s",host,
370 port, strerror(errno));
371 connection->error = MPD_ERROR_NORESPONSE;
372 return connection;
373 }
374 connection->buflen+=readed;
375 connection->buffer[connection->buflen] = '\0';
376 }
377 else if(err<0) {
378 if (SELECT_ERRNO_IGNORE)
379 continue;
380 snprintf(connection->errorStr,
381 MPD_BUFFER_MAX_LENGTH,
382 "problems connecting to \"%s\" on port"
383 " %i",host,port);
384 connection->error = MPD_ERROR_CONNPORT;
385 return connection;
386 }
387 else {
388 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
389 "timeout in attempting to get a response from"
390 " \"%s\" on port %i",host,port);
391 connection->error = MPD_ERROR_NORESPONSE;
392 return connection;
393 }
394 }
396 *rt = '\0';
397 output = strdup(connection->buffer);
398 strcpy(connection->buffer,rt+1);
399 connection->buflen = strlen(connection->buffer);
401 if(mpd_parseWelcome(connection,host,port,rt,output) == 0) connection->doneProcessing = 1;
403 free(output);
405 return connection;
406 }
408 void mpd_clearError(mpd_Connection * connection) {
409 connection->error = 0;
410 connection->errorStr[0] = '\0';
411 }
413 void mpd_closeConnection(mpd_Connection * connection) {
414 closesocket(connection->sock);
415 if(connection->returnElement) free(connection->returnElement);
416 if(connection->request) free(connection->request);
417 free(connection);
418 WSACleanup();
419 }
421 static void mpd_executeCommand(mpd_Connection * connection, char * command) {
422 int ret;
423 struct timeval tv;
424 fd_set fds;
425 char * commandPtr = command;
426 int commandLen = strlen(command);
428 if(!connection->doneProcessing && !connection->commandList) {
429 strcpy(connection->errorStr,"not done processing current command");
430 connection->error = 1;
431 return;
432 }
434 mpd_clearError(connection);
436 FD_ZERO(&fds);
437 FD_SET(connection->sock,&fds);
438 tv.tv_sec = connection->timeout.tv_sec;
439 tv.tv_usec = connection->timeout.tv_usec;
441 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
442 (ret==-1 && SELECT_ERRNO_IGNORE)) {
443 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
444 if(ret<=0)
445 {
446 if (SENDRECV_ERRNO_IGNORE) continue;
447 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
448 "problems giving command \"%s\"",command);
449 connection->error = MPD_ERROR_SENDING;
450 return;
451 }
452 else {
453 commandPtr+=ret;
454 commandLen-=ret;
455 }
457 if(commandLen<=0) break;
458 }
460 if(commandLen>0) {
461 perror("");
462 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
463 "timeout sending command \"%s\"",command);
464 connection->error = MPD_ERROR_TIMEOUT;
465 return;
466 }
468 if(!connection->commandList) connection->doneProcessing = 0;
469 else if(connection->commandList == COMMAND_LIST_OK) {
470 connection->listOks++;
471 }
472 }
474 static void mpd_getNextReturnElement(mpd_Connection * connection) {
475 char * output = NULL;
476 char * rt = NULL;
477 char * name = NULL;
478 char * value = NULL;
479 fd_set fds;
480 struct timeval tv;
481 char * tok = NULL;
482 int readed;
483 char * bufferCheck = NULL;
484 int err;
485 int pos;
487 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
488 connection->returnElement = NULL;
490 if(connection->doneProcessing || (connection->listOks &&
491 connection->doneListOk))
492 {
493 strcpy(connection->errorStr,"already done processing current command");
494 connection->error = 1;
495 return;
496 }
498 bufferCheck = connection->buffer+connection->bufstart;
499 while(connection->bufstart>=connection->buflen ||
500 !(rt = strchr(bufferCheck,'\n'))) {
501 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
502 memmove(connection->buffer,
503 connection->buffer+
504 connection->bufstart,
505 connection->buflen-
506 connection->bufstart+1);
507 connection->buflen-=connection->bufstart;
508 connection->bufstart = 0;
509 }
510 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
511 strcpy(connection->errorStr,"buffer overrun");
512 connection->error = MPD_ERROR_BUFFEROVERRUN;
513 connection->doneProcessing = 1;
514 connection->doneListOk = 0;
515 return;
516 }
517 bufferCheck = connection->buffer+connection->buflen;
518 tv.tv_sec = connection->timeout.tv_sec;
519 tv.tv_usec = connection->timeout.tv_usec;
520 FD_ZERO(&fds);
521 FD_SET(connection->sock,&fds);
522 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
523 readed = recv(connection->sock,
524 connection->buffer+connection->buflen,
525 MPD_BUFFER_MAX_LENGTH-connection->buflen,
526 MSG_DONTWAIT);
527 if(readed<0 && SENDRECV_ERRNO_IGNORE) {
528 continue;
529 }
530 if(readed<=0) {
531 strcpy(connection->errorStr,"connection"
532 " closed");
533 connection->error = MPD_ERROR_CONNCLOSED;
534 connection->doneProcessing = 1;
535 connection->doneListOk = 0;
536 return;
537 }
538 connection->buflen+=readed;
539 connection->buffer[connection->buflen] = '\0';
540 }
541 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
542 else {
543 strcpy(connection->errorStr,"connection timeout");
544 connection->error = MPD_ERROR_TIMEOUT;
545 connection->doneProcessing = 1;
546 connection->doneListOk = 0;
547 return;
548 }
549 }
551 *rt = '\0';
552 output = connection->buffer+connection->bufstart;
553 connection->bufstart = rt - connection->buffer + 1;
555 if(strcmp(output,"OK")==0) {
556 if(connection->listOks > 0) {
557 strcpy(connection->errorStr, "expected more list_OK's");
558 connection->error = 1;
559 }
560 connection->listOks = 0;
561 connection->doneProcessing = 1;
562 connection->doneListOk = 0;
563 return;
564 }
566 if(strcmp(output, "list_OK") == 0) {
567 if(!connection->listOks) {
568 strcpy(connection->errorStr,
569 "got an unexpected list_OK");
570 connection->error = 1;
571 }
572 else {
573 connection->doneListOk = 1;
574 connection->listOks--;
575 }
576 return;
577 }
579 if(strncmp(output,"ACK",strlen("ACK"))==0) {
580 char * test;
581 char * needle;
582 int val;
584 strcpy(connection->errorStr, output);
585 connection->error = MPD_ERROR_ACK;
586 connection->errorCode = MPD_ACK_ERROR_UNK;
587 connection->errorAt = MPD_ERROR_AT_UNK;
588 connection->doneProcessing = 1;
589 connection->doneListOk = 0;
591 needle = strchr(output, '[');
592 if(!needle) return;
593 val = strtol(needle+1, &test, 10);
594 if(*test != '@') return;
595 connection->errorCode = val;
596 val = strtol(test+1, &test, 10);
597 if(*test != ']') return;
598 connection->errorAt = val;
599 return;
600 }
602 tok = strchr(output, ':');
603 if (!tok) return;
604 pos = tok - output;
605 value = ++tok;
606 name = output;
607 name[pos] = '\0';
609 if(value[0]==' ') {
610 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
611 }
612 else {
613 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
614 "error parsing: %s:%s",name,value);
615 connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0';
616 connection->error = 1;
617 }
618 }
620 void mpd_finishCommand(mpd_Connection * connection) {
621 while(!connection->doneProcessing) {
622 if(connection->doneListOk) connection->doneListOk = 0;
623 mpd_getNextReturnElement(connection);
624 }
625 }
627 static void mpd_finishListOkCommand(mpd_Connection * connection) {
628 while(!connection->doneProcessing && connection->listOks &&
629 !connection->doneListOk)
630 {
631 mpd_getNextReturnElement(connection);
632 }
633 }
635 int mpd_nextListOkCommand(mpd_Connection * connection) {
636 mpd_finishListOkCommand(connection);
637 if(!connection->doneProcessing) connection->doneListOk = 0;
638 if(connection->listOks == 0 || connection->doneProcessing) return -1;
639 return 0;
640 }
642 void mpd_sendStatusCommand(mpd_Connection * connection) {
643 mpd_executeCommand(connection,"status\n");
644 }
646 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
647 mpd_Status * status;
649 /*mpd_executeCommand(connection,"status\n");
651 if(connection->error) return NULL;*/
653 if(connection->doneProcessing || (connection->listOks &&
654 connection->doneListOk))
655 {
656 return NULL;
657 }
659 if(!connection->returnElement) mpd_getNextReturnElement(connection);
661 status = malloc(sizeof(mpd_Status));
662 status->volume = -1;
663 status->repeat = 0;
664 status->random = 0;
665 status->playlist = -1;
666 status->playlistLength = -1;
667 status->state = -1;
668 status->song = 0;
669 status->songid = 0;
670 status->elapsedTime = 0;
671 status->totalTime = 0;
672 status->bitRate = 0;
673 status->sampleRate = 0;
674 status->bits = 0;
675 status->channels = 0;
676 status->crossfade = -1;
677 status->error = NULL;
678 status->updatingDb = 0;
680 if(connection->error) {
681 free(status);
682 return NULL;
683 }
684 while(connection->returnElement) {
685 mpd_ReturnElement * re = connection->returnElement;
686 if(strcmp(re->name,"volume")==0) {
687 status->volume = atoi(re->value);
688 }
689 else if(strcmp(re->name,"repeat")==0) {
690 status->repeat = atoi(re->value);
691 }
692 else if(strcmp(re->name,"random")==0) {
693 status->random = atoi(re->value);
694 }
695 else if(strcmp(re->name,"playlist")==0) {
696 status->playlist = strtol(re->value,NULL,10);
697 }
698 else if(strcmp(re->name,"playlistlength")==0) {
699 status->playlistLength = atoi(re->value);
700 }
701 else if(strcmp(re->name,"bitrate")==0) {
702 status->bitRate = atoi(re->value);
703 }
704 else if(strcmp(re->name,"state")==0) {
705 if(strcmp(re->value,"play")==0) {
706 status->state = MPD_STATUS_STATE_PLAY;
707 }
708 else if(strcmp(re->value,"stop")==0) {
709 status->state = MPD_STATUS_STATE_STOP;
710 }
711 else if(strcmp(re->value,"pause")==0) {
712 status->state = MPD_STATUS_STATE_PAUSE;
713 }
714 else {
715 status->state = MPD_STATUS_STATE_UNKNOWN;
716 }
717 }
718 else if(strcmp(re->name,"song")==0) {
719 status->song = atoi(re->value);
720 }
721 else if(strcmp(re->name,"songid")==0) {
722 status->songid = atoi(re->value);
723 }
724 else if(strcmp(re->name,"time")==0) {
725 char * tok = strchr(re->value,':');
726 /* the second strchr below is a safety check */
727 if (tok && (strchr(tok,0) > (tok+1))) {
728 /* atoi stops at the first non-[0-9] char: */
729 status->elapsedTime = atoi(re->value);
730 status->totalTime = atoi(tok+1);
731 }
732 }
733 else if(strcmp(re->name,"error")==0) {
734 status->error = strdup(re->value);
735 }
736 else if(strcmp(re->name,"xfade")==0) {
737 status->crossfade = atoi(re->value);
738 }
739 else if(strcmp(re->name,"updating_db")==0) {
740 status->updatingDb = atoi(re->value);
741 }
742 else if(strcmp(re->name,"audio")==0) {
743 char * tok = strchr(re->value,':');
744 if (tok && (strchr(tok,0) > (tok+1))) {
745 status->sampleRate = atoi(re->value);
746 status->bits = atoi(++tok);
747 tok = strchr(tok,':');
748 if (tok && (strchr(tok,0) > (tok+1)))
749 status->channels = atoi(tok+1);
750 }
751 }
753 mpd_getNextReturnElement(connection);
754 if(connection->error) {
755 free(status);
756 return NULL;
757 }
758 }
760 if(connection->error) {
761 free(status);
762 return NULL;
763 }
764 else if(status->state<0) {
765 strcpy(connection->errorStr,"state not found");
766 connection->error = 1;
767 free(status);
768 return NULL;
769 }
771 return status;
772 }
774 void mpd_freeStatus(mpd_Status * status) {
775 if(status->error) free(status->error);
776 free(status);
777 }
779 void mpd_sendStatsCommand(mpd_Connection * connection) {
780 mpd_executeCommand(connection,"stats\n");
781 }
783 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
784 mpd_Stats * stats;
786 /*mpd_executeCommand(connection,"stats\n");
788 if(connection->error) return NULL;*/
790 if(connection->doneProcessing || (connection->listOks &&
791 connection->doneListOk))
792 {
793 return NULL;
794 }
796 if(!connection->returnElement) mpd_getNextReturnElement(connection);
798 stats = malloc(sizeof(mpd_Stats));
799 stats->numberOfArtists = 0;
800 stats->numberOfAlbums = 0;
801 stats->numberOfSongs = 0;
802 stats->uptime = 0;
803 stats->dbUpdateTime = 0;
804 stats->playTime = 0;
805 stats->dbPlayTime = 0;
807 if(connection->error) {
808 free(stats);
809 return NULL;
810 }
811 while(connection->returnElement) {
812 mpd_ReturnElement * re = connection->returnElement;
813 if(strcmp(re->name,"artists")==0) {
814 stats->numberOfArtists = atoi(re->value);
815 }
816 else if(strcmp(re->name,"albums")==0) {
817 stats->numberOfAlbums = atoi(re->value);
818 }
819 else if(strcmp(re->name,"songs")==0) {
820 stats->numberOfSongs = atoi(re->value);
821 }
822 else if(strcmp(re->name,"uptime")==0) {
823 stats->uptime = strtol(re->value,NULL,10);
824 }
825 else if(strcmp(re->name,"db_update")==0) {
826 stats->dbUpdateTime = strtol(re->value,NULL,10);
827 }
828 else if(strcmp(re->name,"playtime")==0) {
829 stats->playTime = strtol(re->value,NULL,10);
830 }
831 else if(strcmp(re->name,"db_playtime")==0) {
832 stats->dbPlayTime = strtol(re->value,NULL,10);
833 }
835 mpd_getNextReturnElement(connection);
836 if(connection->error) {
837 free(stats);
838 return NULL;
839 }
840 }
842 if(connection->error) {
843 free(stats);
844 return NULL;
845 }
847 return stats;
848 }
850 void mpd_freeStats(mpd_Stats * stats) {
851 free(stats);
852 }
854 static void mpd_initSong(mpd_Song * song) {
855 song->file = NULL;
856 song->artist = NULL;
857 song->album = NULL;
858 song->track = NULL;
859 song->title = NULL;
860 song->name = NULL;
861 song->date = NULL;
862 /* added by Qball */
863 song->genre = NULL;
864 song->composer = NULL;
865 song->disc = NULL;
866 song->comment = NULL;
868 song->time = MPD_SONG_NO_TIME;
869 song->pos = MPD_SONG_NO_NUM;
870 song->id = MPD_SONG_NO_ID;
871 }
873 static void mpd_finishSong(mpd_Song * song) {
874 if(song->file) free(song->file);
875 if(song->artist) free(song->artist);
876 if(song->album) free(song->album);
877 if(song->title) free(song->title);
878 if(song->track) free(song->track);
879 if(song->name) free(song->name);
880 if(song->date) free(song->date);
881 if(song->genre) free(song->genre);
882 if(song->composer) free(song->composer);
883 if(song->disc) free(song->disc);
884 if(song->comment) free(song->comment);
885 }
887 mpd_Song * mpd_newSong(void) {
888 mpd_Song * ret = malloc(sizeof(mpd_Song));
890 mpd_initSong(ret);
892 return ret;
893 }
895 void mpd_freeSong(mpd_Song * song) {
896 mpd_finishSong(song);
897 free(song);
898 }
900 mpd_Song * mpd_songDup(mpd_Song * song) {
901 mpd_Song * ret = mpd_newSong();
903 if(song->file) ret->file = strdup(song->file);
904 if(song->artist) ret->artist = strdup(song->artist);
905 if(song->album) ret->album = strdup(song->album);
906 if(song->title) ret->title = strdup(song->title);
907 if(song->track) ret->track = strdup(song->track);
908 if(song->name) ret->name = strdup(song->name);
909 if(song->date) ret->date = strdup(song->date);
910 if(song->genre) ret->genre= strdup(song->genre);
911 if(song->composer) ret->composer= strdup(song->composer);
912 if(song->disc) ret->disc = strdup(song->disc);
913 if(song->comment) ret->comment = strdup(song->comment);
914 ret->time = song->time;
915 ret->pos = song->pos;
916 ret->id = song->id;
918 return ret;
919 }
921 static void mpd_initDirectory(mpd_Directory * directory) {
922 directory->path = NULL;
923 }
925 static void mpd_finishDirectory(mpd_Directory * directory) {
926 if(directory->path) free(directory->path);
927 }
929 mpd_Directory * mpd_newDirectory(void) {
930 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
932 mpd_initDirectory(directory);
934 return directory;
935 }
937 void mpd_freeDirectory(mpd_Directory * directory) {
938 mpd_finishDirectory(directory);
940 free(directory);
941 }
943 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
944 mpd_Directory * ret = mpd_newDirectory();
946 if(directory->path) ret->path = strdup(directory->path);
948 return ret;
949 }
951 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
952 playlist->path = NULL;
953 }
955 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
956 if(playlist->path) free(playlist->path);
957 }
959 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
960 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
962 mpd_initPlaylistFile(playlist);
964 return playlist;
965 }
967 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
968 mpd_finishPlaylistFile(playlist);
969 free(playlist);
970 }
972 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
973 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
975 if(playlist->path) ret->path = strdup(playlist->path);
977 return ret;
978 }
980 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
981 entity->info.directory = NULL;
982 }
984 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
985 if(entity->info.directory) {
986 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
987 mpd_freeDirectory(entity->info.directory);
988 }
989 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
990 mpd_freeSong(entity->info.song);
991 }
992 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
993 mpd_freePlaylistFile(entity->info.playlistFile);
994 }
995 }
996 }
998 mpd_InfoEntity * mpd_newInfoEntity(void) {
999 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
1001 mpd_initInfoEntity(entity);
1003 return entity;
1004 }
1006 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1007 mpd_finishInfoEntity(entity);
1008 free(entity);
1009 }
1011 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1012 mpd_executeCommand(connection,command);
1013 }
1015 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1016 mpd_InfoEntity * entity = NULL;
1018 if(connection->doneProcessing || (connection->listOks &&
1019 connection->doneListOk))
1020 {
1021 return NULL;
1022 }
1024 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1026 if(connection->returnElement) {
1027 if(strcmp(connection->returnElement->name,"file")==0) {
1028 entity = mpd_newInfoEntity();
1029 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1030 entity->info.song = mpd_newSong();
1031 entity->info.song->file =
1032 strdup(connection->returnElement->value);
1033 }
1034 else if(strcmp(connection->returnElement->name,
1035 "directory")==0) {
1036 entity = mpd_newInfoEntity();
1037 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1038 entity->info.directory = mpd_newDirectory();
1039 entity->info.directory->path =
1040 strdup(connection->returnElement->value);
1041 }
1042 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1043 entity = mpd_newInfoEntity();
1044 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1045 entity->info.playlistFile = mpd_newPlaylistFile();
1046 entity->info.playlistFile->path =
1047 strdup(connection->returnElement->value);
1048 }
1049 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1050 entity = mpd_newInfoEntity();
1051 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1052 entity->info.song = mpd_newSong();
1053 entity->info.song->pos = atoi(connection->returnElement->value);
1054 }
1055 else {
1056 connection->error = 1;
1057 strcpy(connection->errorStr,"problem parsing song info");
1058 return NULL;
1059 }
1060 }
1061 else return NULL;
1063 mpd_getNextReturnElement(connection);
1064 while(connection->returnElement) {
1065 mpd_ReturnElement * re = connection->returnElement;
1067 if(strcmp(re->name,"file")==0) return entity;
1068 else if(strcmp(re->name,"directory")==0) return entity;
1069 else if(strcmp(re->name,"playlist")==0) return entity;
1070 else if(strcmp(re->name,"cpos")==0) return entity;
1072 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1073 strlen(re->value)) {
1074 if(!entity->info.song->artist &&
1075 strcmp(re->name,"Artist")==0) {
1076 entity->info.song->artist = strdup(re->value);
1077 }
1078 else if(!entity->info.song->album &&
1079 strcmp(re->name,"Album")==0) {
1080 entity->info.song->album = strdup(re->value);
1081 }
1082 else if(!entity->info.song->title &&
1083 strcmp(re->name,"Title")==0) {
1084 entity->info.song->title = strdup(re->value);
1085 }
1086 else if(!entity->info.song->track &&
1087 strcmp(re->name,"Track")==0) {
1088 entity->info.song->track = strdup(re->value);
1089 }
1090 else if(!entity->info.song->name &&
1091 strcmp(re->name,"Name")==0) {
1092 entity->info.song->name = strdup(re->value);
1093 }
1094 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1095 strcmp(re->name,"Time")==0) {
1096 entity->info.song->time = atoi(re->value);
1097 }
1098 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1099 strcmp(re->name,"Pos")==0) {
1100 entity->info.song->pos = atoi(re->value);
1101 }
1102 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1103 strcmp(re->name,"Id")==0) {
1104 entity->info.song->id = atoi(re->value);
1105 }
1106 else if(!entity->info.song->date &&
1107 strcmp(re->name, "Date") == 0) {
1108 entity->info.song->date = strdup(re->value);
1109 }
1110 else if(!entity->info.song->genre &&
1111 strcmp(re->name, "Genre") == 0) {
1112 entity->info.song->genre = strdup(re->value);
1113 }
1114 else if(!entity->info.song->composer &&
1115 strcmp(re->name, "Composer") == 0) {
1116 entity->info.song->composer = strdup(re->value);
1117 }
1118 else if(!entity->info.song->disc &&
1119 strcmp(re->name, "Disc") == 0) {
1120 entity->info.song->disc = strdup(re->value);
1121 }
1122 else if(!entity->info.song->comment &&
1123 strcmp(re->name, "Comment") == 0) {
1124 entity->info.song->comment = strdup(re->value);
1125 }
1126 }
1127 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1128 }
1129 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1130 }
1132 mpd_getNextReturnElement(connection);
1133 }
1135 return entity;
1136 }
1138 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1139 const char * name)
1140 {
1141 if(connection->doneProcessing || (connection->listOks &&
1142 connection->doneListOk))
1143 {
1144 return NULL;
1145 }
1147 mpd_getNextReturnElement(connection);
1148 while(connection->returnElement) {
1149 mpd_ReturnElement * re = connection->returnElement;
1151 if(strcmp(re->name,name)==0) return strdup(re->value);
1152 mpd_getNextReturnElement(connection);
1153 }
1155 return NULL;
1156 }
1158 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1159 if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1160 {
1161 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1162 }
1163 return NULL;
1164 }
1166 char * mpd_getNextArtist(mpd_Connection * connection) {
1167 return mpd_getNextReturnElementNamed(connection,"Artist");
1168 }
1170 char * mpd_getNextAlbum(mpd_Connection * connection) {
1171 return mpd_getNextReturnElementNamed(connection,"Album");
1172 }
1174 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1175 char * string = malloc(strlen("playlistinfo")+25);
1176 sprintf(string,"playlistinfo \"%i\"\n",songPos);
1177 mpd_sendInfoCommand(connection,string);
1178 free(string);
1179 }
1181 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1182 char * string = malloc(strlen("playlistid")+25);
1183 sprintf(string, "playlistid \"%i\"\n", id);
1184 mpd_sendInfoCommand(connection, string);
1185 free(string);
1186 }
1188 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1189 char * string = malloc(strlen("plchanges")+25);
1190 sprintf(string,"plchanges \"%lld\"\n",playlist);
1191 mpd_sendInfoCommand(connection,string);
1192 free(string);
1193 }
1195 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1196 char * string = malloc(strlen("plchangesposid")+25);
1197 sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1198 mpd_sendInfoCommand(connection,string);
1199 free(string);
1200 }
1202 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1203 char * sDir = mpd_sanitizeArg(dir);
1204 char * string = malloc(strlen("listall")+strlen(sDir)+5);
1205 sprintf(string,"listall \"%s\"\n",sDir);
1206 mpd_sendInfoCommand(connection,string);
1207 free(string);
1208 free(sDir);
1209 }
1211 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1212 char * sDir = mpd_sanitizeArg(dir);
1213 char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1214 sprintf(string,"listallinfo \"%s\"\n",sDir);
1215 mpd_sendInfoCommand(connection,string);
1216 free(string);
1217 free(sDir);
1218 }
1220 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1221 char * sDir = mpd_sanitizeArg(dir);
1222 char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1223 sprintf(string,"lsinfo \"%s\"\n",sDir);
1224 mpd_sendInfoCommand(connection,string);
1225 free(string);
1226 free(sDir);
1227 }
1229 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1230 mpd_executeCommand(connection,"currentsong\n");
1231 }
1233 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1234 const char * str)
1235 {
1236 char st[10];
1237 char * string;
1238 char * sanitStr = mpd_sanitizeArg(str);
1239 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1240 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1241 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1242 else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1243 else {
1244 connection->error = 1;
1245 strcpy(connection->errorStr,"unknown table for search");
1246 return;
1247 }
1248 string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1249 sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1250 mpd_sendInfoCommand(connection,string);
1251 free(string);
1252 free(sanitStr);
1253 }
1255 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1256 const char * str)
1257 {
1258 char st[10];
1259 char * string;
1260 char * sanitStr = mpd_sanitizeArg(str);
1261 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1262 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1263 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1264 else {
1265 connection->error = 1;
1266 strcpy(connection->errorStr,"unknown table for find");
1267 return;
1268 }
1269 string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1270 sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1271 mpd_sendInfoCommand(connection,string);
1272 free(string);
1273 free(sanitStr);
1274 }
1276 void mpd_sendListCommand(mpd_Connection * connection, int table,
1277 const char * arg1)
1278 {
1279 char st[10];
1280 char * string;
1281 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1282 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1283 else {
1284 connection->error = 1;
1285 strcpy(connection->errorStr,"unknown table for list");
1286 return;
1287 }
1288 if(arg1) {
1289 char * sanitArg1 = mpd_sanitizeArg(arg1);
1290 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1291 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1292 free(sanitArg1);
1293 }
1294 else {
1295 string = malloc(strlen("list")+strlen(st)+3);
1296 sprintf(string,"list %s\n",st);
1297 }
1298 mpd_sendInfoCommand(connection,string);
1299 free(string);
1300 }
1302 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1303 char * sFile = mpd_sanitizeArg(file);
1304 char * string = malloc(strlen("add")+strlen(sFile)+5);
1305 sprintf(string,"add \"%s\"\n",sFile);
1306 mpd_executeCommand(connection,string);
1307 free(string);
1308 free(sFile);
1309 }
1311 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1312 char * string = malloc(strlen("delete")+25);
1313 sprintf(string,"delete \"%i\"\n",songPos);
1314 mpd_sendInfoCommand(connection,string);
1315 free(string);
1316 }
1318 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1319 char * string = malloc(strlen("deleteid")+25);
1320 sprintf(string, "deleteid \"%i\"\n", id);
1321 mpd_sendInfoCommand(connection,string);
1322 free(string);
1323 }
1325 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1326 char * sName = mpd_sanitizeArg(name);
1327 char * string = malloc(strlen("save")+strlen(sName)+5);
1328 sprintf(string,"save \"%s\"\n",sName);
1329 mpd_executeCommand(connection,string);
1330 free(string);
1331 free(sName);
1332 }
1334 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1335 char * sName = mpd_sanitizeArg(name);
1336 char * string = malloc(strlen("load")+strlen(sName)+5);
1337 sprintf(string,"load \"%s\"\n",sName);
1338 mpd_executeCommand(connection,string);
1339 free(string);
1340 free(sName);
1341 }
1343 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1344 char * sName = mpd_sanitizeArg(name);
1345 char * string = malloc(strlen("rm")+strlen(sName)+5);
1346 sprintf(string,"rm \"%s\"\n",sName);
1347 mpd_executeCommand(connection,string);
1348 free(string);
1349 free(sName);
1350 }
1352 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1353 mpd_executeCommand(connection,"shuffle\n");
1354 }
1356 void mpd_sendClearCommand(mpd_Connection * connection) {
1357 mpd_executeCommand(connection,"clear\n");
1358 }
1360 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1361 char * string = malloc(strlen("play")+25);
1362 sprintf(string,"play \"%i\"\n",songPos);
1363 mpd_sendInfoCommand(connection,string);
1364 free(string);
1365 }
1367 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1368 char * string = malloc(strlen("playid")+25);
1369 sprintf(string,"playid \"%i\"\n",id);
1370 mpd_sendInfoCommand(connection,string);
1371 free(string);
1372 }
1374 void mpd_sendStopCommand(mpd_Connection * connection) {
1375 mpd_executeCommand(connection,"stop\n");
1376 }
1378 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1379 char * string = malloc(strlen("pause")+25);
1380 sprintf(string,"pause \"%i\"\n",pauseMode);
1381 mpd_executeCommand(connection,string);
1382 free(string);
1383 }
1385 void mpd_sendNextCommand(mpd_Connection * connection) {
1386 mpd_executeCommand(connection,"next\n");
1387 }
1389 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1390 char * string = malloc(strlen("move")+25);
1391 sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1392 mpd_sendInfoCommand(connection,string);
1393 free(string);
1394 }
1396 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1397 char * string = malloc(strlen("moveid")+25);
1398 sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1399 mpd_sendInfoCommand(connection,string);
1400 free(string);
1401 }
1403 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1404 char * string = malloc(strlen("swap")+25);
1405 sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1406 mpd_sendInfoCommand(connection,string);
1407 free(string);
1408 }
1410 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1411 char * string = malloc(strlen("swapid")+25);
1412 sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1413 mpd_sendInfoCommand(connection,string);
1414 free(string);
1415 }
1417 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1418 char * string = malloc(strlen("seek")+25);
1419 sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1420 mpd_sendInfoCommand(connection,string);
1421 free(string);
1422 }
1424 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1425 char * string = malloc(strlen("seekid")+25);
1426 sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1427 mpd_sendInfoCommand(connection,string);
1428 free(string);
1429 }
1431 void mpd_sendUpdateCommand(mpd_Connection * connection, char * path) {
1432 char * sPath = mpd_sanitizeArg(path);
1433 char * string = malloc(strlen("update")+strlen(sPath)+5);
1434 sprintf(string,"update \"%s\"\n",sPath);
1435 mpd_sendInfoCommand(connection,string);
1436 free(string);
1437 free(sPath);
1438 }
1440 int mpd_getUpdateId(mpd_Connection * connection) {
1441 char * jobid;
1442 int ret = 0;
1444 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1445 if(jobid) {
1446 ret = atoi(jobid);
1447 free(jobid);
1448 }
1450 return ret;
1451 }
1453 void mpd_sendPrevCommand(mpd_Connection * connection) {
1454 mpd_executeCommand(connection,"previous\n");
1455 }
1457 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1458 char * string = malloc(strlen("repeat")+25);
1459 sprintf(string,"repeat \"%i\"\n",repeatMode);
1460 mpd_executeCommand(connection,string);
1461 free(string);
1462 }
1464 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1465 char * string = malloc(strlen("random")+25);
1466 sprintf(string,"random \"%i\"\n",randomMode);
1467 mpd_executeCommand(connection,string);
1468 free(string);
1469 }
1471 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1472 char * string = malloc(strlen("setvol")+25);
1473 sprintf(string,"setvol \"%i\"\n",volumeChange);
1474 mpd_executeCommand(connection,string);
1475 free(string);
1476 }
1478 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1479 char * string = malloc(strlen("volume")+25);
1480 sprintf(string,"volume \"%i\"\n",volumeChange);
1481 mpd_executeCommand(connection,string);
1482 free(string);
1483 }
1485 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1486 char * string = malloc(strlen("crossfade")+25);
1487 sprintf(string,"crossfade \"%i\"\n",seconds);
1488 mpd_executeCommand(connection,string);
1489 free(string);
1490 }
1492 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1493 char * sPass = mpd_sanitizeArg(pass);
1494 char * string = malloc(strlen("password")+strlen(sPass)+5);
1495 sprintf(string,"password \"%s\"\n",sPass);
1496 mpd_executeCommand(connection,string);
1497 free(string);
1498 free(sPass);
1499 }
1501 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1502 if(connection->commandList) {
1503 strcpy(connection->errorStr,"already in command list mode");
1504 connection->error = 1;
1505 return;
1506 }
1507 connection->commandList = COMMAND_LIST;
1508 mpd_executeCommand(connection,"command_list_begin\n");
1509 }
1511 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1512 if(connection->commandList) {
1513 strcpy(connection->errorStr,"already in command list mode");
1514 connection->error = 1;
1515 return;
1516 }
1517 connection->commandList = COMMAND_LIST_OK;
1518 mpd_executeCommand(connection,"command_list_ok_begin\n");
1519 connection->listOks = 0;
1520 }
1522 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1523 if(!connection->commandList) {
1524 strcpy(connection->errorStr,"not in command list mode");
1525 connection->error = 1;
1526 return;
1527 }
1528 connection->commandList = 0;
1529 mpd_executeCommand(connection,"command_list_end\n");
1530 }
1532 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1533 mpd_executeCommand(connection,"outputs\n");
1534 }
1536 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1537 mpd_OutputEntity * output = NULL;
1539 if(connection->doneProcessing || (connection->listOks &&
1540 connection->doneListOk))
1541 {
1542 return NULL;
1543 }
1545 if(connection->error) return NULL;
1547 output = malloc(sizeof(mpd_OutputEntity));
1548 output->id = -10;
1549 output->name = NULL;
1550 output->enabled = 0;
1552 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1554 while(connection->returnElement) {
1555 mpd_ReturnElement * re = connection->returnElement;
1556 if(strcmp(re->name,"outputid")==0) {
1557 if(output!=NULL && output->id>=0) return output;
1558 output->id = atoi(re->value);
1559 }
1560 else if(strcmp(re->name,"outputname")==0) {
1561 output->name = strdup(re->value);
1562 }
1563 else if(strcmp(re->name,"outputenabled")==0) {
1564 output->enabled = atoi(re->value);
1565 }
1567 mpd_getNextReturnElement(connection);
1568 if(connection->error) {
1569 free(output);
1570 return NULL;
1571 }
1573 }
1575 return output;
1576 }
1578 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1579 char * string = malloc(strlen("enableoutput")+25);
1580 sprintf(string,"enableoutput \"%i\"\n",outputId);
1581 mpd_executeCommand(connection,string);
1582 free(string);
1583 }
1585 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1586 char * string = malloc(strlen("disableoutput")+25);
1587 sprintf(string,"disableoutput \"%i\"\n",outputId);
1588 mpd_executeCommand(connection,string);
1589 free(string);
1590 }
1592 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1593 free(output->name);
1594 free(output);
1595 }
1597 /**
1598 * mpd_sendNotCommandsCommand
1599 * odd naming, but it gets the not allowed commands
1600 */
1602 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1603 mpd_executeCommand(connection,"notcommands\n");
1604 }
1606 /**
1607 * mpd_sendCommandsCommand
1608 * odd naming, but it gets the allowed commands
1609 */
1611 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1612 mpd_executeCommand(connection,"commands\n");
1613 }
1614 /**
1615 * Get the next returned command
1616 */
1617 char * mpd_getNextCommand(mpd_Connection * connection) {
1618 return mpd_getNextReturnElementNamed(connection,"command");
1619 }
1621 void mpd_startSearch(mpd_Connection * connection,int exact) {
1622 if(connection->request) {
1623 /* search/find allready in progress */
1624 /* TODO: set error here? */
1625 return;
1626 }
1627 if(exact){
1628 connection->request = strdup("find");
1629 }
1630 else{
1631 connection->request = strdup("search");
1632 }
1633 }
1636 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1637 if(connection->request) {
1638 /* search/find allready in progress */
1639 /* TODO: set error here? */
1640 return;
1641 }
1642 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1643 /* set error here */
1644 return;
1645 }
1647 connection->request = malloc(sizeof(char)*(
1648 /* length of the field name */
1649 strlen(mpdTagItemKeys[field])+
1650 /* "list"+space+\0 */
1651 6
1652 ));
1653 sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1654 }
1658 void mpd_addConstraintSearch(mpd_Connection *connection,
1659 int field,
1660 char *name)
1661 {
1662 char *arg = NULL;
1663 if(!connection->request){
1664 return;
1665 }
1666 if(name == NULL) {
1667 return;
1668 }
1669 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1670 return;
1671 }
1672 /* clean up the query */
1673 arg = mpd_sanitizeArg(name);
1674 /* create space for the query */
1675 connection->request = realloc(connection->request, (
1676 /* length of the old string */
1677 strlen(connection->request)+
1678 /* space between */
1679 1+
1680 /* length of the field name */
1681 strlen(mpdTagItemKeys[field])+
1682 /* space plus starting " */
1683 2+
1684 /* length of search term */
1685 strlen(arg)+
1686 /* closign " +\0 that is added sprintf */
1687 2
1688 )*sizeof(char));
1689 /* and form the query */
1690 sprintf(connection->request, "%s %s \"%s\"",
1691 connection->request,
1692 mpdTagItemKeys[field],
1693 arg);
1694 free(arg);
1695 }
1698 void mpd_commitSearch(mpd_Connection *connection)
1699 {
1700 if(connection->request)
1701 {
1702 int length = strlen(connection->request);
1703 /* fixing up the string for mpd to like */
1704 connection->request = realloc(connection->request,
1705 (length+ /* old length */
1706 2 /* closing \n and \0 */
1707 )*sizeof(char));
1708 connection->request[length] = '\n';
1709 connection->request[length+1] = '\0';
1710 /* and off we go */
1711 mpd_sendInfoCommand(connection, connection->request);
1712 /* clean up a bit */
1713 free(connection->request);
1714 connection->request = NULL;
1715 }
1716 }
1718 /**
1719 * @param connection a MpdConnection
1720 * @param path the path to the playlist.
1721 *
1722 * List the content, with full metadata, of a stored playlist.
1723 *
1724 */
1725 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1726 {
1727 char *arg = mpd_sanitizeArg(path);
1728 char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1729 sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1730 mpd_sendInfoCommand(connection, query);
1731 free(arg);
1732 free(query);
1733 }
1735 /**
1736 * @param connection a MpdConnection
1737 * @param path the path to the playlist.
1738 *
1739 * List the content of a stored playlist.
1740 *
1741 */
1742 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1743 {
1744 char *arg = mpd_sanitizeArg(path);
1745 char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1746 sprintf(query, "listplaylist \"%s\"\n",arg);
1747 mpd_sendInfoCommand(connection, query);
1748 free(arg);
1749 free(query);
1750 }