1b0851ff125c08b4d8d86dc9806b5af676a6345d
1 /* libmpdclient
2 (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
3 This project's homepage is: http://www.musicpd.org
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
12 - Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 - Neither the name of the Music Player Daemon nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
24 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
33 #include "libmpdclient.h"
34 #include "str_pool.h"
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <stdio.h>
39 #include <sys/param.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <fcntl.h>
45 #ifdef WIN32
46 # include <ws2tcpip.h>
47 # include <winsock.h>
48 #else
49 # include <netinet/in.h>
50 # include <arpa/inet.h>
51 # include <sys/socket.h>
52 # include <netdb.h>
53 #endif
55 #ifndef WIN32
56 #include <sys/un.h>
57 #endif
59 #ifndef MSG_DONTWAIT
60 # define MSG_DONTWAIT 0
61 #endif
63 #ifndef MPD_NO_GAI
64 # ifdef AI_ADDRCONFIG
65 # define MPD_HAVE_GAI
66 # endif
67 #endif
69 #define COMMAND_LIST 1
70 #define COMMAND_LIST_OK 2
72 #ifdef WIN32
73 # define SELECT_ERRNO_IGNORE (errno == WSAEINTR || errno == WSAEINPROGRESS)
74 # define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
75 #else
76 # define SELECT_ERRNO_IGNORE (errno == EINTR)
77 # define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
78 # define winsock_dll_error(c) 0
79 # define closesocket(s) close(s)
80 # define WSACleanup() do { /* nothing */ } while (0)
81 #endif
83 #ifdef WIN32
84 static int winsock_dll_error(mpd_Connection *connection)
85 {
86 WSADATA wsaData;
87 if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
88 LOBYTE(wsaData.wVersion) != 2 ||
89 HIBYTE(wsaData.wVersion) != 2 ) {
90 snprintf(connection->errorStr, sizeof(connection->errorStr),
91 "Could not find usable WinSock DLL.");
92 connection->error = MPD_ERROR_SYSTEM;
93 return 1;
94 }
95 return 0;
96 }
98 static int do_connect_fail(mpd_Connection *connection,
99 const struct sockaddr *serv_addr, int addrlen)
100 {
101 int iMode = 1; /* 0 = blocking, else non-blocking */
102 ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
103 return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR
104 && WSAGetLastError() != WSAEWOULDBLOCK);
105 }
106 #else /* !WIN32 (sane operating systems) */
107 static int do_connect_fail(mpd_Connection *connection,
108 const struct sockaddr *serv_addr, int addrlen)
109 {
110 int flags = fcntl(connection->sock, F_GETFL, 0);
111 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
112 return (connect(connection->sock,serv_addr,addrlen)<0 &&
113 errno!=EINPROGRESS);
114 }
115 #endif /* !WIN32 */
117 #ifdef MPD_HAVE_GAI
118 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
119 float timeout)
120 {
121 int error;
122 char service[20];
123 struct addrinfo hints;
124 struct addrinfo *res = NULL;
125 struct addrinfo *addrinfo = NULL;
127 /**
128 * Setup hints
129 */
130 hints.ai_flags = AI_ADDRCONFIG;
131 hints.ai_family = PF_UNSPEC;
132 hints.ai_socktype = SOCK_STREAM;
133 hints.ai_protocol = IPPROTO_TCP;
134 hints.ai_addrlen = 0;
135 hints.ai_addr = NULL;
136 hints.ai_canonname = NULL;
137 hints.ai_next = NULL;
139 snprintf(service, sizeof(service), "%d", port);
141 error = getaddrinfo(host, service, &hints, &addrinfo);
143 if (error) {
144 snprintf(connection->errorStr, sizeof(connection->errorStr),
145 "host \"%s\" not found: %s",host, gai_strerror(error));
146 connection->error = MPD_ERROR_UNKHOST;
147 return -1;
148 }
150 for (res = addrinfo; res; res = res->ai_next) {
151 /* create socket */
152 connection->sock = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
153 if (connection->sock < 0) {
154 snprintf(connection->errorStr, sizeof(connection->errorStr),
155 "problems creating socket: %s",
156 strerror(errno));
157 connection->error = MPD_ERROR_SYSTEM;
158 freeaddrinfo(addrinfo);
159 return -1;
160 }
162 mpd_setConnectionTimeout(connection,timeout);
164 /* connect stuff */
165 if (do_connect_fail(connection, res->ai_addr, res->ai_addrlen)) {
166 /* try the next address family */
167 closesocket(connection->sock);
168 connection->sock = -1;
169 continue;
170 }
171 }
172 freeaddrinfo(addrinfo);
174 if (connection->sock < 0) {
175 snprintf(connection->errorStr, sizeof(connection->errorStr),
176 "problems connecting to \"%s\" on port"
177 " %i: %s",host,port, strerror(errno));
178 connection->error = MPD_ERROR_CONNPORT;
180 return -1;
181 }
183 return 0;
184 }
185 #else /* !MPD_HAVE_GAI */
186 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
187 float timeout)
188 {
189 struct hostent * he;
190 struct sockaddr * dest;
191 int destlen;
192 struct sockaddr_in sin;
194 if(!(he=gethostbyname(host))) {
195 snprintf(connection->errorStr, sizeof(connection->errorStr),
196 "host \"%s\" not found",host);
197 connection->error = MPD_ERROR_UNKHOST;
198 return -1;
199 }
201 memset(&sin,0,sizeof(struct sockaddr_in));
202 /*dest.sin_family = he->h_addrtype;*/
203 sin.sin_family = AF_INET;
204 sin.sin_port = htons(port);
206 switch(he->h_addrtype) {
207 case AF_INET:
208 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
209 he->h_length);
210 dest = (struct sockaddr *)&sin;
211 destlen = sizeof(struct sockaddr_in);
212 break;
213 default:
214 strcpy(connection->errorStr,"address type is not IPv4\n");
215 connection->error = MPD_ERROR_SYSTEM;
216 return -1;
217 break;
218 }
220 if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
221 strcpy(connection->errorStr,"problems creating socket");
222 connection->error = MPD_ERROR_SYSTEM;
223 return -1;
224 }
226 mpd_setConnectionTimeout(connection,timeout);
228 /* connect stuff */
229 if (do_connect_fail(connection, dest, destlen)) {
230 snprintf(connection->errorStr, sizeof(connection->errorStr),
231 "problems connecting to \"%s\" on port"
232 " %i",host,port);
233 connection->error = MPD_ERROR_CONNPORT;
234 return -1;
235 }
237 return 0;
238 }
239 #endif /* !MPD_HAVE_GAI */
241 const char *const mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
242 {
243 "Artist",
244 "Album",
245 "Title",
246 "Track",
247 "Name",
248 "Genre",
249 "Date",
250 "Composer",
251 "Performer",
252 "Comment",
253 "Disc",
254 "filename"
255 };
257 static char * mpd_sanitizeArg(const char * arg) {
258 size_t i;
259 char * ret;
260 register const char *c;
261 register char *rc;
263 /* instead of counting in that loop above, just
264 * use a bit more memory and half running time
265 */
266 ret = malloc(strlen(arg) * 2 + 1);
268 c = arg;
269 rc = ret;
270 for(i = strlen(arg)+1; i != 0; --i) {
271 if(*c=='"' || *c=='\\')
272 *rc++ = '\\';
273 *(rc++) = *(c++);
274 }
276 return ret;
277 }
279 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
280 {
281 mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
283 ret->name = str_pool_get(name);
284 ret->value = str_pool_get(value);
286 return ret;
287 }
289 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
290 str_pool_put(re->name);
291 str_pool_put(re->value);
292 free(re);
293 }
295 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
296 connection->timeout.tv_sec = (int)timeout;
297 connection->timeout.tv_usec = (int)(timeout*1e6 -
298 connection->timeout.tv_sec*1000000 +
299 0.5);
300 }
302 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
303 char * output) {
304 char * tmp;
305 char * test;
306 int i;
308 if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
309 snprintf(connection->errorStr, sizeof(connection->errorStr),
310 "mpd not running on port %i on host \"%s\"",
311 port,host);
312 connection->error = MPD_ERROR_NOTMPD;
313 return 1;
314 }
316 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
318 for(i=0;i<3;i++) {
319 if(tmp) connection->version[i] = strtol(tmp,&test,10);
321 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
322 snprintf(connection->errorStr, sizeof(connection->errorStr),
323 "error parsing version number at "
324 "\"%s\"",
325 &output[strlen(MPD_WELCOME_MESSAGE)]);
326 connection->error = MPD_ERROR_NOTMPD;
327 return 1;
328 }
329 tmp = ++test;
330 }
332 return 0;
333 }
335 #ifndef WIN32
336 static int mpd_connect_un(mpd_Connection * connection,
337 const char * host, float timeout)
338 {
339 int error, flags;
340 size_t path_length;
341 struct sockaddr_un sun;
343 path_length = strlen(host);
344 if (path_length >= sizeof(sun.sun_path)) {
345 strcpy(connection->errorStr, "unix socket path is too long");
346 connection->error = MPD_ERROR_UNKHOST;
347 return -1;
348 }
350 sun.sun_family = AF_UNIX;
351 memcpy(sun.sun_path, host, path_length + 1);
353 connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
354 if (connection->sock < 0) {
355 strcpy(connection->errorStr, "problems creating socket");
356 connection->error = MPD_ERROR_SYSTEM;
357 return -1;
358 }
360 mpd_setConnectionTimeout(connection, timeout);
362 flags = fcntl(connection->sock, F_GETFL, 0);
363 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
365 error = connect(connection->sock, (struct sockaddr*)&sun, sizeof(sun));
366 if (error < 0) {
367 /* try the next address family */
368 close(connection->sock);
369 connection->sock = 0;
371 snprintf(connection->errorStr, sizeof(connection->errorStr),
372 "problems connecting to \"%s\": %s",
373 host, strerror(errno));
374 connection->error = MPD_ERROR_CONNPORT;
375 return -1;
376 }
378 return 0;
379 }
380 #endif /* WIN32 */
382 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
383 int err;
384 char * rt;
385 mpd_Connection * connection = malloc(sizeof(mpd_Connection));
386 struct timeval tv;
387 fd_set fds;
389 connection->buflen = 0;
390 connection->bufstart = 0;
391 mpd_clearError(connection);
392 connection->doneProcessing = 0;
393 connection->commandList = 0;
394 connection->listOks = 0;
395 connection->doneListOk = 0;
396 connection->returnElement = NULL;
397 connection->request = NULL;
399 if (winsock_dll_error(connection))
400 return connection;
402 #ifndef WIN32
403 if (host[0] == '/')
404 err = mpd_connect_un(connection, host, timeout);
405 else
406 #endif
407 err = mpd_connect(connection, host, port, timeout);
408 if (err < 0)
409 return connection;
411 while(!(rt = memchr(connection->buffer, '\n', connection->buflen))) {
412 tv.tv_sec = connection->timeout.tv_sec;
413 tv.tv_usec = connection->timeout.tv_usec;
414 FD_ZERO(&fds);
415 FD_SET(connection->sock,&fds);
416 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
417 ssize_t readed;
418 readed = recv(connection->sock,
419 &(connection->buffer[connection->buflen]),
420 sizeof(connection->buffer) - connection->buflen, 0);
421 if(readed<=0) {
422 snprintf(connection->errorStr, sizeof(connection->errorStr),
423 "problems getting a response from"
424 " \"%s\" on port %i : %s",host,
425 port, strerror(errno));
426 connection->error = MPD_ERROR_NORESPONSE;
427 return connection;
428 }
429 connection->buflen+=readed;
430 }
431 else if(err<0) {
432 if (SELECT_ERRNO_IGNORE)
433 continue;
434 snprintf(connection->errorStr,
435 sizeof(connection->errorStr),
436 "problems connecting to \"%s\" on port"
437 " %i",host,port);
438 connection->error = MPD_ERROR_CONNPORT;
439 return connection;
440 }
441 else {
442 snprintf(connection->errorStr, sizeof(connection->errorStr),
443 "timeout in attempting to get a response from"
444 " \"%s\" on port %i",host,port);
445 connection->error = MPD_ERROR_NORESPONSE;
446 return connection;
447 }
448 }
450 *rt = '\0';
451 if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
452 connection->doneProcessing = 1;
454 connection->buflen -= rt + 1 - connection->buffer;
455 memmove(connection->buffer, rt + 1, connection->buflen);
457 return connection;
458 }
460 void mpd_clearError(mpd_Connection * connection) {
461 connection->error = 0;
462 connection->errorStr[0] = '\0';
463 }
465 void mpd_closeConnection(mpd_Connection * connection) {
466 closesocket(connection->sock);
467 if(connection->returnElement) free(connection->returnElement);
468 if(connection->request) free(connection->request);
469 free(connection);
470 WSACleanup();
471 }
473 static void mpd_executeCommand(mpd_Connection *connection,
474 const char *command) {
475 int ret;
476 struct timeval tv;
477 fd_set fds;
478 const char *commandPtr = command;
479 int commandLen = strlen(command);
481 if (!connection->doneProcessing && !connection->commandList) {
482 strcpy(connection->errorStr,
483 "not done processing current command");
484 connection->error = 1;
485 return;
486 }
488 mpd_clearError(connection);
490 FD_ZERO(&fds);
491 FD_SET(connection->sock,&fds);
492 tv.tv_sec = connection->timeout.tv_sec;
493 tv.tv_usec = connection->timeout.tv_usec;
495 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
496 (ret==-1 && SELECT_ERRNO_IGNORE)) {
497 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
498 if(ret<=0)
499 {
500 if (SENDRECV_ERRNO_IGNORE) continue;
501 snprintf(connection->errorStr, sizeof(connection->errorStr),
502 "problems giving command \"%s\"",command);
503 connection->error = MPD_ERROR_SENDING;
504 return;
505 }
506 else {
507 commandPtr+=ret;
508 commandLen-=ret;
509 }
511 if(commandLen<=0) break;
512 }
514 if(commandLen>0) {
515 perror("");
516 snprintf(connection->errorStr, sizeof(connection->errorStr),
517 "timeout sending command \"%s\"",command);
518 connection->error = MPD_ERROR_TIMEOUT;
519 return;
520 }
522 if(!connection->commandList) connection->doneProcessing = 0;
523 else if(connection->commandList == COMMAND_LIST_OK) {
524 connection->listOks++;
525 }
526 }
528 static void mpd_getNextReturnElement(mpd_Connection * connection) {
529 char * output = NULL;
530 char * rt = NULL;
531 char * name = NULL;
532 char * value = NULL;
533 fd_set fds;
534 struct timeval tv;
535 char * tok = NULL;
536 ssize_t readed;
537 char * bufferCheck = NULL;
538 int err;
539 int pos;
541 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
542 connection->returnElement = NULL;
544 if (connection->doneProcessing ||
545 (connection->listOks && connection->doneListOk)) {
546 strcpy(connection->errorStr,"already done processing current command");
547 connection->error = 1;
548 return;
549 }
551 bufferCheck = connection->buffer+connection->bufstart;
552 while (connection->bufstart >= connection->buflen ||
553 !(rt = memchr(bufferCheck, '\n',
554 connection->buffer + connection->buflen -
555 bufferCheck))) {
556 if (connection->buflen >= sizeof(connection->buffer)) {
557 memmove(connection->buffer,
558 connection->buffer + connection->bufstart,
559 connection->buflen - connection->bufstart);
560 connection->buflen -= connection->bufstart;
561 connection->bufstart = 0;
562 }
563 if (connection->buflen >= sizeof(connection->buffer)) {
564 strcpy(connection->errorStr,"buffer overrun");
565 connection->error = MPD_ERROR_BUFFEROVERRUN;
566 connection->doneProcessing = 1;
567 connection->doneListOk = 0;
568 return;
569 }
570 bufferCheck = connection->buffer+connection->buflen;
571 tv.tv_sec = connection->timeout.tv_sec;
572 tv.tv_usec = connection->timeout.tv_usec;
573 FD_ZERO(&fds);
574 FD_SET(connection->sock,&fds);
575 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
576 readed = recv(connection->sock,
577 connection->buffer+connection->buflen,
578 sizeof(connection->buffer) - connection->buflen,
579 MSG_DONTWAIT);
580 if(readed<0 && SENDRECV_ERRNO_IGNORE) {
581 continue;
582 }
583 if(readed<=0) {
584 strcpy(connection->errorStr,"connection"
585 " closed");
586 connection->error = MPD_ERROR_CONNCLOSED;
587 connection->doneProcessing = 1;
588 connection->doneListOk = 0;
589 return;
590 }
591 connection->buflen+=readed;
592 }
593 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
594 else {
595 strcpy(connection->errorStr,"connection timeout");
596 connection->error = MPD_ERROR_TIMEOUT;
597 connection->doneProcessing = 1;
598 connection->doneListOk = 0;
599 return;
600 }
601 }
603 *rt = '\0';
604 output = connection->buffer+connection->bufstart;
605 connection->bufstart = rt - connection->buffer + 1;
607 if(strcmp(output,"OK")==0) {
608 if(connection->listOks > 0) {
609 strcpy(connection->errorStr, "expected more list_OK's");
610 connection->error = 1;
611 }
612 connection->listOks = 0;
613 connection->doneProcessing = 1;
614 connection->doneListOk = 0;
615 return;
616 }
618 if(strcmp(output, "list_OK") == 0) {
619 if(!connection->listOks) {
620 strcpy(connection->errorStr,
621 "got an unexpected list_OK");
622 connection->error = 1;
623 }
624 else {
625 connection->doneListOk = 1;
626 connection->listOks--;
627 }
628 return;
629 }
631 if(strncmp(output,"ACK",strlen("ACK"))==0) {
632 size_t length = strlen(output);
633 char * test;
634 char * needle;
635 int val;
637 if (length >= sizeof(connection->errorStr))
638 length = sizeof(connection->errorStr) - 1;
640 memcpy(connection->errorStr, output, length);
641 connection->errorStr[length] = 0;
642 connection->error = MPD_ERROR_ACK;
643 connection->errorCode = MPD_ACK_ERROR_UNK;
644 connection->errorAt = MPD_ERROR_AT_UNK;
645 connection->doneProcessing = 1;
646 connection->doneListOk = 0;
648 needle = strchr(output, '[');
649 if(!needle) return;
650 val = strtol(needle+1, &test, 10);
651 if(*test != '@') return;
652 connection->errorCode = val;
653 val = strtol(test+1, &test, 10);
654 if(*test != ']') return;
655 connection->errorAt = val;
656 return;
657 }
659 tok = strchr(output, ':');
660 if (!tok) return;
661 pos = tok - output;
662 value = ++tok;
663 name = output;
664 name[pos] = '\0';
666 if(value[0]==' ') {
667 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
668 }
669 else {
670 snprintf(connection->errorStr, sizeof(connection->errorStr),
671 "error parsing: %s:%s",name,value);
672 connection->error = 1;
673 }
674 }
676 void mpd_finishCommand(mpd_Connection * connection) {
677 while(!connection->doneProcessing) {
678 if(connection->doneListOk) connection->doneListOk = 0;
679 mpd_getNextReturnElement(connection);
680 }
681 }
683 static void mpd_finishListOkCommand(mpd_Connection * connection) {
684 while(!connection->doneProcessing && connection->listOks &&
685 !connection->doneListOk)
686 {
687 mpd_getNextReturnElement(connection);
688 }
689 }
691 int mpd_nextListOkCommand(mpd_Connection * connection) {
692 mpd_finishListOkCommand(connection);
693 if(!connection->doneProcessing) connection->doneListOk = 0;
694 if(connection->listOks == 0 || connection->doneProcessing) return -1;
695 return 0;
696 }
698 void mpd_sendStatusCommand(mpd_Connection * connection) {
699 mpd_executeCommand(connection,"status\n");
700 }
702 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
703 mpd_Status * status;
705 /*mpd_executeCommand(connection,"status\n");
707 if(connection->error) return NULL;*/
709 if(connection->doneProcessing || (connection->listOks &&
710 connection->doneListOk))
711 {
712 return NULL;
713 }
715 if(!connection->returnElement) mpd_getNextReturnElement(connection);
717 status = malloc(sizeof(mpd_Status));
718 status->volume = -1;
719 status->repeat = 0;
720 status->random = 0;
721 status->playlist = -1;
722 status->playlistLength = -1;
723 status->state = -1;
724 status->song = 0;
725 status->songid = 0;
726 status->elapsedTime = 0;
727 status->totalTime = 0;
728 status->bitRate = 0;
729 status->sampleRate = 0;
730 status->bits = 0;
731 status->channels = 0;
732 status->crossfade = -1;
733 status->error = NULL;
734 status->updatingDb = 0;
736 if(connection->error) {
737 free(status);
738 return NULL;
739 }
740 while(connection->returnElement) {
741 mpd_ReturnElement * re = connection->returnElement;
742 if(strcmp(re->name,"volume")==0) {
743 status->volume = atoi(re->value);
744 }
745 else if(strcmp(re->name,"repeat")==0) {
746 status->repeat = atoi(re->value);
747 }
748 else if(strcmp(re->name,"random")==0) {
749 status->random = atoi(re->value);
750 }
751 else if(strcmp(re->name,"playlist")==0) {
752 status->playlist = strtol(re->value,NULL,10);
753 }
754 else if(strcmp(re->name,"playlistlength")==0) {
755 status->playlistLength = atoi(re->value);
756 }
757 else if(strcmp(re->name,"bitrate")==0) {
758 status->bitRate = atoi(re->value);
759 }
760 else if(strcmp(re->name,"state")==0) {
761 if(strcmp(re->value,"play")==0) {
762 status->state = MPD_STATUS_STATE_PLAY;
763 }
764 else if(strcmp(re->value,"stop")==0) {
765 status->state = MPD_STATUS_STATE_STOP;
766 }
767 else if(strcmp(re->value,"pause")==0) {
768 status->state = MPD_STATUS_STATE_PAUSE;
769 }
770 else {
771 status->state = MPD_STATUS_STATE_UNKNOWN;
772 }
773 }
774 else if(strcmp(re->name,"song")==0) {
775 status->song = atoi(re->value);
776 }
777 else if(strcmp(re->name,"songid")==0) {
778 status->songid = atoi(re->value);
779 }
780 else if(strcmp(re->name,"time")==0) {
781 char * tok = strchr(re->value,':');
782 /* the second strchr below is a safety check */
783 if (tok && (strchr(tok,0) > (tok+1))) {
784 /* atoi stops at the first non-[0-9] char: */
785 status->elapsedTime = atoi(re->value);
786 status->totalTime = atoi(tok+1);
787 }
788 }
789 else if(strcmp(re->name,"error")==0) {
790 status->error = strdup(re->value);
791 }
792 else if(strcmp(re->name,"xfade")==0) {
793 status->crossfade = atoi(re->value);
794 }
795 else if(strcmp(re->name,"updating_db")==0) {
796 status->updatingDb = atoi(re->value);
797 }
798 else if(strcmp(re->name,"audio")==0) {
799 char * tok = strchr(re->value,':');
800 if (tok && (strchr(tok,0) > (tok+1))) {
801 status->sampleRate = atoi(re->value);
802 status->bits = atoi(++tok);
803 tok = strchr(tok,':');
804 if (tok && (strchr(tok,0) > (tok+1)))
805 status->channels = atoi(tok+1);
806 }
807 }
809 mpd_getNextReturnElement(connection);
810 if(connection->error) {
811 free(status);
812 return NULL;
813 }
814 }
816 if(connection->error) {
817 free(status);
818 return NULL;
819 }
820 else if(status->state<0) {
821 strcpy(connection->errorStr,"state not found");
822 connection->error = 1;
823 free(status);
824 return NULL;
825 }
827 return status;
828 }
830 void mpd_freeStatus(mpd_Status * status) {
831 if(status->error) free(status->error);
832 free(status);
833 }
835 void mpd_sendStatsCommand(mpd_Connection * connection) {
836 mpd_executeCommand(connection,"stats\n");
837 }
839 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
840 mpd_Stats * stats;
842 /*mpd_executeCommand(connection,"stats\n");
844 if(connection->error) return NULL;*/
846 if(connection->doneProcessing || (connection->listOks &&
847 connection->doneListOk))
848 {
849 return NULL;
850 }
852 if(!connection->returnElement) mpd_getNextReturnElement(connection);
854 stats = malloc(sizeof(mpd_Stats));
855 stats->numberOfArtists = 0;
856 stats->numberOfAlbums = 0;
857 stats->numberOfSongs = 0;
858 stats->uptime = 0;
859 stats->dbUpdateTime = 0;
860 stats->playTime = 0;
861 stats->dbPlayTime = 0;
863 if(connection->error) {
864 free(stats);
865 return NULL;
866 }
867 while(connection->returnElement) {
868 mpd_ReturnElement * re = connection->returnElement;
869 if(strcmp(re->name,"artists")==0) {
870 stats->numberOfArtists = atoi(re->value);
871 }
872 else if(strcmp(re->name,"albums")==0) {
873 stats->numberOfAlbums = atoi(re->value);
874 }
875 else if(strcmp(re->name,"songs")==0) {
876 stats->numberOfSongs = atoi(re->value);
877 }
878 else if(strcmp(re->name,"uptime")==0) {
879 stats->uptime = strtol(re->value,NULL,10);
880 }
881 else if(strcmp(re->name,"db_update")==0) {
882 stats->dbUpdateTime = strtol(re->value,NULL,10);
883 }
884 else if(strcmp(re->name,"playtime")==0) {
885 stats->playTime = strtol(re->value,NULL,10);
886 }
887 else if(strcmp(re->name,"db_playtime")==0) {
888 stats->dbPlayTime = strtol(re->value,NULL,10);
889 }
891 mpd_getNextReturnElement(connection);
892 if(connection->error) {
893 free(stats);
894 return NULL;
895 }
896 }
898 if(connection->error) {
899 free(stats);
900 return NULL;
901 }
903 return stats;
904 }
906 void mpd_freeStats(mpd_Stats * stats) {
907 free(stats);
908 }
910 static void mpd_initDirectory(mpd_Directory * directory) {
911 directory->path = NULL;
912 }
914 static void mpd_finishDirectory(mpd_Directory * directory) {
915 if (directory->path)
916 str_pool_put(directory->path);
917 }
919 mpd_Directory * mpd_newDirectory(void) {
920 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
922 mpd_initDirectory(directory);
924 return directory;
925 }
927 void mpd_freeDirectory(mpd_Directory * directory) {
928 mpd_finishDirectory(directory);
930 free(directory);
931 }
933 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
934 mpd_Directory * ret = mpd_newDirectory();
936 if (directory->path)
937 ret->path = str_pool_dup(directory->path);
939 return ret;
940 }
942 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
943 playlist->path = NULL;
944 }
946 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
947 if (playlist->path)
948 str_pool_put(playlist->path);
949 }
951 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
952 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
954 mpd_initPlaylistFile(playlist);
956 return playlist;
957 }
959 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
960 mpd_finishPlaylistFile(playlist);
961 free(playlist);
962 }
964 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
965 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
967 if (playlist->path)
968 ret->path = str_pool_dup(playlist->path);
970 return ret;
971 }
973 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
974 entity->info.directory = NULL;
975 }
977 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
978 if(entity->info.directory) {
979 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
980 mpd_freeDirectory(entity->info.directory);
981 }
982 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
983 mpd_freeSong(entity->info.song);
984 }
985 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
986 mpd_freePlaylistFile(entity->info.playlistFile);
987 }
988 }
989 }
991 mpd_InfoEntity * mpd_newInfoEntity(void) {
992 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
994 mpd_initInfoEntity(entity);
996 return entity;
997 }
999 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1000 mpd_finishInfoEntity(entity);
1001 free(entity);
1002 }
1004 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1005 mpd_executeCommand(connection,command);
1006 }
1008 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1009 mpd_InfoEntity * entity = NULL;
1011 if(connection->doneProcessing || (connection->listOks &&
1012 connection->doneListOk)) {
1013 return NULL;
1014 }
1016 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1018 if(connection->returnElement) {
1019 if(strcmp(connection->returnElement->name,"file")==0) {
1020 entity = mpd_newInfoEntity();
1021 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1022 entity->info.song = mpd_newSong();
1023 entity->info.song->file =
1024 str_pool_dup(connection->returnElement->value);
1025 }
1026 else if(strcmp(connection->returnElement->name,
1027 "directory")==0) {
1028 entity = mpd_newInfoEntity();
1029 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1030 entity->info.directory = mpd_newDirectory();
1031 entity->info.directory->path =
1032 str_pool_dup(connection->returnElement->value);
1033 }
1034 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1035 entity = mpd_newInfoEntity();
1036 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1037 entity->info.playlistFile = mpd_newPlaylistFile();
1038 entity->info.playlistFile->path =
1039 str_pool_dup(connection->returnElement->value);
1040 }
1041 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1042 entity = mpd_newInfoEntity();
1043 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1044 entity->info.song = mpd_newSong();
1045 entity->info.song->pos = atoi(connection->returnElement->value);
1046 }
1047 else {
1048 connection->error = 1;
1049 strcpy(connection->errorStr,"problem parsing song info");
1050 return NULL;
1051 }
1052 }
1053 else return NULL;
1055 mpd_getNextReturnElement(connection);
1056 while(connection->returnElement) {
1057 mpd_ReturnElement * re = connection->returnElement;
1059 if(strcmp(re->name,"file")==0) return entity;
1060 else if(strcmp(re->name,"directory")==0) return entity;
1061 else if(strcmp(re->name,"playlist")==0) return entity;
1062 else if(strcmp(re->name,"cpos")==0) return entity;
1064 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1065 strlen(re->value)) {
1066 if(!entity->info.song->artist &&
1067 strcmp(re->name,"Artist")==0) {
1068 entity->info.song->artist = str_pool_dup(re->value);
1069 }
1070 else if(!entity->info.song->album &&
1071 strcmp(re->name,"Album")==0) {
1072 entity->info.song->album = str_pool_dup(re->value);
1073 }
1074 else if(!entity->info.song->title &&
1075 strcmp(re->name,"Title")==0) {
1076 entity->info.song->title = str_pool_dup(re->value);
1077 }
1078 else if(!entity->info.song->track &&
1079 strcmp(re->name,"Track")==0) {
1080 entity->info.song->track = str_pool_dup(re->value);
1081 }
1082 else if(!entity->info.song->name &&
1083 strcmp(re->name,"Name")==0) {
1084 entity->info.song->name = str_pool_dup(re->value);
1085 }
1086 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1087 strcmp(re->name,"Time")==0) {
1088 entity->info.song->time = atoi(re->value);
1089 }
1090 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1091 strcmp(re->name,"Pos")==0) {
1092 entity->info.song->pos = atoi(re->value);
1093 }
1094 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1095 strcmp(re->name,"Id")==0) {
1096 entity->info.song->id = atoi(re->value);
1097 }
1098 else if(!entity->info.song->date &&
1099 strcmp(re->name, "Date") == 0) {
1100 entity->info.song->date = str_pool_dup(re->value);
1101 }
1102 else if(!entity->info.song->genre &&
1103 strcmp(re->name, "Genre") == 0) {
1104 entity->info.song->genre = str_pool_dup(re->value);
1105 }
1106 else if(!entity->info.song->composer &&
1107 strcmp(re->name, "Composer") == 0) {
1108 entity->info.song->composer = str_pool_dup(re->value);
1109 }
1110 else if(!entity->info.song->disc &&
1111 strcmp(re->name, "Disc") == 0) {
1112 entity->info.song->disc = str_pool_dup(re->value);
1113 }
1114 else if(!entity->info.song->comment &&
1115 strcmp(re->name, "Comment") == 0) {
1116 entity->info.song->comment = str_pool_dup(re->value);
1117 }
1118 }
1119 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1120 }
1121 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1122 }
1124 mpd_getNextReturnElement(connection);
1125 }
1127 return entity;
1128 }
1130 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1131 const char * name)
1132 {
1133 if(connection->doneProcessing || (connection->listOks &&
1134 connection->doneListOk))
1135 {
1136 return NULL;
1137 }
1139 mpd_getNextReturnElement(connection);
1140 while(connection->returnElement) {
1141 mpd_ReturnElement * re = connection->returnElement;
1143 if(strcmp(re->name,name)==0) return strdup(re->value);
1144 mpd_getNextReturnElement(connection);
1145 }
1147 return NULL;
1148 }
1150 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1151 if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1152 {
1153 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1154 }
1155 return NULL;
1156 }
1158 char * mpd_getNextArtist(mpd_Connection * connection) {
1159 return mpd_getNextReturnElementNamed(connection,"Artist");
1160 }
1162 char * mpd_getNextAlbum(mpd_Connection * connection) {
1163 return mpd_getNextReturnElementNamed(connection,"Album");
1164 }
1166 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1167 char * string = malloc(strlen("playlistinfo")+25);
1168 sprintf(string,"playlistinfo \"%i\"\n",songPos);
1169 mpd_sendInfoCommand(connection,string);
1170 free(string);
1171 }
1173 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1174 char * string = malloc(strlen("playlistid")+25);
1175 sprintf(string, "playlistid \"%i\"\n", id);
1176 mpd_sendInfoCommand(connection, string);
1177 free(string);
1178 }
1180 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1181 char * string = malloc(strlen("plchanges")+25);
1182 sprintf(string,"plchanges \"%lld\"\n",playlist);
1183 mpd_sendInfoCommand(connection,string);
1184 free(string);
1185 }
1187 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1188 char * string = malloc(strlen("plchangesposid")+25);
1189 sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1190 mpd_sendInfoCommand(connection,string);
1191 free(string);
1192 }
1194 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1195 char * sDir = mpd_sanitizeArg(dir);
1196 char * string = malloc(strlen("listall")+strlen(sDir)+5);
1197 sprintf(string,"listall \"%s\"\n",sDir);
1198 mpd_sendInfoCommand(connection,string);
1199 free(string);
1200 free(sDir);
1201 }
1203 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1204 char * sDir = mpd_sanitizeArg(dir);
1205 char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1206 sprintf(string,"listallinfo \"%s\"\n",sDir);
1207 mpd_sendInfoCommand(connection,string);
1208 free(string);
1209 free(sDir);
1210 }
1212 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1213 char * sDir = mpd_sanitizeArg(dir);
1214 char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1215 sprintf(string,"lsinfo \"%s\"\n",sDir);
1216 mpd_sendInfoCommand(connection,string);
1217 free(string);
1218 free(sDir);
1219 }
1221 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1222 mpd_executeCommand(connection,"currentsong\n");
1223 }
1225 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1226 const char * str)
1227 {
1228 char st[10];
1229 char * string;
1230 char * sanitStr = mpd_sanitizeArg(str);
1231 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1232 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1233 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1234 else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1235 else {
1236 connection->error = 1;
1237 strcpy(connection->errorStr,"unknown table for search");
1238 return;
1239 }
1240 string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1241 sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1242 mpd_sendInfoCommand(connection,string);
1243 free(string);
1244 free(sanitStr);
1245 }
1247 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1248 const char * str)
1249 {
1250 char st[10];
1251 char * string;
1252 char * sanitStr = mpd_sanitizeArg(str);
1253 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1254 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1255 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1256 else {
1257 connection->error = 1;
1258 strcpy(connection->errorStr,"unknown table for find");
1259 return;
1260 }
1261 string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1262 sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1263 mpd_sendInfoCommand(connection,string);
1264 free(string);
1265 free(sanitStr);
1266 }
1268 void mpd_sendListCommand(mpd_Connection * connection, int table,
1269 const char * arg1)
1270 {
1271 char st[10];
1272 char * string;
1273 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1274 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1275 else {
1276 connection->error = 1;
1277 strcpy(connection->errorStr,"unknown table for list");
1278 return;
1279 }
1280 if(arg1) {
1281 char * sanitArg1 = mpd_sanitizeArg(arg1);
1282 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1283 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1284 free(sanitArg1);
1285 }
1286 else {
1287 string = malloc(strlen("list")+strlen(st)+3);
1288 sprintf(string,"list %s\n",st);
1289 }
1290 mpd_sendInfoCommand(connection,string);
1291 free(string);
1292 }
1294 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1295 char * sFile = mpd_sanitizeArg(file);
1296 char * string = malloc(strlen("add")+strlen(sFile)+5);
1297 sprintf(string,"add \"%s\"\n",sFile);
1298 mpd_executeCommand(connection,string);
1299 free(string);
1300 free(sFile);
1301 }
1303 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1304 char * string = malloc(strlen("delete")+25);
1305 sprintf(string,"delete \"%i\"\n",songPos);
1306 mpd_sendInfoCommand(connection,string);
1307 free(string);
1308 }
1310 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1311 char * string = malloc(strlen("deleteid")+25);
1312 sprintf(string, "deleteid \"%i\"\n", id);
1313 mpd_sendInfoCommand(connection,string);
1314 free(string);
1315 }
1317 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1318 char * sName = mpd_sanitizeArg(name);
1319 char * string = malloc(strlen("save")+strlen(sName)+5);
1320 sprintf(string,"save \"%s\"\n",sName);
1321 mpd_executeCommand(connection,string);
1322 free(string);
1323 free(sName);
1324 }
1326 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1327 char * sName = mpd_sanitizeArg(name);
1328 char * string = malloc(strlen("load")+strlen(sName)+5);
1329 sprintf(string,"load \"%s\"\n",sName);
1330 mpd_executeCommand(connection,string);
1331 free(string);
1332 free(sName);
1333 }
1335 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1336 char * sName = mpd_sanitizeArg(name);
1337 char * string = malloc(strlen("rm")+strlen(sName)+5);
1338 sprintf(string,"rm \"%s\"\n",sName);
1339 mpd_executeCommand(connection,string);
1340 free(string);
1341 free(sName);
1342 }
1344 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1345 mpd_executeCommand(connection,"shuffle\n");
1346 }
1348 void mpd_sendClearCommand(mpd_Connection * connection) {
1349 mpd_executeCommand(connection,"clear\n");
1350 }
1352 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1353 char * string = malloc(strlen("play")+25);
1354 sprintf(string,"play \"%i\"\n",songPos);
1355 mpd_sendInfoCommand(connection,string);
1356 free(string);
1357 }
1359 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1360 char * string = malloc(strlen("playid")+25);
1361 sprintf(string,"playid \"%i\"\n",id);
1362 mpd_sendInfoCommand(connection,string);
1363 free(string);
1364 }
1366 void mpd_sendStopCommand(mpd_Connection * connection) {
1367 mpd_executeCommand(connection,"stop\n");
1368 }
1370 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1371 char * string = malloc(strlen("pause")+25);
1372 sprintf(string,"pause \"%i\"\n",pauseMode);
1373 mpd_executeCommand(connection,string);
1374 free(string);
1375 }
1377 void mpd_sendNextCommand(mpd_Connection * connection) {
1378 mpd_executeCommand(connection,"next\n");
1379 }
1381 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1382 char * string = malloc(strlen("move")+25);
1383 sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1384 mpd_sendInfoCommand(connection,string);
1385 free(string);
1386 }
1388 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1389 char * string = malloc(strlen("moveid")+25);
1390 sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1391 mpd_sendInfoCommand(connection,string);
1392 free(string);
1393 }
1395 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1396 char * string = malloc(strlen("swap")+25);
1397 sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1398 mpd_sendInfoCommand(connection,string);
1399 free(string);
1400 }
1402 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1403 char * string = malloc(strlen("swapid")+25);
1404 sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1405 mpd_sendInfoCommand(connection,string);
1406 free(string);
1407 }
1409 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1410 char * string = malloc(strlen("seek")+25);
1411 sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1412 mpd_sendInfoCommand(connection,string);
1413 free(string);
1414 }
1416 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1417 char * string = malloc(strlen("seekid")+25);
1418 sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1419 mpd_sendInfoCommand(connection,string);
1420 free(string);
1421 }
1423 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1424 char *sPath = mpd_sanitizeArg(path);
1425 char * string = malloc(strlen("update")+strlen(sPath)+5);
1426 sprintf(string,"update \"%s\"\n",sPath);
1427 mpd_sendInfoCommand(connection,string);
1428 free(string);
1429 free(sPath);
1430 }
1432 int mpd_getUpdateId(mpd_Connection * connection) {
1433 char * jobid;
1434 int ret = 0;
1436 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1437 if(jobid) {
1438 ret = atoi(jobid);
1439 free(jobid);
1440 }
1442 return ret;
1443 }
1445 void mpd_sendPrevCommand(mpd_Connection * connection) {
1446 mpd_executeCommand(connection,"previous\n");
1447 }
1449 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1450 char * string = malloc(strlen("repeat")+25);
1451 sprintf(string,"repeat \"%i\"\n",repeatMode);
1452 mpd_executeCommand(connection,string);
1453 free(string);
1454 }
1456 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1457 char * string = malloc(strlen("random")+25);
1458 sprintf(string,"random \"%i\"\n",randomMode);
1459 mpd_executeCommand(connection,string);
1460 free(string);
1461 }
1463 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1464 char * string = malloc(strlen("setvol")+25);
1465 sprintf(string,"setvol \"%i\"\n",volumeChange);
1466 mpd_executeCommand(connection,string);
1467 free(string);
1468 }
1470 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1471 char * string = malloc(strlen("volume")+25);
1472 sprintf(string,"volume \"%i\"\n",volumeChange);
1473 mpd_executeCommand(connection,string);
1474 free(string);
1475 }
1477 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1478 char * string = malloc(strlen("crossfade")+25);
1479 sprintf(string,"crossfade \"%i\"\n",seconds);
1480 mpd_executeCommand(connection,string);
1481 free(string);
1482 }
1484 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1485 char * sPass = mpd_sanitizeArg(pass);
1486 char * string = malloc(strlen("password")+strlen(sPass)+5);
1487 sprintf(string,"password \"%s\"\n",sPass);
1488 mpd_executeCommand(connection,string);
1489 free(string);
1490 free(sPass);
1491 }
1493 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1494 if(connection->commandList) {
1495 strcpy(connection->errorStr,"already in command list mode");
1496 connection->error = 1;
1497 return;
1498 }
1499 connection->commandList = COMMAND_LIST;
1500 mpd_executeCommand(connection,"command_list_begin\n");
1501 }
1503 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1504 if(connection->commandList) {
1505 strcpy(connection->errorStr,"already in command list mode");
1506 connection->error = 1;
1507 return;
1508 }
1509 connection->commandList = COMMAND_LIST_OK;
1510 mpd_executeCommand(connection,"command_list_ok_begin\n");
1511 connection->listOks = 0;
1512 }
1514 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1515 if(!connection->commandList) {
1516 strcpy(connection->errorStr,"not in command list mode");
1517 connection->error = 1;
1518 return;
1519 }
1520 connection->commandList = 0;
1521 mpd_executeCommand(connection,"command_list_end\n");
1522 }
1524 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1525 mpd_executeCommand(connection,"outputs\n");
1526 }
1528 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1529 mpd_OutputEntity * output = NULL;
1531 if(connection->doneProcessing || (connection->listOks &&
1532 connection->doneListOk))
1533 {
1534 return NULL;
1535 }
1537 if(connection->error) return NULL;
1539 output = malloc(sizeof(mpd_OutputEntity));
1540 output->id = -10;
1541 output->name = NULL;
1542 output->enabled = 0;
1544 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1546 while(connection->returnElement) {
1547 mpd_ReturnElement * re = connection->returnElement;
1548 if(strcmp(re->name,"outputid")==0) {
1549 if(output!=NULL && output->id>=0) return output;
1550 output->id = atoi(re->value);
1551 }
1552 else if(strcmp(re->name,"outputname")==0) {
1553 output->name = strdup(re->value);
1554 }
1555 else if(strcmp(re->name,"outputenabled")==0) {
1556 output->enabled = atoi(re->value);
1557 }
1559 mpd_getNextReturnElement(connection);
1560 if(connection->error) {
1561 free(output);
1562 return NULL;
1563 }
1565 }
1567 return output;
1568 }
1570 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1571 char * string = malloc(strlen("enableoutput")+25);
1572 sprintf(string,"enableoutput \"%i\"\n",outputId);
1573 mpd_executeCommand(connection,string);
1574 free(string);
1575 }
1577 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1578 char * string = malloc(strlen("disableoutput")+25);
1579 sprintf(string,"disableoutput \"%i\"\n",outputId);
1580 mpd_executeCommand(connection,string);
1581 free(string);
1582 }
1584 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1585 free(output->name);
1586 free(output);
1587 }
1589 /**
1590 * mpd_sendNotCommandsCommand
1591 * odd naming, but it gets the not allowed commands
1592 */
1594 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1595 mpd_executeCommand(connection,"notcommands\n");
1596 }
1598 /**
1599 * mpd_sendCommandsCommand
1600 * odd naming, but it gets the allowed commands
1601 */
1603 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1604 mpd_executeCommand(connection,"commands\n");
1605 }
1606 /**
1607 * Get the next returned command
1608 */
1609 char * mpd_getNextCommand(mpd_Connection * connection) {
1610 return mpd_getNextReturnElementNamed(connection,"command");
1611 }
1613 void mpd_startSearch(mpd_Connection * connection,int exact) {
1614 if(connection->request) {
1615 /* search/find allready in progress */
1616 /* TODO: set error here? */
1617 return;
1618 }
1619 if(exact){
1620 connection->request = strdup("find");
1621 }
1622 else{
1623 connection->request = strdup("search");
1624 }
1625 }
1628 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1629 if(connection->request) {
1630 /* search/find allready in progress */
1631 /* TODO: set error here? */
1632 return;
1633 }
1634 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1635 /* set error here */
1636 return;
1637 }
1639 connection->request = malloc(sizeof(char)*(
1640 /* length of the field name */
1641 strlen(mpdTagItemKeys[field])+
1642 /* "list"+space+\0 */
1643 6
1644 ));
1645 sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1646 }
1650 void mpd_addConstraintSearch(mpd_Connection *connection,
1651 int field,
1652 char *name)
1653 {
1654 char *arg = NULL;
1655 if(!connection->request){
1656 return;
1657 }
1658 if(name == NULL) {
1659 return;
1660 }
1661 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1662 return;
1663 }
1664 /* clean up the query */
1665 arg = mpd_sanitizeArg(name);
1666 /* create space for the query */
1667 connection->request = realloc(connection->request, (
1668 /* length of the old string */
1669 strlen(connection->request)+
1670 /* space between */
1671 1+
1672 /* length of the field name */
1673 strlen(mpdTagItemKeys[field])+
1674 /* space plus starting " */
1675 2+
1676 /* length of search term */
1677 strlen(arg)+
1678 /* closign " +\0 that is added sprintf */
1679 2
1680 )*sizeof(char));
1681 /* and form the query */
1682 sprintf(connection->request, "%s %s \"%s\"",
1683 connection->request,
1684 mpdTagItemKeys[field],
1685 arg);
1686 free(arg);
1687 }
1690 void mpd_commitSearch(mpd_Connection *connection)
1691 {
1692 if(connection->request)
1693 {
1694 int length = strlen(connection->request);
1695 /* fixing up the string for mpd to like */
1696 connection->request = realloc(connection->request,
1697 (length+ /* old length */
1698 2 /* closing \n and \0 */
1699 )*sizeof(char));
1700 connection->request[length] = '\n';
1701 connection->request[length+1] = '\0';
1702 /* and off we go */
1703 mpd_sendInfoCommand(connection, connection->request);
1704 /* clean up a bit */
1705 free(connection->request);
1706 connection->request = NULL;
1707 }
1708 }
1710 /**
1711 * @param connection a MpdConnection
1712 * @param path the path to the playlist.
1713 *
1714 * List the content, with full metadata, of a stored playlist.
1715 *
1716 */
1717 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1718 {
1719 char *arg = mpd_sanitizeArg(path);
1720 char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1721 sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1722 mpd_sendInfoCommand(connection, query);
1723 free(arg);
1724 free(query);
1725 }
1727 /**
1728 * @param connection a MpdConnection
1729 * @param path the path to the playlist.
1730 *
1731 * List the content of a stored playlist.
1732 *
1733 */
1734 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1735 {
1736 char *arg = mpd_sanitizeArg(path);
1737 char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1738 sprintf(query, "listplaylist \"%s\"\n",arg);
1739 mpd_sendInfoCommand(connection, query);
1740 free(arg);
1741 free(query);
1742 }