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"
34 #include "str_pool.h"
36 #include <assert.h>
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <stdio.h>
40 #include <sys/param.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
46 #ifdef WIN32
47 # include <ws2tcpip.h>
48 # include <winsock.h>
49 #else
50 # include <netinet/in.h>
51 # include <arpa/inet.h>
52 # include <sys/socket.h>
53 # include <netdb.h>
54 #endif
56 #ifndef WIN32
57 #include <sys/un.h>
58 #endif
60 #ifndef MSG_DONTWAIT
61 # define MSG_DONTWAIT 0
62 #endif
64 #ifndef MPD_NO_GAI
65 # ifdef AI_ADDRCONFIG
66 # define MPD_HAVE_GAI
67 # endif
68 #endif
70 #define COMMAND_LIST 1
71 #define COMMAND_LIST_OK 2
73 #ifdef WIN32
74 # define SELECT_ERRNO_IGNORE (errno == WSAEINTR || errno == WSAEINPROGRESS)
75 # define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
76 #else
77 # define SELECT_ERRNO_IGNORE (errno == EINTR)
78 # define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
79 # define winsock_dll_error(c) 0
80 # define closesocket(s) close(s)
81 # define WSACleanup() do { /* nothing */ } while (0)
82 #endif
84 #ifdef WIN32
85 static int winsock_dll_error(mpd_Connection *connection)
86 {
87 WSADATA wsaData;
88 if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
89 LOBYTE(wsaData.wVersion) != 2 ||
90 HIBYTE(wsaData.wVersion) != 2 ) {
91 snprintf(connection->errorStr, sizeof(connection->errorStr),
92 "Could not find usable WinSock DLL.");
93 connection->error = MPD_ERROR_SYSTEM;
94 return 1;
95 }
96 return 0;
97 }
99 static int do_connect_fail(mpd_Connection *connection,
100 const struct sockaddr *serv_addr, int addrlen)
101 {
102 int iMode = 1; /* 0 = blocking, else non-blocking */
103 ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
104 return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR
105 && WSAGetLastError() != WSAEWOULDBLOCK);
106 }
107 #else /* !WIN32 (sane operating systems) */
108 static int do_connect_fail(mpd_Connection *connection,
109 const struct sockaddr *serv_addr, int addrlen)
110 {
111 int flags = fcntl(connection->sock, F_GETFL, 0);
112 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
113 return (connect(connection->sock,serv_addr,addrlen)<0 &&
114 errno!=EINPROGRESS);
115 }
116 #endif /* !WIN32 */
118 #ifdef MPD_HAVE_GAI
119 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
120 float timeout)
121 {
122 int error;
123 char service[20];
124 struct addrinfo hints;
125 struct addrinfo *res = NULL;
126 struct addrinfo *addrinfo = NULL;
128 /**
129 * Setup hints
130 */
131 hints.ai_flags = AI_ADDRCONFIG;
132 hints.ai_family = PF_UNSPEC;
133 hints.ai_socktype = SOCK_STREAM;
134 hints.ai_protocol = IPPROTO_TCP;
135 hints.ai_addrlen = 0;
136 hints.ai_addr = NULL;
137 hints.ai_canonname = NULL;
138 hints.ai_next = NULL;
140 snprintf(service, sizeof(service), "%d", port);
142 error = getaddrinfo(host, service, &hints, &addrinfo);
144 if (error) {
145 snprintf(connection->errorStr, sizeof(connection->errorStr),
146 "host \"%s\" not found: %s",host, gai_strerror(error));
147 connection->error = MPD_ERROR_UNKHOST;
148 return -1;
149 }
151 for (res = addrinfo; res; res = res->ai_next) {
152 /* create socket */
153 connection->sock = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
154 if (connection->sock < 0) {
155 snprintf(connection->errorStr, sizeof(connection->errorStr),
156 "problems creating socket: %s",
157 strerror(errno));
158 connection->error = MPD_ERROR_SYSTEM;
159 freeaddrinfo(addrinfo);
160 return -1;
161 }
163 mpd_setConnectionTimeout(connection,timeout);
165 /* connect stuff */
166 if (do_connect_fail(connection, res->ai_addr, res->ai_addrlen)) {
167 /* try the next address family */
168 closesocket(connection->sock);
169 connection->sock = -1;
170 continue;
171 }
172 }
173 freeaddrinfo(addrinfo);
175 if (connection->sock < 0) {
176 snprintf(connection->errorStr, sizeof(connection->errorStr),
177 "problems connecting to \"%s\" on port"
178 " %i: %s",host,port, strerror(errno));
179 connection->error = MPD_ERROR_CONNPORT;
181 return -1;
182 }
184 return 0;
185 }
186 #else /* !MPD_HAVE_GAI */
187 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
188 float timeout)
189 {
190 struct hostent * he;
191 struct sockaddr * dest;
192 int destlen;
193 struct sockaddr_in sin;
195 if(!(he=gethostbyname(host))) {
196 snprintf(connection->errorStr, sizeof(connection->errorStr),
197 "host \"%s\" not found",host);
198 connection->error = MPD_ERROR_UNKHOST;
199 return -1;
200 }
202 memset(&sin,0,sizeof(struct sockaddr_in));
203 /*dest.sin_family = he->h_addrtype;*/
204 sin.sin_family = AF_INET;
205 sin.sin_port = htons(port);
207 switch(he->h_addrtype) {
208 case AF_INET:
209 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
210 he->h_length);
211 dest = (struct sockaddr *)&sin;
212 destlen = sizeof(struct sockaddr_in);
213 break;
214 default:
215 strcpy(connection->errorStr,"address type is not IPv4\n");
216 connection->error = MPD_ERROR_SYSTEM;
217 return -1;
218 break;
219 }
221 if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
222 strcpy(connection->errorStr,"problems creating socket");
223 connection->error = MPD_ERROR_SYSTEM;
224 return -1;
225 }
227 mpd_setConnectionTimeout(connection,timeout);
229 /* connect stuff */
230 if (do_connect_fail(connection, dest, destlen)) {
231 snprintf(connection->errorStr, sizeof(connection->errorStr),
232 "problems connecting to \"%s\" on port"
233 " %i",host,port);
234 connection->error = MPD_ERROR_CONNPORT;
235 return -1;
236 }
238 return 0;
239 }
240 #endif /* !MPD_HAVE_GAI */
242 const char *const mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
243 {
244 "Artist",
245 "Album",
246 "Title",
247 "Track",
248 "Name",
249 "Genre",
250 "Date",
251 "Composer",
252 "Performer",
253 "Comment",
254 "Disc",
255 "filename"
256 };
258 static char * mpd_sanitizeArg(const char * arg) {
259 size_t i;
260 char * ret;
261 register const char *c;
262 register char *rc;
264 /* instead of counting in that loop above, just
265 * use a bit more memory and half running time
266 */
267 ret = malloc(strlen(arg) * 2 + 1);
269 c = arg;
270 rc = ret;
271 for(i = strlen(arg)+1; i != 0; --i) {
272 if(*c=='"' || *c=='\\')
273 *rc++ = '\\';
274 *(rc++) = *(c++);
275 }
277 return ret;
278 }
280 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
281 {
282 mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
284 ret->name = str_pool_get(name);
285 ret->value = str_pool_get(value);
287 return ret;
288 }
290 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
291 str_pool_put(re->name);
292 str_pool_put(re->value);
293 free(re);
294 }
296 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
297 connection->timeout.tv_sec = (int)timeout;
298 connection->timeout.tv_usec = (int)(timeout*1e6 -
299 connection->timeout.tv_sec*1000000 +
300 0.5);
301 }
303 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
304 char * output) {
305 char * tmp;
306 char * test;
307 int i;
309 if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
310 snprintf(connection->errorStr, sizeof(connection->errorStr),
311 "mpd not running on port %i on host \"%s\"",
312 port,host);
313 connection->error = MPD_ERROR_NOTMPD;
314 return 1;
315 }
317 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
319 for(i=0;i<3;i++) {
320 if(tmp) connection->version[i] = strtol(tmp,&test,10);
322 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
323 snprintf(connection->errorStr, sizeof(connection->errorStr),
324 "error parsing version number at "
325 "\"%s\"",
326 &output[strlen(MPD_WELCOME_MESSAGE)]);
327 connection->error = MPD_ERROR_NOTMPD;
328 return 1;
329 }
330 tmp = ++test;
331 }
333 return 0;
334 }
336 #ifndef WIN32
337 static int mpd_connect_un(mpd_Connection * connection,
338 const char * host, float timeout)
339 {
340 int error, flags;
341 size_t path_length;
342 struct sockaddr_un sun;
344 path_length = strlen(host);
345 if (path_length >= sizeof(sun.sun_path)) {
346 strcpy(connection->errorStr, "unix socket path is too long");
347 connection->error = MPD_ERROR_UNKHOST;
348 return -1;
349 }
351 sun.sun_family = AF_UNIX;
352 memcpy(sun.sun_path, host, path_length + 1);
354 connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
355 if (connection->sock < 0) {
356 strcpy(connection->errorStr, "problems creating socket");
357 connection->error = MPD_ERROR_SYSTEM;
358 return -1;
359 }
361 mpd_setConnectionTimeout(connection, timeout);
363 flags = fcntl(connection->sock, F_GETFL, 0);
364 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
366 error = connect(connection->sock, (struct sockaddr*)&sun, sizeof(sun));
367 if (error < 0) {
368 /* try the next address family */
369 close(connection->sock);
370 connection->sock = 0;
372 snprintf(connection->errorStr, sizeof(connection->errorStr),
373 "problems connecting to \"%s\": %s",
374 host, strerror(errno));
375 connection->error = MPD_ERROR_CONNPORT;
376 return -1;
377 }
379 return 0;
380 }
381 #endif /* WIN32 */
383 /**
384 * Wait for the socket to become readable.
385 */
386 static int mpd_wait(mpd_Connection *connection)
387 {
388 struct timeval tv;
389 fd_set fds;
390 int ret;
392 while (1) {
393 tv = connection->timeout;
394 FD_ZERO(&fds);
395 FD_SET(connection->sock, &fds);
397 ret = select(connection->sock + 1, &fds, NULL, NULL, &tv);
398 if (ret > 0)
399 return 0;
401 if (ret == 0 || !SELECT_ERRNO_IGNORE)
402 return -1;
403 }
404 }
406 /**
407 * Wait until the socket is connected and check its result. Returns 1
408 * on success, 0 on timeout, -errno on error.
409 */
410 static int mpd_wait_connected(mpd_Connection *connection)
411 {
412 int ret;
413 int s_err = 0;
414 socklen_t s_err_size = sizeof(s_err);
416 ret = mpd_wait(connection);
417 if (ret < 0)
418 return 0;
420 ret = getsockopt(connection->sock, SOL_SOCKET, SO_ERROR,
421 (char*)&s_err, &s_err_size);
422 if (ret < 0)
423 return -errno;
425 if (s_err != 0)
426 return -s_err;
428 return 1;
429 }
431 /**
432 * Attempt to read data from the socket into the input buffer.
433 * Returns 0 on success, -1 on error.
434 */
435 static int mpd_recv(mpd_Connection *connection)
436 {
437 int ret;
438 ssize_t nbytes;
440 assert(connection != NULL);
441 assert(connection->buflen <= sizeof(connection->buffer));
442 assert(connection->bufstart <= connection->buflen);
444 if (connection->buflen >= sizeof(connection->buffer)) {
445 /* delete consumed data from beginning of buffer */
446 connection->buflen -= connection->bufstart;
447 memmove(connection->buffer,
448 connection->buffer + connection->bufstart,
449 connection->buflen);
450 connection->bufstart = 0;
451 }
453 if (connection->buflen >= sizeof(connection->buffer)) {
454 strcpy(connection->errorStr, "buffer overrun");
455 connection->error = MPD_ERROR_BUFFEROVERRUN;
456 connection->doneProcessing = 1;
457 connection->doneListOk = 0;
458 return -1;
459 }
461 while (1) {
462 ret = mpd_wait(connection);
463 if (ret < 0) {
464 strcpy(connection->errorStr, "connection timeout");
465 connection->error = MPD_ERROR_TIMEOUT;
466 connection->doneProcessing = 1;
467 connection->doneListOk = 0;
468 return -1;
469 }
471 nbytes = read(connection->sock,
472 connection->buffer + connection->buflen,
473 sizeof(connection->buffer) - connection->buflen);
474 if (nbytes > 0) {
475 connection->buflen += nbytes;
476 return 0;
477 }
479 if (nbytes == 0 || !SENDRECV_ERRNO_IGNORE) {
480 strcpy(connection->errorStr, "connection closed");
481 connection->error = MPD_ERROR_CONNCLOSED;
482 connection->doneProcessing = 1;
483 connection->doneListOk = 0;
484 return -1;
485 }
486 }
487 }
489 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
490 int err;
491 char * rt;
492 mpd_Connection * connection = malloc(sizeof(mpd_Connection));
494 connection->buflen = 0;
495 connection->bufstart = 0;
496 mpd_clearError(connection);
497 connection->doneProcessing = 0;
498 connection->commandList = 0;
499 connection->listOks = 0;
500 connection->doneListOk = 0;
501 connection->returnElement = NULL;
502 connection->request = NULL;
504 if (winsock_dll_error(connection))
505 return connection;
507 #ifndef WIN32
508 if (host[0] == '/')
509 err = mpd_connect_un(connection, host, timeout);
510 else
511 #endif
512 err = mpd_connect(connection, host, port, timeout);
513 if (err < 0)
514 return connection;
516 err = mpd_wait_connected(connection);
517 if (err == 0) {
518 snprintf(connection->errorStr, sizeof(connection->errorStr),
519 "timeout in attempting to get a response from"
520 " \"%s\" on port %i",host,port);
521 connection->error = MPD_ERROR_NORESPONSE;
522 return connection;
523 } else if (err < 0) {
524 snprintf(connection->errorStr,
525 sizeof(connection->errorStr),
526 "problems connecting to \"%s\" on port %i: %s",
527 host, port, strerror(-err));
528 connection->error = MPD_ERROR_CONNPORT;
529 return connection;
530 }
532 while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
533 err = mpd_recv(connection);
534 if (err < 0)
535 return connection;
536 }
538 *rt = '\0';
539 if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
540 connection->doneProcessing = 1;
542 connection->buflen -= rt + 1 - connection->buffer;
543 memmove(connection->buffer, rt + 1, connection->buflen);
545 return connection;
546 }
548 void mpd_clearError(mpd_Connection * connection) {
549 connection->error = 0;
550 connection->errorStr[0] = '\0';
551 }
553 void mpd_closeConnection(mpd_Connection * connection) {
554 closesocket(connection->sock);
555 if(connection->returnElement) free(connection->returnElement);
556 if(connection->request) free(connection->request);
557 free(connection);
558 WSACleanup();
559 }
561 static void mpd_executeCommand(mpd_Connection *connection,
562 const char *command) {
563 int ret;
564 struct timeval tv;
565 fd_set fds;
566 const char *commandPtr = command;
567 int commandLen = strlen(command);
569 if (!connection->doneProcessing && !connection->commandList) {
570 strcpy(connection->errorStr,
571 "not done processing current command");
572 connection->error = 1;
573 return;
574 }
576 mpd_clearError(connection);
578 FD_ZERO(&fds);
579 FD_SET(connection->sock,&fds);
580 tv.tv_sec = connection->timeout.tv_sec;
581 tv.tv_usec = connection->timeout.tv_usec;
583 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
584 (ret==-1 && SELECT_ERRNO_IGNORE)) {
585 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
586 if(ret<=0)
587 {
588 if (SENDRECV_ERRNO_IGNORE) continue;
589 snprintf(connection->errorStr, sizeof(connection->errorStr),
590 "problems giving command \"%s\"",command);
591 connection->error = MPD_ERROR_SENDING;
592 return;
593 }
594 else {
595 commandPtr+=ret;
596 commandLen-=ret;
597 }
599 if(commandLen<=0) break;
600 }
602 if(commandLen>0) {
603 perror("");
604 snprintf(connection->errorStr, sizeof(connection->errorStr),
605 "timeout sending command \"%s\"",command);
606 connection->error = MPD_ERROR_TIMEOUT;
607 return;
608 }
610 if(!connection->commandList) connection->doneProcessing = 0;
611 else if(connection->commandList == COMMAND_LIST_OK) {
612 connection->listOks++;
613 }
614 }
616 static void mpd_getNextReturnElement(mpd_Connection * connection) {
617 char * output = NULL;
618 char * rt = NULL;
619 char * name = NULL;
620 char * value = NULL;
621 char * tok = NULL;
622 int err;
623 int pos;
625 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
626 connection->returnElement = NULL;
628 if (connection->doneProcessing ||
629 (connection->listOks && connection->doneListOk)) {
630 strcpy(connection->errorStr,"already done processing current command");
631 connection->error = 1;
632 return;
633 }
635 while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
636 connection->buflen - connection->bufstart))) {
637 err = mpd_recv(connection);
638 if (err < 0)
639 return;
640 }
642 *rt = '\0';
643 output = connection->buffer+connection->bufstart;
644 connection->bufstart = rt - connection->buffer + 1;
646 if(strcmp(output,"OK")==0) {
647 if(connection->listOks > 0) {
648 strcpy(connection->errorStr, "expected more list_OK's");
649 connection->error = 1;
650 }
651 connection->listOks = 0;
652 connection->doneProcessing = 1;
653 connection->doneListOk = 0;
654 return;
655 }
657 if(strcmp(output, "list_OK") == 0) {
658 if(!connection->listOks) {
659 strcpy(connection->errorStr,
660 "got an unexpected list_OK");
661 connection->error = 1;
662 }
663 else {
664 connection->doneListOk = 1;
665 connection->listOks--;
666 }
667 return;
668 }
670 if(strncmp(output,"ACK",strlen("ACK"))==0) {
671 size_t length = strlen(output);
672 char * test;
673 char * needle;
674 int val;
676 if (length >= sizeof(connection->errorStr))
677 length = sizeof(connection->errorStr) - 1;
679 memcpy(connection->errorStr, output, length);
680 connection->errorStr[length] = 0;
681 connection->error = MPD_ERROR_ACK;
682 connection->errorCode = MPD_ACK_ERROR_UNK;
683 connection->errorAt = MPD_ERROR_AT_UNK;
684 connection->doneProcessing = 1;
685 connection->doneListOk = 0;
687 needle = strchr(output, '[');
688 if(!needle) return;
689 val = strtol(needle+1, &test, 10);
690 if(*test != '@') return;
691 connection->errorCode = val;
692 val = strtol(test+1, &test, 10);
693 if(*test != ']') return;
694 connection->errorAt = val;
695 return;
696 }
698 tok = strchr(output, ':');
699 if (!tok) return;
700 pos = tok - output;
701 value = ++tok;
702 name = output;
703 name[pos] = '\0';
705 if(value[0]==' ') {
706 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
707 }
708 else {
709 snprintf(connection->errorStr, sizeof(connection->errorStr),
710 "error parsing: %s:%s",name,value);
711 connection->error = 1;
712 }
713 }
715 void mpd_finishCommand(mpd_Connection * connection) {
716 while(!connection->doneProcessing) {
717 if(connection->doneListOk) connection->doneListOk = 0;
718 mpd_getNextReturnElement(connection);
719 }
720 }
722 static void mpd_finishListOkCommand(mpd_Connection * connection) {
723 while(!connection->doneProcessing && connection->listOks &&
724 !connection->doneListOk)
725 {
726 mpd_getNextReturnElement(connection);
727 }
728 }
730 int mpd_nextListOkCommand(mpd_Connection * connection) {
731 mpd_finishListOkCommand(connection);
732 if(!connection->doneProcessing) connection->doneListOk = 0;
733 if(connection->listOks == 0 || connection->doneProcessing) return -1;
734 return 0;
735 }
737 void mpd_sendStatusCommand(mpd_Connection * connection) {
738 mpd_executeCommand(connection,"status\n");
739 }
741 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
742 mpd_Status * status;
744 /*mpd_executeCommand(connection,"status\n");
746 if(connection->error) return NULL;*/
748 if(connection->doneProcessing || (connection->listOks &&
749 connection->doneListOk))
750 {
751 return NULL;
752 }
754 if(!connection->returnElement) mpd_getNextReturnElement(connection);
756 status = malloc(sizeof(mpd_Status));
757 status->volume = -1;
758 status->repeat = 0;
759 status->random = 0;
760 status->playlist = -1;
761 status->playlistLength = -1;
762 status->state = -1;
763 status->song = 0;
764 status->songid = 0;
765 status->elapsedTime = 0;
766 status->totalTime = 0;
767 status->bitRate = 0;
768 status->sampleRate = 0;
769 status->bits = 0;
770 status->channels = 0;
771 status->crossfade = -1;
772 status->error = NULL;
773 status->updatingDb = 0;
775 if(connection->error) {
776 free(status);
777 return NULL;
778 }
779 while(connection->returnElement) {
780 mpd_ReturnElement * re = connection->returnElement;
781 if(strcmp(re->name,"volume")==0) {
782 status->volume = atoi(re->value);
783 }
784 else if(strcmp(re->name,"repeat")==0) {
785 status->repeat = atoi(re->value);
786 }
787 else if(strcmp(re->name,"random")==0) {
788 status->random = atoi(re->value);
789 }
790 else if(strcmp(re->name,"playlist")==0) {
791 status->playlist = strtol(re->value,NULL,10);
792 }
793 else if(strcmp(re->name,"playlistlength")==0) {
794 status->playlistLength = atoi(re->value);
795 }
796 else if(strcmp(re->name,"bitrate")==0) {
797 status->bitRate = atoi(re->value);
798 }
799 else if(strcmp(re->name,"state")==0) {
800 if(strcmp(re->value,"play")==0) {
801 status->state = MPD_STATUS_STATE_PLAY;
802 }
803 else if(strcmp(re->value,"stop")==0) {
804 status->state = MPD_STATUS_STATE_STOP;
805 }
806 else if(strcmp(re->value,"pause")==0) {
807 status->state = MPD_STATUS_STATE_PAUSE;
808 }
809 else {
810 status->state = MPD_STATUS_STATE_UNKNOWN;
811 }
812 }
813 else if(strcmp(re->name,"song")==0) {
814 status->song = atoi(re->value);
815 }
816 else if(strcmp(re->name,"songid")==0) {
817 status->songid = atoi(re->value);
818 }
819 else if(strcmp(re->name,"time")==0) {
820 char * tok = strchr(re->value,':');
821 /* the second strchr below is a safety check */
822 if (tok && (strchr(tok,0) > (tok+1))) {
823 /* atoi stops at the first non-[0-9] char: */
824 status->elapsedTime = atoi(re->value);
825 status->totalTime = atoi(tok+1);
826 }
827 }
828 else if(strcmp(re->name,"error")==0) {
829 status->error = strdup(re->value);
830 }
831 else if(strcmp(re->name,"xfade")==0) {
832 status->crossfade = atoi(re->value);
833 }
834 else if(strcmp(re->name,"updating_db")==0) {
835 status->updatingDb = atoi(re->value);
836 }
837 else if(strcmp(re->name,"audio")==0) {
838 char * tok = strchr(re->value,':');
839 if (tok && (strchr(tok,0) > (tok+1))) {
840 status->sampleRate = atoi(re->value);
841 status->bits = atoi(++tok);
842 tok = strchr(tok,':');
843 if (tok && (strchr(tok,0) > (tok+1)))
844 status->channels = atoi(tok+1);
845 }
846 }
848 mpd_getNextReturnElement(connection);
849 if(connection->error) {
850 free(status);
851 return NULL;
852 }
853 }
855 if(connection->error) {
856 free(status);
857 return NULL;
858 }
859 else if(status->state<0) {
860 strcpy(connection->errorStr,"state not found");
861 connection->error = 1;
862 free(status);
863 return NULL;
864 }
866 return status;
867 }
869 void mpd_freeStatus(mpd_Status * status) {
870 if(status->error) free(status->error);
871 free(status);
872 }
874 void mpd_sendStatsCommand(mpd_Connection * connection) {
875 mpd_executeCommand(connection,"stats\n");
876 }
878 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
879 mpd_Stats * stats;
881 /*mpd_executeCommand(connection,"stats\n");
883 if(connection->error) return NULL;*/
885 if(connection->doneProcessing || (connection->listOks &&
886 connection->doneListOk))
887 {
888 return NULL;
889 }
891 if(!connection->returnElement) mpd_getNextReturnElement(connection);
893 stats = malloc(sizeof(mpd_Stats));
894 stats->numberOfArtists = 0;
895 stats->numberOfAlbums = 0;
896 stats->numberOfSongs = 0;
897 stats->uptime = 0;
898 stats->dbUpdateTime = 0;
899 stats->playTime = 0;
900 stats->dbPlayTime = 0;
902 if(connection->error) {
903 free(stats);
904 return NULL;
905 }
906 while(connection->returnElement) {
907 mpd_ReturnElement * re = connection->returnElement;
908 if(strcmp(re->name,"artists")==0) {
909 stats->numberOfArtists = atoi(re->value);
910 }
911 else if(strcmp(re->name,"albums")==0) {
912 stats->numberOfAlbums = atoi(re->value);
913 }
914 else if(strcmp(re->name,"songs")==0) {
915 stats->numberOfSongs = atoi(re->value);
916 }
917 else if(strcmp(re->name,"uptime")==0) {
918 stats->uptime = strtol(re->value,NULL,10);
919 }
920 else if(strcmp(re->name,"db_update")==0) {
921 stats->dbUpdateTime = strtol(re->value,NULL,10);
922 }
923 else if(strcmp(re->name,"playtime")==0) {
924 stats->playTime = strtol(re->value,NULL,10);
925 }
926 else if(strcmp(re->name,"db_playtime")==0) {
927 stats->dbPlayTime = strtol(re->value,NULL,10);
928 }
930 mpd_getNextReturnElement(connection);
931 if(connection->error) {
932 free(stats);
933 return NULL;
934 }
935 }
937 if(connection->error) {
938 free(stats);
939 return NULL;
940 }
942 return stats;
943 }
945 void mpd_freeStats(mpd_Stats * stats) {
946 free(stats);
947 }
949 static void mpd_initDirectory(mpd_Directory * directory) {
950 directory->path = NULL;
951 }
953 static void mpd_finishDirectory(mpd_Directory * directory) {
954 if (directory->path)
955 str_pool_put(directory->path);
956 }
958 mpd_Directory * mpd_newDirectory(void) {
959 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
961 mpd_initDirectory(directory);
963 return directory;
964 }
966 void mpd_freeDirectory(mpd_Directory * directory) {
967 mpd_finishDirectory(directory);
969 free(directory);
970 }
972 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
973 mpd_Directory * ret = mpd_newDirectory();
975 if (directory->path)
976 ret->path = str_pool_dup(directory->path);
978 return ret;
979 }
981 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
982 playlist->path = NULL;
983 }
985 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
986 if (playlist->path)
987 str_pool_put(playlist->path);
988 }
990 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
991 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
993 mpd_initPlaylistFile(playlist);
995 return playlist;
996 }
998 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
999 mpd_finishPlaylistFile(playlist);
1000 free(playlist);
1001 }
1003 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1004 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1006 if (playlist->path)
1007 ret->path = str_pool_dup(playlist->path);
1009 return ret;
1010 }
1012 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
1013 entity->info.directory = NULL;
1014 }
1016 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1017 if(entity->info.directory) {
1018 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1019 mpd_freeDirectory(entity->info.directory);
1020 }
1021 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1022 mpd_freeSong(entity->info.song);
1023 }
1024 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1025 mpd_freePlaylistFile(entity->info.playlistFile);
1026 }
1027 }
1028 }
1030 mpd_InfoEntity * mpd_newInfoEntity(void) {
1031 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
1033 mpd_initInfoEntity(entity);
1035 return entity;
1036 }
1038 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1039 mpd_finishInfoEntity(entity);
1040 free(entity);
1041 }
1043 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1044 mpd_executeCommand(connection,command);
1045 }
1047 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1048 mpd_InfoEntity * entity = NULL;
1050 if(connection->doneProcessing || (connection->listOks &&
1051 connection->doneListOk)) {
1052 return NULL;
1053 }
1055 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1057 if(connection->returnElement) {
1058 if(strcmp(connection->returnElement->name,"file")==0) {
1059 entity = mpd_newInfoEntity();
1060 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1061 entity->info.song = mpd_newSong();
1062 entity->info.song->file =
1063 str_pool_dup(connection->returnElement->value);
1064 }
1065 else if(strcmp(connection->returnElement->name,
1066 "directory")==0) {
1067 entity = mpd_newInfoEntity();
1068 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1069 entity->info.directory = mpd_newDirectory();
1070 entity->info.directory->path =
1071 str_pool_dup(connection->returnElement->value);
1072 }
1073 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1074 entity = mpd_newInfoEntity();
1075 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1076 entity->info.playlistFile = mpd_newPlaylistFile();
1077 entity->info.playlistFile->path =
1078 str_pool_dup(connection->returnElement->value);
1079 }
1080 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1081 entity = mpd_newInfoEntity();
1082 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1083 entity->info.song = mpd_newSong();
1084 entity->info.song->pos = atoi(connection->returnElement->value);
1085 }
1086 else {
1087 connection->error = 1;
1088 strcpy(connection->errorStr,"problem parsing song info");
1089 return NULL;
1090 }
1091 }
1092 else return NULL;
1094 mpd_getNextReturnElement(connection);
1095 while(connection->returnElement) {
1096 mpd_ReturnElement * re = connection->returnElement;
1098 if(strcmp(re->name,"file")==0) return entity;
1099 else if(strcmp(re->name,"directory")==0) return entity;
1100 else if(strcmp(re->name,"playlist")==0) return entity;
1101 else if(strcmp(re->name,"cpos")==0) return entity;
1103 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1104 strlen(re->value)) {
1105 if(!entity->info.song->artist &&
1106 strcmp(re->name,"Artist")==0) {
1107 entity->info.song->artist = str_pool_dup(re->value);
1108 }
1109 else if(!entity->info.song->album &&
1110 strcmp(re->name,"Album")==0) {
1111 entity->info.song->album = str_pool_dup(re->value);
1112 }
1113 else if(!entity->info.song->title &&
1114 strcmp(re->name,"Title")==0) {
1115 entity->info.song->title = str_pool_dup(re->value);
1116 }
1117 else if(!entity->info.song->track &&
1118 strcmp(re->name,"Track")==0) {
1119 entity->info.song->track = str_pool_dup(re->value);
1120 }
1121 else if(!entity->info.song->name &&
1122 strcmp(re->name,"Name")==0) {
1123 entity->info.song->name = str_pool_dup(re->value);
1124 }
1125 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1126 strcmp(re->name,"Time")==0) {
1127 entity->info.song->time = atoi(re->value);
1128 }
1129 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1130 strcmp(re->name,"Pos")==0) {
1131 entity->info.song->pos = atoi(re->value);
1132 }
1133 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1134 strcmp(re->name,"Id")==0) {
1135 entity->info.song->id = atoi(re->value);
1136 }
1137 else if(!entity->info.song->date &&
1138 strcmp(re->name, "Date") == 0) {
1139 entity->info.song->date = str_pool_dup(re->value);
1140 }
1141 else if(!entity->info.song->genre &&
1142 strcmp(re->name, "Genre") == 0) {
1143 entity->info.song->genre = str_pool_dup(re->value);
1144 }
1145 else if(!entity->info.song->composer &&
1146 strcmp(re->name, "Composer") == 0) {
1147 entity->info.song->composer = str_pool_dup(re->value);
1148 }
1149 else if(!entity->info.song->disc &&
1150 strcmp(re->name, "Disc") == 0) {
1151 entity->info.song->disc = str_pool_dup(re->value);
1152 }
1153 else if(!entity->info.song->comment &&
1154 strcmp(re->name, "Comment") == 0) {
1155 entity->info.song->comment = str_pool_dup(re->value);
1156 }
1157 }
1158 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1159 }
1160 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1161 }
1163 mpd_getNextReturnElement(connection);
1164 }
1166 return entity;
1167 }
1169 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1170 const char * name)
1171 {
1172 if(connection->doneProcessing || (connection->listOks &&
1173 connection->doneListOk))
1174 {
1175 return NULL;
1176 }
1178 mpd_getNextReturnElement(connection);
1179 while(connection->returnElement) {
1180 mpd_ReturnElement * re = connection->returnElement;
1182 if(strcmp(re->name,name)==0) return strdup(re->value);
1183 mpd_getNextReturnElement(connection);
1184 }
1186 return NULL;
1187 }
1189 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1190 if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1191 {
1192 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1193 }
1194 return NULL;
1195 }
1197 char * mpd_getNextArtist(mpd_Connection * connection) {
1198 return mpd_getNextReturnElementNamed(connection,"Artist");
1199 }
1201 char * mpd_getNextAlbum(mpd_Connection * connection) {
1202 return mpd_getNextReturnElementNamed(connection,"Album");
1203 }
1205 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1206 char * string = malloc(strlen("playlistinfo")+25);
1207 sprintf(string,"playlistinfo \"%i\"\n",songPos);
1208 mpd_sendInfoCommand(connection,string);
1209 free(string);
1210 }
1212 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1213 char * string = malloc(strlen("playlistid")+25);
1214 sprintf(string, "playlistid \"%i\"\n", id);
1215 mpd_sendInfoCommand(connection, string);
1216 free(string);
1217 }
1219 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1220 char * string = malloc(strlen("plchanges")+25);
1221 sprintf(string,"plchanges \"%lld\"\n",playlist);
1222 mpd_sendInfoCommand(connection,string);
1223 free(string);
1224 }
1226 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1227 char * string = malloc(strlen("plchangesposid")+25);
1228 sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1229 mpd_sendInfoCommand(connection,string);
1230 free(string);
1231 }
1233 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1234 char * sDir = mpd_sanitizeArg(dir);
1235 char * string = malloc(strlen("listall")+strlen(sDir)+5);
1236 sprintf(string,"listall \"%s\"\n",sDir);
1237 mpd_sendInfoCommand(connection,string);
1238 free(string);
1239 free(sDir);
1240 }
1242 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1243 char * sDir = mpd_sanitizeArg(dir);
1244 char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1245 sprintf(string,"listallinfo \"%s\"\n",sDir);
1246 mpd_sendInfoCommand(connection,string);
1247 free(string);
1248 free(sDir);
1249 }
1251 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1252 char * sDir = mpd_sanitizeArg(dir);
1253 char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1254 sprintf(string,"lsinfo \"%s\"\n",sDir);
1255 mpd_sendInfoCommand(connection,string);
1256 free(string);
1257 free(sDir);
1258 }
1260 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1261 mpd_executeCommand(connection,"currentsong\n");
1262 }
1264 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1265 const char * str)
1266 {
1267 char st[10];
1268 char * string;
1269 char * sanitStr = mpd_sanitizeArg(str);
1270 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1271 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1272 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1273 else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1274 else {
1275 connection->error = 1;
1276 strcpy(connection->errorStr,"unknown table for search");
1277 return;
1278 }
1279 string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1280 sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1281 mpd_sendInfoCommand(connection,string);
1282 free(string);
1283 free(sanitStr);
1284 }
1286 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1287 const char * str)
1288 {
1289 char st[10];
1290 char * string;
1291 char * sanitStr = mpd_sanitizeArg(str);
1292 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1293 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1294 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1295 else {
1296 connection->error = 1;
1297 strcpy(connection->errorStr,"unknown table for find");
1298 return;
1299 }
1300 string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1301 sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1302 mpd_sendInfoCommand(connection,string);
1303 free(string);
1304 free(sanitStr);
1305 }
1307 void mpd_sendListCommand(mpd_Connection * connection, int table,
1308 const char * arg1)
1309 {
1310 char st[10];
1311 char * string;
1312 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1313 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1314 else {
1315 connection->error = 1;
1316 strcpy(connection->errorStr,"unknown table for list");
1317 return;
1318 }
1319 if(arg1) {
1320 char * sanitArg1 = mpd_sanitizeArg(arg1);
1321 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1322 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1323 free(sanitArg1);
1324 }
1325 else {
1326 string = malloc(strlen("list")+strlen(st)+3);
1327 sprintf(string,"list %s\n",st);
1328 }
1329 mpd_sendInfoCommand(connection,string);
1330 free(string);
1331 }
1333 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1334 char * sFile = mpd_sanitizeArg(file);
1335 char * string = malloc(strlen("add")+strlen(sFile)+5);
1336 sprintf(string,"add \"%s\"\n",sFile);
1337 mpd_executeCommand(connection,string);
1338 free(string);
1339 free(sFile);
1340 }
1342 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1343 char * string = malloc(strlen("delete")+25);
1344 sprintf(string,"delete \"%i\"\n",songPos);
1345 mpd_sendInfoCommand(connection,string);
1346 free(string);
1347 }
1349 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1350 char * string = malloc(strlen("deleteid")+25);
1351 sprintf(string, "deleteid \"%i\"\n", id);
1352 mpd_sendInfoCommand(connection,string);
1353 free(string);
1354 }
1356 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1357 char * sName = mpd_sanitizeArg(name);
1358 char * string = malloc(strlen("save")+strlen(sName)+5);
1359 sprintf(string,"save \"%s\"\n",sName);
1360 mpd_executeCommand(connection,string);
1361 free(string);
1362 free(sName);
1363 }
1365 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1366 char * sName = mpd_sanitizeArg(name);
1367 char * string = malloc(strlen("load")+strlen(sName)+5);
1368 sprintf(string,"load \"%s\"\n",sName);
1369 mpd_executeCommand(connection,string);
1370 free(string);
1371 free(sName);
1372 }
1374 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1375 char * sName = mpd_sanitizeArg(name);
1376 char * string = malloc(strlen("rm")+strlen(sName)+5);
1377 sprintf(string,"rm \"%s\"\n",sName);
1378 mpd_executeCommand(connection,string);
1379 free(string);
1380 free(sName);
1381 }
1383 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1384 mpd_executeCommand(connection,"shuffle\n");
1385 }
1387 void mpd_sendClearCommand(mpd_Connection * connection) {
1388 mpd_executeCommand(connection,"clear\n");
1389 }
1391 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1392 char * string = malloc(strlen("play")+25);
1393 sprintf(string,"play \"%i\"\n",songPos);
1394 mpd_sendInfoCommand(connection,string);
1395 free(string);
1396 }
1398 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1399 char * string = malloc(strlen("playid")+25);
1400 sprintf(string,"playid \"%i\"\n",id);
1401 mpd_sendInfoCommand(connection,string);
1402 free(string);
1403 }
1405 void mpd_sendStopCommand(mpd_Connection * connection) {
1406 mpd_executeCommand(connection,"stop\n");
1407 }
1409 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1410 char * string = malloc(strlen("pause")+25);
1411 sprintf(string,"pause \"%i\"\n",pauseMode);
1412 mpd_executeCommand(connection,string);
1413 free(string);
1414 }
1416 void mpd_sendNextCommand(mpd_Connection * connection) {
1417 mpd_executeCommand(connection,"next\n");
1418 }
1420 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1421 char * string = malloc(strlen("move")+25);
1422 sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1423 mpd_sendInfoCommand(connection,string);
1424 free(string);
1425 }
1427 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1428 char * string = malloc(strlen("moveid")+25);
1429 sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1430 mpd_sendInfoCommand(connection,string);
1431 free(string);
1432 }
1434 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1435 char * string = malloc(strlen("swap")+25);
1436 sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1437 mpd_sendInfoCommand(connection,string);
1438 free(string);
1439 }
1441 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1442 char * string = malloc(strlen("swapid")+25);
1443 sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1444 mpd_sendInfoCommand(connection,string);
1445 free(string);
1446 }
1448 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1449 char * string = malloc(strlen("seek")+25);
1450 sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1451 mpd_sendInfoCommand(connection,string);
1452 free(string);
1453 }
1455 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1456 char * string = malloc(strlen("seekid")+25);
1457 sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1458 mpd_sendInfoCommand(connection,string);
1459 free(string);
1460 }
1462 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1463 char *sPath = mpd_sanitizeArg(path);
1464 char * string = malloc(strlen("update")+strlen(sPath)+5);
1465 sprintf(string,"update \"%s\"\n",sPath);
1466 mpd_sendInfoCommand(connection,string);
1467 free(string);
1468 free(sPath);
1469 }
1471 int mpd_getUpdateId(mpd_Connection * connection) {
1472 char * jobid;
1473 int ret = 0;
1475 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1476 if(jobid) {
1477 ret = atoi(jobid);
1478 free(jobid);
1479 }
1481 return ret;
1482 }
1484 void mpd_sendPrevCommand(mpd_Connection * connection) {
1485 mpd_executeCommand(connection,"previous\n");
1486 }
1488 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1489 char * string = malloc(strlen("repeat")+25);
1490 sprintf(string,"repeat \"%i\"\n",repeatMode);
1491 mpd_executeCommand(connection,string);
1492 free(string);
1493 }
1495 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1496 char * string = malloc(strlen("random")+25);
1497 sprintf(string,"random \"%i\"\n",randomMode);
1498 mpd_executeCommand(connection,string);
1499 free(string);
1500 }
1502 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1503 char * string = malloc(strlen("setvol")+25);
1504 sprintf(string,"setvol \"%i\"\n",volumeChange);
1505 mpd_executeCommand(connection,string);
1506 free(string);
1507 }
1509 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1510 char * string = malloc(strlen("volume")+25);
1511 sprintf(string,"volume \"%i\"\n",volumeChange);
1512 mpd_executeCommand(connection,string);
1513 free(string);
1514 }
1516 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1517 char * string = malloc(strlen("crossfade")+25);
1518 sprintf(string,"crossfade \"%i\"\n",seconds);
1519 mpd_executeCommand(connection,string);
1520 free(string);
1521 }
1523 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1524 char * sPass = mpd_sanitizeArg(pass);
1525 char * string = malloc(strlen("password")+strlen(sPass)+5);
1526 sprintf(string,"password \"%s\"\n",sPass);
1527 mpd_executeCommand(connection,string);
1528 free(string);
1529 free(sPass);
1530 }
1532 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1533 if(connection->commandList) {
1534 strcpy(connection->errorStr,"already in command list mode");
1535 connection->error = 1;
1536 return;
1537 }
1538 connection->commandList = COMMAND_LIST;
1539 mpd_executeCommand(connection,"command_list_begin\n");
1540 }
1542 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1543 if(connection->commandList) {
1544 strcpy(connection->errorStr,"already in command list mode");
1545 connection->error = 1;
1546 return;
1547 }
1548 connection->commandList = COMMAND_LIST_OK;
1549 mpd_executeCommand(connection,"command_list_ok_begin\n");
1550 connection->listOks = 0;
1551 }
1553 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1554 if(!connection->commandList) {
1555 strcpy(connection->errorStr,"not in command list mode");
1556 connection->error = 1;
1557 return;
1558 }
1559 connection->commandList = 0;
1560 mpd_executeCommand(connection,"command_list_end\n");
1561 }
1563 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1564 mpd_executeCommand(connection,"outputs\n");
1565 }
1567 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1568 mpd_OutputEntity * output = NULL;
1570 if(connection->doneProcessing || (connection->listOks &&
1571 connection->doneListOk))
1572 {
1573 return NULL;
1574 }
1576 if(connection->error) return NULL;
1578 output = malloc(sizeof(mpd_OutputEntity));
1579 output->id = -10;
1580 output->name = NULL;
1581 output->enabled = 0;
1583 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1585 while(connection->returnElement) {
1586 mpd_ReturnElement * re = connection->returnElement;
1587 if(strcmp(re->name,"outputid")==0) {
1588 if(output!=NULL && output->id>=0) return output;
1589 output->id = atoi(re->value);
1590 }
1591 else if(strcmp(re->name,"outputname")==0) {
1592 output->name = strdup(re->value);
1593 }
1594 else if(strcmp(re->name,"outputenabled")==0) {
1595 output->enabled = atoi(re->value);
1596 }
1598 mpd_getNextReturnElement(connection);
1599 if(connection->error) {
1600 free(output);
1601 return NULL;
1602 }
1604 }
1606 return output;
1607 }
1609 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1610 char * string = malloc(strlen("enableoutput")+25);
1611 sprintf(string,"enableoutput \"%i\"\n",outputId);
1612 mpd_executeCommand(connection,string);
1613 free(string);
1614 }
1616 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1617 char * string = malloc(strlen("disableoutput")+25);
1618 sprintf(string,"disableoutput \"%i\"\n",outputId);
1619 mpd_executeCommand(connection,string);
1620 free(string);
1621 }
1623 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1624 free(output->name);
1625 free(output);
1626 }
1628 /**
1629 * mpd_sendNotCommandsCommand
1630 * odd naming, but it gets the not allowed commands
1631 */
1633 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1634 mpd_executeCommand(connection,"notcommands\n");
1635 }
1637 /**
1638 * mpd_sendCommandsCommand
1639 * odd naming, but it gets the allowed commands
1640 */
1642 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1643 mpd_executeCommand(connection,"commands\n");
1644 }
1645 /**
1646 * Get the next returned command
1647 */
1648 char * mpd_getNextCommand(mpd_Connection * connection) {
1649 return mpd_getNextReturnElementNamed(connection,"command");
1650 }
1652 void mpd_startSearch(mpd_Connection * connection,int exact) {
1653 if(connection->request) {
1654 /* search/find allready in progress */
1655 /* TODO: set error here? */
1656 return;
1657 }
1658 if(exact){
1659 connection->request = strdup("find");
1660 }
1661 else{
1662 connection->request = strdup("search");
1663 }
1664 }
1667 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1668 if(connection->request) {
1669 /* search/find allready in progress */
1670 /* TODO: set error here? */
1671 return;
1672 }
1673 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1674 /* set error here */
1675 return;
1676 }
1678 connection->request = malloc(sizeof(char)*(
1679 /* length of the field name */
1680 strlen(mpdTagItemKeys[field])+
1681 /* "list"+space+\0 */
1682 6
1683 ));
1684 sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1685 }
1689 void mpd_addConstraintSearch(mpd_Connection *connection,
1690 int field,
1691 char *name)
1692 {
1693 char *arg = NULL;
1694 if(!connection->request){
1695 return;
1696 }
1697 if(name == NULL) {
1698 return;
1699 }
1700 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1701 return;
1702 }
1703 /* clean up the query */
1704 arg = mpd_sanitizeArg(name);
1705 /* create space for the query */
1706 connection->request = realloc(connection->request, (
1707 /* length of the old string */
1708 strlen(connection->request)+
1709 /* space between */
1710 1+
1711 /* length of the field name */
1712 strlen(mpdTagItemKeys[field])+
1713 /* space plus starting " */
1714 2+
1715 /* length of search term */
1716 strlen(arg)+
1717 /* closign " +\0 that is added sprintf */
1718 2
1719 )*sizeof(char));
1720 /* and form the query */
1721 sprintf(connection->request, "%s %s \"%s\"",
1722 connection->request,
1723 mpdTagItemKeys[field],
1724 arg);
1725 free(arg);
1726 }
1729 void mpd_commitSearch(mpd_Connection *connection)
1730 {
1731 if(connection->request)
1732 {
1733 int length = strlen(connection->request);
1734 /* fixing up the string for mpd to like */
1735 connection->request = realloc(connection->request,
1736 (length+ /* old length */
1737 2 /* closing \n and \0 */
1738 )*sizeof(char));
1739 connection->request[length] = '\n';
1740 connection->request[length+1] = '\0';
1741 /* and off we go */
1742 mpd_sendInfoCommand(connection, connection->request);
1743 /* clean up a bit */
1744 free(connection->request);
1745 connection->request = NULL;
1746 }
1747 }
1749 /**
1750 * @param connection a MpdConnection
1751 * @param path the path to the playlist.
1752 *
1753 * List the content, with full metadata, of a stored playlist.
1754 *
1755 */
1756 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1757 {
1758 char *arg = mpd_sanitizeArg(path);
1759 char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1760 sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1761 mpd_sendInfoCommand(connection, query);
1762 free(arg);
1763 free(query);
1764 }
1766 /**
1767 * @param connection a MpdConnection
1768 * @param path the path to the playlist.
1769 *
1770 * List the content of a stored playlist.
1771 *
1772 */
1773 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1774 {
1775 char *arg = mpd_sanitizeArg(path);
1776 char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1777 sprintf(query, "listplaylist \"%s\"\n",arg);
1778 mpd_sendInfoCommand(connection, query);
1779 free(arg);
1780 free(query);
1781 }