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->doneProcessing && !connection->commandList) {
455 strcpy(connection->errorStr,
456 "not done processing current command");
457 connection->error = 1;
458 return;
459 }
461 mpd_clearError(connection);
463 FD_ZERO(&fds);
464 FD_SET(connection->sock,&fds);
465 tv.tv_sec = connection->timeout.tv_sec;
466 tv.tv_usec = connection->timeout.tv_usec;
468 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
469 (ret==-1 && SELECT_ERRNO_IGNORE)) {
470 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
471 if(ret<=0)
472 {
473 if (SENDRECV_ERRNO_IGNORE) continue;
474 snprintf(connection->errorStr, sizeof(connection->errorStr),
475 "problems giving command \"%s\"",command);
476 connection->error = MPD_ERROR_SENDING;
477 return;
478 }
479 else {
480 commandPtr+=ret;
481 commandLen-=ret;
482 }
484 if(commandLen<=0) break;
485 }
487 if(commandLen>0) {
488 perror("");
489 snprintf(connection->errorStr, sizeof(connection->errorStr),
490 "timeout sending command \"%s\"",command);
491 connection->error = MPD_ERROR_TIMEOUT;
492 return;
493 }
495 if(!connection->commandList) connection->doneProcessing = 0;
496 else if(connection->commandList == COMMAND_LIST_OK) {
497 connection->listOks++;
498 }
499 }
501 static void mpd_getNextReturnElement(mpd_Connection * connection) {
502 char * output = NULL;
503 char * rt = NULL;
504 char * name = NULL;
505 char * value = NULL;
506 char * tok = NULL;
507 int err;
508 int pos;
510 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
511 connection->returnElement = NULL;
513 if (connection->doneProcessing ||
514 (connection->listOks && connection->doneListOk)) {
515 strcpy(connection->errorStr,"already done processing current command");
516 connection->error = 1;
517 return;
518 }
520 while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
521 connection->buflen - connection->bufstart))) {
522 err = mpd_recv(connection);
523 if (err < 0)
524 return;
525 }
527 *rt = '\0';
528 output = connection->buffer+connection->bufstart;
529 connection->bufstart = rt - connection->buffer + 1;
531 if(strcmp(output,"OK")==0) {
532 if(connection->listOks > 0) {
533 strcpy(connection->errorStr, "expected more list_OK's");
534 connection->error = 1;
535 }
536 connection->listOks = 0;
537 connection->doneProcessing = 1;
538 connection->doneListOk = 0;
539 return;
540 }
542 if(strcmp(output, "list_OK") == 0) {
543 if(!connection->listOks) {
544 strcpy(connection->errorStr,
545 "got an unexpected list_OK");
546 connection->error = 1;
547 }
548 else {
549 connection->doneListOk = 1;
550 connection->listOks--;
551 }
552 return;
553 }
555 if(strncmp(output,"ACK",strlen("ACK"))==0) {
556 size_t length = strlen(output);
557 char * test;
558 char * needle;
559 int val;
561 if (length >= sizeof(connection->errorStr))
562 length = sizeof(connection->errorStr) - 1;
564 memcpy(connection->errorStr, output, length);
565 connection->errorStr[length] = 0;
566 connection->error = MPD_ERROR_ACK;
567 connection->errorCode = MPD_ACK_ERROR_UNK;
568 connection->errorAt = MPD_ERROR_AT_UNK;
569 connection->doneProcessing = 1;
570 connection->doneListOk = 0;
572 needle = strchr(output, '[');
573 if(!needle) return;
574 val = strtol(needle+1, &test, 10);
575 if(*test != '@') return;
576 connection->errorCode = val;
577 val = strtol(test+1, &test, 10);
578 if(*test != ']') return;
579 connection->errorAt = val;
580 return;
581 }
583 tok = strchr(output, ':');
584 if (!tok) return;
585 pos = tok - output;
586 value = ++tok;
587 name = output;
588 name[pos] = '\0';
590 if(value[0]==' ') {
591 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
592 }
593 else {
594 snprintf(connection->errorStr, sizeof(connection->errorStr),
595 "error parsing: %s:%s",name,value);
596 connection->error = 1;
597 }
598 }
600 void mpd_finishCommand(mpd_Connection * connection) {
601 while(!connection->doneProcessing) {
602 if(connection->doneListOk) connection->doneListOk = 0;
603 mpd_getNextReturnElement(connection);
604 }
605 }
607 static void mpd_finishListOkCommand(mpd_Connection * connection) {
608 while(!connection->doneProcessing && connection->listOks &&
609 !connection->doneListOk)
610 {
611 mpd_getNextReturnElement(connection);
612 }
613 }
615 int mpd_nextListOkCommand(mpd_Connection * connection) {
616 mpd_finishListOkCommand(connection);
617 if(!connection->doneProcessing) connection->doneListOk = 0;
618 if(connection->listOks == 0 || connection->doneProcessing) return -1;
619 return 0;
620 }
622 void mpd_sendStatusCommand(mpd_Connection * connection) {
623 mpd_executeCommand(connection,"status\n");
624 }
626 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
627 mpd_Status * status;
629 /*mpd_executeCommand(connection,"status\n");
631 if(connection->error) return NULL;*/
633 if(connection->doneProcessing || (connection->listOks &&
634 connection->doneListOk))
635 {
636 return NULL;
637 }
639 if(!connection->returnElement) mpd_getNextReturnElement(connection);
641 status = malloc(sizeof(mpd_Status));
642 status->volume = -1;
643 status->repeat = 0;
644 status->random = 0;
645 status->playlist = -1;
646 status->playlistLength = -1;
647 status->state = -1;
648 status->song = 0;
649 status->songid = 0;
650 status->elapsedTime = 0;
651 status->totalTime = 0;
652 status->bitRate = 0;
653 status->sampleRate = 0;
654 status->bits = 0;
655 status->channels = 0;
656 status->crossfade = -1;
657 status->error = NULL;
658 status->updatingDb = 0;
660 if(connection->error) {
661 free(status);
662 return NULL;
663 }
664 while(connection->returnElement) {
665 mpd_ReturnElement * re = connection->returnElement;
666 if(strcmp(re->name,"volume")==0) {
667 status->volume = atoi(re->value);
668 }
669 else if(strcmp(re->name,"repeat")==0) {
670 status->repeat = atoi(re->value);
671 }
672 else if(strcmp(re->name,"random")==0) {
673 status->random = atoi(re->value);
674 }
675 else if(strcmp(re->name,"playlist")==0) {
676 status->playlist = strtol(re->value,NULL,10);
677 }
678 else if(strcmp(re->name,"playlistlength")==0) {
679 status->playlistLength = atoi(re->value);
680 }
681 else if(strcmp(re->name,"bitrate")==0) {
682 status->bitRate = atoi(re->value);
683 }
684 else if(strcmp(re->name,"state")==0) {
685 if(strcmp(re->value,"play")==0) {
686 status->state = MPD_STATUS_STATE_PLAY;
687 }
688 else if(strcmp(re->value,"stop")==0) {
689 status->state = MPD_STATUS_STATE_STOP;
690 }
691 else if(strcmp(re->value,"pause")==0) {
692 status->state = MPD_STATUS_STATE_PAUSE;
693 }
694 else {
695 status->state = MPD_STATUS_STATE_UNKNOWN;
696 }
697 }
698 else if(strcmp(re->name,"song")==0) {
699 status->song = atoi(re->value);
700 }
701 else if(strcmp(re->name,"songid")==0) {
702 status->songid = atoi(re->value);
703 }
704 else if(strcmp(re->name,"time")==0) {
705 char * tok = strchr(re->value,':');
706 /* the second strchr below is a safety check */
707 if (tok && (strchr(tok,0) > (tok+1))) {
708 /* atoi stops at the first non-[0-9] char: */
709 status->elapsedTime = atoi(re->value);
710 status->totalTime = atoi(tok+1);
711 }
712 }
713 else if(strcmp(re->name,"error")==0) {
714 status->error = strdup(re->value);
715 }
716 else if(strcmp(re->name,"xfade")==0) {
717 status->crossfade = atoi(re->value);
718 }
719 else if(strcmp(re->name,"updating_db")==0) {
720 status->updatingDb = atoi(re->value);
721 }
722 else if(strcmp(re->name,"audio")==0) {
723 char * tok = strchr(re->value,':');
724 if (tok && (strchr(tok,0) > (tok+1))) {
725 status->sampleRate = atoi(re->value);
726 status->bits = atoi(++tok);
727 tok = strchr(tok,':');
728 if (tok && (strchr(tok,0) > (tok+1)))
729 status->channels = atoi(tok+1);
730 }
731 }
733 mpd_getNextReturnElement(connection);
734 if(connection->error) {
735 free(status);
736 return NULL;
737 }
738 }
740 if(connection->error) {
741 free(status);
742 return NULL;
743 }
744 else if(status->state<0) {
745 strcpy(connection->errorStr,"state not found");
746 connection->error = 1;
747 free(status);
748 return NULL;
749 }
751 return status;
752 }
754 void mpd_freeStatus(mpd_Status * status) {
755 if(status->error) free(status->error);
756 free(status);
757 }
759 void mpd_sendStatsCommand(mpd_Connection * connection) {
760 mpd_executeCommand(connection,"stats\n");
761 }
763 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
764 mpd_Stats * stats;
766 /*mpd_executeCommand(connection,"stats\n");
768 if(connection->error) return NULL;*/
770 if(connection->doneProcessing || (connection->listOks &&
771 connection->doneListOk))
772 {
773 return NULL;
774 }
776 if(!connection->returnElement) mpd_getNextReturnElement(connection);
778 stats = malloc(sizeof(mpd_Stats));
779 stats->numberOfArtists = 0;
780 stats->numberOfAlbums = 0;
781 stats->numberOfSongs = 0;
782 stats->uptime = 0;
783 stats->dbUpdateTime = 0;
784 stats->playTime = 0;
785 stats->dbPlayTime = 0;
787 if(connection->error) {
788 free(stats);
789 return NULL;
790 }
791 while(connection->returnElement) {
792 mpd_ReturnElement * re = connection->returnElement;
793 if(strcmp(re->name,"artists")==0) {
794 stats->numberOfArtists = atoi(re->value);
795 }
796 else if(strcmp(re->name,"albums")==0) {
797 stats->numberOfAlbums = atoi(re->value);
798 }
799 else if(strcmp(re->name,"songs")==0) {
800 stats->numberOfSongs = atoi(re->value);
801 }
802 else if(strcmp(re->name,"uptime")==0) {
803 stats->uptime = strtol(re->value,NULL,10);
804 }
805 else if(strcmp(re->name,"db_update")==0) {
806 stats->dbUpdateTime = strtol(re->value,NULL,10);
807 }
808 else if(strcmp(re->name,"playtime")==0) {
809 stats->playTime = strtol(re->value,NULL,10);
810 }
811 else if(strcmp(re->name,"db_playtime")==0) {
812 stats->dbPlayTime = strtol(re->value,NULL,10);
813 }
815 mpd_getNextReturnElement(connection);
816 if(connection->error) {
817 free(stats);
818 return NULL;
819 }
820 }
822 if(connection->error) {
823 free(stats);
824 return NULL;
825 }
827 return stats;
828 }
830 void mpd_freeStats(mpd_Stats * stats) {
831 free(stats);
832 }
834 static void mpd_initDirectory(mpd_Directory * directory) {
835 directory->path = NULL;
836 }
838 static void mpd_finishDirectory(mpd_Directory * directory) {
839 if (directory->path)
840 str_pool_put(directory->path);
841 }
843 mpd_Directory * mpd_newDirectory(void) {
844 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
846 mpd_initDirectory(directory);
848 return directory;
849 }
851 void mpd_freeDirectory(mpd_Directory * directory) {
852 mpd_finishDirectory(directory);
854 free(directory);
855 }
857 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
858 mpd_Directory * ret = mpd_newDirectory();
860 if (directory->path)
861 ret->path = str_pool_dup(directory->path);
863 return ret;
864 }
866 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
867 playlist->path = NULL;
868 }
870 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
871 if (playlist->path)
872 str_pool_put(playlist->path);
873 }
875 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
876 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
878 mpd_initPlaylistFile(playlist);
880 return playlist;
881 }
883 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
884 mpd_finishPlaylistFile(playlist);
885 free(playlist);
886 }
888 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
889 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
891 if (playlist->path)
892 ret->path = str_pool_dup(playlist->path);
894 return ret;
895 }
897 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
898 entity->info.directory = NULL;
899 }
901 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
902 if(entity->info.directory) {
903 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
904 mpd_freeDirectory(entity->info.directory);
905 }
906 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
907 mpd_freeSong(entity->info.song);
908 }
909 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
910 mpd_freePlaylistFile(entity->info.playlistFile);
911 }
912 }
913 }
915 mpd_InfoEntity * mpd_newInfoEntity(void) {
916 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
918 mpd_initInfoEntity(entity);
920 return entity;
921 }
923 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
924 mpd_finishInfoEntity(entity);
925 free(entity);
926 }
928 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
929 mpd_executeCommand(connection,command);
930 }
932 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
933 mpd_InfoEntity * entity = NULL;
935 if(connection->doneProcessing || (connection->listOks &&
936 connection->doneListOk)) {
937 return NULL;
938 }
940 if(!connection->returnElement) mpd_getNextReturnElement(connection);
942 if(connection->returnElement) {
943 if(strcmp(connection->returnElement->name,"file")==0) {
944 entity = mpd_newInfoEntity();
945 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
946 entity->info.song = mpd_newSong();
947 entity->info.song->file =
948 str_pool_dup(connection->returnElement->value);
949 }
950 else if(strcmp(connection->returnElement->name,
951 "directory")==0) {
952 entity = mpd_newInfoEntity();
953 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
954 entity->info.directory = mpd_newDirectory();
955 entity->info.directory->path =
956 str_pool_dup(connection->returnElement->value);
957 }
958 else if(strcmp(connection->returnElement->name,"playlist")==0) {
959 entity = mpd_newInfoEntity();
960 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
961 entity->info.playlistFile = mpd_newPlaylistFile();
962 entity->info.playlistFile->path =
963 str_pool_dup(connection->returnElement->value);
964 }
965 else if(strcmp(connection->returnElement->name, "cpos") == 0){
966 entity = mpd_newInfoEntity();
967 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
968 entity->info.song = mpd_newSong();
969 entity->info.song->pos = atoi(connection->returnElement->value);
970 }
971 else {
972 connection->error = 1;
973 strcpy(connection->errorStr,"problem parsing song info");
974 return NULL;
975 }
976 }
977 else return NULL;
979 mpd_getNextReturnElement(connection);
980 while(connection->returnElement) {
981 mpd_ReturnElement * re = connection->returnElement;
983 if(strcmp(re->name,"file")==0) return entity;
984 else if(strcmp(re->name,"directory")==0) return entity;
985 else if(strcmp(re->name,"playlist")==0) return entity;
986 else if(strcmp(re->name,"cpos")==0) return entity;
988 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
989 strlen(re->value)) {
990 if(!entity->info.song->artist &&
991 strcmp(re->name,"Artist")==0) {
992 entity->info.song->artist = str_pool_dup(re->value);
993 }
994 else if(!entity->info.song->album &&
995 strcmp(re->name,"Album")==0) {
996 entity->info.song->album = str_pool_dup(re->value);
997 }
998 else if(!entity->info.song->title &&
999 strcmp(re->name,"Title")==0) {
1000 entity->info.song->title = str_pool_dup(re->value);
1001 }
1002 else if(!entity->info.song->track &&
1003 strcmp(re->name,"Track")==0) {
1004 entity->info.song->track = str_pool_dup(re->value);
1005 }
1006 else if(!entity->info.song->name &&
1007 strcmp(re->name,"Name")==0) {
1008 entity->info.song->name = str_pool_dup(re->value);
1009 }
1010 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1011 strcmp(re->name,"Time")==0) {
1012 entity->info.song->time = atoi(re->value);
1013 }
1014 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1015 strcmp(re->name,"Pos")==0) {
1016 entity->info.song->pos = atoi(re->value);
1017 }
1018 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1019 strcmp(re->name,"Id")==0) {
1020 entity->info.song->id = atoi(re->value);
1021 }
1022 else if(!entity->info.song->date &&
1023 strcmp(re->name, "Date") == 0) {
1024 entity->info.song->date = str_pool_dup(re->value);
1025 }
1026 else if(!entity->info.song->genre &&
1027 strcmp(re->name, "Genre") == 0) {
1028 entity->info.song->genre = str_pool_dup(re->value);
1029 }
1030 else if(!entity->info.song->composer &&
1031 strcmp(re->name, "Composer") == 0) {
1032 entity->info.song->composer = str_pool_dup(re->value);
1033 }
1034 else if(!entity->info.song->disc &&
1035 strcmp(re->name, "Disc") == 0) {
1036 entity->info.song->disc = str_pool_dup(re->value);
1037 }
1038 else if(!entity->info.song->comment &&
1039 strcmp(re->name, "Comment") == 0) {
1040 entity->info.song->comment = str_pool_dup(re->value);
1041 }
1042 }
1043 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1044 }
1045 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1046 }
1048 mpd_getNextReturnElement(connection);
1049 }
1051 return entity;
1052 }
1054 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1055 const char * name)
1056 {
1057 if(connection->doneProcessing || (connection->listOks &&
1058 connection->doneListOk))
1059 {
1060 return NULL;
1061 }
1063 mpd_getNextReturnElement(connection);
1064 while(connection->returnElement) {
1065 mpd_ReturnElement * re = connection->returnElement;
1067 if(strcmp(re->name,name)==0) return strdup(re->value);
1068 mpd_getNextReturnElement(connection);
1069 }
1071 return NULL;
1072 }
1074 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1075 if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1076 {
1077 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1078 }
1079 return NULL;
1080 }
1082 char * mpd_getNextArtist(mpd_Connection * connection) {
1083 return mpd_getNextReturnElementNamed(connection,"Artist");
1084 }
1086 char * mpd_getNextAlbum(mpd_Connection * connection) {
1087 return mpd_getNextReturnElementNamed(connection,"Album");
1088 }
1090 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1091 char * string = malloc(strlen("playlistinfo")+25);
1092 sprintf(string,"playlistinfo \"%i\"\n",songPos);
1093 mpd_sendInfoCommand(connection,string);
1094 free(string);
1095 }
1097 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1098 char * string = malloc(strlen("playlistid")+25);
1099 sprintf(string, "playlistid \"%i\"\n", id);
1100 mpd_sendInfoCommand(connection, string);
1101 free(string);
1102 }
1104 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1105 char * string = malloc(strlen("plchanges")+25);
1106 sprintf(string,"plchanges \"%lld\"\n",playlist);
1107 mpd_sendInfoCommand(connection,string);
1108 free(string);
1109 }
1111 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1112 char * string = malloc(strlen("plchangesposid")+25);
1113 sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1114 mpd_sendInfoCommand(connection,string);
1115 free(string);
1116 }
1118 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1119 char * sDir = mpd_sanitizeArg(dir);
1120 char * string = malloc(strlen("listall")+strlen(sDir)+5);
1121 sprintf(string,"listall \"%s\"\n",sDir);
1122 mpd_sendInfoCommand(connection,string);
1123 free(string);
1124 free(sDir);
1125 }
1127 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1128 char * sDir = mpd_sanitizeArg(dir);
1129 char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1130 sprintf(string,"listallinfo \"%s\"\n",sDir);
1131 mpd_sendInfoCommand(connection,string);
1132 free(string);
1133 free(sDir);
1134 }
1136 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1137 char * sDir = mpd_sanitizeArg(dir);
1138 char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1139 sprintf(string,"lsinfo \"%s\"\n",sDir);
1140 mpd_sendInfoCommand(connection,string);
1141 free(string);
1142 free(sDir);
1143 }
1145 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1146 mpd_executeCommand(connection,"currentsong\n");
1147 }
1149 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1150 const char * str)
1151 {
1152 char st[10];
1153 char * string;
1154 char * sanitStr = mpd_sanitizeArg(str);
1155 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1156 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1157 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1158 else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1159 else {
1160 connection->error = 1;
1161 strcpy(connection->errorStr,"unknown table for search");
1162 return;
1163 }
1164 string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1165 sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1166 mpd_sendInfoCommand(connection,string);
1167 free(string);
1168 free(sanitStr);
1169 }
1171 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1172 const char * str)
1173 {
1174 char st[10];
1175 char * string;
1176 char * sanitStr = mpd_sanitizeArg(str);
1177 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1178 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1179 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1180 else {
1181 connection->error = 1;
1182 strcpy(connection->errorStr,"unknown table for find");
1183 return;
1184 }
1185 string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1186 sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1187 mpd_sendInfoCommand(connection,string);
1188 free(string);
1189 free(sanitStr);
1190 }
1192 void mpd_sendListCommand(mpd_Connection * connection, int table,
1193 const char * arg1)
1194 {
1195 char st[10];
1196 char * string;
1197 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1198 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1199 else {
1200 connection->error = 1;
1201 strcpy(connection->errorStr,"unknown table for list");
1202 return;
1203 }
1204 if(arg1) {
1205 char * sanitArg1 = mpd_sanitizeArg(arg1);
1206 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1207 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1208 free(sanitArg1);
1209 }
1210 else {
1211 string = malloc(strlen("list")+strlen(st)+3);
1212 sprintf(string,"list %s\n",st);
1213 }
1214 mpd_sendInfoCommand(connection,string);
1215 free(string);
1216 }
1218 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1219 char * sFile = mpd_sanitizeArg(file);
1220 char * string = malloc(strlen("add")+strlen(sFile)+5);
1221 sprintf(string,"add \"%s\"\n",sFile);
1222 mpd_executeCommand(connection,string);
1223 free(string);
1224 free(sFile);
1225 }
1227 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1228 char * string = malloc(strlen("delete")+25);
1229 sprintf(string,"delete \"%i\"\n",songPos);
1230 mpd_sendInfoCommand(connection,string);
1231 free(string);
1232 }
1234 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1235 char * string = malloc(strlen("deleteid")+25);
1236 sprintf(string, "deleteid \"%i\"\n", id);
1237 mpd_sendInfoCommand(connection,string);
1238 free(string);
1239 }
1241 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1242 char * sName = mpd_sanitizeArg(name);
1243 char * string = malloc(strlen("save")+strlen(sName)+5);
1244 sprintf(string,"save \"%s\"\n",sName);
1245 mpd_executeCommand(connection,string);
1246 free(string);
1247 free(sName);
1248 }
1250 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1251 char * sName = mpd_sanitizeArg(name);
1252 char * string = malloc(strlen("load")+strlen(sName)+5);
1253 sprintf(string,"load \"%s\"\n",sName);
1254 mpd_executeCommand(connection,string);
1255 free(string);
1256 free(sName);
1257 }
1259 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1260 char * sName = mpd_sanitizeArg(name);
1261 char * string = malloc(strlen("rm")+strlen(sName)+5);
1262 sprintf(string,"rm \"%s\"\n",sName);
1263 mpd_executeCommand(connection,string);
1264 free(string);
1265 free(sName);
1266 }
1268 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1269 mpd_executeCommand(connection,"shuffle\n");
1270 }
1272 void mpd_sendClearCommand(mpd_Connection * connection) {
1273 mpd_executeCommand(connection,"clear\n");
1274 }
1276 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1277 char * string = malloc(strlen("play")+25);
1278 sprintf(string,"play \"%i\"\n",songPos);
1279 mpd_sendInfoCommand(connection,string);
1280 free(string);
1281 }
1283 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1284 char * string = malloc(strlen("playid")+25);
1285 sprintf(string,"playid \"%i\"\n",id);
1286 mpd_sendInfoCommand(connection,string);
1287 free(string);
1288 }
1290 void mpd_sendStopCommand(mpd_Connection * connection) {
1291 mpd_executeCommand(connection,"stop\n");
1292 }
1294 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1295 char * string = malloc(strlen("pause")+25);
1296 sprintf(string,"pause \"%i\"\n",pauseMode);
1297 mpd_executeCommand(connection,string);
1298 free(string);
1299 }
1301 void mpd_sendNextCommand(mpd_Connection * connection) {
1302 mpd_executeCommand(connection,"next\n");
1303 }
1305 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1306 char * string = malloc(strlen("move")+25);
1307 sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1308 mpd_sendInfoCommand(connection,string);
1309 free(string);
1310 }
1312 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1313 char * string = malloc(strlen("moveid")+25);
1314 sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1315 mpd_sendInfoCommand(connection,string);
1316 free(string);
1317 }
1319 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1320 char * string = malloc(strlen("swap")+25);
1321 sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1322 mpd_sendInfoCommand(connection,string);
1323 free(string);
1324 }
1326 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1327 char * string = malloc(strlen("swapid")+25);
1328 sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1329 mpd_sendInfoCommand(connection,string);
1330 free(string);
1331 }
1333 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1334 char * string = malloc(strlen("seek")+25);
1335 sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1336 mpd_sendInfoCommand(connection,string);
1337 free(string);
1338 }
1340 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1341 char * string = malloc(strlen("seekid")+25);
1342 sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1343 mpd_sendInfoCommand(connection,string);
1344 free(string);
1345 }
1347 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1348 char *sPath = mpd_sanitizeArg(path);
1349 char * string = malloc(strlen("update")+strlen(sPath)+5);
1350 sprintf(string,"update \"%s\"\n",sPath);
1351 mpd_sendInfoCommand(connection,string);
1352 free(string);
1353 free(sPath);
1354 }
1356 int mpd_getUpdateId(mpd_Connection * connection) {
1357 char * jobid;
1358 int ret = 0;
1360 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1361 if(jobid) {
1362 ret = atoi(jobid);
1363 free(jobid);
1364 }
1366 return ret;
1367 }
1369 void mpd_sendPrevCommand(mpd_Connection * connection) {
1370 mpd_executeCommand(connection,"previous\n");
1371 }
1373 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1374 char * string = malloc(strlen("repeat")+25);
1375 sprintf(string,"repeat \"%i\"\n",repeatMode);
1376 mpd_executeCommand(connection,string);
1377 free(string);
1378 }
1380 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1381 char * string = malloc(strlen("random")+25);
1382 sprintf(string,"random \"%i\"\n",randomMode);
1383 mpd_executeCommand(connection,string);
1384 free(string);
1385 }
1387 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1388 char * string = malloc(strlen("setvol")+25);
1389 sprintf(string,"setvol \"%i\"\n",volumeChange);
1390 mpd_executeCommand(connection,string);
1391 free(string);
1392 }
1394 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1395 char * string = malloc(strlen("volume")+25);
1396 sprintf(string,"volume \"%i\"\n",volumeChange);
1397 mpd_executeCommand(connection,string);
1398 free(string);
1399 }
1401 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1402 char * string = malloc(strlen("crossfade")+25);
1403 sprintf(string,"crossfade \"%i\"\n",seconds);
1404 mpd_executeCommand(connection,string);
1405 free(string);
1406 }
1408 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1409 char * sPass = mpd_sanitizeArg(pass);
1410 char * string = malloc(strlen("password")+strlen(sPass)+5);
1411 sprintf(string,"password \"%s\"\n",sPass);
1412 mpd_executeCommand(connection,string);
1413 free(string);
1414 free(sPass);
1415 }
1417 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1418 if(connection->commandList) {
1419 strcpy(connection->errorStr,"already in command list mode");
1420 connection->error = 1;
1421 return;
1422 }
1423 connection->commandList = COMMAND_LIST;
1424 mpd_executeCommand(connection,"command_list_begin\n");
1425 }
1427 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1428 if(connection->commandList) {
1429 strcpy(connection->errorStr,"already in command list mode");
1430 connection->error = 1;
1431 return;
1432 }
1433 connection->commandList = COMMAND_LIST_OK;
1434 mpd_executeCommand(connection,"command_list_ok_begin\n");
1435 connection->listOks = 0;
1436 }
1438 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1439 if(!connection->commandList) {
1440 strcpy(connection->errorStr,"not in command list mode");
1441 connection->error = 1;
1442 return;
1443 }
1444 connection->commandList = 0;
1445 mpd_executeCommand(connection,"command_list_end\n");
1446 }
1448 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1449 mpd_executeCommand(connection,"outputs\n");
1450 }
1452 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1453 mpd_OutputEntity * output = NULL;
1455 if(connection->doneProcessing || (connection->listOks &&
1456 connection->doneListOk))
1457 {
1458 return NULL;
1459 }
1461 if(connection->error) return NULL;
1463 output = malloc(sizeof(mpd_OutputEntity));
1464 output->id = -10;
1465 output->name = NULL;
1466 output->enabled = 0;
1468 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1470 while(connection->returnElement) {
1471 mpd_ReturnElement * re = connection->returnElement;
1472 if(strcmp(re->name,"outputid")==0) {
1473 if(output!=NULL && output->id>=0) return output;
1474 output->id = atoi(re->value);
1475 }
1476 else if(strcmp(re->name,"outputname")==0) {
1477 output->name = strdup(re->value);
1478 }
1479 else if(strcmp(re->name,"outputenabled")==0) {
1480 output->enabled = atoi(re->value);
1481 }
1483 mpd_getNextReturnElement(connection);
1484 if(connection->error) {
1485 free(output);
1486 return NULL;
1487 }
1489 }
1491 return output;
1492 }
1494 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1495 char * string = malloc(strlen("enableoutput")+25);
1496 sprintf(string,"enableoutput \"%i\"\n",outputId);
1497 mpd_executeCommand(connection,string);
1498 free(string);
1499 }
1501 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1502 char * string = malloc(strlen("disableoutput")+25);
1503 sprintf(string,"disableoutput \"%i\"\n",outputId);
1504 mpd_executeCommand(connection,string);
1505 free(string);
1506 }
1508 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1509 free(output->name);
1510 free(output);
1511 }
1513 /**
1514 * mpd_sendNotCommandsCommand
1515 * odd naming, but it gets the not allowed commands
1516 */
1518 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1519 mpd_executeCommand(connection,"notcommands\n");
1520 }
1522 /**
1523 * mpd_sendCommandsCommand
1524 * odd naming, but it gets the allowed commands
1525 */
1527 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1528 mpd_executeCommand(connection,"commands\n");
1529 }
1530 /**
1531 * Get the next returned command
1532 */
1533 char * mpd_getNextCommand(mpd_Connection * connection) {
1534 return mpd_getNextReturnElementNamed(connection,"command");
1535 }
1537 void mpd_startSearch(mpd_Connection * connection,int exact) {
1538 if(connection->request) {
1539 /* search/find allready in progress */
1540 /* TODO: set error here? */
1541 return;
1542 }
1543 if(exact){
1544 connection->request = strdup("find");
1545 }
1546 else{
1547 connection->request = strdup("search");
1548 }
1549 }
1552 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1553 if(connection->request) {
1554 /* search/find allready in progress */
1555 /* TODO: set error here? */
1556 return;
1557 }
1558 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1559 /* set error here */
1560 return;
1561 }
1563 connection->request = malloc(sizeof(char)*(
1564 /* length of the field name */
1565 strlen(mpdTagItemKeys[field])+
1566 /* "list"+space+\0 */
1567 6
1568 ));
1569 sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1570 }
1574 void mpd_addConstraintSearch(mpd_Connection *connection,
1575 int field,
1576 char *name)
1577 {
1578 char *arg = NULL;
1579 if(!connection->request){
1580 return;
1581 }
1582 if(name == NULL) {
1583 return;
1584 }
1585 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1586 return;
1587 }
1588 /* clean up the query */
1589 arg = mpd_sanitizeArg(name);
1590 /* create space for the query */
1591 connection->request = realloc(connection->request, (
1592 /* length of the old string */
1593 strlen(connection->request)+
1594 /* space between */
1595 1+
1596 /* length of the field name */
1597 strlen(mpdTagItemKeys[field])+
1598 /* space plus starting " */
1599 2+
1600 /* length of search term */
1601 strlen(arg)+
1602 /* closign " +\0 that is added sprintf */
1603 2
1604 )*sizeof(char));
1605 /* and form the query */
1606 sprintf(connection->request, "%s %s \"%s\"",
1607 connection->request,
1608 mpdTagItemKeys[field],
1609 arg);
1610 free(arg);
1611 }
1614 void mpd_commitSearch(mpd_Connection *connection)
1615 {
1616 if(connection->request)
1617 {
1618 int length = strlen(connection->request);
1619 /* fixing up the string for mpd to like */
1620 connection->request = realloc(connection->request,
1621 (length+ /* old length */
1622 2 /* closing \n and \0 */
1623 )*sizeof(char));
1624 connection->request[length] = '\n';
1625 connection->request[length+1] = '\0';
1626 /* and off we go */
1627 mpd_sendInfoCommand(connection, connection->request);
1628 /* clean up a bit */
1629 free(connection->request);
1630 connection->request = NULL;
1631 }
1632 }
1634 /**
1635 * @param connection a MpdConnection
1636 * @param path the path to the playlist.
1637 *
1638 * List the content, with full metadata, of a stored playlist.
1639 *
1640 */
1641 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1642 {
1643 char *arg = mpd_sanitizeArg(path);
1644 char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1645 sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1646 mpd_sendInfoCommand(connection, query);
1647 free(arg);
1648 free(query);
1649 }
1651 /**
1652 * @param connection a MpdConnection
1653 * @param path the path to the playlist.
1654 *
1655 * List the content of a stored playlist.
1656 *
1657 */
1658 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1659 {
1660 char *arg = mpd_sanitizeArg(path);
1661 char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1662 sprintf(query, "listplaylist \"%s\"\n",arg);
1663 mpd_sendInfoCommand(connection, query);
1664 free(arg);
1665 free(query);
1666 }