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,MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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, MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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,MPD_BUFFER_MAX_LENGTH,
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,
323 MPD_BUFFER_MAX_LENGTH,
324 "error parsing version number at "
325 "\"%s\"",
326 &output[strlen(MPD_WELCOME_MESSAGE)]);
327 connection->error = MPD_ERROR_NOTMPD;
328 return 1;
329 }
330 tmp = ++test;
331 }
333 return 0;
334 }
336 #ifndef WIN32
337 static int mpd_connect_un(mpd_Connection * connection,
338 const char * host, float timeout)
339 {
340 int error, flags;
341 size_t path_length;
342 struct sockaddr_un sun;
344 path_length = strlen(host);
345 if (path_length >= sizeof(sun.sun_path)) {
346 strcpy(connection->errorStr, "unix socket path is too long");
347 connection->error = MPD_ERROR_UNKHOST;
348 return -1;
349 }
351 sun.sun_family = AF_UNIX;
352 memcpy(sun.sun_path, host, path_length + 1);
354 connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
355 if (connection->sock < 0) {
356 strcpy(connection->errorStr, "problems creating socket");
357 connection->error = MPD_ERROR_SYSTEM;
358 return -1;
359 }
361 mpd_setConnectionTimeout(connection, timeout);
363 flags = fcntl(connection->sock, F_GETFL, 0);
364 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
366 error = connect(connection->sock, (struct sockaddr*)&sun, sizeof(sun));
367 if (error < 0) {
368 /* try the next address family */
369 close(connection->sock);
370 connection->sock = 0;
372 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
373 "problems connecting to \"%s\": %s",
374 host, strerror(errno));
375 connection->error = MPD_ERROR_CONNPORT;
376 return -1;
377 }
379 return 0;
380 }
381 #endif /* WIN32 */
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));
387 struct timeval tv;
388 fd_set fds;
389 strcpy(connection->buffer,"");
390 connection->buflen = 0;
391 connection->bufstart = 0;
392 strcpy(connection->errorStr,"");
393 connection->error = 0;
394 connection->doneProcessing = 0;
395 connection->commandList = 0;
396 connection->listOks = 0;
397 connection->doneListOk = 0;
398 connection->returnElement = NULL;
399 connection->request = NULL;
401 if (winsock_dll_error(connection))
402 return connection;
404 #ifndef WIN32
405 if (host[0] == '/')
406 err = mpd_connect_un(connection, host, timeout);
407 else
408 #endif
409 err = mpd_connect(connection, host, port, timeout);
410 if (err < 0)
411 return connection;
413 while(!(rt = strstr(connection->buffer,"\n"))) {
414 tv.tv_sec = connection->timeout.tv_sec;
415 tv.tv_usec = connection->timeout.tv_usec;
416 FD_ZERO(&fds);
417 FD_SET(connection->sock,&fds);
418 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
419 ssize_t readed;
420 readed = recv(connection->sock,
421 &(connection->buffer[connection->buflen]),
422 MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
423 if(readed<=0) {
424 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
425 "problems getting a response from"
426 " \"%s\" on port %i : %s",host,
427 port, strerror(errno));
428 connection->error = MPD_ERROR_NORESPONSE;
429 return connection;
430 }
431 connection->buflen+=readed;
432 connection->buffer[connection->buflen] = '\0';
433 }
434 else if(err<0) {
435 if (SELECT_ERRNO_IGNORE)
436 continue;
437 snprintf(connection->errorStr,
438 MPD_BUFFER_MAX_LENGTH,
439 "problems connecting to \"%s\" on port"
440 " %i",host,port);
441 connection->error = MPD_ERROR_CONNPORT;
442 return connection;
443 }
444 else {
445 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
446 "timeout in attempting to get a response from"
447 " \"%s\" on port %i",host,port);
448 connection->error = MPD_ERROR_NORESPONSE;
449 return connection;
450 }
451 }
453 *rt = '\0';
454 if (mpd_parseWelcome(connection, host, port, connection->buffer) == 0)
455 connection->doneProcessing = 1;
457 strcpy(connection->buffer,rt+1);
458 connection->buflen = strlen(connection->buffer);
460 return connection;
461 }
463 void mpd_clearError(mpd_Connection * connection) {
464 connection->error = 0;
465 connection->errorStr[0] = '\0';
466 }
468 void mpd_closeConnection(mpd_Connection * connection) {
469 closesocket(connection->sock);
470 if(connection->returnElement) free(connection->returnElement);
471 if(connection->request) free(connection->request);
472 free(connection);
473 WSACleanup();
474 }
476 static void mpd_executeCommand(mpd_Connection *connection,
477 const char *command) {
478 int ret;
479 struct timeval tv;
480 fd_set fds;
481 const char *commandPtr = command;
482 int commandLen = strlen(command);
484 if (!connection->doneProcessing && !connection->commandList) {
485 strcpy(connection->errorStr,
486 "not done processing current command");
487 connection->error = 1;
488 return;
489 }
491 mpd_clearError(connection);
493 FD_ZERO(&fds);
494 FD_SET(connection->sock,&fds);
495 tv.tv_sec = connection->timeout.tv_sec;
496 tv.tv_usec = connection->timeout.tv_usec;
498 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
499 (ret==-1 && SELECT_ERRNO_IGNORE)) {
500 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
501 if(ret<=0)
502 {
503 if (SENDRECV_ERRNO_IGNORE) continue;
504 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
505 "problems giving command \"%s\"",command);
506 connection->error = MPD_ERROR_SENDING;
507 return;
508 }
509 else {
510 commandPtr+=ret;
511 commandLen-=ret;
512 }
514 if(commandLen<=0) break;
515 }
517 if(commandLen>0) {
518 perror("");
519 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
520 "timeout sending command \"%s\"",command);
521 connection->error = MPD_ERROR_TIMEOUT;
522 return;
523 }
525 if(!connection->commandList) connection->doneProcessing = 0;
526 else if(connection->commandList == COMMAND_LIST_OK) {
527 connection->listOks++;
528 }
529 }
531 static void mpd_getNextReturnElement(mpd_Connection * connection) {
532 char * output = NULL;
533 char * rt = NULL;
534 char * name = NULL;
535 char * value = NULL;
536 fd_set fds;
537 struct timeval tv;
538 char * tok = NULL;
539 ssize_t readed;
540 char * bufferCheck = NULL;
541 int err;
542 int pos;
544 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
545 connection->returnElement = NULL;
547 if (connection->doneProcessing ||
548 (connection->listOks && connection->doneListOk)) {
549 strcpy(connection->errorStr,"already done processing current command");
550 connection->error = 1;
551 return;
552 }
554 bufferCheck = connection->buffer+connection->bufstart;
555 while (connection->bufstart >= connection->buflen ||
556 !(rt = strchr(bufferCheck, '\n'))) {
557 if (connection->buflen >= MPD_BUFFER_MAX_LENGTH) {
558 memmove(connection->buffer,
559 connection->buffer + connection->bufstart,
560 connection->buflen - connection->bufstart + 1);
561 connection->buflen -= connection->bufstart;
562 connection->bufstart = 0;
563 }
564 if (connection->buflen >= MPD_BUFFER_MAX_LENGTH) {
565 strcpy(connection->errorStr,"buffer overrun");
566 connection->error = MPD_ERROR_BUFFEROVERRUN;
567 connection->doneProcessing = 1;
568 connection->doneListOk = 0;
569 return;
570 }
571 bufferCheck = connection->buffer+connection->buflen;
572 tv.tv_sec = connection->timeout.tv_sec;
573 tv.tv_usec = connection->timeout.tv_usec;
574 FD_ZERO(&fds);
575 FD_SET(connection->sock,&fds);
576 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
577 readed = recv(connection->sock,
578 connection->buffer+connection->buflen,
579 MPD_BUFFER_MAX_LENGTH-connection->buflen,
580 MSG_DONTWAIT);
581 if(readed<0 && SENDRECV_ERRNO_IGNORE) {
582 continue;
583 }
584 if(readed<=0) {
585 strcpy(connection->errorStr,"connection"
586 " closed");
587 connection->error = MPD_ERROR_CONNCLOSED;
588 connection->doneProcessing = 1;
589 connection->doneListOk = 0;
590 return;
591 }
592 connection->buflen+=readed;
593 connection->buffer[connection->buflen] = '\0';
594 }
595 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
596 else {
597 strcpy(connection->errorStr,"connection timeout");
598 connection->error = MPD_ERROR_TIMEOUT;
599 connection->doneProcessing = 1;
600 connection->doneListOk = 0;
601 return;
602 }
603 }
605 *rt = '\0';
606 output = connection->buffer+connection->bufstart;
607 connection->bufstart = rt - connection->buffer + 1;
609 if(strcmp(output,"OK")==0) {
610 if(connection->listOks > 0) {
611 strcpy(connection->errorStr, "expected more list_OK's");
612 connection->error = 1;
613 }
614 connection->listOks = 0;
615 connection->doneProcessing = 1;
616 connection->doneListOk = 0;
617 return;
618 }
620 if(strcmp(output, "list_OK") == 0) {
621 if(!connection->listOks) {
622 strcpy(connection->errorStr,
623 "got an unexpected list_OK");
624 connection->error = 1;
625 }
626 else {
627 connection->doneListOk = 1;
628 connection->listOks--;
629 }
630 return;
631 }
633 if(strncmp(output,"ACK",strlen("ACK"))==0) {
634 char * test;
635 char * needle;
636 int val;
638 strcpy(connection->errorStr, output);
639 connection->error = MPD_ERROR_ACK;
640 connection->errorCode = MPD_ACK_ERROR_UNK;
641 connection->errorAt = MPD_ERROR_AT_UNK;
642 connection->doneProcessing = 1;
643 connection->doneListOk = 0;
645 needle = strchr(output, '[');
646 if(!needle) return;
647 val = strtol(needle+1, &test, 10);
648 if(*test != '@') return;
649 connection->errorCode = val;
650 val = strtol(test+1, &test, 10);
651 if(*test != ']') return;
652 connection->errorAt = val;
653 return;
654 }
656 tok = strchr(output, ':');
657 if (!tok) return;
658 pos = tok - output;
659 value = ++tok;
660 name = output;
661 name[pos] = '\0';
663 if(value[0]==' ') {
664 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
665 }
666 else {
667 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
668 "error parsing: %s:%s",name,value);
669 connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0';
670 connection->error = 1;
671 }
672 }
674 void mpd_finishCommand(mpd_Connection * connection) {
675 while(!connection->doneProcessing) {
676 if(connection->doneListOk) connection->doneListOk = 0;
677 mpd_getNextReturnElement(connection);
678 }
679 }
681 static void mpd_finishListOkCommand(mpd_Connection * connection) {
682 while(!connection->doneProcessing && connection->listOks &&
683 !connection->doneListOk)
684 {
685 mpd_getNextReturnElement(connection);
686 }
687 }
689 int mpd_nextListOkCommand(mpd_Connection * connection) {
690 mpd_finishListOkCommand(connection);
691 if(!connection->doneProcessing) connection->doneListOk = 0;
692 if(connection->listOks == 0 || connection->doneProcessing) return -1;
693 return 0;
694 }
696 void mpd_sendStatusCommand(mpd_Connection * connection) {
697 mpd_executeCommand(connection,"status\n");
698 }
700 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
701 mpd_Status * status;
703 /*mpd_executeCommand(connection,"status\n");
705 if(connection->error) return NULL;*/
707 if(connection->doneProcessing || (connection->listOks &&
708 connection->doneListOk))
709 {
710 return NULL;
711 }
713 if(!connection->returnElement) mpd_getNextReturnElement(connection);
715 status = malloc(sizeof(mpd_Status));
716 status->volume = -1;
717 status->repeat = 0;
718 status->random = 0;
719 status->playlist = -1;
720 status->playlistLength = -1;
721 status->state = -1;
722 status->song = 0;
723 status->songid = 0;
724 status->elapsedTime = 0;
725 status->totalTime = 0;
726 status->bitRate = 0;
727 status->sampleRate = 0;
728 status->bits = 0;
729 status->channels = 0;
730 status->crossfade = -1;
731 status->error = NULL;
732 status->updatingDb = 0;
734 if(connection->error) {
735 free(status);
736 return NULL;
737 }
738 while(connection->returnElement) {
739 mpd_ReturnElement * re = connection->returnElement;
740 if(strcmp(re->name,"volume")==0) {
741 status->volume = atoi(re->value);
742 }
743 else if(strcmp(re->name,"repeat")==0) {
744 status->repeat = atoi(re->value);
745 }
746 else if(strcmp(re->name,"random")==0) {
747 status->random = atoi(re->value);
748 }
749 else if(strcmp(re->name,"playlist")==0) {
750 status->playlist = strtol(re->value,NULL,10);
751 }
752 else if(strcmp(re->name,"playlistlength")==0) {
753 status->playlistLength = atoi(re->value);
754 }
755 else if(strcmp(re->name,"bitrate")==0) {
756 status->bitRate = atoi(re->value);
757 }
758 else if(strcmp(re->name,"state")==0) {
759 if(strcmp(re->value,"play")==0) {
760 status->state = MPD_STATUS_STATE_PLAY;
761 }
762 else if(strcmp(re->value,"stop")==0) {
763 status->state = MPD_STATUS_STATE_STOP;
764 }
765 else if(strcmp(re->value,"pause")==0) {
766 status->state = MPD_STATUS_STATE_PAUSE;
767 }
768 else {
769 status->state = MPD_STATUS_STATE_UNKNOWN;
770 }
771 }
772 else if(strcmp(re->name,"song")==0) {
773 status->song = atoi(re->value);
774 }
775 else if(strcmp(re->name,"songid")==0) {
776 status->songid = atoi(re->value);
777 }
778 else if(strcmp(re->name,"time")==0) {
779 char * tok = strchr(re->value,':');
780 /* the second strchr below is a safety check */
781 if (tok && (strchr(tok,0) > (tok+1))) {
782 /* atoi stops at the first non-[0-9] char: */
783 status->elapsedTime = atoi(re->value);
784 status->totalTime = atoi(tok+1);
785 }
786 }
787 else if(strcmp(re->name,"error")==0) {
788 status->error = strdup(re->value);
789 }
790 else if(strcmp(re->name,"xfade")==0) {
791 status->crossfade = atoi(re->value);
792 }
793 else if(strcmp(re->name,"updating_db")==0) {
794 status->updatingDb = atoi(re->value);
795 }
796 else if(strcmp(re->name,"audio")==0) {
797 char * tok = strchr(re->value,':');
798 if (tok && (strchr(tok,0) > (tok+1))) {
799 status->sampleRate = atoi(re->value);
800 status->bits = atoi(++tok);
801 tok = strchr(tok,':');
802 if (tok && (strchr(tok,0) > (tok+1)))
803 status->channels = atoi(tok+1);
804 }
805 }
807 mpd_getNextReturnElement(connection);
808 if(connection->error) {
809 free(status);
810 return NULL;
811 }
812 }
814 if(connection->error) {
815 free(status);
816 return NULL;
817 }
818 else if(status->state<0) {
819 strcpy(connection->errorStr,"state not found");
820 connection->error = 1;
821 free(status);
822 return NULL;
823 }
825 return status;
826 }
828 void mpd_freeStatus(mpd_Status * status) {
829 if(status->error) free(status->error);
830 free(status);
831 }
833 void mpd_sendStatsCommand(mpd_Connection * connection) {
834 mpd_executeCommand(connection,"stats\n");
835 }
837 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
838 mpd_Stats * stats;
840 /*mpd_executeCommand(connection,"stats\n");
842 if(connection->error) return NULL;*/
844 if(connection->doneProcessing || (connection->listOks &&
845 connection->doneListOk))
846 {
847 return NULL;
848 }
850 if(!connection->returnElement) mpd_getNextReturnElement(connection);
852 stats = malloc(sizeof(mpd_Stats));
853 stats->numberOfArtists = 0;
854 stats->numberOfAlbums = 0;
855 stats->numberOfSongs = 0;
856 stats->uptime = 0;
857 stats->dbUpdateTime = 0;
858 stats->playTime = 0;
859 stats->dbPlayTime = 0;
861 if(connection->error) {
862 free(stats);
863 return NULL;
864 }
865 while(connection->returnElement) {
866 mpd_ReturnElement * re = connection->returnElement;
867 if(strcmp(re->name,"artists")==0) {
868 stats->numberOfArtists = atoi(re->value);
869 }
870 else if(strcmp(re->name,"albums")==0) {
871 stats->numberOfAlbums = atoi(re->value);
872 }
873 else if(strcmp(re->name,"songs")==0) {
874 stats->numberOfSongs = atoi(re->value);
875 }
876 else if(strcmp(re->name,"uptime")==0) {
877 stats->uptime = strtol(re->value,NULL,10);
878 }
879 else if(strcmp(re->name,"db_update")==0) {
880 stats->dbUpdateTime = strtol(re->value,NULL,10);
881 }
882 else if(strcmp(re->name,"playtime")==0) {
883 stats->playTime = strtol(re->value,NULL,10);
884 }
885 else if(strcmp(re->name,"db_playtime")==0) {
886 stats->dbPlayTime = strtol(re->value,NULL,10);
887 }
889 mpd_getNextReturnElement(connection);
890 if(connection->error) {
891 free(stats);
892 return NULL;
893 }
894 }
896 if(connection->error) {
897 free(stats);
898 return NULL;
899 }
901 return stats;
902 }
904 void mpd_freeStats(mpd_Stats * stats) {
905 free(stats);
906 }
908 static void mpd_initDirectory(mpd_Directory * directory) {
909 directory->path = NULL;
910 }
912 static void mpd_finishDirectory(mpd_Directory * directory) {
913 if (directory->path)
914 str_pool_put(directory->path);
915 }
917 mpd_Directory * mpd_newDirectory(void) {
918 mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
920 mpd_initDirectory(directory);
922 return directory;
923 }
925 void mpd_freeDirectory(mpd_Directory * directory) {
926 mpd_finishDirectory(directory);
928 free(directory);
929 }
931 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
932 mpd_Directory * ret = mpd_newDirectory();
934 if (directory->path)
935 ret->path = str_pool_dup(directory->path);
937 return ret;
938 }
940 static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
941 playlist->path = NULL;
942 }
944 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
945 if (playlist->path)
946 str_pool_put(playlist->path);
947 }
949 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
950 mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
952 mpd_initPlaylistFile(playlist);
954 return playlist;
955 }
957 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
958 mpd_finishPlaylistFile(playlist);
959 free(playlist);
960 }
962 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
963 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
965 if (playlist->path)
966 ret->path = str_pool_dup(playlist->path);
968 return ret;
969 }
971 static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
972 entity->info.directory = NULL;
973 }
975 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
976 if(entity->info.directory) {
977 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
978 mpd_freeDirectory(entity->info.directory);
979 }
980 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
981 mpd_freeSong(entity->info.song);
982 }
983 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
984 mpd_freePlaylistFile(entity->info.playlistFile);
985 }
986 }
987 }
989 mpd_InfoEntity * mpd_newInfoEntity(void) {
990 mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
992 mpd_initInfoEntity(entity);
994 return entity;
995 }
997 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
998 mpd_finishInfoEntity(entity);
999 free(entity);
1000 }
1002 static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
1003 mpd_executeCommand(connection,command);
1004 }
1006 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1007 mpd_InfoEntity * entity = NULL;
1009 if(connection->doneProcessing || (connection->listOks &&
1010 connection->doneListOk)) {
1011 return NULL;
1012 }
1014 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1016 if(connection->returnElement) {
1017 if(strcmp(connection->returnElement->name,"file")==0) {
1018 entity = mpd_newInfoEntity();
1019 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1020 entity->info.song = mpd_newSong();
1021 entity->info.song->file =
1022 str_pool_dup(connection->returnElement->value);
1023 }
1024 else if(strcmp(connection->returnElement->name,
1025 "directory")==0) {
1026 entity = mpd_newInfoEntity();
1027 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1028 entity->info.directory = mpd_newDirectory();
1029 entity->info.directory->path =
1030 str_pool_dup(connection->returnElement->value);
1031 }
1032 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1033 entity = mpd_newInfoEntity();
1034 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1035 entity->info.playlistFile = mpd_newPlaylistFile();
1036 entity->info.playlistFile->path =
1037 str_pool_dup(connection->returnElement->value);
1038 }
1039 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1040 entity = mpd_newInfoEntity();
1041 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1042 entity->info.song = mpd_newSong();
1043 entity->info.song->pos = atoi(connection->returnElement->value);
1044 }
1045 else {
1046 connection->error = 1;
1047 strcpy(connection->errorStr,"problem parsing song info");
1048 return NULL;
1049 }
1050 }
1051 else return NULL;
1053 mpd_getNextReturnElement(connection);
1054 while(connection->returnElement) {
1055 mpd_ReturnElement * re = connection->returnElement;
1057 if(strcmp(re->name,"file")==0) return entity;
1058 else if(strcmp(re->name,"directory")==0) return entity;
1059 else if(strcmp(re->name,"playlist")==0) return entity;
1060 else if(strcmp(re->name,"cpos")==0) return entity;
1062 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1063 strlen(re->value)) {
1064 if(!entity->info.song->artist &&
1065 strcmp(re->name,"Artist")==0) {
1066 entity->info.song->artist = str_pool_dup(re->value);
1067 }
1068 else if(!entity->info.song->album &&
1069 strcmp(re->name,"Album")==0) {
1070 entity->info.song->album = str_pool_dup(re->value);
1071 }
1072 else if(!entity->info.song->title &&
1073 strcmp(re->name,"Title")==0) {
1074 entity->info.song->title = str_pool_dup(re->value);
1075 }
1076 else if(!entity->info.song->track &&
1077 strcmp(re->name,"Track")==0) {
1078 entity->info.song->track = str_pool_dup(re->value);
1079 }
1080 else if(!entity->info.song->name &&
1081 strcmp(re->name,"Name")==0) {
1082 entity->info.song->name = str_pool_dup(re->value);
1083 }
1084 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1085 strcmp(re->name,"Time")==0) {
1086 entity->info.song->time = atoi(re->value);
1087 }
1088 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1089 strcmp(re->name,"Pos")==0) {
1090 entity->info.song->pos = atoi(re->value);
1091 }
1092 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1093 strcmp(re->name,"Id")==0) {
1094 entity->info.song->id = atoi(re->value);
1095 }
1096 else if(!entity->info.song->date &&
1097 strcmp(re->name, "Date") == 0) {
1098 entity->info.song->date = str_pool_dup(re->value);
1099 }
1100 else if(!entity->info.song->genre &&
1101 strcmp(re->name, "Genre") == 0) {
1102 entity->info.song->genre = str_pool_dup(re->value);
1103 }
1104 else if(!entity->info.song->composer &&
1105 strcmp(re->name, "Composer") == 0) {
1106 entity->info.song->composer = str_pool_dup(re->value);
1107 }
1108 else if(!entity->info.song->disc &&
1109 strcmp(re->name, "Disc") == 0) {
1110 entity->info.song->disc = str_pool_dup(re->value);
1111 }
1112 else if(!entity->info.song->comment &&
1113 strcmp(re->name, "Comment") == 0) {
1114 entity->info.song->comment = str_pool_dup(re->value);
1115 }
1116 }
1117 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1118 }
1119 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1120 }
1122 mpd_getNextReturnElement(connection);
1123 }
1125 return entity;
1126 }
1128 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1129 const char * name)
1130 {
1131 if(connection->doneProcessing || (connection->listOks &&
1132 connection->doneListOk))
1133 {
1134 return NULL;
1135 }
1137 mpd_getNextReturnElement(connection);
1138 while(connection->returnElement) {
1139 mpd_ReturnElement * re = connection->returnElement;
1141 if(strcmp(re->name,name)==0) return strdup(re->value);
1142 mpd_getNextReturnElement(connection);
1143 }
1145 return NULL;
1146 }
1148 char * mpd_getNextTag(mpd_Connection * connection,int table) {
1149 if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES)
1150 {
1151 return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]);
1152 }
1153 return NULL;
1154 }
1156 char * mpd_getNextArtist(mpd_Connection * connection) {
1157 return mpd_getNextReturnElementNamed(connection,"Artist");
1158 }
1160 char * mpd_getNextAlbum(mpd_Connection * connection) {
1161 return mpd_getNextReturnElementNamed(connection,"Album");
1162 }
1164 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1165 char * string = malloc(strlen("playlistinfo")+25);
1166 sprintf(string,"playlistinfo \"%i\"\n",songPos);
1167 mpd_sendInfoCommand(connection,string);
1168 free(string);
1169 }
1171 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1172 char * string = malloc(strlen("playlistid")+25);
1173 sprintf(string, "playlistid \"%i\"\n", id);
1174 mpd_sendInfoCommand(connection, string);
1175 free(string);
1176 }
1178 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1179 char * string = malloc(strlen("plchanges")+25);
1180 sprintf(string,"plchanges \"%lld\"\n",playlist);
1181 mpd_sendInfoCommand(connection,string);
1182 free(string);
1183 }
1185 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1186 char * string = malloc(strlen("plchangesposid")+25);
1187 sprintf(string,"plchangesposid \"%lld\"\n",playlist);
1188 mpd_sendInfoCommand(connection,string);
1189 free(string);
1190 }
1192 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1193 char * sDir = mpd_sanitizeArg(dir);
1194 char * string = malloc(strlen("listall")+strlen(sDir)+5);
1195 sprintf(string,"listall \"%s\"\n",sDir);
1196 mpd_sendInfoCommand(connection,string);
1197 free(string);
1198 free(sDir);
1199 }
1201 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1202 char * sDir = mpd_sanitizeArg(dir);
1203 char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
1204 sprintf(string,"listallinfo \"%s\"\n",sDir);
1205 mpd_sendInfoCommand(connection,string);
1206 free(string);
1207 free(sDir);
1208 }
1210 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1211 char * sDir = mpd_sanitizeArg(dir);
1212 char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
1213 sprintf(string,"lsinfo \"%s\"\n",sDir);
1214 mpd_sendInfoCommand(connection,string);
1215 free(string);
1216 free(sDir);
1217 }
1219 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1220 mpd_executeCommand(connection,"currentsong\n");
1221 }
1223 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1224 const char * str)
1225 {
1226 char st[10];
1227 char * string;
1228 char * sanitStr = mpd_sanitizeArg(str);
1229 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1230 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1231 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1232 else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
1233 else {
1234 connection->error = 1;
1235 strcpy(connection->errorStr,"unknown table for search");
1236 return;
1237 }
1238 string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
1239 sprintf(string,"search %s \"%s\"\n",st,sanitStr);
1240 mpd_sendInfoCommand(connection,string);
1241 free(string);
1242 free(sanitStr);
1243 }
1245 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1246 const char * str)
1247 {
1248 char st[10];
1249 char * string;
1250 char * sanitStr = mpd_sanitizeArg(str);
1251 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1252 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1253 else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
1254 else {
1255 connection->error = 1;
1256 strcpy(connection->errorStr,"unknown table for find");
1257 return;
1258 }
1259 string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
1260 sprintf(string,"find %s \"%s\"\n",st,sanitStr);
1261 mpd_sendInfoCommand(connection,string);
1262 free(string);
1263 free(sanitStr);
1264 }
1266 void mpd_sendListCommand(mpd_Connection * connection, int table,
1267 const char * arg1)
1268 {
1269 char st[10];
1270 char * string;
1271 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1272 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1273 else {
1274 connection->error = 1;
1275 strcpy(connection->errorStr,"unknown table for list");
1276 return;
1277 }
1278 if(arg1) {
1279 char * sanitArg1 = mpd_sanitizeArg(arg1);
1280 string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
1281 sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
1282 free(sanitArg1);
1283 }
1284 else {
1285 string = malloc(strlen("list")+strlen(st)+3);
1286 sprintf(string,"list %s\n",st);
1287 }
1288 mpd_sendInfoCommand(connection,string);
1289 free(string);
1290 }
1292 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1293 char * sFile = mpd_sanitizeArg(file);
1294 char * string = malloc(strlen("add")+strlen(sFile)+5);
1295 sprintf(string,"add \"%s\"\n",sFile);
1296 mpd_executeCommand(connection,string);
1297 free(string);
1298 free(sFile);
1299 }
1301 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1302 char * string = malloc(strlen("delete")+25);
1303 sprintf(string,"delete \"%i\"\n",songPos);
1304 mpd_sendInfoCommand(connection,string);
1305 free(string);
1306 }
1308 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1309 char * string = malloc(strlen("deleteid")+25);
1310 sprintf(string, "deleteid \"%i\"\n", id);
1311 mpd_sendInfoCommand(connection,string);
1312 free(string);
1313 }
1315 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1316 char * sName = mpd_sanitizeArg(name);
1317 char * string = malloc(strlen("save")+strlen(sName)+5);
1318 sprintf(string,"save \"%s\"\n",sName);
1319 mpd_executeCommand(connection,string);
1320 free(string);
1321 free(sName);
1322 }
1324 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1325 char * sName = mpd_sanitizeArg(name);
1326 char * string = malloc(strlen("load")+strlen(sName)+5);
1327 sprintf(string,"load \"%s\"\n",sName);
1328 mpd_executeCommand(connection,string);
1329 free(string);
1330 free(sName);
1331 }
1333 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1334 char * sName = mpd_sanitizeArg(name);
1335 char * string = malloc(strlen("rm")+strlen(sName)+5);
1336 sprintf(string,"rm \"%s\"\n",sName);
1337 mpd_executeCommand(connection,string);
1338 free(string);
1339 free(sName);
1340 }
1342 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1343 mpd_executeCommand(connection,"shuffle\n");
1344 }
1346 void mpd_sendClearCommand(mpd_Connection * connection) {
1347 mpd_executeCommand(connection,"clear\n");
1348 }
1350 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1351 char * string = malloc(strlen("play")+25);
1352 sprintf(string,"play \"%i\"\n",songPos);
1353 mpd_sendInfoCommand(connection,string);
1354 free(string);
1355 }
1357 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1358 char * string = malloc(strlen("playid")+25);
1359 sprintf(string,"playid \"%i\"\n",id);
1360 mpd_sendInfoCommand(connection,string);
1361 free(string);
1362 }
1364 void mpd_sendStopCommand(mpd_Connection * connection) {
1365 mpd_executeCommand(connection,"stop\n");
1366 }
1368 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1369 char * string = malloc(strlen("pause")+25);
1370 sprintf(string,"pause \"%i\"\n",pauseMode);
1371 mpd_executeCommand(connection,string);
1372 free(string);
1373 }
1375 void mpd_sendNextCommand(mpd_Connection * connection) {
1376 mpd_executeCommand(connection,"next\n");
1377 }
1379 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1380 char * string = malloc(strlen("move")+25);
1381 sprintf(string,"move \"%i\" \"%i\"\n",from,to);
1382 mpd_sendInfoCommand(connection,string);
1383 free(string);
1384 }
1386 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1387 char * string = malloc(strlen("moveid")+25);
1388 sprintf(string, "moveid \"%i\" \"%i\"\n", id, to);
1389 mpd_sendInfoCommand(connection,string);
1390 free(string);
1391 }
1393 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1394 char * string = malloc(strlen("swap")+25);
1395 sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
1396 mpd_sendInfoCommand(connection,string);
1397 free(string);
1398 }
1400 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1401 char * string = malloc(strlen("swapid")+25);
1402 sprintf(string, "swapid \"%i\" \"%i\"\n", id1, id2);
1403 mpd_sendInfoCommand(connection,string);
1404 free(string);
1405 }
1407 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
1408 char * string = malloc(strlen("seek")+25);
1409 sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
1410 mpd_sendInfoCommand(connection,string);
1411 free(string);
1412 }
1414 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
1415 char * string = malloc(strlen("seekid")+25);
1416 sprintf(string,"seekid \"%i\" \"%i\"\n",id,time);
1417 mpd_sendInfoCommand(connection,string);
1418 free(string);
1419 }
1421 void mpd_sendUpdateCommand(mpd_Connection * connection, const char *path) {
1422 char *sPath = mpd_sanitizeArg(path);
1423 char * string = malloc(strlen("update")+strlen(sPath)+5);
1424 sprintf(string,"update \"%s\"\n",sPath);
1425 mpd_sendInfoCommand(connection,string);
1426 free(string);
1427 free(sPath);
1428 }
1430 int mpd_getUpdateId(mpd_Connection * connection) {
1431 char * jobid;
1432 int ret = 0;
1434 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1435 if(jobid) {
1436 ret = atoi(jobid);
1437 free(jobid);
1438 }
1440 return ret;
1441 }
1443 void mpd_sendPrevCommand(mpd_Connection * connection) {
1444 mpd_executeCommand(connection,"previous\n");
1445 }
1447 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1448 char * string = malloc(strlen("repeat")+25);
1449 sprintf(string,"repeat \"%i\"\n",repeatMode);
1450 mpd_executeCommand(connection,string);
1451 free(string);
1452 }
1454 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1455 char * string = malloc(strlen("random")+25);
1456 sprintf(string,"random \"%i\"\n",randomMode);
1457 mpd_executeCommand(connection,string);
1458 free(string);
1459 }
1461 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1462 char * string = malloc(strlen("setvol")+25);
1463 sprintf(string,"setvol \"%i\"\n",volumeChange);
1464 mpd_executeCommand(connection,string);
1465 free(string);
1466 }
1468 void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
1469 char * string = malloc(strlen("volume")+25);
1470 sprintf(string,"volume \"%i\"\n",volumeChange);
1471 mpd_executeCommand(connection,string);
1472 free(string);
1473 }
1475 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1476 char * string = malloc(strlen("crossfade")+25);
1477 sprintf(string,"crossfade \"%i\"\n",seconds);
1478 mpd_executeCommand(connection,string);
1479 free(string);
1480 }
1482 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1483 char * sPass = mpd_sanitizeArg(pass);
1484 char * string = malloc(strlen("password")+strlen(sPass)+5);
1485 sprintf(string,"password \"%s\"\n",sPass);
1486 mpd_executeCommand(connection,string);
1487 free(string);
1488 free(sPass);
1489 }
1491 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1492 if(connection->commandList) {
1493 strcpy(connection->errorStr,"already in command list mode");
1494 connection->error = 1;
1495 return;
1496 }
1497 connection->commandList = COMMAND_LIST;
1498 mpd_executeCommand(connection,"command_list_begin\n");
1499 }
1501 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1502 if(connection->commandList) {
1503 strcpy(connection->errorStr,"already in command list mode");
1504 connection->error = 1;
1505 return;
1506 }
1507 connection->commandList = COMMAND_LIST_OK;
1508 mpd_executeCommand(connection,"command_list_ok_begin\n");
1509 connection->listOks = 0;
1510 }
1512 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1513 if(!connection->commandList) {
1514 strcpy(connection->errorStr,"not in command list mode");
1515 connection->error = 1;
1516 return;
1517 }
1518 connection->commandList = 0;
1519 mpd_executeCommand(connection,"command_list_end\n");
1520 }
1522 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1523 mpd_executeCommand(connection,"outputs\n");
1524 }
1526 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1527 mpd_OutputEntity * output = NULL;
1529 if(connection->doneProcessing || (connection->listOks &&
1530 connection->doneListOk))
1531 {
1532 return NULL;
1533 }
1535 if(connection->error) return NULL;
1537 output = malloc(sizeof(mpd_OutputEntity));
1538 output->id = -10;
1539 output->name = NULL;
1540 output->enabled = 0;
1542 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1544 while(connection->returnElement) {
1545 mpd_ReturnElement * re = connection->returnElement;
1546 if(strcmp(re->name,"outputid")==0) {
1547 if(output!=NULL && output->id>=0) return output;
1548 output->id = atoi(re->value);
1549 }
1550 else if(strcmp(re->name,"outputname")==0) {
1551 output->name = strdup(re->value);
1552 }
1553 else if(strcmp(re->name,"outputenabled")==0) {
1554 output->enabled = atoi(re->value);
1555 }
1557 mpd_getNextReturnElement(connection);
1558 if(connection->error) {
1559 free(output);
1560 return NULL;
1561 }
1563 }
1565 return output;
1566 }
1568 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1569 char * string = malloc(strlen("enableoutput")+25);
1570 sprintf(string,"enableoutput \"%i\"\n",outputId);
1571 mpd_executeCommand(connection,string);
1572 free(string);
1573 }
1575 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1576 char * string = malloc(strlen("disableoutput")+25);
1577 sprintf(string,"disableoutput \"%i\"\n",outputId);
1578 mpd_executeCommand(connection,string);
1579 free(string);
1580 }
1582 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1583 free(output->name);
1584 free(output);
1585 }
1587 /**
1588 * mpd_sendNotCommandsCommand
1589 * odd naming, but it gets the not allowed commands
1590 */
1592 void mpd_sendNotCommandsCommand(mpd_Connection * connection) {
1593 mpd_executeCommand(connection,"notcommands\n");
1594 }
1596 /**
1597 * mpd_sendCommandsCommand
1598 * odd naming, but it gets the allowed commands
1599 */
1601 void mpd_sendCommandsCommand(mpd_Connection * connection) {
1602 mpd_executeCommand(connection,"commands\n");
1603 }
1604 /**
1605 * Get the next returned command
1606 */
1607 char * mpd_getNextCommand(mpd_Connection * connection) {
1608 return mpd_getNextReturnElementNamed(connection,"command");
1609 }
1611 void mpd_startSearch(mpd_Connection * connection,int exact) {
1612 if(connection->request) {
1613 /* search/find allready in progress */
1614 /* TODO: set error here? */
1615 return;
1616 }
1617 if(exact){
1618 connection->request = strdup("find");
1619 }
1620 else{
1621 connection->request = strdup("search");
1622 }
1623 }
1626 void mpd_startFieldSearch(mpd_Connection * connection,int field) {
1627 if(connection->request) {
1628 /* search/find allready in progress */
1629 /* TODO: set error here? */
1630 return;
1631 }
1632 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1633 /* set error here */
1634 return;
1635 }
1637 connection->request = malloc(sizeof(char)*(
1638 /* length of the field name */
1639 strlen(mpdTagItemKeys[field])+
1640 /* "list"+space+\0 */
1641 6
1642 ));
1643 sprintf(connection->request, "list %s", mpdTagItemKeys[field]);
1644 }
1648 void mpd_addConstraintSearch(mpd_Connection *connection,
1649 int field,
1650 char *name)
1651 {
1652 char *arg = NULL;
1653 if(!connection->request){
1654 return;
1655 }
1656 if(name == NULL) {
1657 return;
1658 }
1659 if(field < 0 || field >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1660 return;
1661 }
1662 /* clean up the query */
1663 arg = mpd_sanitizeArg(name);
1664 /* create space for the query */
1665 connection->request = realloc(connection->request, (
1666 /* length of the old string */
1667 strlen(connection->request)+
1668 /* space between */
1669 1+
1670 /* length of the field name */
1671 strlen(mpdTagItemKeys[field])+
1672 /* space plus starting " */
1673 2+
1674 /* length of search term */
1675 strlen(arg)+
1676 /* closign " +\0 that is added sprintf */
1677 2
1678 )*sizeof(char));
1679 /* and form the query */
1680 sprintf(connection->request, "%s %s \"%s\"",
1681 connection->request,
1682 mpdTagItemKeys[field],
1683 arg);
1684 free(arg);
1685 }
1688 void mpd_commitSearch(mpd_Connection *connection)
1689 {
1690 if(connection->request)
1691 {
1692 int length = strlen(connection->request);
1693 /* fixing up the string for mpd to like */
1694 connection->request = realloc(connection->request,
1695 (length+ /* old length */
1696 2 /* closing \n and \0 */
1697 )*sizeof(char));
1698 connection->request[length] = '\n';
1699 connection->request[length+1] = '\0';
1700 /* and off we go */
1701 mpd_sendInfoCommand(connection, connection->request);
1702 /* clean up a bit */
1703 free(connection->request);
1704 connection->request = NULL;
1705 }
1706 }
1708 /**
1709 * @param connection a MpdConnection
1710 * @param path the path to the playlist.
1711 *
1712 * List the content, with full metadata, of a stored playlist.
1713 *
1714 */
1715 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
1716 {
1717 char *arg = mpd_sanitizeArg(path);
1718 char *query = malloc(strlen("listplaylistinfo")+strlen(arg)+5);
1719 sprintf(query, "listplaylistinfo \"%s\"\n",arg);
1720 mpd_sendInfoCommand(connection, query);
1721 free(arg);
1722 free(query);
1723 }
1725 /**
1726 * @param connection a MpdConnection
1727 * @param path the path to the playlist.
1728 *
1729 * List the content of a stored playlist.
1730 *
1731 */
1732 void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
1733 {
1734 char *arg = mpd_sanitizeArg(path);
1735 char *query = malloc(strlen("listplaylist")+strlen(arg)+5);
1736 sprintf(query, "listplaylist \"%s\"\n",arg);
1737 mpd_sendInfoCommand(connection, query);
1738 free(arg);
1739 free(query);
1740 }