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 "resolver.h"
35 #include "str_pool.h"
37 #include <assert.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <stdio.h>
41 #include <sys/param.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45 #include <fcntl.h>
47 #ifdef WIN32
48 # include <ws2tcpip.h>
49 # include <winsock.h>
50 #else
51 # include <netinet/in.h>
52 # include <arpa/inet.h>
53 # include <sys/socket.h>
54 # include <netdb.h>
55 #endif
57 #ifndef WIN32
58 #include <sys/un.h>
59 #endif
61 #ifndef MSG_DONTWAIT
62 # define MSG_DONTWAIT 0
63 #endif
65 #define COMMAND_LIST 1
66 #define COMMAND_LIST_OK 2
68 #ifdef WIN32
69 # define SELECT_ERRNO_IGNORE (errno == WSAEINTR || errno == WSAEINPROGRESS)
70 # define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
71 #else
72 # define SELECT_ERRNO_IGNORE (errno == EINTR)
73 # define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
74 # define winsock_dll_error(c) 0
75 # define closesocket(s) close(s)
76 # define WSACleanup() do { /* nothing */ } while (0)
77 #endif
79 #ifdef WIN32
80 static int winsock_dll_error(mpd_Connection *connection)
81 {
82 WSADATA wsaData;
83 if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
84 LOBYTE(wsaData.wVersion) != 2 ||
85 HIBYTE(wsaData.wVersion) != 2 ) {
86 snprintf(connection->errorStr, sizeof(connection->errorStr),
87 "Could not find usable WinSock DLL.");
88 connection->error = MPD_ERROR_SYSTEM;
89 return 1;
90 }
91 return 0;
92 }
94 static int do_connect_fail(mpd_Connection *connection,
95 const struct sockaddr *serv_addr, int addrlen)
96 {
97 int iMode = 1; /* 0 = blocking, else non-blocking */
98 ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
99 return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR
100 && WSAGetLastError() != WSAEWOULDBLOCK);
101 }
102 #else /* !WIN32 (sane operating systems) */
103 static int do_connect_fail(mpd_Connection *connection,
104 const struct sockaddr *serv_addr, int addrlen)
105 {
106 int flags = fcntl(connection->sock, F_GETFL, 0);
107 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
108 return (connect(connection->sock,serv_addr,addrlen)<0 &&
109 errno!=EINPROGRESS);
110 }
111 #endif /* !WIN32 */
113 const char *const mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
114 {
115 "Artist",
116 "Album",
117 "Title",
118 "Track",
119 "Name",
120 "Genre",
121 "Date",
122 "Composer",
123 "Performer",
124 "Comment",
125 "Disc",
126 "filename"
127 };
129 static char * mpd_sanitizeArg(const char * arg) {
130 size_t i;
131 char * ret;
132 register const char *c;
133 register char *rc;
135 /* instead of counting in that loop above, just
136 * use a bit more memory and half running time
137 */
138 ret = malloc(strlen(arg) * 2 + 1);
140 c = arg;
141 rc = ret;
142 for(i = strlen(arg)+1; i != 0; --i) {
143 if(*c=='"' || *c=='\\')
144 *rc++ = '\\';
145 *(rc++) = *(c++);
146 }
148 return ret;
149 }
151 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
152 {
153 mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
155 ret->name = str_pool_get(name);
156 ret->value = str_pool_get(value);
158 return ret;
159 }
161 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
162 str_pool_put(re->name);
163 str_pool_put(re->value);
164 free(re);
165 }
167 void
168 mpd_setConnectionTimeout(mpd_Connection *connection, float timeout_) {
169 connection->timeout.tv_sec = (int)timeout_;
170 connection->timeout.tv_usec = (int)(timeout_ * 1e6 -
171 connection->timeout.tv_sec*1000000 +
172 0.5);
173 }
175 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
176 char * output) {
177 char * tmp;
178 char * test;
179 int i;
181 if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
182 snprintf(connection->errorStr, sizeof(connection->errorStr),
183 "mpd not running on port %i on host \"%s\"",
184 port,host);
185 connection->error = MPD_ERROR_NOTMPD;
186 return 1;
187 }
189 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
191 for(i=0;i<3;i++) {
192 if(tmp) connection->version[i] = strtol(tmp,&test,10);
194 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
195 snprintf(connection->errorStr, sizeof(connection->errorStr),
196 "error parsing version number at "
197 "\"%s\"",
198 &output[strlen(MPD_WELCOME_MESSAGE)]);
199 connection->error = MPD_ERROR_NOTMPD;
200 return 1;
201 }
202 tmp = ++test;
203 }
205 return 0;
206 }
208 /**
209 * Wait for the socket to become readable.
210 */
211 static int mpd_wait(mpd_Connection *connection)
212 {
213 struct timeval tv;
214 fd_set fds;
215 int ret;
217 assert(connection->sock >= 0);
219 while (1) {
220 tv = connection->timeout;
221 FD_ZERO(&fds);
222 FD_SET(connection->sock, &fds);
224 ret = select(connection->sock + 1, &fds, NULL, NULL, &tv);
225 if (ret > 0)
226 return 0;
228 if (ret == 0 || !SELECT_ERRNO_IGNORE)
229 return -1;
230 }
231 }
233 /**
234 * Wait until the socket is connected and check its result. Returns 1
235 * on success, 0 on timeout, -errno on error.
236 */
237 static int mpd_wait_connected(mpd_Connection *connection)
238 {
239 int ret;
240 int s_err = 0;
241 socklen_t s_err_size = sizeof(s_err);
243 ret = mpd_wait(connection);
244 if (ret < 0)
245 return 0;
247 ret = getsockopt(connection->sock, SOL_SOCKET, SO_ERROR,
248 (char*)&s_err, &s_err_size);
249 if (ret < 0)
250 return -errno;
252 if (s_err != 0)
253 return -s_err;
255 return 1;
256 }
258 /**
259 * Attempt to read data from the socket into the input buffer.
260 * Returns 0 on success, -1 on error.
261 */
262 static int mpd_recv(mpd_Connection *connection)
263 {
264 int ret;
265 ssize_t nbytes;
267 assert(connection != NULL);
268 assert(connection->buflen <= sizeof(connection->buffer));
269 assert(connection->bufstart <= connection->buflen);
271 if (connection->sock < 0) {
272 strcpy(connection->errorStr, "not connected");
273 connection->error = MPD_ERROR_CONNCLOSED;
274 connection->doneProcessing = 1;
275 connection->doneListOk = 0;
276 return -1;
277 }
279 if (connection->buflen >= sizeof(connection->buffer)) {
280 /* delete consumed data from beginning of buffer */
281 connection->buflen -= connection->bufstart;
282 memmove(connection->buffer,
283 connection->buffer + connection->bufstart,
284 connection->buflen);
285 connection->bufstart = 0;
286 }
288 if (connection->buflen >= sizeof(connection->buffer)) {
289 strcpy(connection->errorStr, "buffer overrun");
290 connection->error = MPD_ERROR_BUFFEROVERRUN;
291 connection->doneProcessing = 1;
292 connection->doneListOk = 0;
293 return -1;
294 }
296 while (1) {
297 ret = mpd_wait(connection);
298 if (ret < 0) {
299 strcpy(connection->errorStr, "connection timeout");
300 connection->error = MPD_ERROR_TIMEOUT;
301 connection->doneProcessing = 1;
302 connection->doneListOk = 0;
303 return -1;
304 }
306 nbytes = read(connection->sock,
307 connection->buffer + connection->buflen,
308 sizeof(connection->buffer) - connection->buflen);
309 if (nbytes > 0) {
310 connection->buflen += nbytes;
311 return 0;
312 }
314 if (nbytes == 0 || !SENDRECV_ERRNO_IGNORE) {
315 strcpy(connection->errorStr, "connection closed");
316 connection->error = MPD_ERROR_CONNCLOSED;
317 connection->doneProcessing = 1;
318 connection->doneListOk = 0;
319 return -1;
320 }
321 }
322 }
324 static int
325 mpd_connect(mpd_Connection *connection, const char * host, int port)
326 {
327 struct resolver *resolver;
328 const struct resolver_address *address;
329 int ret;
331 resolver = resolver_new(host, port);
332 if (resolver == NULL) {
333 snprintf(connection->errorStr, sizeof(connection->errorStr),
334 "host \"%s\" not found", host);
335 connection->error = MPD_ERROR_UNKHOST;
336 return -1;
337 }
339 while ((address = resolver_next(resolver)) != NULL) {
340 connection->sock = socket(address->family, SOCK_STREAM,
341 address->protocol);
342 if (connection->sock < 0) {
343 snprintf(connection->errorStr,
344 sizeof(connection->errorStr),
345 "problems creating socket: %s",
346 strerror(errno));
347 connection->error = MPD_ERROR_SYSTEM;
348 continue;
349 }
351 ret = do_connect_fail(connection,
352 address->addr, address->addrlen);
353 if (ret != 0) {
354 snprintf(connection->errorStr,
355 sizeof(connection->errorStr),
356 "problems connecting to \"%s\" on port"
357 " %i: %s", host, port, strerror(errno));
358 connection->error = MPD_ERROR_CONNPORT;
360 closesocket(connection->sock);
361 connection->sock = -1;
362 continue;
363 }
365 ret = mpd_wait_connected(connection);
366 if (ret > 0) {
367 resolver_free(resolver);
368 mpd_clearError(connection);
369 return 0;
370 }
372 if (ret == 0) {
373 snprintf(connection->errorStr,
374 sizeof(connection->errorStr),
375 "timeout in attempting to get a response from"
376 " \"%s\" on port %i", host, port);
377 connection->error = MPD_ERROR_NORESPONSE;
378 } else if (ret < 0) {
379 snprintf(connection->errorStr,
380 sizeof(connection->errorStr),
381 "problems connecting to \"%s\" on port %i: %s",
382 host, port, strerror(-ret));
383 connection->error = MPD_ERROR_CONNPORT;
384 }
386 closesocket(connection->sock);
387 connection->sock = -1;
388 }
390 resolver_free(resolver);
391 return -1;
392 }
394 mpd_Connection *
395 mpd_newConnection(const char *host, int port, float timeout_) {
396 int err;
397 char * rt;
398 mpd_Connection * connection = malloc(sizeof(mpd_Connection));
400 connection->buflen = 0;
401 connection->bufstart = 0;
402 mpd_clearError(connection);
403 connection->doneProcessing = 0;
404 connection->commandList = 0;
405 connection->listOks = 0;
406 connection->doneListOk = 0;
407 connection->returnElement = NULL;
408 connection->request = NULL;
410 if (winsock_dll_error(connection))
411 return connection;
413 mpd_setConnectionTimeout(connection, timeout_);
415 err = mpd_connect(connection, host, port);
416 if (err < 0)
417 return connection;
419 while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
420 err = mpd_recv(connection);
421 if (err < 0)
422 return connection;
423 }
425 *rt = '\0';
426 if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
427 connection->doneProcessing = 1;
429 connection->buflen -= rt + 1 - connection->buffer;
430 memmove(connection->buffer, rt + 1, connection->buflen);
432 return connection;
433 }
435 void mpd_clearError(mpd_Connection * connection) {
436 connection->error = 0;
437 connection->errorStr[0] = '\0';
438 }
440 void mpd_closeConnection(mpd_Connection * connection) {
441 closesocket(connection->sock);
442 if(connection->returnElement) free(connection->returnElement);
443 if(connection->request) free(connection->request);
444 free(connection);
445 WSACleanup();
446 }
448 static void mpd_executeCommand(mpd_Connection *connection,
449 const char *command) {
450 int ret;
451 struct timeval tv;
452 fd_set fds;
453 const char *commandPtr = command;
454 int commandLen = strlen(command);
456 if (connection->sock < 0) {
457 strcpy(connection->errorStr, "not connected");
458 connection->error = MPD_ERROR_CONNCLOSED;
459 return;
460 }
462 if (!connection->doneProcessing && !connection->commandList) {
463 strcpy(connection->errorStr,
464 "not done processing current command");
465 connection->error = 1;
466 return;
467 }
469 mpd_clearError(connection);
471 FD_ZERO(&fds);
472 FD_SET(connection->sock,&fds);
473 tv.tv_sec = connection->timeout.tv_sec;
474 tv.tv_usec = connection->timeout.tv_usec;
476 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
477 (ret==-1 && SELECT_ERRNO_IGNORE)) {
478 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
479 if(ret<=0)
480 {
481 if (SENDRECV_ERRNO_IGNORE) continue;
482 snprintf(connection->errorStr, sizeof(connection->errorStr),
483 "problems giving command \"%s\"",command);
484 connection->error = MPD_ERROR_SENDING;
485 return;
486 }
487 else {
488 commandPtr+=ret;
489 commandLen-=ret;
490 }
492 if(commandLen<=0) break;
493 }
495 if(commandLen>0) {
496 perror("");
497 snprintf(connection->errorStr, sizeof(connection->errorStr),
498 "timeout sending command \"%s\"",command);
499 connection->error = MPD_ERROR_TIMEOUT;
500 return;
501 }
503 if(!connection->commandList) connection->doneProcessing = 0;
504 else if(connection->commandList == COMMAND_LIST_OK) {
505 connection->listOks++;
506 }
507 }
509 static void mpd_getNextReturnElement(mpd_Connection * connection) {
510 char * output = NULL;
511 char * rt = NULL;
512 char * name = NULL;
513 char * value = NULL;
514 char * tok = NULL;
515 int err;
516 int pos;
518 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
519 connection->returnElement = NULL;
521 if (connection->doneProcessing ||
522 (connection->listOks && connection->doneListOk)) {
523 strcpy(connection->errorStr,"already done processing current command");
524 connection->error = 1;
525 return;
526 }
528 while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
529 connection->buflen - connection->bufstart))) {
530 err = mpd_recv(connection);
531 if (err < 0)
532 return;
533 }
535 *rt = '\0';
536 output = connection->buffer+connection->bufstart;
537 connection->bufstart = rt - connection->buffer + 1;
539 if(strcmp(output,"OK")==0) {
540 if(connection->listOks > 0) {
541 strcpy(connection->errorStr, "expected more list_OK's");
542 connection->error = 1;
543 }
544 connection->listOks = 0;
545 connection->doneProcessing = 1;
546 connection->doneListOk = 0;
547 return;
548 }
550 if(strcmp(output, "list_OK") == 0) {
551 if(!connection->listOks) {
552 strcpy(connection->errorStr,
553 "got an unexpected list_OK");
554 connection->error = 1;
555 }
556 else {
557 connection->doneListOk = 1;
558 connection->listOks--;
559 }
560 return;
561 }
563 if(strncmp(output,"ACK",strlen("ACK"))==0) {
564 size_t length = strlen(output);
565 char * test;
566 char * needle;
567 int val;
569 if (length >= sizeof(connection->errorStr))
570 length = sizeof(connection->errorStr) - 1;
572 memcpy(connection->errorStr, output, length);
573 connection->errorStr[length] = 0;
574 connection->error = MPD_ERROR_ACK;
575 connection->errorCode = MPD_ACK_ERROR_UNK;
576 connection->errorAt = MPD_ERROR_AT_UNK;
577 connection->doneProcessing = 1;
578 connection->doneListOk = 0;
580 needle = strchr(output, '[');
581 if(!needle) return;
582 val = strtol(needle+1, &test, 10);
583 if(*test != '@') return;
584 connection->errorCode = val;
585 val = strtol(test+1, &test, 10);
586 if(*test != ']') return;
587 connection->errorAt = val;
588 return;
589 }
591 tok = strchr(output, ':');
592 if (!tok) return;
593 pos = tok - output;
594 value = ++tok;
595 name = output;
596 name[pos] = '\0';
598 if(value[0]==' ') {
599 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
600 }
601 else {
602 snprintf(connection->errorStr, sizeof(connection->errorStr),
603 "error parsing: %s:%s",name,value);
604 connection->error = 1;
605 }
606 }
608 void mpd_finishCommand(mpd_Connection * connection) {
609 while(!connection->doneProcessing) {
610 if(connection->doneListOk) connection->doneListOk = 0;
611 mpd_getNextReturnElement(connection);
612 }
613 }
615 static void mpd_finishListOkCommand(mpd_Connection * connection) {
616 while(!connection->doneProcessing && connection->listOks &&
617 !connection->doneListOk)
618 {
619 mpd_getNextReturnElement(connection);
620 }
621 }
623 int mpd_nextListOkCommand(mpd_Connection * connection) {
624 mpd_finishListOkCommand(connection);
625 if(!connection->doneProcessing) connection->doneListOk = 0;
626 if(connection->listOks == 0 || connection->doneProcessing) return -1;
627 return 0;
628 }
630 void mpd_sendStatusCommand(mpd_Connection * connection) {
631 mpd_executeCommand(connection,"status\n");
632 }
634 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
635 mpd_Status * status;
637 /*mpd_executeCommand(connection,"status\n");
639 if(connection->error) return NULL;*/
641 if(connection->doneProcessing || (connection->listOks &&
642 connection->doneListOk))
643 {
644 return NULL;
645 }
647 if(!connection->returnElement) mpd_getNextReturnElement(connection);
649 status = malloc(sizeof(mpd_Status));
650 status->volume = -1;
651 status->repeat = 0;
652 status->random = 0;
653 status->single = 0;
654 status->consume = 0;
655 status->playlist = -1;
656 status->playlistLength = -1;
657 status->state = -1;
658 status->song = 0;
659 status->songid = 0;
660 status->elapsedTime = 0;
661 status->totalTime = 0;
662 status->bitRate = 0;
663 status->sampleRate = 0;
664 status->bits = 0;
665 status->channels = 0;
666 status->crossfade = -1;
667 status->error = NULL;
668 status->updatingDb = 0;
670 if(connection->error) {
671 free(status);
672 return NULL;
673 }
674 while(connection->returnElement) {
675 mpd_ReturnElement * re = connection->returnElement;
676 if(strcmp(re->name,"volume")==0) {
677 status->volume = atoi(re->value);
678 }
679 else if(strcmp(re->name,"repeat")==0) {
680 status->repeat = atoi(re->value);
681 }
682 else if(strcmp(re->name,"random")==0) {
683 status->random = atoi(re->value);
684 }
685 else if(strcmp(re->name,"single")==0) {
686 status->single = atoi(re->value);
687 }
688 else if(strcmp(re->name,"consume")==0) {
689 status->consume = atoi(re->value);
690 }
691 else if(strcmp(re->name,"playlist")==0) {
692 status->playlist = strtol(re->value,NULL,10);
693 }
694 else if(strcmp(re->name,"playlistlength")==0) {
695 status->playlistLength = atoi(re->value);
696 }
697 else if(strcmp(re->name,"bitrate")==0) {
698 status->bitRate = atoi(re->value);
699 }
700 else if(strcmp(re->name,"state")==0) {
701 if(strcmp(re->value,"play")==0) {
702 status->state = MPD_STATUS_STATE_PLAY;
703 }
704 else if(strcmp(re->value,"stop")==0) {
705 status->state = MPD_STATUS_STATE_STOP;
706 }
707 else if(strcmp(re->value,"pause")==0) {
708 status->state = MPD_STATUS_STATE_PAUSE;
709 }
710 else {
711 status->state = MPD_STATUS_STATE_UNKNOWN;
712 }
713 }
714 else if(strcmp(re->name,"song")==0) {
715 status->song = atoi(re->value);
716 }
717 else if(strcmp(re->name,"songid")==0) {
718 status->songid = atoi(re->value);
719 }
720 else if(strcmp(re->name,"time")==0) {
721 char * tok = strchr(re->value,':');
722 /* the second strchr below is a safety check */
723 if (tok && (strchr(tok,0) > (tok+1))) {
724 /* atoi stops at the first non-[0-9] char: */
725 status->elapsedTime = atoi(re->value);
726 status->totalTime = atoi(tok+1);
727 }
728 }
729 else if(strcmp(re->name,"error")==0) {
730 status->error = strdup(re->value);
731 }
732 else if(strcmp(re->name,"xfade")==0) {
733 status->crossfade = atoi(re->value);
734 }
735 else if(strcmp(re->name,"updating_db")==0) {
736 status->updatingDb = atoi(re->value);
737 }
738 else if(strcmp(re->name,"audio")==0) {
739 char * tok = strchr(re->value,':');
740 if (tok && (strchr(tok,0) > (tok+1))) {
741 status->sampleRate = atoi(re->value);
742 status->bits = atoi(++tok);
743 tok = strchr(tok,':');
744 if (tok && (strchr(tok,0) > (tok+1)))
745 status->channels = atoi(tok+1);
746 }
747 }
749 mpd_getNextReturnElement(connection);
750 if(connection->error) {
751 free(status);
752 return NULL;
753 }
754 }
756 if(connection->error) {
757 free(status);
758 return NULL;
759 }
760 else if(status->state<0) {
761 strcpy(connection->errorStr,"state not found");
762 connection->error = 1;
763 free(status);
764 return NULL;
765 }
767 return status;
768 }
770 void mpd_freeStatus(mpd_Status * status) {
771 if(status->error) free(status->error);
772 free(status);
773 }
775 void mpd_sendStatsCommand(mpd_Connection * connection) {
776 mpd_executeCommand(connection,"stats\n");
777 }
779 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
780 mpd_Stats * stats;
782 /*mpd_executeCommand(connection,"stats\n");
784 if(connection->error) return NULL;*/
786 if(connection->doneProcessing || (connection->listOks &&
787 connection->doneListOk))
788 {
789 return NULL;
790 }
792 if(!connection->returnElement) mpd_getNextReturnElement(connection);
794 stats = malloc(sizeof(mpd_Stats));
795 stats->numberOfArtists = 0;
796 stats->numberOfAlbums = 0;
797 stats->numberOfSongs = 0;
798 stats->uptime = 0;
799 stats->dbUpdateTime = 0;
800 stats->playTime = 0;
801 stats->dbPlayTime = 0;
803 if(connection->error) {
804 free(stats);
805 return NULL;
806 }
807 while(connection->returnElement) {
808 mpd_ReturnElement * re = connection->returnElement;
809 if(strcmp(re->name,"artists")==0) {
810 stats->numberOfArtists = atoi(re->value);
811 }
812 else if(strcmp(re->name,"albums")==0) {
813 stats->numberOfAlbums = atoi(re->value);
814 }
815 else if(strcmp(re->name,"songs")==0) {
816 stats->numberOfSongs = atoi(re->value);
817 }
818 else if(strcmp(re->name,"uptime")==0) {
819 stats->uptime = strtol(re->value,NULL,10);
820 }
821 else if(strcmp(re->name,"db_update")==0) {
822 stats->dbUpdateTime = strtol(re->value,NULL,10);
823 }
824 else if(strcmp(re->name,"playtime")==0) {
825 stats->playTime = strtol(re->value,NULL,10);
826 }
827 else if(strcmp(re->name,"db_playtime")==0) {
828 stats->dbPlayTime = strtol(re->value,NULL,10);
829 }
831 mpd_getNextReturnElement(connection);
832 if(connection->error) {
833 free(stats);
834 return NULL;
835 }
836 }
838 if(connection->error) {
839 free(stats);
840 return NULL;
841 }
843 return stats;
844 }
846 void mpd_freeStats(mpd_Stats * stats) {
847 free(stats);
848 }
850 static void mpd_initDirectory(mpd_Directory * directory) {
851 directory->path = NULL;
852 }
854 static void mpd_finishDirectory(mpd_Directory * directory) {
855 if (directory->path)
856 str_pool_put(directory->path);
857 }
859 mpd_Directory * mpd_newDirectory(void) {
860 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
862 mpd_initDirectory(directory);
864 return directory;
865 }
867 void mpd_freeDirectory(mpd_Directory * directory) {
868 mpd_finishDirectory(directory);
870 free(directory);
871 }
873 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
874 mpd_Directory * ret = mpd_newDirectory();
876 if (directory->path)
877 ret->path = str_pool_dup(directory->path);
879 return ret;
880 }
882 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
883 playlist->path = NULL;
884 }
886 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
887 if (playlist->path)
888 str_pool_put(playlist->path);
889 }
891 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
892 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
894 mpd_initPlaylistFile(playlist);
896 return playlist;
897 }
899 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
900 mpd_finishPlaylistFile(playlist);
901 free(playlist);
902 }
904 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
905 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
907 if (playlist->path)
908 ret->path = str_pool_dup(playlist->path);
910 return ret;
911 }
913 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
914 entity->info.directory = NULL;
915 }
917 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
918 if(entity->info.directory) {
919 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
920 mpd_freeDirectory(entity->info.directory);
921 }
922 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
923 mpd_freeSong(entity->info.song);
924 }
925 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
926 mpd_freePlaylistFile(entity->info.playlistFile);
927 }
928 }
929 }
931 mpd_InfoEntity * mpd_newInfoEntity(void) {
932 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
934 mpd_initInfoEntity(entity);
936 return entity;
937 }
939 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
940 mpd_finishInfoEntity(entity);
941 free(entity);
942 }
944 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
945 mpd_executeCommand(connection,command);
946 }
948 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
949 mpd_InfoEntity * entity = NULL;
951 if(connection->doneProcessing || (connection->listOks &&
952 connection->doneListOk)) {
953 return NULL;
954 }
956 if(!connection->returnElement) mpd_getNextReturnElement(connection);
958 if(connection->returnElement) {
959 if(strcmp(connection->returnElement->name,"file")==0) {
960 entity = mpd_newInfoEntity();
961 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
962 entity->info.song = mpd_newSong();
963 entity->info.song->file =
964 str_pool_dup(connection->returnElement->value);
965 }
966 else if(strcmp(connection->returnElement->name,
967 "directory")==0) {
968 entity = mpd_newInfoEntity();
969 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
970 entity->info.directory = mpd_newDirectory();
971 entity->info.directory->path =
972 str_pool_dup(connection->returnElement->value);
973 }
974 else if(strcmp(connection->returnElement->name,"playlist")==0) {
975 entity = mpd_newInfoEntity();
976 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
977 entity->info.playlistFile = mpd_newPlaylistFile();
978 entity->info.playlistFile->path =
979 str_pool_dup(connection->returnElement->value);
980 }
981 else if(strcmp(connection->returnElement->name, "cpos") == 0){
982 entity = mpd_newInfoEntity();
983 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
984 entity->info.song = mpd_newSong();
985 entity->info.song->pos = atoi(connection->returnElement->value);
986 }
987 else {
988 connection->error = 1;
989 strcpy(connection->errorStr,"problem parsing song info");
990 return NULL;
991 }
992 }
993 else return NULL;
995 mpd_getNextReturnElement(connection);
996 while(connection->returnElement) {
997 mpd_ReturnElement * re = connection->returnElement;
999 if(strcmp(re->name,"file")==0) return entity;
1000 else if(strcmp(re->name,"directory")==0) return entity;
1001 else if(strcmp(re->name,"playlist")==0) return entity;
1002 else if(strcmp(re->name,"cpos")==0) return entity;
1004 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1005 strlen(re->value)) {
1006 if(!entity->info.song->artist &&
1007 strcmp(re->name,"Artist")==0) {
1008 entity->info.song->artist = str_pool_dup(re->value);
1009 }
1010 else if(!entity->info.song->album &&
1011 strcmp(re->name,"Album")==0) {
1012 entity->info.song->album = str_pool_dup(re->value);
1013 }
1014 else if(!entity->info.song->title &&
1015 strcmp(re->name,"Title")==0) {
1016 entity->info.song->title = str_pool_dup(re->value);
1017 }
1018 else if(!entity->info.song->track &&
1019 strcmp(re->name,"Track")==0) {
1020 entity->info.song->track = str_pool_dup(re->value);
1021 }
1022 else if(!entity->info.song->name &&
1023 strcmp(re->name,"Name")==0) {
1024 entity->info.song->name = str_pool_dup(re->value);
1025 }
1026 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1027 strcmp(re->name,"Time")==0) {
1028 entity->info.song->time = atoi(re->value);
1029 }
1030 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1031 strcmp(re->name,"Pos")==0) {
1032 entity->info.song->pos = atoi(re->value);
1033 }
1034 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1035 strcmp(re->name,"Id")==0) {
1036 entity->info.song->id = atoi(re->value);
1037 }
1038 else if(!entity->info.song->date &&
1039 strcmp(re->name, "Date") == 0) {
1040 entity->info.song->date = str_pool_dup(re->value);
1041 }
1042 else if(!entity->info.song->genre &&
1043 strcmp(re->name, "Genre") == 0) {
1044 entity->info.song->genre = str_pool_dup(re->value);
1045 }
1046 else if(!entity->info.song->composer &&
1047 strcmp(re->name, "Composer") == 0) {
1048 entity->info.song->composer = str_pool_dup(re->value);
1049 }
1050 else if(!entity->info.song->disc &&
1051 strcmp(re->name, "Disc") == 0) {
1052 entity->info.song->disc = str_pool_dup(re->value);
1053 }
1054 else if(!entity->info.song->comment &&
1055 strcmp(re->name, "Comment") == 0) {
1056 entity->info.song->comment = str_pool_dup(re->value);
1057 }
1058 }
1059 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1060 }
1061 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1062 }
1064 mpd_getNextReturnElement(connection);
1065 }
1067 return entity;
1068 }
1070 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1071 const char * name)
1072 {
1073 if(connection->doneProcessing || (connection->listOks &&
1074 connection->doneListOk))
1075 {
1076 return NULL;
1077 }
1079 mpd_getNextReturnElement(connection);
1080 while(connection->returnElement) {
1081 mpd_ReturnElement * re = connection->returnElement;
1083 if(strcmp(re->name,name)==0) return strdup(re->value);
1084 mpd_getNextReturnElement(connection);
1085 }
1087 return NULL;
1088 }
1090 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1091 if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1092 {
1093 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1094 }
1095 return NULL;
1096 }
1098 char * mpd_getNextArtist(mpd_Connection * connection) {
1099 return mpd_getNextReturnElementNamed(connection,"Artist");
1100 }
1102 char * mpd_getNextAlbum(mpd_Connection * connection) {
1103 return mpd_getNextReturnElementNamed(connection,"Album");
1104 }
1106 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1107 char * string = malloc(strlen("playlistinfo")+25);
1108 sprintf(string,"playlistinfo \"%i\"\n",songPos);
1109 mpd_sendInfoCommand(connection,string);
1110 free(string);
1111 }
1113 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1114 char * string = malloc(strlen("playlistid")+25);
1115 sprintf(string, "playlistid \"%i\"\n", id);
1116 mpd_sendInfoCommand(connection, string);
1117 free(string);
1118 }
1120 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1121 char * string = malloc(strlen("plchanges")+25);
1122 sprintf(string,"plchanges \"%lld\"\n",playlist);
1123 mpd_sendInfoCommand(connection,string);
1124 free(string);
1125 }
1127 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1128 char * string = malloc(strlen("plchangesposid")+25);
1129 sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1130 mpd_sendInfoCommand(connection,string);
1131 free(string);
1132 }
1134 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1135 char * sDir = mpd_sanitizeArg(dir);
1136 char * string = malloc(strlen("listall")+strlen(sDir)+5);
1137 sprintf(string,"listall \"%s\"\n",sDir);
1138 mpd_sendInfoCommand(connection,string);
1139 free(string);
1140 free(sDir);
1141 }
1143 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1144 char * sDir = mpd_sanitizeArg(dir);
1145 char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1146 sprintf(string,"listallinfo \"%s\"\n",sDir);
1147 mpd_sendInfoCommand(connection,string);
1148 free(string);
1149 free(sDir);
1150 }
1152 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1153 char * sDir = mpd_sanitizeArg(dir);
1154 char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1155 sprintf(string,"lsinfo \"%s\"\n",sDir);
1156 mpd_sendInfoCommand(connection,string);
1157 free(string);
1158 free(sDir);
1159 }
1161 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1162 mpd_executeCommand(connection,"currentsong\n");
1163 }
1165 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1166 const char * str)
1167 {
1168 char st[10];
1169 char * string;
1170 char * sanitStr = mpd_sanitizeArg(str);
1171 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1172 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1173 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1174 else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1175 else {
1176 connection->error = 1;
1177 strcpy(connection->errorStr,"unknown table for search");
1178 return;
1179 }
1180 string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1181 sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1182 mpd_sendInfoCommand(connection,string);
1183 free(string);
1184 free(sanitStr);
1185 }
1187 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1188 const char * str)
1189 {
1190 char st[10];
1191 char * string;
1192 char * sanitStr = mpd_sanitizeArg(str);
1193 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1194 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1195 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1196 else {
1197 connection->error = 1;
1198 strcpy(connection->errorStr,"unknown table for find");
1199 return;
1200 }
1201 string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1202 sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1203 mpd_sendInfoCommand(connection,string);
1204 free(string);
1205 free(sanitStr);
1206 }
1208 void mpd_sendListCommand(mpd_Connection * connection, int table,
1209 const char * arg1)
1210 {
1211 char st[10];
1212 char * string;
1213 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1214 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1215 else {
1216 connection->error = 1;
1217 strcpy(connection->errorStr,"unknown table for list");
1218 return;
1219 }
1220 if(arg1) {
1221 char * sanitArg1 = mpd_sanitizeArg(arg1);
1222 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1223 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1224 free(sanitArg1);
1225 }
1226 else {
1227 string = malloc(strlen("list")+strlen(st)+3);
1228 sprintf(string,"list %s\n",st);
1229 }
1230 mpd_sendInfoCommand(connection,string);
1231 free(string);
1232 }
1234 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1235 char * sFile = mpd_sanitizeArg(file);
1236 char * string = malloc(strlen("add")+strlen(sFile)+5);
1237 sprintf(string,"add \"%s\"\n",sFile);
1238 mpd_executeCommand(connection,string);
1239 free(string);
1240 free(sFile);
1241 }
1243 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1244 char * string = malloc(strlen("delete")+25);
1245 sprintf(string,"delete \"%i\"\n",songPos);
1246 mpd_sendInfoCommand(connection,string);
1247 free(string);
1248 }
1250 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1251 char * string = malloc(strlen("deleteid")+25);
1252 sprintf(string, "deleteid \"%i\"\n", id);
1253 mpd_sendInfoCommand(connection,string);
1254 free(string);
1255 }
1257 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1258 char * sName = mpd_sanitizeArg(name);
1259 char * string = malloc(strlen("save")+strlen(sName)+5);
1260 sprintf(string,"save \"%s\"\n",sName);
1261 mpd_executeCommand(connection,string);
1262 free(string);
1263 free(sName);
1264 }
1266 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1267 char * sName = mpd_sanitizeArg(name);
1268 char * string = malloc(strlen("load")+strlen(sName)+5);
1269 sprintf(string,"load \"%s\"\n",sName);
1270 mpd_executeCommand(connection,string);
1271 free(string);
1272 free(sName);
1273 }
1275 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1276 char * sName = mpd_sanitizeArg(name);
1277 char * string = malloc(strlen("rm")+strlen(sName)+5);
1278 sprintf(string,"rm \"%s\"\n",sName);
1279 mpd_executeCommand(connection,string);
1280 free(string);
1281 free(sName);
1282 }
1284 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1285 mpd_executeCommand(connection,"shuffle\n");
1286 }
1288 void mpd_sendShuffleRangeCommand(mpd_Connection * connection, unsigned start, unsigned end) {
1289 char * string = malloc(strlen("shufflerange")+25);
1290 sprintf(string,"shuffle \"%u:%u\"\n", start, end);
1291 mpd_executeCommand(connection,string);
1292 free(string);
1293 }
1295 void mpd_sendClearCommand(mpd_Connection * connection) {
1296 mpd_executeCommand(connection,"clear\n");
1297 }
1299 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1300 char * string = malloc(strlen("play")+25);
1301 sprintf(string,"play \"%i\"\n",songPos);
1302 mpd_sendInfoCommand(connection,string);
1303 free(string);
1304 }
1306 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1307 char * string = malloc(strlen("playid")+25);
1308 sprintf(string,"playid \"%i\"\n",id);
1309 mpd_sendInfoCommand(connection,string);
1310 free(string);
1311 }
1313 void mpd_sendStopCommand(mpd_Connection * connection) {
1314 mpd_executeCommand(connection,"stop\n");
1315 }
1317 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1318 char * string = malloc(strlen("pause")+25);
1319 sprintf(string,"pause \"%i\"\n",pauseMode);
1320 mpd_executeCommand(connection,string);
1321 free(string);
1322 }
1324 void mpd_sendNextCommand(mpd_Connection * connection) {
1325 mpd_executeCommand(connection,"next\n");
1326 }
1328 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1329 char * string = malloc(strlen("move")+25);
1330 sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1331 mpd_sendInfoCommand(connection,string);
1332 free(string);
1333 }
1335 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1336 char * string = malloc(strlen("moveid")+25);
1337 sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1338 mpd_sendInfoCommand(connection,string);
1339 free(string);
1340 }
1342 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1343 char * string = malloc(strlen("swap")+25);
1344 sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1345 mpd_sendInfoCommand(connection,string);
1346 free(string);
1347 }
1349 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1350 char * string = malloc(strlen("swapid")+25);
1351 sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1352 mpd_sendInfoCommand(connection,string);
1353 free(string);
1354 }
1356 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int to) {
1357 char * string = malloc(strlen("seek")+25);
1358 sprintf(string,"seek \"%i\" \"%i\"\n", song, to);
1359 mpd_sendInfoCommand(connection,string);
1360 free(string);
1361 }
1363 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int to) {
1364 char * string = malloc(strlen("seekid")+25);
1365 sprintf(string,"seekid \"%i\" \"%i\"\n", id, to);
1366 mpd_sendInfoCommand(connection,string);
1367 free(string);
1368 }
1370 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1371 char *sPath = mpd_sanitizeArg(path);
1372 char * string = malloc(strlen("update")+strlen(sPath)+5);
1373 sprintf(string,"update \"%s\"\n",sPath);
1374 mpd_sendInfoCommand(connection,string);
1375 free(string);
1376 free(sPath);
1377 }
1379 int mpd_getUpdateId(mpd_Connection * connection) {
1380 char * jobid;
1381 int ret = 0;
1383 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1384 if(jobid) {
1385 ret = atoi(jobid);
1386 free(jobid);
1387 }
1389 return ret;
1390 }
1392 void mpd_sendPrevCommand(mpd_Connection * connection) {
1393 mpd_executeCommand(connection,"previous\n");
1394 }
1396 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1397 char * string = malloc(strlen("repeat")+25);
1398 sprintf(string,"repeat \"%i\"\n",repeatMode);
1399 mpd_executeCommand(connection,string);
1400 free(string);
1401 }
1403 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1404 char * string = malloc(strlen("random")+25);
1405 sprintf(string,"random \"%i\"\n",randomMode);
1406 mpd_executeCommand(connection,string);
1407 free(string);
1408 }
1410 void mpd_sendSingleCommand(mpd_Connection * connection, int singleMode) {
1411 char * string = malloc(strlen("single")+25);
1412 sprintf(string,"single \"%i\"\n",singleMode);
1413 mpd_executeCommand(connection,string);
1414 free(string);
1415 }
1417 void mpd_sendConsumeCommand(mpd_Connection * connection, int consumeMode) {
1418 char * string = malloc(strlen("consume")+25);
1419 sprintf(string,"consume \"%i\"\n",consumeMode);
1420 mpd_executeCommand(connection,string);
1421 free(string);
1422 }
1424 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1425 char * string = malloc(strlen("setvol")+25);
1426 sprintf(string,"setvol \"%i\"\n",volumeChange);
1427 mpd_executeCommand(connection,string);
1428 free(string);
1429 }
1431 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1432 char * string = malloc(strlen("volume")+25);
1433 sprintf(string,"volume \"%i\"\n",volumeChange);
1434 mpd_executeCommand(connection,string);
1435 free(string);
1436 }
1438 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1439 char * string = malloc(strlen("crossfade")+25);
1440 sprintf(string,"crossfade \"%i\"\n",seconds);
1441 mpd_executeCommand(connection,string);
1442 free(string);
1443 }
1445 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1446 char * sPass = mpd_sanitizeArg(pass);
1447 char * string = malloc(strlen("password")+strlen(sPass)+5);
1448 sprintf(string,"password \"%s\"\n",sPass);
1449 mpd_executeCommand(connection,string);
1450 free(string);
1451 free(sPass);
1452 }
1454 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1455 if(connection->commandList) {
1456 strcpy(connection->errorStr,"already in command list mode");
1457 connection->error = 1;
1458 return;
1459 }
1460 connection->commandList = COMMAND_LIST;
1461 mpd_executeCommand(connection,"command_list_begin\n");
1462 }
1464 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1465 if(connection->commandList) {
1466 strcpy(connection->errorStr,"already in command list mode");
1467 connection->error = 1;
1468 return;
1469 }
1470 connection->commandList = COMMAND_LIST_OK;
1471 mpd_executeCommand(connection,"command_list_ok_begin\n");
1472 connection->listOks = 0;
1473 }
1475 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1476 if(!connection->commandList) {
1477 strcpy(connection->errorStr,"not in command list mode");
1478 connection->error = 1;
1479 return;
1480 }
1481 connection->commandList = 0;
1482 mpd_executeCommand(connection,"command_list_end\n");
1483 }
1485 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1486 mpd_executeCommand(connection,"outputs\n");
1487 }
1489 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1490 mpd_OutputEntity * output = NULL;
1492 if(connection->doneProcessing || (connection->listOks &&
1493 connection->doneListOk))
1494 {
1495 return NULL;
1496 }
1498 if(connection->error) return NULL;
1500 output = malloc(sizeof(mpd_OutputEntity));
1501 output->id = -10;
1502 output->name = NULL;
1503 output->enabled = 0;
1505 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1507 while(connection->returnElement) {
1508 mpd_ReturnElement * re = connection->returnElement;
1509 if(strcmp(re->name,"outputid")==0) {
1510 if(output!=NULL && output->id>=0) return output;
1511 output->id = atoi(re->value);
1512 }
1513 else if(strcmp(re->name,"outputname")==0) {
1514 output->name = strdup(re->value);
1515 }
1516 else if(strcmp(re->name,"outputenabled")==0) {
1517 output->enabled = atoi(re->value);
1518 }
1520 mpd_getNextReturnElement(connection);
1521 if(connection->error) {
1522 free(output);
1523 return NULL;
1524 }
1526 }
1528 return output;
1529 }
1531 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1532 char * string = malloc(strlen("enableoutput")+25);
1533 sprintf(string,"enableoutput \"%i\"\n",outputId);
1534 mpd_executeCommand(connection,string);
1535 free(string);
1536 }
1538 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1539 char * string = malloc(strlen("disableoutput")+25);
1540 sprintf(string,"disableoutput \"%i\"\n",outputId);
1541 mpd_executeCommand(connection,string);
1542 free(string);
1543 }
1545 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1546 free(output->name);
1547 free(output);
1548 }
1550 /**
1551 * mpd_sendNotCommandsCommand
1552 * odd naming, but it gets the not allowed commands
1553 */
1555 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1556 mpd_executeCommand(connection,"notcommands\n");
1557 }
1559 /**
1560 * mpd_sendCommandsCommand
1561 * odd naming, but it gets the allowed commands
1562 */
1564 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1565 mpd_executeCommand(connection,"commands\n");
1566 }
1567 /**
1568 * Get the next returned command
1569 */
1570 char * mpd_getNextCommand(mpd_Connection * connection) {
1571 return mpd_getNextReturnElementNamed(connection,"command");
1572 }
1574 void mpd_startSearch(mpd_Connection * connection,int exact) {
1575 if(connection->request) {
1576 /* search/find already in progress */
1577 /* TODO: set error here? */
1578 return;
1579 }
1580 if(exact){
1581 connection->request = strdup("find");
1582 }
1583 else{
1584 connection->request = strdup("search");
1585 }
1586 }
1589 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1590 if(connection->request) {
1591 /* search/find already in progress */
1592 /* TODO: set error here? */
1593 return;
1594 }
1595 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1596 /* set error here */
1597 return;
1598 }
1600 connection->request = malloc(sizeof(char)*(
1601 /* length of the field name */
1602 strlen(mpdTagItemKeys[field])+
1603 /* "list"+space+\0 */
1604 6
1605 ));
1606 sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1607 }
1611 void mpd_addConstraintSearch(mpd_Connection *connection,
1612 int field,
1613 char *name)
1614 {
1615 char *arg = NULL;
1616 if(!connection->request){
1617 return;
1618 }
1619 if(name == NULL) {
1620 return;
1621 }
1622 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1623 return;
1624 }
1625 /* clean up the query */
1626 arg = mpd_sanitizeArg(name);
1627 /* create space for the query */
1628 connection->request = realloc(connection->request, (
1629 /* length of the old string */
1630 strlen(connection->request)+
1631 /* space between */
1632 1+
1633 /* length of the field name */
1634 strlen(mpdTagItemKeys[field])+
1635 /* space plus starting " */
1636 2+
1637 /* length of search term */
1638 strlen(arg)+
1639 /* closing " +\0 that is added sprintf */
1640 2
1641 )*sizeof(char));
1642 /* and form the query */
1643 sprintf(connection->request, "%s %s \"%s\"",
1644 connection->request,
1645 mpdTagItemKeys[field],
1646 arg);
1647 free(arg);
1648 }
1651 void mpd_commitSearch(mpd_Connection *connection)
1652 {
1653 if(connection->request)
1654 {
1655 int length = strlen(connection->request);
1656 /* fixing up the string for mpd to like */
1657 connection->request = realloc(connection->request,
1658 (length+ /* old length */
1659 2 /* closing \n and \0 */
1660 )*sizeof(char));
1661 connection->request[length] = '\n';
1662 connection->request[length+1] = '\0';
1663 /* and off we go */
1664 mpd_sendInfoCommand(connection, connection->request);
1665 /* clean up a bit */
1666 free(connection->request);
1667 connection->request = NULL;
1668 }
1669 }
1671 /**
1672 * @param connection a MpdConnection
1673 * @param path the path to the playlist.
1674 *
1675 * List the content, with full metadata, of a stored playlist.
1676 *
1677 */
1678 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1679 {
1680 char *arg = mpd_sanitizeArg(path);
1681 char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1682 sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1683 mpd_sendInfoCommand(connection, query);
1684 free(arg);
1685 free(query);
1686 }
1688 /**
1689 * @param connection a MpdConnection
1690 * @param path the path to the playlist.
1691 *
1692 * List the content of a stored playlist.
1693 *
1694 */
1695 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1696 {
1697 char *arg = mpd_sanitizeArg(path);
1698 char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1699 sprintf(query, "listplaylist \"%s\"\n",arg);
1700 mpd_sendInfoCommand(connection, query);
1701 free(arg);
1702 free(query);
1703 }