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 mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
168 connection->timeout.tv_sec = (int)timeout;
169 connection->timeout.tv_usec = (int)(timeout*1e6 -
170 connection->timeout.tv_sec*1000000 +
171 0.5);
172 }
174 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
175 char * output) {
176 char * tmp;
177 char * test;
178 int i;
180 if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
181 snprintf(connection->errorStr, sizeof(connection->errorStr),
182 "mpd not running on port %i on host \"%s\"",
183 port,host);
184 connection->error = MPD_ERROR_NOTMPD;
185 return 1;
186 }
188 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
190 for(i=0;i<3;i++) {
191 if(tmp) connection->version[i] = strtol(tmp,&test,10);
193 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
194 snprintf(connection->errorStr, sizeof(connection->errorStr),
195 "error parsing version number at "
196 "\"%s\"",
197 &output[strlen(MPD_WELCOME_MESSAGE)]);
198 connection->error = MPD_ERROR_NOTMPD;
199 return 1;
200 }
201 tmp = ++test;
202 }
204 return 0;
205 }
207 /**
208 * Wait for the socket to become readable.
209 */
210 static int mpd_wait(mpd_Connection *connection)
211 {
212 struct timeval tv;
213 fd_set fds;
214 int ret;
216 assert(connection->sock >= 0);
218 while (1) {
219 tv = connection->timeout;
220 FD_ZERO(&fds);
221 FD_SET(connection->sock, &fds);
223 ret = select(connection->sock + 1, &fds, NULL, NULL, &tv);
224 if (ret > 0)
225 return 0;
227 if (ret == 0 || !SELECT_ERRNO_IGNORE)
228 return -1;
229 }
230 }
232 /**
233 * Wait until the socket is connected and check its result. Returns 1
234 * on success, 0 on timeout, -errno on error.
235 */
236 static int mpd_wait_connected(mpd_Connection *connection)
237 {
238 int ret;
239 int s_err = 0;
240 socklen_t s_err_size = sizeof(s_err);
242 ret = mpd_wait(connection);
243 if (ret < 0)
244 return 0;
246 ret = getsockopt(connection->sock, SOL_SOCKET, SO_ERROR,
247 (char*)&s_err, &s_err_size);
248 if (ret < 0)
249 return -errno;
251 if (s_err != 0)
252 return -s_err;
254 return 1;
255 }
257 /**
258 * Attempt to read data from the socket into the input buffer.
259 * Returns 0 on success, -1 on error.
260 */
261 static int mpd_recv(mpd_Connection *connection)
262 {
263 int ret;
264 ssize_t nbytes;
266 assert(connection != NULL);
267 assert(connection->buflen <= sizeof(connection->buffer));
268 assert(connection->bufstart <= connection->buflen);
270 if (connection->sock < 0) {
271 strcpy(connection->errorStr, "not connected");
272 connection->error = MPD_ERROR_CONNCLOSED;
273 connection->doneProcessing = 1;
274 connection->doneListOk = 0;
275 return -1;
276 }
278 if (connection->buflen >= sizeof(connection->buffer)) {
279 /* delete consumed data from beginning of buffer */
280 connection->buflen -= connection->bufstart;
281 memmove(connection->buffer,
282 connection->buffer + connection->bufstart,
283 connection->buflen);
284 connection->bufstart = 0;
285 }
287 if (connection->buflen >= sizeof(connection->buffer)) {
288 strcpy(connection->errorStr, "buffer overrun");
289 connection->error = MPD_ERROR_BUFFEROVERRUN;
290 connection->doneProcessing = 1;
291 connection->doneListOk = 0;
292 return -1;
293 }
295 while (1) {
296 ret = mpd_wait(connection);
297 if (ret < 0) {
298 strcpy(connection->errorStr, "connection timeout");
299 connection->error = MPD_ERROR_TIMEOUT;
300 connection->doneProcessing = 1;
301 connection->doneListOk = 0;
302 return -1;
303 }
305 nbytes = read(connection->sock,
306 connection->buffer + connection->buflen,
307 sizeof(connection->buffer) - connection->buflen);
308 if (nbytes > 0) {
309 connection->buflen += nbytes;
310 return 0;
311 }
313 if (nbytes == 0 || !SENDRECV_ERRNO_IGNORE) {
314 strcpy(connection->errorStr, "connection closed");
315 connection->error = MPD_ERROR_CONNCLOSED;
316 connection->doneProcessing = 1;
317 connection->doneListOk = 0;
318 return -1;
319 }
320 }
321 }
323 static int
324 mpd_connect(mpd_Connection *connection, const char * host, int port)
325 {
326 struct resolver *resolver;
327 const struct resolver_address *address;
328 int ret;
330 resolver = resolver_new(host, port);
331 if (resolver == NULL) {
332 snprintf(connection->errorStr, sizeof(connection->errorStr),
333 "host \"%s\" not found", host);
334 connection->error = MPD_ERROR_UNKHOST;
335 return -1;
336 }
338 while ((address = resolver_next(resolver)) != NULL) {
339 connection->sock = socket(address->family, SOCK_STREAM,
340 address->protocol);
341 if (connection->sock < 0) {
342 snprintf(connection->errorStr,
343 sizeof(connection->errorStr),
344 "problems creating socket: %s",
345 strerror(errno));
346 connection->error = MPD_ERROR_SYSTEM;
347 continue;
348 }
350 ret = do_connect_fail(connection,
351 address->addr, address->addrlen);
352 if (ret != 0) {
353 snprintf(connection->errorStr,
354 sizeof(connection->errorStr),
355 "problems connecting to \"%s\" on port"
356 " %i: %s", host, port, strerror(errno));
357 connection->error = MPD_ERROR_CONNPORT;
359 closesocket(connection->sock);
360 connection->sock = -1;
361 continue;
362 }
364 ret = mpd_wait_connected(connection);
365 if (ret > 0) {
366 resolver_free(resolver);
367 mpd_clearError(connection);
368 return 0;
369 }
371 if (ret == 0) {
372 snprintf(connection->errorStr,
373 sizeof(connection->errorStr),
374 "timeout in attempting to get a response from"
375 " \"%s\" on port %i", host, port);
376 connection->error = MPD_ERROR_NORESPONSE;
377 } else if (ret < 0) {
378 snprintf(connection->errorStr,
379 sizeof(connection->errorStr),
380 "problems connecting to \"%s\" on port %i: %s",
381 host, port, strerror(-ret));
382 connection->error = MPD_ERROR_CONNPORT;
383 }
385 closesocket(connection->sock);
386 connection->sock = -1;
387 }
389 resolver_free(resolver);
390 return -1;
391 }
393 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
394 int err;
395 char * rt;
396 mpd_Connection * connection = malloc(sizeof(mpd_Connection));
398 connection->buflen = 0;
399 connection->bufstart = 0;
400 mpd_clearError(connection);
401 connection->doneProcessing = 0;
402 connection->commandList = 0;
403 connection->listOks = 0;
404 connection->doneListOk = 0;
405 connection->returnElement = NULL;
406 connection->request = NULL;
408 if (winsock_dll_error(connection))
409 return connection;
411 mpd_setConnectionTimeout(connection,timeout);
413 err = mpd_connect(connection, host, port);
414 if (err < 0)
415 return connection;
417 while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
418 err = mpd_recv(connection);
419 if (err < 0)
420 return connection;
421 }
423 *rt = '\0';
424 if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
425 connection->doneProcessing = 1;
427 connection->buflen -= rt + 1 - connection->buffer;
428 memmove(connection->buffer, rt + 1, connection->buflen);
430 return connection;
431 }
433 void mpd_clearError(mpd_Connection * connection) {
434 connection->error = 0;
435 connection->errorStr[0] = '\0';
436 }
438 void mpd_closeConnection(mpd_Connection * connection) {
439 closesocket(connection->sock);
440 if(connection->returnElement) free(connection->returnElement);
441 if(connection->request) free(connection->request);
442 free(connection);
443 WSACleanup();
444 }
446 static void mpd_executeCommand(mpd_Connection *connection,
447 const char *command) {
448 int ret;
449 struct timeval tv;
450 fd_set fds;
451 const char *commandPtr = command;
452 int commandLen = strlen(command);
454 if (connection->sock < 0) {
455 strcpy(connection->errorStr, "not connected");
456 connection->error = MPD_ERROR_CONNCLOSED;
457 return;
458 }
460 if (!connection->doneProcessing && !connection->commandList) {
461 strcpy(connection->errorStr,
462 "not done processing current command");
463 connection->error = 1;
464 return;
465 }
467 mpd_clearError(connection);
469 FD_ZERO(&fds);
470 FD_SET(connection->sock,&fds);
471 tv.tv_sec = connection->timeout.tv_sec;
472 tv.tv_usec = connection->timeout.tv_usec;
474 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
475 (ret==-1 && SELECT_ERRNO_IGNORE)) {
476 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
477 if(ret<=0)
478 {
479 if (SENDRECV_ERRNO_IGNORE) continue;
480 snprintf(connection->errorStr, sizeof(connection->errorStr),
481 "problems giving command \"%s\"",command);
482 connection->error = MPD_ERROR_SENDING;
483 return;
484 }
485 else {
486 commandPtr+=ret;
487 commandLen-=ret;
488 }
490 if(commandLen<=0) break;
491 }
493 if(commandLen>0) {
494 perror("");
495 snprintf(connection->errorStr, sizeof(connection->errorStr),
496 "timeout sending command \"%s\"",command);
497 connection->error = MPD_ERROR_TIMEOUT;
498 return;
499 }
501 if(!connection->commandList) connection->doneProcessing = 0;
502 else if(connection->commandList == COMMAND_LIST_OK) {
503 connection->listOks++;
504 }
505 }
507 static void mpd_getNextReturnElement(mpd_Connection * connection) {
508 char * output = NULL;
509 char * rt = NULL;
510 char * name = NULL;
511 char * value = NULL;
512 char * tok = NULL;
513 int err;
514 int pos;
516 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
517 connection->returnElement = NULL;
519 if (connection->doneProcessing ||
520 (connection->listOks && connection->doneListOk)) {
521 strcpy(connection->errorStr,"already done processing current command");
522 connection->error = 1;
523 return;
524 }
526 while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
527 connection->buflen - connection->bufstart))) {
528 err = mpd_recv(connection);
529 if (err < 0)
530 return;
531 }
533 *rt = '\0';
534 output = connection->buffer+connection->bufstart;
535 connection->bufstart = rt - connection->buffer + 1;
537 if(strcmp(output,"OK")==0) {
538 if(connection->listOks > 0) {
539 strcpy(connection->errorStr, "expected more list_OK's");
540 connection->error = 1;
541 }
542 connection->listOks = 0;
543 connection->doneProcessing = 1;
544 connection->doneListOk = 0;
545 return;
546 }
548 if(strcmp(output, "list_OK") == 0) {
549 if(!connection->listOks) {
550 strcpy(connection->errorStr,
551 "got an unexpected list_OK");
552 connection->error = 1;
553 }
554 else {
555 connection->doneListOk = 1;
556 connection->listOks--;
557 }
558 return;
559 }
561 if(strncmp(output,"ACK",strlen("ACK"))==0) {
562 size_t length = strlen(output);
563 char * test;
564 char * needle;
565 int val;
567 if (length >= sizeof(connection->errorStr))
568 length = sizeof(connection->errorStr) - 1;
570 memcpy(connection->errorStr, output, length);
571 connection->errorStr[length] = 0;
572 connection->error = MPD_ERROR_ACK;
573 connection->errorCode = MPD_ACK_ERROR_UNK;
574 connection->errorAt = MPD_ERROR_AT_UNK;
575 connection->doneProcessing = 1;
576 connection->doneListOk = 0;
578 needle = strchr(output, '[');
579 if(!needle) return;
580 val = strtol(needle+1, &test, 10);
581 if(*test != '@') return;
582 connection->errorCode = val;
583 val = strtol(test+1, &test, 10);
584 if(*test != ']') return;
585 connection->errorAt = val;
586 return;
587 }
589 tok = strchr(output, ':');
590 if (!tok) return;
591 pos = tok - output;
592 value = ++tok;
593 name = output;
594 name[pos] = '\0';
596 if(value[0]==' ') {
597 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
598 }
599 else {
600 snprintf(connection->errorStr, sizeof(connection->errorStr),
601 "error parsing: %s:%s",name,value);
602 connection->error = 1;
603 }
604 }
606 void mpd_finishCommand(mpd_Connection * connection) {
607 while(!connection->doneProcessing) {
608 if(connection->doneListOk) connection->doneListOk = 0;
609 mpd_getNextReturnElement(connection);
610 }
611 }
613 static void mpd_finishListOkCommand(mpd_Connection * connection) {
614 while(!connection->doneProcessing && connection->listOks &&
615 !connection->doneListOk)
616 {
617 mpd_getNextReturnElement(connection);
618 }
619 }
621 int mpd_nextListOkCommand(mpd_Connection * connection) {
622 mpd_finishListOkCommand(connection);
623 if(!connection->doneProcessing) connection->doneListOk = 0;
624 if(connection->listOks == 0 || connection->doneProcessing) return -1;
625 return 0;
626 }
628 void mpd_sendStatusCommand(mpd_Connection * connection) {
629 mpd_executeCommand(connection,"status\n");
630 }
632 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
633 mpd_Status * status;
635 /*mpd_executeCommand(connection,"status\n");
637 if(connection->error) return NULL;*/
639 if(connection->doneProcessing || (connection->listOks &&
640 connection->doneListOk))
641 {
642 return NULL;
643 }
645 if(!connection->returnElement) mpd_getNextReturnElement(connection);
647 status = malloc(sizeof(mpd_Status));
648 status->volume = -1;
649 status->repeat = 0;
650 status->random = 0;
651 status->playlist = -1;
652 status->playlistLength = -1;
653 status->state = -1;
654 status->song = 0;
655 status->songid = 0;
656 status->elapsedTime = 0;
657 status->totalTime = 0;
658 status->bitRate = 0;
659 status->sampleRate = 0;
660 status->bits = 0;
661 status->channels = 0;
662 status->crossfade = -1;
663 status->error = NULL;
664 status->updatingDb = 0;
666 if(connection->error) {
667 free(status);
668 return NULL;
669 }
670 while(connection->returnElement) {
671 mpd_ReturnElement * re = connection->returnElement;
672 if(strcmp(re->name,"volume")==0) {
673 status->volume = atoi(re->value);
674 }
675 else if(strcmp(re->name,"repeat")==0) {
676 status->repeat = atoi(re->value);
677 }
678 else if(strcmp(re->name,"random")==0) {
679 status->random = atoi(re->value);
680 }
681 else if(strcmp(re->name,"playlist")==0) {
682 status->playlist = strtol(re->value,NULL,10);
683 }
684 else if(strcmp(re->name,"playlistlength")==0) {
685 status->playlistLength = atoi(re->value);
686 }
687 else if(strcmp(re->name,"bitrate")==0) {
688 status->bitRate = atoi(re->value);
689 }
690 else if(strcmp(re->name,"state")==0) {
691 if(strcmp(re->value,"play")==0) {
692 status->state = MPD_STATUS_STATE_PLAY;
693 }
694 else if(strcmp(re->value,"stop")==0) {
695 status->state = MPD_STATUS_STATE_STOP;
696 }
697 else if(strcmp(re->value,"pause")==0) {
698 status->state = MPD_STATUS_STATE_PAUSE;
699 }
700 else {
701 status->state = MPD_STATUS_STATE_UNKNOWN;
702 }
703 }
704 else if(strcmp(re->name,"song")==0) {
705 status->song = atoi(re->value);
706 }
707 else if(strcmp(re->name,"songid")==0) {
708 status->songid = atoi(re->value);
709 }
710 else if(strcmp(re->name,"time")==0) {
711 char * tok = strchr(re->value,':');
712 /* the second strchr below is a safety check */
713 if (tok && (strchr(tok,0) > (tok+1))) {
714 /* atoi stops at the first non-[0-9] char: */
715 status->elapsedTime = atoi(re->value);
716 status->totalTime = atoi(tok+1);
717 }
718 }
719 else if(strcmp(re->name,"error")==0) {
720 status->error = strdup(re->value);
721 }
722 else if(strcmp(re->name,"xfade")==0) {
723 status->crossfade = atoi(re->value);
724 }
725 else if(strcmp(re->name,"updating_db")==0) {
726 status->updatingDb = atoi(re->value);
727 }
728 else if(strcmp(re->name,"audio")==0) {
729 char * tok = strchr(re->value,':');
730 if (tok && (strchr(tok,0) > (tok+1))) {
731 status->sampleRate = atoi(re->value);
732 status->bits = atoi(++tok);
733 tok = strchr(tok,':');
734 if (tok && (strchr(tok,0) > (tok+1)))
735 status->channels = atoi(tok+1);
736 }
737 }
739 mpd_getNextReturnElement(connection);
740 if(connection->error) {
741 free(status);
742 return NULL;
743 }
744 }
746 if(connection->error) {
747 free(status);
748 return NULL;
749 }
750 else if(status->state<0) {
751 strcpy(connection->errorStr,"state not found");
752 connection->error = 1;
753 free(status);
754 return NULL;
755 }
757 return status;
758 }
760 void mpd_freeStatus(mpd_Status * status) {
761 if(status->error) free(status->error);
762 free(status);
763 }
765 void mpd_sendStatsCommand(mpd_Connection * connection) {
766 mpd_executeCommand(connection,"stats\n");
767 }
769 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
770 mpd_Stats * stats;
772 /*mpd_executeCommand(connection,"stats\n");
774 if(connection->error) return NULL;*/
776 if(connection->doneProcessing || (connection->listOks &&
777 connection->doneListOk))
778 {
779 return NULL;
780 }
782 if(!connection->returnElement) mpd_getNextReturnElement(connection);
784 stats = malloc(sizeof(mpd_Stats));
785 stats->numberOfArtists = 0;
786 stats->numberOfAlbums = 0;
787 stats->numberOfSongs = 0;
788 stats->uptime = 0;
789 stats->dbUpdateTime = 0;
790 stats->playTime = 0;
791 stats->dbPlayTime = 0;
793 if(connection->error) {
794 free(stats);
795 return NULL;
796 }
797 while(connection->returnElement) {
798 mpd_ReturnElement * re = connection->returnElement;
799 if(strcmp(re->name,"artists")==0) {
800 stats->numberOfArtists = atoi(re->value);
801 }
802 else if(strcmp(re->name,"albums")==0) {
803 stats->numberOfAlbums = atoi(re->value);
804 }
805 else if(strcmp(re->name,"songs")==0) {
806 stats->numberOfSongs = atoi(re->value);
807 }
808 else if(strcmp(re->name,"uptime")==0) {
809 stats->uptime = strtol(re->value,NULL,10);
810 }
811 else if(strcmp(re->name,"db_update")==0) {
812 stats->dbUpdateTime = strtol(re->value,NULL,10);
813 }
814 else if(strcmp(re->name,"playtime")==0) {
815 stats->playTime = strtol(re->value,NULL,10);
816 }
817 else if(strcmp(re->name,"db_playtime")==0) {
818 stats->dbPlayTime = strtol(re->value,NULL,10);
819 }
821 mpd_getNextReturnElement(connection);
822 if(connection->error) {
823 free(stats);
824 return NULL;
825 }
826 }
828 if(connection->error) {
829 free(stats);
830 return NULL;
831 }
833 return stats;
834 }
836 void mpd_freeStats(mpd_Stats * stats) {
837 free(stats);
838 }
840 static void mpd_initDirectory(mpd_Directory * directory) {
841 directory->path = NULL;
842 }
844 static void mpd_finishDirectory(mpd_Directory * directory) {
845 if (directory->path)
846 str_pool_put(directory->path);
847 }
849 mpd_Directory * mpd_newDirectory(void) {
850 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
852 mpd_initDirectory(directory);
854 return directory;
855 }
857 void mpd_freeDirectory(mpd_Directory * directory) {
858 mpd_finishDirectory(directory);
860 free(directory);
861 }
863 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
864 mpd_Directory * ret = mpd_newDirectory();
866 if (directory->path)
867 ret->path = str_pool_dup(directory->path);
869 return ret;
870 }
872 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
873 playlist->path = NULL;
874 }
876 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
877 if (playlist->path)
878 str_pool_put(playlist->path);
879 }
881 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
882 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
884 mpd_initPlaylistFile(playlist);
886 return playlist;
887 }
889 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
890 mpd_finishPlaylistFile(playlist);
891 free(playlist);
892 }
894 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
895 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
897 if (playlist->path)
898 ret->path = str_pool_dup(playlist->path);
900 return ret;
901 }
903 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
904 entity->info.directory = NULL;
905 }
907 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
908 if(entity->info.directory) {
909 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
910 mpd_freeDirectory(entity->info.directory);
911 }
912 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
913 mpd_freeSong(entity->info.song);
914 }
915 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
916 mpd_freePlaylistFile(entity->info.playlistFile);
917 }
918 }
919 }
921 mpd_InfoEntity * mpd_newInfoEntity(void) {
922 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
924 mpd_initInfoEntity(entity);
926 return entity;
927 }
929 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
930 mpd_finishInfoEntity(entity);
931 free(entity);
932 }
934 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
935 mpd_executeCommand(connection,command);
936 }
938 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
939 mpd_InfoEntity * entity = NULL;
941 if(connection->doneProcessing || (connection->listOks &&
942 connection->doneListOk)) {
943 return NULL;
944 }
946 if(!connection->returnElement) mpd_getNextReturnElement(connection);
948 if(connection->returnElement) {
949 if(strcmp(connection->returnElement->name,"file")==0) {
950 entity = mpd_newInfoEntity();
951 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
952 entity->info.song = mpd_newSong();
953 entity->info.song->file =
954 str_pool_dup(connection->returnElement->value);
955 }
956 else if(strcmp(connection->returnElement->name,
957 "directory")==0) {
958 entity = mpd_newInfoEntity();
959 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
960 entity->info.directory = mpd_newDirectory();
961 entity->info.directory->path =
962 str_pool_dup(connection->returnElement->value);
963 }
964 else if(strcmp(connection->returnElement->name,"playlist")==0) {
965 entity = mpd_newInfoEntity();
966 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
967 entity->info.playlistFile = mpd_newPlaylistFile();
968 entity->info.playlistFile->path =
969 str_pool_dup(connection->returnElement->value);
970 }
971 else if(strcmp(connection->returnElement->name, "cpos") == 0){
972 entity = mpd_newInfoEntity();
973 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
974 entity->info.song = mpd_newSong();
975 entity->info.song->pos = atoi(connection->returnElement->value);
976 }
977 else {
978 connection->error = 1;
979 strcpy(connection->errorStr,"problem parsing song info");
980 return NULL;
981 }
982 }
983 else return NULL;
985 mpd_getNextReturnElement(connection);
986 while(connection->returnElement) {
987 mpd_ReturnElement * re = connection->returnElement;
989 if(strcmp(re->name,"file")==0) return entity;
990 else if(strcmp(re->name,"directory")==0) return entity;
991 else if(strcmp(re->name,"playlist")==0) return entity;
992 else if(strcmp(re->name,"cpos")==0) return entity;
994 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
995 strlen(re->value)) {
996 if(!entity->info.song->artist &&
997 strcmp(re->name,"Artist")==0) {
998 entity->info.song->artist = str_pool_dup(re->value);
999 }
1000 else if(!entity->info.song->album &&
1001 strcmp(re->name,"Album")==0) {
1002 entity->info.song->album = str_pool_dup(re->value);
1003 }
1004 else if(!entity->info.song->title &&
1005 strcmp(re->name,"Title")==0) {
1006 entity->info.song->title = str_pool_dup(re->value);
1007 }
1008 else if(!entity->info.song->track &&
1009 strcmp(re->name,"Track")==0) {
1010 entity->info.song->track = str_pool_dup(re->value);
1011 }
1012 else if(!entity->info.song->name &&
1013 strcmp(re->name,"Name")==0) {
1014 entity->info.song->name = str_pool_dup(re->value);
1015 }
1016 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1017 strcmp(re->name,"Time")==0) {
1018 entity->info.song->time = atoi(re->value);
1019 }
1020 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1021 strcmp(re->name,"Pos")==0) {
1022 entity->info.song->pos = atoi(re->value);
1023 }
1024 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1025 strcmp(re->name,"Id")==0) {
1026 entity->info.song->id = atoi(re->value);
1027 }
1028 else if(!entity->info.song->date &&
1029 strcmp(re->name, "Date") == 0) {
1030 entity->info.song->date = str_pool_dup(re->value);
1031 }
1032 else if(!entity->info.song->genre &&
1033 strcmp(re->name, "Genre") == 0) {
1034 entity->info.song->genre = str_pool_dup(re->value);
1035 }
1036 else if(!entity->info.song->composer &&
1037 strcmp(re->name, "Composer") == 0) {
1038 entity->info.song->composer = str_pool_dup(re->value);
1039 }
1040 else if(!entity->info.song->disc &&
1041 strcmp(re->name, "Disc") == 0) {
1042 entity->info.song->disc = str_pool_dup(re->value);
1043 }
1044 else if(!entity->info.song->comment &&
1045 strcmp(re->name, "Comment") == 0) {
1046 entity->info.song->comment = str_pool_dup(re->value);
1047 }
1048 }
1049 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1050 }
1051 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1052 }
1054 mpd_getNextReturnElement(connection);
1055 }
1057 return entity;
1058 }
1060 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1061 const char * name)
1062 {
1063 if(connection->doneProcessing || (connection->listOks &&
1064 connection->doneListOk))
1065 {
1066 return NULL;
1067 }
1069 mpd_getNextReturnElement(connection);
1070 while(connection->returnElement) {
1071 mpd_ReturnElement * re = connection->returnElement;
1073 if(strcmp(re->name,name)==0) return strdup(re->value);
1074 mpd_getNextReturnElement(connection);
1075 }
1077 return NULL;
1078 }
1080 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1081 if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1082 {
1083 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1084 }
1085 return NULL;
1086 }
1088 char * mpd_getNextArtist(mpd_Connection * connection) {
1089 return mpd_getNextReturnElementNamed(connection,"Artist");
1090 }
1092 char * mpd_getNextAlbum(mpd_Connection * connection) {
1093 return mpd_getNextReturnElementNamed(connection,"Album");
1094 }
1096 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1097 char * string = malloc(strlen("playlistinfo")+25);
1098 sprintf(string,"playlistinfo \"%i\"\n",songPos);
1099 mpd_sendInfoCommand(connection,string);
1100 free(string);
1101 }
1103 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1104 char * string = malloc(strlen("playlistid")+25);
1105 sprintf(string, "playlistid \"%i\"\n", id);
1106 mpd_sendInfoCommand(connection, string);
1107 free(string);
1108 }
1110 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1111 char * string = malloc(strlen("plchanges")+25);
1112 sprintf(string,"plchanges \"%lld\"\n",playlist);
1113 mpd_sendInfoCommand(connection,string);
1114 free(string);
1115 }
1117 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1118 char * string = malloc(strlen("plchangesposid")+25);
1119 sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1120 mpd_sendInfoCommand(connection,string);
1121 free(string);
1122 }
1124 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1125 char * sDir = mpd_sanitizeArg(dir);
1126 char * string = malloc(strlen("listall")+strlen(sDir)+5);
1127 sprintf(string,"listall \"%s\"\n",sDir);
1128 mpd_sendInfoCommand(connection,string);
1129 free(string);
1130 free(sDir);
1131 }
1133 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1134 char * sDir = mpd_sanitizeArg(dir);
1135 char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1136 sprintf(string,"listallinfo \"%s\"\n",sDir);
1137 mpd_sendInfoCommand(connection,string);
1138 free(string);
1139 free(sDir);
1140 }
1142 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1143 char * sDir = mpd_sanitizeArg(dir);
1144 char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1145 sprintf(string,"lsinfo \"%s\"\n",sDir);
1146 mpd_sendInfoCommand(connection,string);
1147 free(string);
1148 free(sDir);
1149 }
1151 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1152 mpd_executeCommand(connection,"currentsong\n");
1153 }
1155 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1156 const char * str)
1157 {
1158 char st[10];
1159 char * string;
1160 char * sanitStr = mpd_sanitizeArg(str);
1161 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1162 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1163 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1164 else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1165 else {
1166 connection->error = 1;
1167 strcpy(connection->errorStr,"unknown table for search");
1168 return;
1169 }
1170 string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1171 sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1172 mpd_sendInfoCommand(connection,string);
1173 free(string);
1174 free(sanitStr);
1175 }
1177 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1178 const char * str)
1179 {
1180 char st[10];
1181 char * string;
1182 char * sanitStr = mpd_sanitizeArg(str);
1183 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1184 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1185 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1186 else {
1187 connection->error = 1;
1188 strcpy(connection->errorStr,"unknown table for find");
1189 return;
1190 }
1191 string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1192 sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1193 mpd_sendInfoCommand(connection,string);
1194 free(string);
1195 free(sanitStr);
1196 }
1198 void mpd_sendListCommand(mpd_Connection * connection, int table,
1199 const char * arg1)
1200 {
1201 char st[10];
1202 char * string;
1203 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1204 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1205 else {
1206 connection->error = 1;
1207 strcpy(connection->errorStr,"unknown table for list");
1208 return;
1209 }
1210 if(arg1) {
1211 char * sanitArg1 = mpd_sanitizeArg(arg1);
1212 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1213 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1214 free(sanitArg1);
1215 }
1216 else {
1217 string = malloc(strlen("list")+strlen(st)+3);
1218 sprintf(string,"list %s\n",st);
1219 }
1220 mpd_sendInfoCommand(connection,string);
1221 free(string);
1222 }
1224 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1225 char * sFile = mpd_sanitizeArg(file);
1226 char * string = malloc(strlen("add")+strlen(sFile)+5);
1227 sprintf(string,"add \"%s\"\n",sFile);
1228 mpd_executeCommand(connection,string);
1229 free(string);
1230 free(sFile);
1231 }
1233 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1234 char * string = malloc(strlen("delete")+25);
1235 sprintf(string,"delete \"%i\"\n",songPos);
1236 mpd_sendInfoCommand(connection,string);
1237 free(string);
1238 }
1240 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1241 char * string = malloc(strlen("deleteid")+25);
1242 sprintf(string, "deleteid \"%i\"\n", id);
1243 mpd_sendInfoCommand(connection,string);
1244 free(string);
1245 }
1247 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1248 char * sName = mpd_sanitizeArg(name);
1249 char * string = malloc(strlen("save")+strlen(sName)+5);
1250 sprintf(string,"save \"%s\"\n",sName);
1251 mpd_executeCommand(connection,string);
1252 free(string);
1253 free(sName);
1254 }
1256 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1257 char * sName = mpd_sanitizeArg(name);
1258 char * string = malloc(strlen("load")+strlen(sName)+5);
1259 sprintf(string,"load \"%s\"\n",sName);
1260 mpd_executeCommand(connection,string);
1261 free(string);
1262 free(sName);
1263 }
1265 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1266 char * sName = mpd_sanitizeArg(name);
1267 char * string = malloc(strlen("rm")+strlen(sName)+5);
1268 sprintf(string,"rm \"%s\"\n",sName);
1269 mpd_executeCommand(connection,string);
1270 free(string);
1271 free(sName);
1272 }
1274 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1275 mpd_executeCommand(connection,"shuffle\n");
1276 }
1278 void mpd_sendClearCommand(mpd_Connection * connection) {
1279 mpd_executeCommand(connection,"clear\n");
1280 }
1282 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1283 char * string = malloc(strlen("play")+25);
1284 sprintf(string,"play \"%i\"\n",songPos);
1285 mpd_sendInfoCommand(connection,string);
1286 free(string);
1287 }
1289 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1290 char * string = malloc(strlen("playid")+25);
1291 sprintf(string,"playid \"%i\"\n",id);
1292 mpd_sendInfoCommand(connection,string);
1293 free(string);
1294 }
1296 void mpd_sendStopCommand(mpd_Connection * connection) {
1297 mpd_executeCommand(connection,"stop\n");
1298 }
1300 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1301 char * string = malloc(strlen("pause")+25);
1302 sprintf(string,"pause \"%i\"\n",pauseMode);
1303 mpd_executeCommand(connection,string);
1304 free(string);
1305 }
1307 void mpd_sendNextCommand(mpd_Connection * connection) {
1308 mpd_executeCommand(connection,"next\n");
1309 }
1311 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1312 char * string = malloc(strlen("move")+25);
1313 sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1314 mpd_sendInfoCommand(connection,string);
1315 free(string);
1316 }
1318 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1319 char * string = malloc(strlen("moveid")+25);
1320 sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1321 mpd_sendInfoCommand(connection,string);
1322 free(string);
1323 }
1325 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1326 char * string = malloc(strlen("swap")+25);
1327 sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1328 mpd_sendInfoCommand(connection,string);
1329 free(string);
1330 }
1332 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1333 char * string = malloc(strlen("swapid")+25);
1334 sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1335 mpd_sendInfoCommand(connection,string);
1336 free(string);
1337 }
1339 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1340 char * string = malloc(strlen("seek")+25);
1341 sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1342 mpd_sendInfoCommand(connection,string);
1343 free(string);
1344 }
1346 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1347 char * string = malloc(strlen("seekid")+25);
1348 sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1349 mpd_sendInfoCommand(connection,string);
1350 free(string);
1351 }
1353 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1354 char *sPath = mpd_sanitizeArg(path);
1355 char * string = malloc(strlen("update")+strlen(sPath)+5);
1356 sprintf(string,"update \"%s\"\n",sPath);
1357 mpd_sendInfoCommand(connection,string);
1358 free(string);
1359 free(sPath);
1360 }
1362 int mpd_getUpdateId(mpd_Connection * connection) {
1363 char * jobid;
1364 int ret = 0;
1366 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1367 if(jobid) {
1368 ret = atoi(jobid);
1369 free(jobid);
1370 }
1372 return ret;
1373 }
1375 void mpd_sendPrevCommand(mpd_Connection * connection) {
1376 mpd_executeCommand(connection,"previous\n");
1377 }
1379 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1380 char * string = malloc(strlen("repeat")+25);
1381 sprintf(string,"repeat \"%i\"\n",repeatMode);
1382 mpd_executeCommand(connection,string);
1383 free(string);
1384 }
1386 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1387 char * string = malloc(strlen("random")+25);
1388 sprintf(string,"random \"%i\"\n",randomMode);
1389 mpd_executeCommand(connection,string);
1390 free(string);
1391 }
1393 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1394 char * string = malloc(strlen("setvol")+25);
1395 sprintf(string,"setvol \"%i\"\n",volumeChange);
1396 mpd_executeCommand(connection,string);
1397 free(string);
1398 }
1400 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1401 char * string = malloc(strlen("volume")+25);
1402 sprintf(string,"volume \"%i\"\n",volumeChange);
1403 mpd_executeCommand(connection,string);
1404 free(string);
1405 }
1407 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1408 char * string = malloc(strlen("crossfade")+25);
1409 sprintf(string,"crossfade \"%i\"\n",seconds);
1410 mpd_executeCommand(connection,string);
1411 free(string);
1412 }
1414 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1415 char * sPass = mpd_sanitizeArg(pass);
1416 char * string = malloc(strlen("password")+strlen(sPass)+5);
1417 sprintf(string,"password \"%s\"\n",sPass);
1418 mpd_executeCommand(connection,string);
1419 free(string);
1420 free(sPass);
1421 }
1423 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1424 if(connection->commandList) {
1425 strcpy(connection->errorStr,"already in command list mode");
1426 connection->error = 1;
1427 return;
1428 }
1429 connection->commandList = COMMAND_LIST;
1430 mpd_executeCommand(connection,"command_list_begin\n");
1431 }
1433 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1434 if(connection->commandList) {
1435 strcpy(connection->errorStr,"already in command list mode");
1436 connection->error = 1;
1437 return;
1438 }
1439 connection->commandList = COMMAND_LIST_OK;
1440 mpd_executeCommand(connection,"command_list_ok_begin\n");
1441 connection->listOks = 0;
1442 }
1444 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1445 if(!connection->commandList) {
1446 strcpy(connection->errorStr,"not in command list mode");
1447 connection->error = 1;
1448 return;
1449 }
1450 connection->commandList = 0;
1451 mpd_executeCommand(connection,"command_list_end\n");
1452 }
1454 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1455 mpd_executeCommand(connection,"outputs\n");
1456 }
1458 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1459 mpd_OutputEntity * output = NULL;
1461 if(connection->doneProcessing || (connection->listOks &&
1462 connection->doneListOk))
1463 {
1464 return NULL;
1465 }
1467 if(connection->error) return NULL;
1469 output = malloc(sizeof(mpd_OutputEntity));
1470 output->id = -10;
1471 output->name = NULL;
1472 output->enabled = 0;
1474 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1476 while(connection->returnElement) {
1477 mpd_ReturnElement * re = connection->returnElement;
1478 if(strcmp(re->name,"outputid")==0) {
1479 if(output!=NULL && output->id>=0) return output;
1480 output->id = atoi(re->value);
1481 }
1482 else if(strcmp(re->name,"outputname")==0) {
1483 output->name = strdup(re->value);
1484 }
1485 else if(strcmp(re->name,"outputenabled")==0) {
1486 output->enabled = atoi(re->value);
1487 }
1489 mpd_getNextReturnElement(connection);
1490 if(connection->error) {
1491 free(output);
1492 return NULL;
1493 }
1495 }
1497 return output;
1498 }
1500 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1501 char * string = malloc(strlen("enableoutput")+25);
1502 sprintf(string,"enableoutput \"%i\"\n",outputId);
1503 mpd_executeCommand(connection,string);
1504 free(string);
1505 }
1507 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1508 char * string = malloc(strlen("disableoutput")+25);
1509 sprintf(string,"disableoutput \"%i\"\n",outputId);
1510 mpd_executeCommand(connection,string);
1511 free(string);
1512 }
1514 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1515 free(output->name);
1516 free(output);
1517 }
1519 /**
1520 * mpd_sendNotCommandsCommand
1521 * odd naming, but it gets the not allowed commands
1522 */
1524 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1525 mpd_executeCommand(connection,"notcommands\n");
1526 }
1528 /**
1529 * mpd_sendCommandsCommand
1530 * odd naming, but it gets the allowed commands
1531 */
1533 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1534 mpd_executeCommand(connection,"commands\n");
1535 }
1536 /**
1537 * Get the next returned command
1538 */
1539 char * mpd_getNextCommand(mpd_Connection * connection) {
1540 return mpd_getNextReturnElementNamed(connection,"command");
1541 }
1543 void mpd_startSearch(mpd_Connection * connection,int exact) {
1544 if(connection->request) {
1545 /* search/find allready in progress */
1546 /* TODO: set error here? */
1547 return;
1548 }
1549 if(exact){
1550 connection->request = strdup("find");
1551 }
1552 else{
1553 connection->request = strdup("search");
1554 }
1555 }
1558 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1559 if(connection->request) {
1560 /* search/find allready in progress */
1561 /* TODO: set error here? */
1562 return;
1563 }
1564 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1565 /* set error here */
1566 return;
1567 }
1569 connection->request = malloc(sizeof(char)*(
1570 /* length of the field name */
1571 strlen(mpdTagItemKeys[field])+
1572 /* "list"+space+\0 */
1573 6
1574 ));
1575 sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1576 }
1580 void mpd_addConstraintSearch(mpd_Connection *connection,
1581 int field,
1582 char *name)
1583 {
1584 char *arg = NULL;
1585 if(!connection->request){
1586 return;
1587 }
1588 if(name == NULL) {
1589 return;
1590 }
1591 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1592 return;
1593 }
1594 /* clean up the query */
1595 arg = mpd_sanitizeArg(name);
1596 /* create space for the query */
1597 connection->request = realloc(connection->request, (
1598 /* length of the old string */
1599 strlen(connection->request)+
1600 /* space between */
1601 1+
1602 /* length of the field name */
1603 strlen(mpdTagItemKeys[field])+
1604 /* space plus starting " */
1605 2+
1606 /* length of search term */
1607 strlen(arg)+
1608 /* closign " +\0 that is added sprintf */
1609 2
1610 )*sizeof(char));
1611 /* and form the query */
1612 sprintf(connection->request, "%s %s \"%s\"",
1613 connection->request,
1614 mpdTagItemKeys[field],
1615 arg);
1616 free(arg);
1617 }
1620 void mpd_commitSearch(mpd_Connection *connection)
1621 {
1622 if(connection->request)
1623 {
1624 int length = strlen(connection->request);
1625 /* fixing up the string for mpd to like */
1626 connection->request = realloc(connection->request,
1627 (length+ /* old length */
1628 2 /* closing \n and \0 */
1629 )*sizeof(char));
1630 connection->request[length] = '\n';
1631 connection->request[length+1] = '\0';
1632 /* and off we go */
1633 mpd_sendInfoCommand(connection, connection->request);
1634 /* clean up a bit */
1635 free(connection->request);
1636 connection->request = NULL;
1637 }
1638 }
1640 /**
1641 * @param connection a MpdConnection
1642 * @param path the path to the playlist.
1643 *
1644 * List the content, with full metadata, of a stored playlist.
1645 *
1646 */
1647 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1648 {
1649 char *arg = mpd_sanitizeArg(path);
1650 char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1651 sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1652 mpd_sendInfoCommand(connection, query);
1653 free(arg);
1654 free(query);
1655 }
1657 /**
1658 * @param connection a MpdConnection
1659 * @param path the path to the playlist.
1660 *
1661 * List the content of a stored playlist.
1662 *
1663 */
1664 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1665 {
1666 char *arg = mpd_sanitizeArg(path);
1667 char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1668 sprintf(query, "listplaylist \"%s\"\n",arg);
1669 mpd_sendInfoCommand(connection, query);
1670 free(arg);
1671 free(query);
1672 }