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 while (1) {
217 tv = connection->timeout;
218 FD_ZERO(&fds);
219 FD_SET(connection->sock, &fds);
221 ret = select(connection->sock + 1, &fds, NULL, NULL, &tv);
222 if (ret > 0)
223 return 0;
225 if (ret == 0 || !SELECT_ERRNO_IGNORE)
226 return -1;
227 }
228 }
230 /**
231 * Wait until the socket is connected and check its result. Returns 1
232 * on success, 0 on timeout, -errno on error.
233 */
234 static int mpd_wait_connected(mpd_Connection *connection)
235 {
236 int ret;
237 int s_err = 0;
238 socklen_t s_err_size = sizeof(s_err);
240 ret = mpd_wait(connection);
241 if (ret < 0)
242 return 0;
244 ret = getsockopt(connection->sock, SOL_SOCKET, SO_ERROR,
245 (char*)&s_err, &s_err_size);
246 if (ret < 0)
247 return -errno;
249 if (s_err != 0)
250 return -s_err;
252 return 1;
253 }
255 /**
256 * Attempt to read data from the socket into the input buffer.
257 * Returns 0 on success, -1 on error.
258 */
259 static int mpd_recv(mpd_Connection *connection)
260 {
261 int ret;
262 ssize_t nbytes;
264 assert(connection != NULL);
265 assert(connection->buflen <= sizeof(connection->buffer));
266 assert(connection->bufstart <= connection->buflen);
268 if (connection->buflen >= sizeof(connection->buffer)) {
269 /* delete consumed data from beginning of buffer */
270 connection->buflen -= connection->bufstart;
271 memmove(connection->buffer,
272 connection->buffer + connection->bufstart,
273 connection->buflen);
274 connection->bufstart = 0;
275 }
277 if (connection->buflen >= sizeof(connection->buffer)) {
278 strcpy(connection->errorStr, "buffer overrun");
279 connection->error = MPD_ERROR_BUFFEROVERRUN;
280 connection->doneProcessing = 1;
281 connection->doneListOk = 0;
282 return -1;
283 }
285 while (1) {
286 ret = mpd_wait(connection);
287 if (ret < 0) {
288 strcpy(connection->errorStr, "connection timeout");
289 connection->error = MPD_ERROR_TIMEOUT;
290 connection->doneProcessing = 1;
291 connection->doneListOk = 0;
292 return -1;
293 }
295 nbytes = read(connection->sock,
296 connection->buffer + connection->buflen,
297 sizeof(connection->buffer) - connection->buflen);
298 if (nbytes > 0) {
299 connection->buflen += nbytes;
300 return 0;
301 }
303 if (nbytes == 0 || !SENDRECV_ERRNO_IGNORE) {
304 strcpy(connection->errorStr, "connection closed");
305 connection->error = MPD_ERROR_CONNCLOSED;
306 connection->doneProcessing = 1;
307 connection->doneListOk = 0;
308 return -1;
309 }
310 }
311 }
313 static int
314 mpd_connect(mpd_Connection *connection, const char * host, int port)
315 {
316 struct resolver *resolver;
317 const struct resolver_address *address;
318 int ret;
320 resolver = resolver_new(host, port);
321 if (resolver == NULL) {
322 snprintf(connection->errorStr, sizeof(connection->errorStr),
323 "host \"%s\" not found", host);
324 connection->error = MPD_ERROR_UNKHOST;
325 return -1;
326 }
328 while ((address = resolver_next(resolver)) != NULL) {
329 connection->sock = socket(address->family, SOCK_STREAM,
330 address->protocol);
331 if (connection->sock < 0) {
332 snprintf(connection->errorStr,
333 sizeof(connection->errorStr),
334 "problems creating socket: %s",
335 strerror(errno));
336 connection->error = MPD_ERROR_SYSTEM;
337 continue;
338 }
340 ret = do_connect_fail(connection,
341 address->addr, address->addrlen);
342 if (ret != 0) {
343 snprintf(connection->errorStr,
344 sizeof(connection->errorStr),
345 "problems connecting to \"%s\" on port"
346 " %i: %s", host, port, strerror(errno));
347 connection->error = MPD_ERROR_CONNPORT;
349 closesocket(connection->sock);
350 connection->sock = -1;
351 continue;
352 }
354 ret = mpd_wait_connected(connection);
355 if (ret > 0) {
356 resolver_free(resolver);
357 mpd_clearError(connection);
358 return 0;
359 }
361 if (ret == 0) {
362 snprintf(connection->errorStr,
363 sizeof(connection->errorStr),
364 "timeout in attempting to get a response from"
365 " \"%s\" on port %i", host, port);
366 connection->error = MPD_ERROR_NORESPONSE;
367 } else if (ret < 0) {
368 snprintf(connection->errorStr,
369 sizeof(connection->errorStr),
370 "problems connecting to \"%s\" on port %i: %s",
371 host, port, strerror(-ret));
372 connection->error = MPD_ERROR_CONNPORT;
373 }
375 closesocket(connection->sock);
376 connection->sock = -1;
377 }
379 resolver_free(resolver);
380 return -1;
381 }
383 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
384 int err;
385 char * rt;
386 mpd_Connection * connection = malloc(sizeof(mpd_Connection));
388 connection->buflen = 0;
389 connection->bufstart = 0;
390 mpd_clearError(connection);
391 connection->doneProcessing = 0;
392 connection->commandList = 0;
393 connection->listOks = 0;
394 connection->doneListOk = 0;
395 connection->returnElement = NULL;
396 connection->request = NULL;
398 if (winsock_dll_error(connection))
399 return connection;
401 mpd_setConnectionTimeout(connection,timeout);
403 err = mpd_connect(connection, host, port);
404 if (err < 0)
405 return connection;
407 while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
408 err = mpd_recv(connection);
409 if (err < 0)
410 return connection;
411 }
413 *rt = '\0';
414 if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
415 connection->doneProcessing = 1;
417 connection->buflen -= rt + 1 - connection->buffer;
418 memmove(connection->buffer, rt + 1, connection->buflen);
420 return connection;
421 }
423 void mpd_clearError(mpd_Connection * connection) {
424 connection->error = 0;
425 connection->errorStr[0] = '\0';
426 }
428 void mpd_closeConnection(mpd_Connection * connection) {
429 closesocket(connection->sock);
430 if(connection->returnElement) free(connection->returnElement);
431 if(connection->request) free(connection->request);
432 free(connection);
433 WSACleanup();
434 }
436 static void mpd_executeCommand(mpd_Connection *connection,
437 const char *command) {
438 int ret;
439 struct timeval tv;
440 fd_set fds;
441 const char *commandPtr = command;
442 int commandLen = strlen(command);
444 if (!connection->doneProcessing && !connection->commandList) {
445 strcpy(connection->errorStr,
446 "not done processing current command");
447 connection->error = 1;
448 return;
449 }
451 mpd_clearError(connection);
453 FD_ZERO(&fds);
454 FD_SET(connection->sock,&fds);
455 tv.tv_sec = connection->timeout.tv_sec;
456 tv.tv_usec = connection->timeout.tv_usec;
458 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
459 (ret==-1 && SELECT_ERRNO_IGNORE)) {
460 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
461 if(ret<=0)
462 {
463 if (SENDRECV_ERRNO_IGNORE) continue;
464 snprintf(connection->errorStr, sizeof(connection->errorStr),
465 "problems giving command \"%s\"",command);
466 connection->error = MPD_ERROR_SENDING;
467 return;
468 }
469 else {
470 commandPtr+=ret;
471 commandLen-=ret;
472 }
474 if(commandLen<=0) break;
475 }
477 if(commandLen>0) {
478 perror("");
479 snprintf(connection->errorStr, sizeof(connection->errorStr),
480 "timeout sending command \"%s\"",command);
481 connection->error = MPD_ERROR_TIMEOUT;
482 return;
483 }
485 if(!connection->commandList) connection->doneProcessing = 0;
486 else if(connection->commandList == COMMAND_LIST_OK) {
487 connection->listOks++;
488 }
489 }
491 static void mpd_getNextReturnElement(mpd_Connection * connection) {
492 char * output = NULL;
493 char * rt = NULL;
494 char * name = NULL;
495 char * value = NULL;
496 char * tok = NULL;
497 int err;
498 int pos;
500 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
501 connection->returnElement = NULL;
503 if (connection->doneProcessing ||
504 (connection->listOks && connection->doneListOk)) {
505 strcpy(connection->errorStr,"already done processing current command");
506 connection->error = 1;
507 return;
508 }
510 while (!(rt = memchr(connection->buffer + connection->bufstart, '\n',
511 connection->buflen - connection->bufstart))) {
512 err = mpd_recv(connection);
513 if (err < 0)
514 return;
515 }
517 *rt = '\0';
518 output = connection->buffer+connection->bufstart;
519 connection->bufstart = rt - connection->buffer + 1;
521 if(strcmp(output,"OK")==0) {
522 if(connection->listOks > 0) {
523 strcpy(connection->errorStr, "expected more list_OK's");
524 connection->error = 1;
525 }
526 connection->listOks = 0;
527 connection->doneProcessing = 1;
528 connection->doneListOk = 0;
529 return;
530 }
532 if(strcmp(output, "list_OK") == 0) {
533 if(!connection->listOks) {
534 strcpy(connection->errorStr,
535 "got an unexpected list_OK");
536 connection->error = 1;
537 }
538 else {
539 connection->doneListOk = 1;
540 connection->listOks--;
541 }
542 return;
543 }
545 if(strncmp(output,"ACK",strlen("ACK"))==0) {
546 size_t length = strlen(output);
547 char * test;
548 char * needle;
549 int val;
551 if (length >= sizeof(connection->errorStr))
552 length = sizeof(connection->errorStr) - 1;
554 memcpy(connection->errorStr, output, length);
555 connection->errorStr[length] = 0;
556 connection->error = MPD_ERROR_ACK;
557 connection->errorCode = MPD_ACK_ERROR_UNK;
558 connection->errorAt = MPD_ERROR_AT_UNK;
559 connection->doneProcessing = 1;
560 connection->doneListOk = 0;
562 needle = strchr(output, '[');
563 if(!needle) return;
564 val = strtol(needle+1, &test, 10);
565 if(*test != '@') return;
566 connection->errorCode = val;
567 val = strtol(test+1, &test, 10);
568 if(*test != ']') return;
569 connection->errorAt = val;
570 return;
571 }
573 tok = strchr(output, ':');
574 if (!tok) return;
575 pos = tok - output;
576 value = ++tok;
577 name = output;
578 name[pos] = '\0';
580 if(value[0]==' ') {
581 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
582 }
583 else {
584 snprintf(connection->errorStr, sizeof(connection->errorStr),
585 "error parsing: %s:%s",name,value);
586 connection->error = 1;
587 }
588 }
590 void mpd_finishCommand(mpd_Connection * connection) {
591 while(!connection->doneProcessing) {
592 if(connection->doneListOk) connection->doneListOk = 0;
593 mpd_getNextReturnElement(connection);
594 }
595 }
597 static void mpd_finishListOkCommand(mpd_Connection * connection) {
598 while(!connection->doneProcessing && connection->listOks &&
599 !connection->doneListOk)
600 {
601 mpd_getNextReturnElement(connection);
602 }
603 }
605 int mpd_nextListOkCommand(mpd_Connection * connection) {
606 mpd_finishListOkCommand(connection);
607 if(!connection->doneProcessing) connection->doneListOk = 0;
608 if(connection->listOks == 0 || connection->doneProcessing) return -1;
609 return 0;
610 }
612 void mpd_sendStatusCommand(mpd_Connection * connection) {
613 mpd_executeCommand(connection,"status\n");
614 }
616 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
617 mpd_Status * status;
619 /*mpd_executeCommand(connection,"status\n");
621 if(connection->error) return NULL;*/
623 if(connection->doneProcessing || (connection->listOks &&
624 connection->doneListOk))
625 {
626 return NULL;
627 }
629 if(!connection->returnElement) mpd_getNextReturnElement(connection);
631 status = malloc(sizeof(mpd_Status));
632 status->volume = -1;
633 status->repeat = 0;
634 status->random = 0;
635 status->playlist = -1;
636 status->playlistLength = -1;
637 status->state = -1;
638 status->song = 0;
639 status->songid = 0;
640 status->elapsedTime = 0;
641 status->totalTime = 0;
642 status->bitRate = 0;
643 status->sampleRate = 0;
644 status->bits = 0;
645 status->channels = 0;
646 status->crossfade = -1;
647 status->error = NULL;
648 status->updatingDb = 0;
650 if(connection->error) {
651 free(status);
652 return NULL;
653 }
654 while(connection->returnElement) {
655 mpd_ReturnElement * re = connection->returnElement;
656 if(strcmp(re->name,"volume")==0) {
657 status->volume = atoi(re->value);
658 }
659 else if(strcmp(re->name,"repeat")==0) {
660 status->repeat = atoi(re->value);
661 }
662 else if(strcmp(re->name,"random")==0) {
663 status->random = atoi(re->value);
664 }
665 else if(strcmp(re->name,"playlist")==0) {
666 status->playlist = strtol(re->value,NULL,10);
667 }
668 else if(strcmp(re->name,"playlistlength")==0) {
669 status->playlistLength = atoi(re->value);
670 }
671 else if(strcmp(re->name,"bitrate")==0) {
672 status->bitRate = atoi(re->value);
673 }
674 else if(strcmp(re->name,"state")==0) {
675 if(strcmp(re->value,"play")==0) {
676 status->state = MPD_STATUS_STATE_PLAY;
677 }
678 else if(strcmp(re->value,"stop")==0) {
679 status->state = MPD_STATUS_STATE_STOP;
680 }
681 else if(strcmp(re->value,"pause")==0) {
682 status->state = MPD_STATUS_STATE_PAUSE;
683 }
684 else {
685 status->state = MPD_STATUS_STATE_UNKNOWN;
686 }
687 }
688 else if(strcmp(re->name,"song")==0) {
689 status->song = atoi(re->value);
690 }
691 else if(strcmp(re->name,"songid")==0) {
692 status->songid = atoi(re->value);
693 }
694 else if(strcmp(re->name,"time")==0) {
695 char * tok = strchr(re->value,':');
696 /* the second strchr below is a safety check */
697 if (tok && (strchr(tok,0) > (tok+1))) {
698 /* atoi stops at the first non-[0-9] char: */
699 status->elapsedTime = atoi(re->value);
700 status->totalTime = atoi(tok+1);
701 }
702 }
703 else if(strcmp(re->name,"error")==0) {
704 status->error = strdup(re->value);
705 }
706 else if(strcmp(re->name,"xfade")==0) {
707 status->crossfade = atoi(re->value);
708 }
709 else if(strcmp(re->name,"updating_db")==0) {
710 status->updatingDb = atoi(re->value);
711 }
712 else if(strcmp(re->name,"audio")==0) {
713 char * tok = strchr(re->value,':');
714 if (tok && (strchr(tok,0) > (tok+1))) {
715 status->sampleRate = atoi(re->value);
716 status->bits = atoi(++tok);
717 tok = strchr(tok,':');
718 if (tok && (strchr(tok,0) > (tok+1)))
719 status->channels = atoi(tok+1);
720 }
721 }
723 mpd_getNextReturnElement(connection);
724 if(connection->error) {
725 free(status);
726 return NULL;
727 }
728 }
730 if(connection->error) {
731 free(status);
732 return NULL;
733 }
734 else if(status->state<0) {
735 strcpy(connection->errorStr,"state not found");
736 connection->error = 1;
737 free(status);
738 return NULL;
739 }
741 return status;
742 }
744 void mpd_freeStatus(mpd_Status * status) {
745 if(status->error) free(status->error);
746 free(status);
747 }
749 void mpd_sendStatsCommand(mpd_Connection * connection) {
750 mpd_executeCommand(connection,"stats\n");
751 }
753 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
754 mpd_Stats * stats;
756 /*mpd_executeCommand(connection,"stats\n");
758 if(connection->error) return NULL;*/
760 if(connection->doneProcessing || (connection->listOks &&
761 connection->doneListOk))
762 {
763 return NULL;
764 }
766 if(!connection->returnElement) mpd_getNextReturnElement(connection);
768 stats = malloc(sizeof(mpd_Stats));
769 stats->numberOfArtists = 0;
770 stats->numberOfAlbums = 0;
771 stats->numberOfSongs = 0;
772 stats->uptime = 0;
773 stats->dbUpdateTime = 0;
774 stats->playTime = 0;
775 stats->dbPlayTime = 0;
777 if(connection->error) {
778 free(stats);
779 return NULL;
780 }
781 while(connection->returnElement) {
782 mpd_ReturnElement * re = connection->returnElement;
783 if(strcmp(re->name,"artists")==0) {
784 stats->numberOfArtists = atoi(re->value);
785 }
786 else if(strcmp(re->name,"albums")==0) {
787 stats->numberOfAlbums = atoi(re->value);
788 }
789 else if(strcmp(re->name,"songs")==0) {
790 stats->numberOfSongs = atoi(re->value);
791 }
792 else if(strcmp(re->name,"uptime")==0) {
793 stats->uptime = strtol(re->value,NULL,10);
794 }
795 else if(strcmp(re->name,"db_update")==0) {
796 stats->dbUpdateTime = strtol(re->value,NULL,10);
797 }
798 else if(strcmp(re->name,"playtime")==0) {
799 stats->playTime = strtol(re->value,NULL,10);
800 }
801 else if(strcmp(re->name,"db_playtime")==0) {
802 stats->dbPlayTime = strtol(re->value,NULL,10);
803 }
805 mpd_getNextReturnElement(connection);
806 if(connection->error) {
807 free(stats);
808 return NULL;
809 }
810 }
812 if(connection->error) {
813 free(stats);
814 return NULL;
815 }
817 return stats;
818 }
820 void mpd_freeStats(mpd_Stats * stats) {
821 free(stats);
822 }
824 static void mpd_initDirectory(mpd_Directory * directory) {
825 directory->path = NULL;
826 }
828 static void mpd_finishDirectory(mpd_Directory * directory) {
829 if (directory->path)
830 str_pool_put(directory->path);
831 }
833 mpd_Directory * mpd_newDirectory(void) {
834 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
836 mpd_initDirectory(directory);
838 return directory;
839 }
841 void mpd_freeDirectory(mpd_Directory * directory) {
842 mpd_finishDirectory(directory);
844 free(directory);
845 }
847 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
848 mpd_Directory * ret = mpd_newDirectory();
850 if (directory->path)
851 ret->path = str_pool_dup(directory->path);
853 return ret;
854 }
856 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
857 playlist->path = NULL;
858 }
860 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
861 if (playlist->path)
862 str_pool_put(playlist->path);
863 }
865 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
866 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
868 mpd_initPlaylistFile(playlist);
870 return playlist;
871 }
873 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
874 mpd_finishPlaylistFile(playlist);
875 free(playlist);
876 }
878 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
879 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
881 if (playlist->path)
882 ret->path = str_pool_dup(playlist->path);
884 return ret;
885 }
887 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
888 entity->info.directory = NULL;
889 }
891 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
892 if(entity->info.directory) {
893 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
894 mpd_freeDirectory(entity->info.directory);
895 }
896 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
897 mpd_freeSong(entity->info.song);
898 }
899 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
900 mpd_freePlaylistFile(entity->info.playlistFile);
901 }
902 }
903 }
905 mpd_InfoEntity * mpd_newInfoEntity(void) {
906 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
908 mpd_initInfoEntity(entity);
910 return entity;
911 }
913 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
914 mpd_finishInfoEntity(entity);
915 free(entity);
916 }
918 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
919 mpd_executeCommand(connection,command);
920 }
922 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
923 mpd_InfoEntity * entity = NULL;
925 if(connection->doneProcessing || (connection->listOks &&
926 connection->doneListOk)) {
927 return NULL;
928 }
930 if(!connection->returnElement) mpd_getNextReturnElement(connection);
932 if(connection->returnElement) {
933 if(strcmp(connection->returnElement->name,"file")==0) {
934 entity = mpd_newInfoEntity();
935 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
936 entity->info.song = mpd_newSong();
937 entity->info.song->file =
938 str_pool_dup(connection->returnElement->value);
939 }
940 else if(strcmp(connection->returnElement->name,
941 "directory")==0) {
942 entity = mpd_newInfoEntity();
943 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
944 entity->info.directory = mpd_newDirectory();
945 entity->info.directory->path =
946 str_pool_dup(connection->returnElement->value);
947 }
948 else if(strcmp(connection->returnElement->name,"playlist")==0) {
949 entity = mpd_newInfoEntity();
950 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
951 entity->info.playlistFile = mpd_newPlaylistFile();
952 entity->info.playlistFile->path =
953 str_pool_dup(connection->returnElement->value);
954 }
955 else if(strcmp(connection->returnElement->name, "cpos") == 0){
956 entity = mpd_newInfoEntity();
957 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
958 entity->info.song = mpd_newSong();
959 entity->info.song->pos = atoi(connection->returnElement->value);
960 }
961 else {
962 connection->error = 1;
963 strcpy(connection->errorStr,"problem parsing song info");
964 return NULL;
965 }
966 }
967 else return NULL;
969 mpd_getNextReturnElement(connection);
970 while(connection->returnElement) {
971 mpd_ReturnElement * re = connection->returnElement;
973 if(strcmp(re->name,"file")==0) return entity;
974 else if(strcmp(re->name,"directory")==0) return entity;
975 else if(strcmp(re->name,"playlist")==0) return entity;
976 else if(strcmp(re->name,"cpos")==0) return entity;
978 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
979 strlen(re->value)) {
980 if(!entity->info.song->artist &&
981 strcmp(re->name,"Artist")==0) {
982 entity->info.song->artist = str_pool_dup(re->value);
983 }
984 else if(!entity->info.song->album &&
985 strcmp(re->name,"Album")==0) {
986 entity->info.song->album = str_pool_dup(re->value);
987 }
988 else if(!entity->info.song->title &&
989 strcmp(re->name,"Title")==0) {
990 entity->info.song->title = str_pool_dup(re->value);
991 }
992 else if(!entity->info.song->track &&
993 strcmp(re->name,"Track")==0) {
994 entity->info.song->track = str_pool_dup(re->value);
995 }
996 else if(!entity->info.song->name &&
997 strcmp(re->name,"Name")==0) {
998 entity->info.song->name = str_pool_dup(re->value);
999 }
1000 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1001 strcmp(re->name,"Time")==0) {
1002 entity->info.song->time = atoi(re->value);
1003 }
1004 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1005 strcmp(re->name,"Pos")==0) {
1006 entity->info.song->pos = atoi(re->value);
1007 }
1008 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1009 strcmp(re->name,"Id")==0) {
1010 entity->info.song->id = atoi(re->value);
1011 }
1012 else if(!entity->info.song->date &&
1013 strcmp(re->name, "Date") == 0) {
1014 entity->info.song->date = str_pool_dup(re->value);
1015 }
1016 else if(!entity->info.song->genre &&
1017 strcmp(re->name, "Genre") == 0) {
1018 entity->info.song->genre = str_pool_dup(re->value);
1019 }
1020 else if(!entity->info.song->composer &&
1021 strcmp(re->name, "Composer") == 0) {
1022 entity->info.song->composer = str_pool_dup(re->value);
1023 }
1024 else if(!entity->info.song->disc &&
1025 strcmp(re->name, "Disc") == 0) {
1026 entity->info.song->disc = str_pool_dup(re->value);
1027 }
1028 else if(!entity->info.song->comment &&
1029 strcmp(re->name, "Comment") == 0) {
1030 entity->info.song->comment = str_pool_dup(re->value);
1031 }
1032 }
1033 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1034 }
1035 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1036 }
1038 mpd_getNextReturnElement(connection);
1039 }
1041 return entity;
1042 }
1044 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1045 const char * name)
1046 {
1047 if(connection->doneProcessing || (connection->listOks &&
1048 connection->doneListOk))
1049 {
1050 return NULL;
1051 }
1053 mpd_getNextReturnElement(connection);
1054 while(connection->returnElement) {
1055 mpd_ReturnElement * re = connection->returnElement;
1057 if(strcmp(re->name,name)==0) return strdup(re->value);
1058 mpd_getNextReturnElement(connection);
1059 }
1061 return NULL;
1062 }
1064 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1065 if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1066 {
1067 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1068 }
1069 return NULL;
1070 }
1072 char * mpd_getNextArtist(mpd_Connection * connection) {
1073 return mpd_getNextReturnElementNamed(connection,"Artist");
1074 }
1076 char * mpd_getNextAlbum(mpd_Connection * connection) {
1077 return mpd_getNextReturnElementNamed(connection,"Album");
1078 }
1080 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1081 char * string = malloc(strlen("playlistinfo")+25);
1082 sprintf(string,"playlistinfo \"%i\"\n",songPos);
1083 mpd_sendInfoCommand(connection,string);
1084 free(string);
1085 }
1087 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1088 char * string = malloc(strlen("playlistid")+25);
1089 sprintf(string, "playlistid \"%i\"\n", id);
1090 mpd_sendInfoCommand(connection, string);
1091 free(string);
1092 }
1094 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1095 char * string = malloc(strlen("plchanges")+25);
1096 sprintf(string,"plchanges \"%lld\"\n",playlist);
1097 mpd_sendInfoCommand(connection,string);
1098 free(string);
1099 }
1101 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1102 char * string = malloc(strlen("plchangesposid")+25);
1103 sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1104 mpd_sendInfoCommand(connection,string);
1105 free(string);
1106 }
1108 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1109 char * sDir = mpd_sanitizeArg(dir);
1110 char * string = malloc(strlen("listall")+strlen(sDir)+5);
1111 sprintf(string,"listall \"%s\"\n",sDir);
1112 mpd_sendInfoCommand(connection,string);
1113 free(string);
1114 free(sDir);
1115 }
1117 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1118 char * sDir = mpd_sanitizeArg(dir);
1119 char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1120 sprintf(string,"listallinfo \"%s\"\n",sDir);
1121 mpd_sendInfoCommand(connection,string);
1122 free(string);
1123 free(sDir);
1124 }
1126 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1127 char * sDir = mpd_sanitizeArg(dir);
1128 char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1129 sprintf(string,"lsinfo \"%s\"\n",sDir);
1130 mpd_sendInfoCommand(connection,string);
1131 free(string);
1132 free(sDir);
1133 }
1135 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1136 mpd_executeCommand(connection,"currentsong\n");
1137 }
1139 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1140 const char * str)
1141 {
1142 char st[10];
1143 char * string;
1144 char * sanitStr = mpd_sanitizeArg(str);
1145 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1146 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1147 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1148 else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1149 else {
1150 connection->error = 1;
1151 strcpy(connection->errorStr,"unknown table for search");
1152 return;
1153 }
1154 string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1155 sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1156 mpd_sendInfoCommand(connection,string);
1157 free(string);
1158 free(sanitStr);
1159 }
1161 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1162 const char * str)
1163 {
1164 char st[10];
1165 char * string;
1166 char * sanitStr = mpd_sanitizeArg(str);
1167 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1168 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1169 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1170 else {
1171 connection->error = 1;
1172 strcpy(connection->errorStr,"unknown table for find");
1173 return;
1174 }
1175 string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1176 sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1177 mpd_sendInfoCommand(connection,string);
1178 free(string);
1179 free(sanitStr);
1180 }
1182 void mpd_sendListCommand(mpd_Connection * connection, int table,
1183 const char * arg1)
1184 {
1185 char st[10];
1186 char * string;
1187 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1188 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1189 else {
1190 connection->error = 1;
1191 strcpy(connection->errorStr,"unknown table for list");
1192 return;
1193 }
1194 if(arg1) {
1195 char * sanitArg1 = mpd_sanitizeArg(arg1);
1196 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1197 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1198 free(sanitArg1);
1199 }
1200 else {
1201 string = malloc(strlen("list")+strlen(st)+3);
1202 sprintf(string,"list %s\n",st);
1203 }
1204 mpd_sendInfoCommand(connection,string);
1205 free(string);
1206 }
1208 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1209 char * sFile = mpd_sanitizeArg(file);
1210 char * string = malloc(strlen("add")+strlen(sFile)+5);
1211 sprintf(string,"add \"%s\"\n",sFile);
1212 mpd_executeCommand(connection,string);
1213 free(string);
1214 free(sFile);
1215 }
1217 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1218 char * string = malloc(strlen("delete")+25);
1219 sprintf(string,"delete \"%i\"\n",songPos);
1220 mpd_sendInfoCommand(connection,string);
1221 free(string);
1222 }
1224 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1225 char * string = malloc(strlen("deleteid")+25);
1226 sprintf(string, "deleteid \"%i\"\n", id);
1227 mpd_sendInfoCommand(connection,string);
1228 free(string);
1229 }
1231 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1232 char * sName = mpd_sanitizeArg(name);
1233 char * string = malloc(strlen("save")+strlen(sName)+5);
1234 sprintf(string,"save \"%s\"\n",sName);
1235 mpd_executeCommand(connection,string);
1236 free(string);
1237 free(sName);
1238 }
1240 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1241 char * sName = mpd_sanitizeArg(name);
1242 char * string = malloc(strlen("load")+strlen(sName)+5);
1243 sprintf(string,"load \"%s\"\n",sName);
1244 mpd_executeCommand(connection,string);
1245 free(string);
1246 free(sName);
1247 }
1249 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1250 char * sName = mpd_sanitizeArg(name);
1251 char * string = malloc(strlen("rm")+strlen(sName)+5);
1252 sprintf(string,"rm \"%s\"\n",sName);
1253 mpd_executeCommand(connection,string);
1254 free(string);
1255 free(sName);
1256 }
1258 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1259 mpd_executeCommand(connection,"shuffle\n");
1260 }
1262 void mpd_sendClearCommand(mpd_Connection * connection) {
1263 mpd_executeCommand(connection,"clear\n");
1264 }
1266 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1267 char * string = malloc(strlen("play")+25);
1268 sprintf(string,"play \"%i\"\n",songPos);
1269 mpd_sendInfoCommand(connection,string);
1270 free(string);
1271 }
1273 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1274 char * string = malloc(strlen("playid")+25);
1275 sprintf(string,"playid \"%i\"\n",id);
1276 mpd_sendInfoCommand(connection,string);
1277 free(string);
1278 }
1280 void mpd_sendStopCommand(mpd_Connection * connection) {
1281 mpd_executeCommand(connection,"stop\n");
1282 }
1284 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1285 char * string = malloc(strlen("pause")+25);
1286 sprintf(string,"pause \"%i\"\n",pauseMode);
1287 mpd_executeCommand(connection,string);
1288 free(string);
1289 }
1291 void mpd_sendNextCommand(mpd_Connection * connection) {
1292 mpd_executeCommand(connection,"next\n");
1293 }
1295 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1296 char * string = malloc(strlen("move")+25);
1297 sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1298 mpd_sendInfoCommand(connection,string);
1299 free(string);
1300 }
1302 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1303 char * string = malloc(strlen("moveid")+25);
1304 sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1305 mpd_sendInfoCommand(connection,string);
1306 free(string);
1307 }
1309 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1310 char * string = malloc(strlen("swap")+25);
1311 sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1312 mpd_sendInfoCommand(connection,string);
1313 free(string);
1314 }
1316 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1317 char * string = malloc(strlen("swapid")+25);
1318 sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1319 mpd_sendInfoCommand(connection,string);
1320 free(string);
1321 }
1323 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1324 char * string = malloc(strlen("seek")+25);
1325 sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1326 mpd_sendInfoCommand(connection,string);
1327 free(string);
1328 }
1330 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1331 char * string = malloc(strlen("seekid")+25);
1332 sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1333 mpd_sendInfoCommand(connection,string);
1334 free(string);
1335 }
1337 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1338 char *sPath = mpd_sanitizeArg(path);
1339 char * string = malloc(strlen("update")+strlen(sPath)+5);
1340 sprintf(string,"update \"%s\"\n",sPath);
1341 mpd_sendInfoCommand(connection,string);
1342 free(string);
1343 free(sPath);
1344 }
1346 int mpd_getUpdateId(mpd_Connection * connection) {
1347 char * jobid;
1348 int ret = 0;
1350 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1351 if(jobid) {
1352 ret = atoi(jobid);
1353 free(jobid);
1354 }
1356 return ret;
1357 }
1359 void mpd_sendPrevCommand(mpd_Connection * connection) {
1360 mpd_executeCommand(connection,"previous\n");
1361 }
1363 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1364 char * string = malloc(strlen("repeat")+25);
1365 sprintf(string,"repeat \"%i\"\n",repeatMode);
1366 mpd_executeCommand(connection,string);
1367 free(string);
1368 }
1370 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1371 char * string = malloc(strlen("random")+25);
1372 sprintf(string,"random \"%i\"\n",randomMode);
1373 mpd_executeCommand(connection,string);
1374 free(string);
1375 }
1377 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1378 char * string = malloc(strlen("setvol")+25);
1379 sprintf(string,"setvol \"%i\"\n",volumeChange);
1380 mpd_executeCommand(connection,string);
1381 free(string);
1382 }
1384 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1385 char * string = malloc(strlen("volume")+25);
1386 sprintf(string,"volume \"%i\"\n",volumeChange);
1387 mpd_executeCommand(connection,string);
1388 free(string);
1389 }
1391 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1392 char * string = malloc(strlen("crossfade")+25);
1393 sprintf(string,"crossfade \"%i\"\n",seconds);
1394 mpd_executeCommand(connection,string);
1395 free(string);
1396 }
1398 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1399 char * sPass = mpd_sanitizeArg(pass);
1400 char * string = malloc(strlen("password")+strlen(sPass)+5);
1401 sprintf(string,"password \"%s\"\n",sPass);
1402 mpd_executeCommand(connection,string);
1403 free(string);
1404 free(sPass);
1405 }
1407 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1408 if(connection->commandList) {
1409 strcpy(connection->errorStr,"already in command list mode");
1410 connection->error = 1;
1411 return;
1412 }
1413 connection->commandList = COMMAND_LIST;
1414 mpd_executeCommand(connection,"command_list_begin\n");
1415 }
1417 void mpd_sendCommandListOkBegin(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_OK;
1424 mpd_executeCommand(connection,"command_list_ok_begin\n");
1425 connection->listOks = 0;
1426 }
1428 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1429 if(!connection->commandList) {
1430 strcpy(connection->errorStr,"not in command list mode");
1431 connection->error = 1;
1432 return;
1433 }
1434 connection->commandList = 0;
1435 mpd_executeCommand(connection,"command_list_end\n");
1436 }
1438 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1439 mpd_executeCommand(connection,"outputs\n");
1440 }
1442 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1443 mpd_OutputEntity * output = NULL;
1445 if(connection->doneProcessing || (connection->listOks &&
1446 connection->doneListOk))
1447 {
1448 return NULL;
1449 }
1451 if(connection->error) return NULL;
1453 output = malloc(sizeof(mpd_OutputEntity));
1454 output->id = -10;
1455 output->name = NULL;
1456 output->enabled = 0;
1458 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1460 while(connection->returnElement) {
1461 mpd_ReturnElement * re = connection->returnElement;
1462 if(strcmp(re->name,"outputid")==0) {
1463 if(output!=NULL && output->id>=0) return output;
1464 output->id = atoi(re->value);
1465 }
1466 else if(strcmp(re->name,"outputname")==0) {
1467 output->name = strdup(re->value);
1468 }
1469 else if(strcmp(re->name,"outputenabled")==0) {
1470 output->enabled = atoi(re->value);
1471 }
1473 mpd_getNextReturnElement(connection);
1474 if(connection->error) {
1475 free(output);
1476 return NULL;
1477 }
1479 }
1481 return output;
1482 }
1484 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1485 char * string = malloc(strlen("enableoutput")+25);
1486 sprintf(string,"enableoutput \"%i\"\n",outputId);
1487 mpd_executeCommand(connection,string);
1488 free(string);
1489 }
1491 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1492 char * string = malloc(strlen("disableoutput")+25);
1493 sprintf(string,"disableoutput \"%i\"\n",outputId);
1494 mpd_executeCommand(connection,string);
1495 free(string);
1496 }
1498 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1499 free(output->name);
1500 free(output);
1501 }
1503 /**
1504 * mpd_sendNotCommandsCommand
1505 * odd naming, but it gets the not allowed commands
1506 */
1508 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1509 mpd_executeCommand(connection,"notcommands\n");
1510 }
1512 /**
1513 * mpd_sendCommandsCommand
1514 * odd naming, but it gets the allowed commands
1515 */
1517 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1518 mpd_executeCommand(connection,"commands\n");
1519 }
1520 /**
1521 * Get the next returned command
1522 */
1523 char * mpd_getNextCommand(mpd_Connection * connection) {
1524 return mpd_getNextReturnElementNamed(connection,"command");
1525 }
1527 void mpd_startSearch(mpd_Connection * connection,int exact) {
1528 if(connection->request) {
1529 /* search/find allready in progress */
1530 /* TODO: set error here? */
1531 return;
1532 }
1533 if(exact){
1534 connection->request = strdup("find");
1535 }
1536 else{
1537 connection->request = strdup("search");
1538 }
1539 }
1542 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1543 if(connection->request) {
1544 /* search/find allready in progress */
1545 /* TODO: set error here? */
1546 return;
1547 }
1548 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1549 /* set error here */
1550 return;
1551 }
1553 connection->request = malloc(sizeof(char)*(
1554 /* length of the field name */
1555 strlen(mpdTagItemKeys[field])+
1556 /* "list"+space+\0 */
1557 6
1558 ));
1559 sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1560 }
1564 void mpd_addConstraintSearch(mpd_Connection *connection,
1565 int field,
1566 char *name)
1567 {
1568 char *arg = NULL;
1569 if(!connection->request){
1570 return;
1571 }
1572 if(name == NULL) {
1573 return;
1574 }
1575 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1576 return;
1577 }
1578 /* clean up the query */
1579 arg = mpd_sanitizeArg(name);
1580 /* create space for the query */
1581 connection->request = realloc(connection->request, (
1582 /* length of the old string */
1583 strlen(connection->request)+
1584 /* space between */
1585 1+
1586 /* length of the field name */
1587 strlen(mpdTagItemKeys[field])+
1588 /* space plus starting " */
1589 2+
1590 /* length of search term */
1591 strlen(arg)+
1592 /* closign " +\0 that is added sprintf */
1593 2
1594 )*sizeof(char));
1595 /* and form the query */
1596 sprintf(connection->request, "%s %s \"%s\"",
1597 connection->request,
1598 mpdTagItemKeys[field],
1599 arg);
1600 free(arg);
1601 }
1604 void mpd_commitSearch(mpd_Connection *connection)
1605 {
1606 if(connection->request)
1607 {
1608 int length = strlen(connection->request);
1609 /* fixing up the string for mpd to like */
1610 connection->request = realloc(connection->request,
1611 (length+ /* old length */
1612 2 /* closing \n and \0 */
1613 )*sizeof(char));
1614 connection->request[length] = '\n';
1615 connection->request[length+1] = '\0';
1616 /* and off we go */
1617 mpd_sendInfoCommand(connection, connection->request);
1618 /* clean up a bit */
1619 free(connection->request);
1620 connection->request = NULL;
1621 }
1622 }
1624 /**
1625 * @param connection a MpdConnection
1626 * @param path the path to the playlist.
1627 *
1628 * List the content, with full metadata, of a stored playlist.
1629 *
1630 */
1631 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1632 {
1633 char *arg = mpd_sanitizeArg(path);
1634 char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1635 sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1636 mpd_sendInfoCommand(connection, query);
1637 free(arg);
1638 free(query);
1639 }
1641 /**
1642 * @param connection a MpdConnection
1643 * @param path the path to the playlist.
1644 *
1645 * List the content of a stored playlist.
1646 *
1647 */
1648 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1649 {
1650 char *arg = mpd_sanitizeArg(path);
1651 char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1652 sprintf(query, "listplaylist \"%s\"\n",arg);
1653 mpd_sendInfoCommand(connection, query);
1654 free(arg);
1655 free(query);
1656 }