Remove mtime from mpd_Song
[libmpd.git] / src / libmpdclient.c
blob4d3096ec553b93f1e49b4fccd0f54e9f97d996d1
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.
34 #include "libmpdclient.h"
35 #include <errno.h>
36 #include <ctype.h>
37 #include <sys/types.h>
38 #include <stdio.h>
39 #include <sys/param.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
45 #include <limits.h>
47 #include <glib.h>
49 #ifdef WIN32
50 # include <ws2tcpip.h>
51 # include <winsock.h>
52 #else
53 # include <netinet/in.h>
54 # include <arpa/inet.h>
55 # include <sys/socket.h>
56 # include <netdb.h>
57 #endif
59 /* (bits+1)/3 (plus the sign character) */
60 #define INTLEN ((sizeof(int) * CHAR_BIT + 1) / 3 + 1)
61 #define LONGLONGLEN ((sizeof(long long) * CHAR_BIT + 1) / 3 + 1)
63 #define COMMAND_LIST 1
64 #define COMMAND_LIST_OK 2
66 #ifndef MPD_NO_GAI
67 # ifdef AI_ADDRCONFIG
68 # define MPD_HAVE_GAI
69 # endif
70 #endif
72 #ifndef WIN32
73 #include <sys/un.h>
74 #endif
76 #ifndef MSG_DONTWAIT
77 # define MSG_DONTWAIT 0
78 #endif
80 #ifdef WIN32
81 # define SELECT_ERRNO_IGNORE (errno == WSAEINTR || errno == WSAEINPROGRESS)
82 # define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
83 #else
84 # define SELECT_ERRNO_IGNORE (errno == EINTR)
85 # define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
86 # define winsock_dll_error(c) 0
87 # define closesocket(s) close(s)
88 # define WSACleanup() do { /* nothing */ } while (0)
89 #endif
91 #ifdef WIN32
92 static int winsock_dll_error(mpd_Connection *connection)
94 WSADATA wsaData;
95 if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
96 LOBYTE(wsaData.wVersion) != 2 ||
97 HIBYTE(wsaData.wVersion) != 2 ) {
98 strcpy(connection->errorStr,
99 "Could not find usable WinSock DLL.");
100 connection->error = MPD_ERROR_SYSTEM;
101 return 1;
103 return 0;
106 static int do_connect_fail(mpd_Connection *connection,
107 const struct sockaddr *serv_addr, int addrlen)
109 int iMode = 1; /* 0 = blocking, else non-blocking */
110 if (connect(connection->sock, serv_addr, addrlen) == SOCKET_ERROR)
111 return 1;
112 ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
113 return 0;
115 #elif defined(__solaris__) || defined(__sun__)
116 static int do_connect_fail(mpd_Connection *connection,
117 const struct sockaddr *serv_addr, int addrlen)
119 int flags;
120 if (connect(connection->sock, serv_addr, addrlen) < 0)
121 return 1;
122 flags = fcntl(connection->sock, F_GETFL, 0);
123 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
124 return 0;
126 #else /* !WIN32 (sane operating systems) */
127 static int do_connect_fail(mpd_Connection *connection,
128 const struct sockaddr *serv_addr, int addrlen)
130 int flags, res,valopt;
131 struct timeval tv;
132 fd_set myset;
133 flags = fcntl(connection->sock, F_GETFL, 0);
134 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
135 /*if (connect(connection->sock, serv_addr, addrlen) < 0)
136 return 1;*/
137 /* Do a non blocking connect */
138 res = connect(connection->sock, serv_addr, addrlen);
139 if (res < 0) {
140 if (errno == EINPROGRESS) {
141 tv.tv_sec = connection->timeout.tv_sec;
142 tv.tv_usec = connection->timeout.tv_usec;
143 FD_ZERO(&myset);
144 FD_SET(connection->sock, &myset);
145 if (select(connection->sock+1, NULL, &myset, NULL, &tv) > 0) {
146 socklen_t lon;
147 lon = sizeof(int);
148 /* Check for errors */
149 getsockopt(connection->sock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
150 if (valopt) {
151 fprintf(stderr, "Error in connection() %d - %s\n", valopt, strerror(valopt));
152 return 1;
155 else {
156 /* Connecting timed out. */
157 fprintf(stderr, "Timeout or error() %d - %s\n", valopt, strerror(valopt));
158 return 1;
161 else {
162 /* Failed to connect */
163 fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno));
164 return 1;
168 return 0;
170 #endif /* !WIN32 */
172 #ifdef MPD_HAVE_GAI
173 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
174 float timeout)
176 int error;
177 char service[INTLEN+1];
178 struct addrinfo hints;
179 struct addrinfo *res = NULL;
180 struct addrinfo *addrinfo = NULL;
183 * Setup hints
185 hints.ai_flags = AI_ADDRCONFIG;
186 hints.ai_family = AF_UNSPEC;
187 hints.ai_socktype = SOCK_STREAM;
188 hints.ai_protocol = IPPROTO_TCP;
189 hints.ai_addrlen = 0;
190 hints.ai_addr = NULL;
191 hints.ai_canonname = NULL;
192 hints.ai_next = NULL;
194 snprintf(service, sizeof(service), "%i", port);
196 error = getaddrinfo(host, service, &hints, &addrinfo);
198 if (error) {
199 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
200 "host \"%s\" not found: %s",
201 host, gai_strerror(error));
202 connection->error = MPD_ERROR_UNKHOST;
203 return -1;
206 for (res = addrinfo; res; res = res->ai_next) {
207 /* create socket */
208 if (connection->sock >= 0)
209 closesocket(connection->sock);
210 connection->sock = socket(res->ai_family, SOCK_STREAM,
211 res->ai_protocol);
212 if (connection->sock < 0) {
213 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
214 "problems creating socket: %s",
215 strerror(errno));
216 connection->error = MPD_ERROR_SYSTEM;
217 freeaddrinfo(addrinfo);
218 return -1;
221 mpd_setConnectionTimeout(connection, timeout);
223 /* connect stuff */
224 if (do_connect_fail(connection,
225 res->ai_addr, res->ai_addrlen)) {
226 /* try the next address */
227 closesocket(connection->sock);
228 connection->sock = -1;
229 continue;
232 break;
235 freeaddrinfo(addrinfo);
237 if (connection->sock < 0) {
238 snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
239 "problems connecting to \"%s\" on port %i: %s",
240 host, port, strerror(errno));
241 connection->error = MPD_ERROR_CONNPORT;
243 return -1;
246 return 0;
248 #else /* !MPD_HAVE_GAI */
249 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
250 float timeout)
252 struct hostent * he;
253 struct sockaddr * dest;
254 int destlen;
255 struct sockaddr_in sin;
257 if(!(he=gethostbyname(host))) {
258 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
259 "host \"%s\" not found",host);
260 connection->error = MPD_ERROR_UNKHOST;
261 return -1;
264 memset(&sin,0,sizeof(struct sockaddr_in));
265 /*dest.sin_family = he->h_addrtype;*/
266 sin.sin_family = AF_INET;
267 sin.sin_port = htons(port);
269 switch(he->h_addrtype) {
270 case AF_INET:
271 memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
272 he->h_length);
273 dest = (struct sockaddr *)&sin;
274 destlen = sizeof(struct sockaddr_in);
275 break;
276 default:
277 strcpy(connection->errorStr,"address type is not IPv4");
278 connection->error = MPD_ERROR_SYSTEM;
279 return -1;
280 break;
283 if (connection->sock >= 0)
284 closesocket(connection->sock);
285 if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
286 strcpy(connection->errorStr,"problems creating socket");
287 connection->error = MPD_ERROR_SYSTEM;
288 return -1;
291 mpd_setConnectionTimeout(connection,timeout);
293 /* connect stuff */
294 if (do_connect_fail(connection, dest, destlen)) {
295 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
296 "problems connecting to \"%s\" on port"
297 " %i",host,port);
298 connection->error = MPD_ERROR_CONNPORT;
299 return -1;
302 return 0;
304 #endif /* !MPD_HAVE_GAI */
306 char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
308 "Artist",
309 "Album",
310 "Title",
311 "Track",
312 "Name",
313 "Genre",
314 "Date",
315 "Composer",
316 "Performer",
317 "Comment",
318 "Disc",
319 "Filename",
320 "AlbumArtist",
321 "Any"
324 static char * mpd_sanitizeArg(const char * arg) {
325 size_t i;
326 char * ret;
327 register const char *c;
328 register char *rc;
330 /* instead of counting in that loop above, just
331 * use a bit more memory and half running time
333 ret = malloc(strlen(arg) * 2 + 1);
335 c = arg;
336 rc = ret;
337 for(i = strlen(arg)+1; i != 0; --i) {
338 if(*c=='"' || *c=='\\')
339 *rc++ = '\\';
340 *(rc++) = *(c++);
343 return ret;
346 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
348 mpd_ReturnElement* ret = g_slice_new(mpd_ReturnElement);
350 ret->name = strdup(name);
351 ret->value = strdup(value);
353 return ret;
356 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
357 free(re->name);
358 free(re->value);
359 g_slice_free(mpd_ReturnElement, re);
362 void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
363 connection->timeout.tv_sec = (int)timeout;
364 connection->timeout.tv_usec = (int)(timeout*1e6 -
365 connection->timeout.tv_sec*1000000 +
366 0.5);
369 static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
370 char * output) {
371 char * tmp;
372 char * test;
373 int i;
375 if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
376 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
377 "mpd not running on port %i on host \"%s\"",
378 port,host);
379 connection->error = MPD_ERROR_NOTMPD;
380 return 1;
383 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
385 for(i=0;i<3;i++) {
386 if(tmp) connection->version[i] = strtol(tmp,&test,10);
388 if (!tmp || (test[0] != '.' && test[0] != '\0')) {
389 snprintf(connection->errorStr,
390 MPD_ERRORSTR_MAX_LENGTH,
391 "error parsing version number at "
392 "\"%s\"",
393 &output[strlen(MPD_WELCOME_MESSAGE)]);
394 connection->error = MPD_ERROR_NOTMPD;
395 return 1;
397 tmp = ++test;
400 return 0;
403 #ifndef WIN32
404 static int mpd_connect_un(mpd_Connection * connection,
405 const char * host, float timeout)
407 int error, flags;
408 size_t path_length;
409 struct sockaddr_un saun;
411 path_length = strlen(host);
412 if (path_length >= sizeof(saun.sun_path)) {
413 strcpy(connection->errorStr, "unix socket path is too long");
414 connection->error = MPD_ERROR_UNKHOST;
415 return -1;
418 saun.sun_family = AF_UNIX;
419 memcpy(saun.sun_path, host, path_length + 1);
421 connection->sock = socket(AF_UNIX, SOCK_STREAM, 0);
422 if (connection->sock < 0) {
423 strcpy(connection->errorStr, "problems creating socket");
424 connection->error = MPD_ERROR_SYSTEM;
425 return -1;
428 mpd_setConnectionTimeout(connection, timeout);
430 flags = fcntl(connection->sock, F_GETFL, 0);
431 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
433 error = connect(connection->sock, (struct sockaddr*)&saun, sizeof(saun));
434 if (error < 0) {
435 /* try the next address family */
436 close(connection->sock);
437 connection->sock = 0;
439 snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
440 "problems connecting to \"%s\": %s",
441 host, strerror(errno));
442 connection->error = MPD_ERROR_CONNPORT;
443 return -1;
446 return 0;
448 #endif /* WIN32 */
450 mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
451 int err;
452 char * rt;
453 char * output = NULL;
454 mpd_Connection * connection = g_slice_new0(mpd_Connection);
455 struct timeval tv;
456 fd_set fds;
457 strcpy(connection->buffer,"");
458 connection->sock = -1;
459 strcpy(connection->errorStr,"");
461 if (winsock_dll_error(connection))
462 return connection;
464 #ifndef WIN32
465 if (host[0] == '/')
466 err = mpd_connect_un(connection, host, timeout);
467 else
468 #endif
469 err = mpd_connect(connection, host, port, timeout);
470 if (err < 0)
471 return connection;
473 while(!(rt = strstr(connection->buffer,"\n"))) {
474 tv.tv_sec = connection->timeout.tv_sec;
475 tv.tv_usec = connection->timeout.tv_usec;
476 FD_ZERO(&fds);
477 FD_SET(connection->sock,&fds);
478 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
479 int readed;
480 readed = recv(connection->sock,
481 &(connection->buffer[connection->buflen]),
482 MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
483 if(readed<=0) {
484 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
485 "problems getting a response from"
486 " \"%s\" on port %i : %s",host,
487 port, strerror(errno));
488 connection->error = MPD_ERROR_NORESPONSE;
489 return connection;
491 connection->buflen+=readed;
492 connection->buffer[connection->buflen] = '\0';
494 else if(err<0) {
495 if (SELECT_ERRNO_IGNORE)
496 continue;
497 snprintf(connection->errorStr,
498 MPD_ERRORSTR_MAX_LENGTH,
499 "problems connecting to \"%s\" on port"
500 " %i",host,port);
501 connection->error = MPD_ERROR_CONNPORT;
502 return connection;
504 else {
505 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
506 "timeout in attempting to get a response from"
507 " \"%s\" on port %i",host,port);
508 connection->error = MPD_ERROR_NORESPONSE;
509 return connection;
513 *rt = '\0';
514 output = strdup(connection->buffer);
515 strcpy(connection->buffer,rt+1);
516 connection->buflen = strlen(connection->buffer);
518 if(mpd_parseWelcome(connection,host,port,output) == 0) connection->doneProcessing = 1;
520 free(output);
522 return connection;
525 void mpd_clearError(mpd_Connection * connection) {
526 connection->error = 0;
527 connection->errorStr[0] = '\0';
530 void mpd_closeConnection(mpd_Connection * connection) {
531 closesocket(connection->sock);
532 if(connection->returnElement) free(connection->returnElement);
533 if(connection->request) free(connection->request);
534 g_slice_free(mpd_Connection, connection);
535 WSACleanup();
538 static void mpd_executeCommand(mpd_Connection * connection,const char * command) {
539 int ret;
540 struct timeval tv;
541 fd_set fds;
542 const char * commandPtr = command;
543 int commandLen = strlen(command);
545 if(!connection->doneProcessing && !connection->commandList) {
546 strcpy(connection->errorStr,"not done processing current command");
547 connection->error = 1;
548 return;
551 mpd_clearError(connection);
553 FD_ZERO(&fds);
554 FD_SET(connection->sock,&fds);
555 tv.tv_sec = connection->timeout.tv_sec;
556 tv.tv_usec = connection->timeout.tv_usec;
557 while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
558 (ret==-1 && SELECT_ERRNO_IGNORE)) {
559 fflush(NULL);
560 ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
561 if(ret<=0)
563 if (SENDRECV_ERRNO_IGNORE) continue;
564 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
565 "problems giving command \"%s\"",command);
566 connection->error = MPD_ERROR_SENDING;
567 return;
569 else {
570 commandPtr+=ret;
571 commandLen-=ret;
574 if(commandLen<=0) break;
576 if(commandLen>0) {
577 perror("");
578 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
579 "timeout sending command \"%s\"",command);
580 connection->error = MPD_ERROR_TIMEOUT;
581 return;
584 if(!connection->commandList) connection->doneProcessing = 0;
585 else if(connection->commandList == COMMAND_LIST_OK) {
586 connection->listOks++;
590 static void mpd_getNextReturnElement(mpd_Connection * connection) {
591 char * output = NULL;
592 char * rt = NULL;
593 char * name = NULL;
594 char * value = NULL;
595 fd_set fds;
596 struct timeval tv;
597 char * tok = NULL;
598 int readed;
599 char * bufferCheck = NULL;
600 int err;
601 int pos;
603 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
604 connection->returnElement = NULL;
606 if(connection->doneProcessing || (connection->listOks &&
607 connection->doneListOk))
609 strcpy(connection->errorStr,"already done processing current command");
610 connection->error = 1;
611 return;
614 bufferCheck = connection->buffer+connection->bufstart;
615 while(connection->bufstart>=connection->buflen ||
616 !(rt = strchr(bufferCheck,'\n'))) {
617 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
618 memmove(connection->buffer,
619 connection->buffer+
620 connection->bufstart,
621 connection->buflen-
622 connection->bufstart+1);
623 connection->buflen-=connection->bufstart;
624 connection->bufstart = 0;
626 if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
627 strcpy(connection->errorStr,"buffer overrun");
628 connection->error = MPD_ERROR_BUFFEROVERRUN;
629 connection->doneProcessing = 1;
630 connection->doneListOk = 0;
631 return;
633 bufferCheck = connection->buffer+connection->buflen;
634 tv.tv_sec = connection->timeout.tv_sec;
635 tv.tv_usec = connection->timeout.tv_usec;
636 FD_ZERO(&fds);
637 FD_SET(connection->sock,&fds);
638 if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
639 readed = recv(connection->sock,
640 connection->buffer+connection->buflen,
641 MPD_BUFFER_MAX_LENGTH-connection->buflen,
642 MSG_DONTWAIT);
643 if(readed<0 && SENDRECV_ERRNO_IGNORE) {
644 continue;
646 if(readed<=0) {
647 strcpy(connection->errorStr,"connection"
648 " closed");
649 connection->error = MPD_ERROR_CONNCLOSED;
650 connection->doneProcessing = 1;
651 connection->doneListOk = 0;
652 return;
654 connection->buflen+=readed;
655 connection->buffer[connection->buflen] = '\0';
657 else if(err<0 && SELECT_ERRNO_IGNORE) continue;
658 else {
659 strcpy(connection->errorStr,"connection timeout");
660 connection->error = MPD_ERROR_TIMEOUT;
661 connection->doneProcessing = 1;
662 connection->doneListOk = 0;
663 return;
667 *rt = '\0';
668 output = connection->buffer+connection->bufstart;
669 connection->bufstart = rt - connection->buffer + 1;
671 if(strcmp(output,"OK")==0) {
672 if(connection->listOks > 0) {
673 strcpy(connection->errorStr, "expected more list_OK's");
674 connection->error = 1;
676 connection->listOks = 0;
677 connection->doneProcessing = 1;
678 connection->doneListOk = 0;
679 return;
682 if(strcmp(output, "list_OK") == 0) {
683 if(!connection->listOks) {
684 strcpy(connection->errorStr,
685 "got an unexpected list_OK");
686 connection->error = 1;
688 else {
689 connection->doneListOk = 1;
690 connection->listOks--;
692 return;
695 if(strncmp(output,"ACK",strlen("ACK"))==0) {
696 char * test;
697 char * needle;
698 int val;
700 strcpy(connection->errorStr, output);
701 connection->error = MPD_ERROR_ACK;
702 connection->errorCode = MPD_ACK_ERROR_UNK;
703 connection->errorAt = MPD_ERROR_AT_UNK;
704 connection->doneProcessing = 1;
705 connection->doneListOk = 0;
707 needle = strchr(output, '[');
708 if(!needle) return;
709 val = strtol(needle+1, &test, 10);
710 if(*test != '@') return;
711 connection->errorCode = val;
712 val = strtol(test+1, &test, 10);
713 if(*test != ']') return;
714 connection->errorAt = val;
715 return;
718 tok = strchr(output, ':');
719 if (!tok) return;
720 pos = tok - output;
721 value = ++tok;
722 name = output;
723 name[pos] = '\0';
725 if(value[0]==' ') {
726 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
728 else {
729 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
730 "error parsing: %s:%s",name,value);
731 connection->error = 1;
735 void mpd_finishCommand(mpd_Connection * connection) {
736 while(!connection->doneProcessing) {
737 if(connection->doneListOk) connection->doneListOk = 0;
738 mpd_getNextReturnElement(connection);
742 static void mpd_finishListOkCommand(mpd_Connection * connection) {
743 while(!connection->doneProcessing && connection->listOks &&
744 !connection->doneListOk)
746 mpd_getNextReturnElement(connection);
750 int mpd_nextListOkCommand(mpd_Connection * connection) {
751 mpd_finishListOkCommand(connection);
752 if(!connection->doneProcessing) connection->doneListOk = 0;
753 if(connection->listOks == 0 || connection->doneProcessing) return -1;
754 return 0;
757 void mpd_sendStatusCommand(mpd_Connection * connection) {
758 mpd_executeCommand(connection,"status\n");
761 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
762 /*mpd_executeCommand(connection,"status\n");
764 if(connection->error) return NULL;*/
766 if(connection->doneProcessing || (connection->listOks &&
767 connection->doneListOk))
769 return NULL;
772 if(!connection->returnElement) mpd_getNextReturnElement(connection);
774 mpd_Status* status = g_slice_new0(mpd_Status);
775 status->volume = -1;
776 status->playlist = -1;
777 status->storedplaylist = -1;
778 status->playlistLength = -1;
779 status->state = -1;
780 status->nextsong = -1;
781 status->nextsongid = -1;
782 status->crossfade = -1;
784 if(connection->error) {
785 g_slice_free(mpd_Status, status);
786 return NULL;
788 while(connection->returnElement) {
789 mpd_ReturnElement * re = connection->returnElement;
790 if(strcmp(re->name,"volume")==0) {
791 status->volume = atoi(re->value);
793 else if(strcmp(re->name,"repeat")==0) {
794 status->repeat = atoi(re->value);
796 else if(strcmp(re->name,"single")==0) {
797 status->single = atoi(re->value);
799 else if(strcmp(re->name,"consume")==0) {
800 status->consume = atoi(re->value);
802 else if(strcmp(re->name,"random")==0) {
803 status->random = atoi(re->value);
805 else if(strcmp(re->name,"playlist")==0) {
806 status->playlist = strtol(re->value,NULL,10);
808 else if(strcmp(re->name,"playlistlength")==0) {
809 status->playlistLength = atoi(re->value);
811 else if(strcmp(re->name,"bitrate")==0) {
812 status->bitRate = atoi(re->value);
814 else if(strcmp(re->name,"state")==0) {
815 if(strcmp(re->value,"play")==0) {
816 status->state = MPD_STATUS_STATE_PLAY;
818 else if(strcmp(re->value,"stop")==0) {
819 status->state = MPD_STATUS_STATE_STOP;
821 else if(strcmp(re->value,"pause")==0) {
822 status->state = MPD_STATUS_STATE_PAUSE;
824 else {
825 status->state = MPD_STATUS_STATE_UNKNOWN;
828 else if(strcmp(re->name,"song")==0) {
829 status->song = atoi(re->value);
831 else if(strcmp(re->name,"songid")==0) {
832 status->songid = atoi(re->value);
834 else if(strcmp(re->name,"nextsong")==0) {
835 status->nextsong = atoi(re->value);
837 else if(strcmp(re->name,"nextsongid")==0) {
838 status->nextsongid = atoi(re->value);
840 else if(strcmp(re->name,"time")==0) {
841 char * tok = strchr(re->value,':');
842 /* the second strchr below is a safety check */
843 if (tok && (strchr(tok,0) > (tok+1))) {
844 /* atoi stops at the first non-[0-9] char: */
845 status->elapsedTime = atoi(re->value);
846 status->totalTime = atoi(tok+1);
849 else if(strcmp(re->name,"error")==0) {
850 status->error = strdup(re->value);
852 else if(strcmp(re->name,"xfade")==0) {
853 status->crossfade = atoi(re->value);
855 else if(strcmp(re->name,"updating_db")==0) {
856 status->updatingDb = atoi(re->value);
858 else if(strcmp(re->name,"audio")==0) {
859 char * tok = strchr(re->value,':');
860 if (tok && (strchr(tok,0) > (tok+1))) {
861 status->sampleRate = atoi(re->value);
862 status->bits = atoi(++tok);
863 tok = strchr(tok,':');
864 if (tok && (strchr(tok,0) > (tok+1)))
865 status->channels = atoi(tok+1);
869 mpd_getNextReturnElement(connection);
870 if(connection->error) {
871 g_slice_free(mpd_Status, status);
872 return NULL;
876 if(connection->error) {
877 g_slice_free(mpd_Status, status);
878 return NULL;
880 else if(status->state<0) {
881 strcpy(connection->errorStr,"state not found");
882 connection->error = 1;
883 g_slice_free(mpd_Status, status);
884 return NULL;
887 return status;
890 void mpd_freeStatus(mpd_Status * status) {
891 if(status->error) free(status->error);
892 g_slice_free(mpd_Status, status);
895 void mpd_sendStatsCommand(mpd_Connection * connection) {
896 mpd_executeCommand(connection,"stats\n");
899 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
900 /*mpd_executeCommand(connection,"stats\n");
902 if(connection->error) return NULL;*/
904 if(connection->doneProcessing || (connection->listOks &&
905 connection->doneListOk))
907 return NULL;
910 if(!connection->returnElement) mpd_getNextReturnElement(connection);
912 mpd_Stats* stats = g_slice_new0(mpd_Stats);
914 if(connection->error) {
915 mpd_freeStats(stats);
916 return NULL;
918 while(connection->returnElement) {
919 mpd_ReturnElement * re = connection->returnElement;
920 if(strcmp(re->name,"artists")==0) {
921 stats->numberOfArtists = atoi(re->value);
923 else if(strcmp(re->name,"albums")==0) {
924 stats->numberOfAlbums = atoi(re->value);
926 else if(strcmp(re->name,"songs")==0) {
927 stats->numberOfSongs = atoi(re->value);
929 else if(strcmp(re->name,"uptime")==0) {
930 stats->uptime = strtol(re->value,NULL,10);
932 else if(strcmp(re->name,"db_update")==0) {
933 stats->dbUpdateTime = strtol(re->value,NULL,10);
935 else if(strcmp(re->name,"playtime")==0) {
936 stats->playTime = strtol(re->value,NULL,10);
938 else if(strcmp(re->name,"db_playtime")==0) {
939 stats->dbPlayTime = strtol(re->value,NULL,10);
942 mpd_getNextReturnElement(connection);
943 if(connection->error) {
944 mpd_freeStats(stats);
945 return NULL;
949 if(connection->error) {
950 mpd_freeStats(stats);
951 return NULL;
954 return stats;
957 void mpd_freeStats(mpd_Stats * stats) {
958 g_slice_free(mpd_Stats, stats);
961 mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection)
963 if (connection->doneProcessing ||
964 (connection->listOks && connection->doneListOk)) {
965 return NULL;
968 if (!connection->returnElement) mpd_getNextReturnElement(connection);
970 if (connection->error)
971 return NULL;
973 mpd_ReturnElement* re;
974 mpd_SearchStats* stats = g_slice_new0(mpd_SearchStats);
976 while (connection->returnElement) {
977 re = connection->returnElement;
979 if (strcmp(re->name, "songs") == 0) {
980 stats->numberOfSongs = atoi(re->value);
981 } else if (strcmp(re->name, "playtime") == 0) {
982 stats->playTime = strtol(re->value, NULL, 10);
985 mpd_getNextReturnElement(connection);
986 if (connection->error) {
987 mpd_freeSearchStats(stats);
988 return NULL;
992 if (connection->error) {
993 mpd_freeSearchStats(stats);
994 return NULL;
997 return stats;
1000 void mpd_freeSearchStats(mpd_SearchStats * stats)
1002 g_slice_free(mpd_SearchStats, stats);
1005 static void mpd_finishSong(mpd_Song * song) {
1006 if(song->file) free(song->file);
1007 if(song->artist) free(song->artist);
1008 if(song->album) free(song->album);
1009 if(song->title) free(song->title);
1010 if(song->track) free(song->track);
1011 if(song->name) free(song->name);
1012 if(song->date) free(song->date);
1013 if(song->genre) free(song->genre);
1014 if(song->composer) free(song->composer);
1015 if(song->performer) free(song->performer);
1016 if(song->disc) free(song->disc);
1017 if(song->comment) free(song->comment);
1018 if(song->albumartist) free(song->albumartist);
1021 mpd_Song * mpd_newSong(void) {
1022 mpd_Song * ret = g_slice_new0(mpd_Song);
1023 ret->time = MPD_SONG_NO_TIME;
1024 ret->pos = MPD_SONG_NO_NUM;
1025 ret->id = MPD_SONG_NO_ID;
1026 return ret;
1029 void mpd_freeSong(mpd_Song * song) {
1030 mpd_finishSong(song);
1031 g_slice_free(mpd_Song, song);
1034 mpd_Song * mpd_songDup(const mpd_Song * song) {
1035 mpd_Song * ret = mpd_newSong();
1037 if(song->file) ret->file = strdup(song->file);
1038 if(song->artist) ret->artist = strdup(song->artist);
1039 if(song->album) ret->album = strdup(song->album);
1040 if(song->title) ret->title = strdup(song->title);
1041 if(song->track) ret->track = strdup(song->track);
1042 if(song->name) ret->name = strdup(song->name);
1043 if(song->date) ret->date = strdup(song->date);
1044 if(song->genre) ret->genre= strdup(song->genre);
1045 if(song->composer) ret->composer= strdup(song->composer);
1046 if(song->performer) ret->performer = strdup(song->performer);
1047 if(song->disc) ret->disc = strdup(song->disc);
1048 if(song->comment) ret->comment = strdup(song->comment);
1049 if(song->albumartist) ret->albumartist = strdup(song->albumartist);
1050 ret->time = song->time;
1051 ret->pos = song->pos;
1052 ret->id = song->id;
1054 return ret;
1058 static void mpd_finishDirectory(mpd_Directory * directory) {
1059 if(directory->path) free(directory->path);
1062 mpd_Directory * mpd_newDirectory(void) {
1063 return g_slice_new0(mpd_Directory);
1066 void mpd_freeDirectory(mpd_Directory * directory) {
1067 mpd_finishDirectory(directory);
1068 g_slice_free(mpd_Directory, directory);
1071 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
1072 mpd_Directory * ret = mpd_newDirectory();
1074 if(directory->path) ret->path = strdup(directory->path);
1076 return ret;
1079 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
1080 if(playlist->path) free(playlist->path);
1081 if(playlist->mtime) free(playlist->mtime);
1084 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
1085 return g_slice_new0(mpd_PlaylistFile);
1088 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
1089 mpd_finishPlaylistFile(playlist);
1090 g_slice_free(mpd_PlaylistFile, playlist);
1093 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1094 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1096 if(playlist->path) ret->path = strdup(playlist->path);
1097 if(playlist->mtime) ret->mtime = strdup(playlist->mtime);
1099 return ret;
1102 static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
1103 if(entity->info.directory) {
1104 if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1105 mpd_freeDirectory(entity->info.directory);
1107 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1108 mpd_freeSong(entity->info.song);
1110 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1111 mpd_freePlaylistFile(entity->info.playlistFile);
1116 mpd_InfoEntity * mpd_newInfoEntity(void) {
1117 return g_slice_new0(mpd_InfoEntity);
1120 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1121 mpd_finishInfoEntity(entity);
1122 g_slice_free(mpd_InfoEntity, entity);
1125 static void mpd_sendInfoCommand(mpd_Connection * connection,const char * command) {
1126 mpd_executeCommand(connection,command);
1129 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1130 mpd_InfoEntity * entity = NULL;
1132 if(connection->doneProcessing || (connection->listOks &&
1133 connection->doneListOk))
1135 return NULL;
1138 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1140 if(connection->returnElement) {
1141 if(strcmp(connection->returnElement->name,"file")==0) {
1142 entity = mpd_newInfoEntity();
1143 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1144 entity->info.song = mpd_newSong();
1145 entity->info.song->file =
1146 strdup(connection->returnElement->value);
1148 else if(strcmp(connection->returnElement->name,
1149 "directory")==0) {
1150 entity = mpd_newInfoEntity();
1151 entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
1152 entity->info.directory = mpd_newDirectory();
1153 entity->info.directory->path =
1154 strdup(connection->returnElement->value);
1156 else if(strcmp(connection->returnElement->name,"playlist")==0) {
1157 entity = mpd_newInfoEntity();
1158 entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
1159 entity->info.playlistFile = mpd_newPlaylistFile();
1160 entity->info.playlistFile->path =
1161 strdup(connection->returnElement->value);
1163 else if(strcmp(connection->returnElement->name, "cpos") == 0){
1164 entity = mpd_newInfoEntity();
1165 entity->type = MPD_INFO_ENTITY_TYPE_SONG;
1166 entity->info.song = mpd_newSong();
1167 entity->info.song->pos = atoi(connection->returnElement->value);
1169 else {
1170 connection->error = 1;
1171 strcpy(connection->errorStr,"problem parsing song info");
1172 return NULL;
1175 else return NULL;
1177 mpd_getNextReturnElement(connection);
1178 while(connection->returnElement) {
1179 mpd_ReturnElement * re = connection->returnElement;
1181 if(strcmp(re->name,"file")==0) return entity;
1182 else if(strcmp(re->name,"directory")==0) return entity;
1183 else if(strcmp(re->name,"playlist")==0) return entity;
1184 else if(strcmp(re->name,"cpos")==0) return entity;
1186 if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
1187 strlen(re->value)) {
1188 if(strcmp(re->name,"Artist")==0) {
1189 if(entity->info.song->artist) {
1190 int length = strlen(entity->info.song->artist);
1191 entity->info.song->artist = realloc(entity->info.song->artist,
1192 length + strlen(re->value) + 3);
1193 strcpy(&((entity->info.song->artist)[length]), ", ");
1194 strcpy(&((entity->info.song->artist)[length + 2]), re->value);
1196 else {
1197 entity->info.song->artist = strdup(re->value);
1200 else if(!entity->info.song->album &&
1201 strcmp(re->name,"Album")==0) {
1202 entity->info.song->album = strdup(re->value);
1204 else if(!entity->info.song->title &&
1205 strcmp(re->name,"Title")==0) {
1206 entity->info.song->title = strdup(re->value);
1208 else if(!entity->info.song->track &&
1209 strcmp(re->name,"Track")==0) {
1210 entity->info.song->track = strdup(re->value);
1212 else if(!entity->info.song->name &&
1213 strcmp(re->name,"Name")==0) {
1214 entity->info.song->name = strdup(re->value);
1216 else if(entity->info.song->time==MPD_SONG_NO_TIME &&
1217 strcmp(re->name,"Time")==0) {
1218 entity->info.song->time = atoi(re->value);
1220 else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
1221 strcmp(re->name,"Pos")==0) {
1222 entity->info.song->pos = atoi(re->value);
1224 else if(entity->info.song->id==MPD_SONG_NO_ID &&
1225 strcmp(re->name,"Id")==0) {
1226 entity->info.song->id = atoi(re->value);
1228 else if(!entity->info.song->date &&
1229 strcmp(re->name, "Date") == 0) {
1230 entity->info.song->date = strdup(re->value);
1232 else if(!entity->info.song->genre &&
1233 strcmp(re->name, "Genre") == 0) {
1234 if(entity->info.song->genre) {
1235 int length = strlen(entity->info.song->genre);
1236 entity->info.song->genre = realloc(entity->info.song->genre,
1237 length + strlen(re->value) + 4);
1238 strcpy(&((entity->info.song->genre)[length]), ", ");
1239 strcpy(&((entity->info.song->genre)[length + 3]), re->value);
1241 else {
1242 entity->info.song->genre = strdup(re->value);
1245 else if(strcmp(re->name, "Composer") == 0) {
1246 if(entity->info.song->composer) {
1247 int length = strlen(entity->info.song->composer);
1248 entity->info.song->composer = realloc(entity->info.song->composer,
1249 length + strlen(re->value) + 3);
1250 strcpy(&((entity->info.song->composer)[length]), ", ");
1251 strcpy(&((entity->info.song->composer)[length + 2]), re->value);
1253 else {
1254 entity->info.song->composer = strdup(re->value);
1257 else if(strcmp(re->name, "Performer") == 0) {
1258 if(entity->info.song->performer) {
1259 int length = strlen(entity->info.song->performer);
1260 entity->info.song->performer = realloc(entity->info.song->performer,
1261 length + strlen(re->value) + 3);
1262 strcpy(&((entity->info.song->performer)[length]), ", ");
1263 strcpy(&((entity->info.song->performer)[length + 2]), re->value);
1265 else {
1266 entity->info.song->performer = strdup(re->value);
1269 else if(!entity->info.song->disc &&
1270 strcmp(re->name, "Disc") == 0) {
1271 entity->info.song->disc = strdup(re->value);
1273 else if(!entity->info.song->comment &&
1274 strcmp(re->name, "Comment") == 0) {
1275 entity->info.song->comment = strdup(re->value);
1278 else if(!entity->info.song->albumartist &&
1279 strcmp(re->name, "AlbumArtist") == 0) {
1280 entity->info.song->albumartist = strdup(re->value);
1283 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1285 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1286 if(!entity->info.playlistFile->mtime &&
1287 strcmp(re->name, "Last-Modified") == 0) {
1288 entity->info.playlistFile->mtime = strdup(re->value);
1292 mpd_getNextReturnElement(connection);
1295 return entity;
1298 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1299 const char * name)
1301 if(connection->doneProcessing || (connection->listOks &&
1302 connection->doneListOk))
1304 return NULL;
1307 mpd_getNextReturnElement(connection);
1308 while(connection->returnElement) {
1309 mpd_ReturnElement * re = connection->returnElement;
1311 if(strcmp(re->name,name)==0) return strdup(re->value);
1312 mpd_getNextReturnElement(connection);
1315 return NULL;
1318 char *mpd_getNextTag(mpd_Connection *connection, int type)
1320 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES ||
1321 type == MPD_TAG_ITEM_ANY)
1322 return NULL;
1323 if (type == MPD_TAG_ITEM_FILENAME)
1324 return mpd_getNextReturnElementNamed(connection, "file");
1325 return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
1328 char * mpd_getNextArtist(mpd_Connection * connection) {
1329 return mpd_getNextReturnElementNamed(connection,"Artist");
1332 char * mpd_getNextAlbum(mpd_Connection * connection) {
1333 return mpd_getNextReturnElementNamed(connection,"Album");
1336 void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
1337 int len = strlen("playlistinfo")+2+INTLEN+3;
1338 char *string = malloc(len);
1339 snprintf(string, len, "playlistinfo \"%i\"\n", songPos);
1340 mpd_sendInfoCommand(connection,string);
1341 free(string);
1344 void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
1345 int len = strlen("playlistid")+2+INTLEN+3;
1346 char *string = malloc(len);
1347 snprintf(string, len, "playlistid \"%i\"\n", id);
1348 mpd_sendInfoCommand(connection, string);
1349 free(string);
1352 void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
1353 int len = strlen("plchanges")+2+LONGLONGLEN+3;
1354 char *string = malloc(len);
1355 snprintf(string, len, "plchanges \"%lld\"\n", playlist);
1356 mpd_sendInfoCommand(connection,string);
1357 free(string);
1360 void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
1361 int len = strlen("plchangesposid")+2+LONGLONGLEN+3;
1362 char *string = malloc(len);
1363 snprintf(string, len, "plchangesposid \"%lld\"\n", playlist);
1364 mpd_sendInfoCommand(connection,string);
1365 free(string);
1368 void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
1369 char * sDir = mpd_sanitizeArg(dir);
1370 int len = strlen("listall")+2+strlen(sDir)+3;
1371 char *string = malloc(len);
1372 snprintf(string, len, "listall \"%s\"\n", sDir);
1373 mpd_sendInfoCommand(connection,string);
1374 free(string);
1375 free(sDir);
1378 void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
1379 char * sDir = mpd_sanitizeArg(dir);
1380 int len = strlen("listallinfo")+2+strlen(sDir)+3;
1381 char *string = malloc(len);
1382 snprintf(string, len, "listallinfo \"%s\"\n", sDir);
1383 mpd_sendInfoCommand(connection,string);
1384 free(string);
1385 free(sDir);
1388 void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
1389 char * sDir = mpd_sanitizeArg(dir);
1390 int len = strlen("lsinfo")+2+strlen(sDir)+3;
1391 char *string = malloc(len);
1392 snprintf(string, len, "lsinfo \"%s\"\n", sDir);
1393 mpd_sendInfoCommand(connection,string);
1394 free(string);
1395 free(sDir);
1398 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1399 mpd_executeCommand(connection,"currentsong\n");
1402 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1403 const char * str)
1405 mpd_startSearch(connection, 0);
1406 mpd_addConstraintSearch(connection, table, str);
1407 mpd_commitSearch(connection);
1410 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1411 const char * str)
1413 mpd_startSearch(connection, 1);
1414 mpd_addConstraintSearch(connection, table, str);
1415 mpd_commitSearch(connection);
1418 void mpd_sendListCommand(mpd_Connection * connection, int table,
1419 const char * arg1)
1421 char st[10];
1422 int len;
1423 char *string;
1424 if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
1425 else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
1426 else {
1427 connection->error = 1;
1428 strcpy(connection->errorStr,"unknown table for list");
1429 return;
1431 if(arg1) {
1432 char * sanitArg1 = mpd_sanitizeArg(arg1);
1433 len = strlen("list")+1+strlen(sanitArg1)+2+strlen(st)+3;
1434 string = malloc(len);
1435 snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1);
1436 free(sanitArg1);
1438 else {
1439 len = strlen("list")+1+strlen(st)+2;
1440 string = malloc(len);
1441 snprintf(string, len, "list %s\n", st);
1443 mpd_sendInfoCommand(connection,string);
1444 free(string);
1447 void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
1448 char * sFile = mpd_sanitizeArg(file);
1449 int len = strlen("add")+2+strlen(sFile)+3;
1450 char *string = malloc(len);
1451 snprintf(string, len, "add \"%s\"\n", sFile);
1452 mpd_executeCommand(connection,string);
1453 free(string);
1454 free(sFile);
1457 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1459 int retval = -1;
1460 char *sFile = mpd_sanitizeArg(file);
1461 int len = strlen("addid")+2+strlen(sFile)+3;
1462 char *string = malloc(len);
1464 snprintf(string, len, "addid \"%s\"\n", sFile);
1465 mpd_sendInfoCommand(connection, string);
1466 free(string);
1467 free(sFile);
1469 string = mpd_getNextReturnElementNamed(connection, "Id");
1470 if (string) {
1471 retval = atoi(string);
1472 free(string);
1475 return retval;
1478 void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
1479 int len = strlen("delete")+2+INTLEN+3;
1480 char *string = malloc(len);
1481 snprintf(string, len, "delete \"%i\"\n", songPos);
1482 mpd_sendInfoCommand(connection,string);
1483 free(string);
1486 void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
1487 int len = strlen("deleteid")+2+INTLEN+3;
1488 char *string = malloc(len);
1489 snprintf(string, len, "deleteid \"%i\"\n", id);
1490 mpd_sendInfoCommand(connection,string);
1491 free(string);
1494 void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
1495 char * sName = mpd_sanitizeArg(name);
1496 int len = strlen("save")+2+strlen(sName)+3;
1497 char *string = malloc(len);
1498 snprintf(string, len, "save \"%s\"\n", sName);
1499 mpd_executeCommand(connection,string);
1500 free(string);
1501 free(sName);
1504 void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
1505 char * sName = mpd_sanitizeArg(name);
1506 int len = strlen("load")+2+strlen(sName)+3;
1507 char *string = malloc(len);
1508 snprintf(string, len, "load \"%s\"\n", sName);
1509 mpd_executeCommand(connection,string);
1510 free(string);
1511 free(sName);
1514 void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
1515 char * sName = mpd_sanitizeArg(name);
1516 int len = strlen("rm")+2+strlen(sName)+3;
1517 char *string = malloc(len);
1518 snprintf(string, len, "rm \"%s\"\n", sName);
1519 mpd_executeCommand(connection,string);
1520 free(string);
1521 free(sName);
1524 void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
1525 const char *to)
1527 char *sFrom = mpd_sanitizeArg(from);
1528 char *sTo = mpd_sanitizeArg(to);
1529 int len = strlen("rename")+2+strlen(sFrom)+3+strlen(sTo)+3;
1530 char *string = malloc(len);
1531 snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo);
1532 mpd_executeCommand(connection, string);
1533 free(string);
1534 free(sFrom);
1535 free(sTo);
1538 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1539 mpd_executeCommand(connection,"shuffle\n");
1542 void mpd_sendClearCommand(mpd_Connection * connection) {
1543 mpd_executeCommand(connection,"clear\n");
1546 void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
1547 int len = strlen("play")+2+INTLEN+3;
1548 char *string = malloc(len);
1549 snprintf(string, len, "play \"%i\"\n", songPos);
1550 mpd_sendInfoCommand(connection,string);
1551 free(string);
1554 void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
1555 int len = strlen("playid")+2+INTLEN+3;
1556 char *string = malloc(len);
1557 snprintf(string, len, "playid \"%i\"\n", id);
1558 mpd_sendInfoCommand(connection,string);
1559 free(string);
1562 void mpd_sendStopCommand(mpd_Connection * connection) {
1563 mpd_executeCommand(connection,"stop\n");
1566 void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
1567 int len = strlen("pause")+2+INTLEN+3;
1568 char *string = malloc(len);
1569 snprintf(string, len, "pause \"%i\"\n", pauseMode);
1570 mpd_executeCommand(connection,string);
1571 free(string);
1574 void mpd_sendNextCommand(mpd_Connection * connection) {
1575 mpd_executeCommand(connection,"next\n");
1578 void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
1579 int len = strlen("move")+2+INTLEN+3+INTLEN+3;
1580 char *string = malloc(len);
1581 snprintf(string, len, "move \"%i\" \"%i\"\n", from, to);
1582 mpd_sendInfoCommand(connection,string);
1583 free(string);
1586 void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
1587 int len = strlen("moveid")+2+INTLEN+3+INTLEN+3;
1588 char *string = malloc(len);
1589 snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to);
1590 mpd_sendInfoCommand(connection,string);
1591 free(string);
1594 void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
1595 int len = strlen("swap")+2+INTLEN+3+INTLEN+3;
1596 char *string = malloc(len);
1597 snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2);
1598 mpd_sendInfoCommand(connection,string);
1599 free(string);
1602 void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
1603 int len = strlen("swapid")+2+INTLEN+3+INTLEN+3;
1604 char *string = malloc(len);
1605 snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2);
1606 mpd_sendInfoCommand(connection,string);
1607 free(string);
1610 void mpd_sendSeekCommand(mpd_Connection * connection, int song, int seek_time) {
1611 int len = strlen("seek")+2+INTLEN+3+INTLEN+3;
1612 char *string = malloc(len);
1613 snprintf(string, len, "seek \"%i\" \"%i\"\n", song, seek_time);
1614 mpd_sendInfoCommand(connection,string);
1615 free(string);
1618 void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int seek_time) {
1619 int len = strlen("seekid")+2+INTLEN+3+INTLEN+3;
1620 char *string = malloc(len);
1621 snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, seek_time);
1622 mpd_sendInfoCommand(connection,string);
1623 free(string);
1626 void mpd_sendUpdateCommand(mpd_Connection * connection,const char * path) {
1627 char * sPath = mpd_sanitizeArg(path);
1628 int len = strlen("update")+2+strlen(sPath)+3;
1629 char *string = malloc(len);
1630 snprintf(string, len, "update \"%s\"\n", sPath);
1631 mpd_sendInfoCommand(connection,string);
1632 free(string);
1633 free(sPath);
1636 int mpd_getUpdateId(mpd_Connection * connection) {
1637 char * jobid;
1638 int ret = 0;
1640 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1641 if(jobid) {
1642 ret = atoi(jobid);
1643 free(jobid);
1646 return ret;
1649 void mpd_sendPrevCommand(mpd_Connection * connection) {
1650 mpd_executeCommand(connection,"previous\n");
1653 void mpd_sendSingleCommand(mpd_Connection * connection, int singleMode) {
1654 int len = strlen("repeat")+2+INTLEN+3;
1655 char *string = malloc(len);
1656 snprintf(string, len, "single \"%i\"\n", singleMode);
1657 mpd_executeCommand(connection,string);
1658 free(string);
1661 void mpd_sendConsumeCommand(mpd_Connection * connection, int consumeMode) {
1662 int len = strlen("repeat")+2+INTLEN+3;
1663 char *string = malloc(len);
1664 snprintf(string, len, "consume \"%i\"\n", consumeMode);
1665 mpd_executeCommand(connection,string);
1666 free(string);
1669 void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
1670 int len = strlen("repeat")+2+INTLEN+3;
1671 char *string = malloc(len);
1672 snprintf(string, len, "repeat \"%i\"\n", repeatMode);
1673 mpd_executeCommand(connection,string);
1674 free(string);
1677 void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
1678 int len = strlen("random")+2+INTLEN+3;
1679 char *string = malloc(len);
1680 snprintf(string, len, "random \"%i\"\n", randomMode);
1681 mpd_executeCommand(connection,string);
1682 free(string);
1685 void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
1686 int len = strlen("setvol")+2+INTLEN+3;
1687 char *string = malloc(len);
1688 snprintf(string, len, "setvol \"%i\"\n", volumeChange);
1689 mpd_executeCommand(connection,string);
1690 free(string);
1693 void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
1694 int len = strlen("crossfade")+2+INTLEN+3;
1695 char *string = malloc(len);
1696 snprintf(string, len, "crossfade \"%i\"\n", seconds);
1697 mpd_executeCommand(connection,string);
1698 free(string);
1701 void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
1702 char * sPass = mpd_sanitizeArg(pass);
1703 int len = strlen("password")+2+strlen(sPass)+3;
1704 char *string = malloc(len);
1705 snprintf(string, len, "password \"%s\"\n", sPass);
1706 mpd_executeCommand(connection,string);
1707 free(string);
1708 free(sPass);
1711 void mpd_sendCommandListBegin(mpd_Connection * connection) {
1712 if(connection->commandList) {
1713 strcpy(connection->errorStr,"already in command list mode");
1714 connection->error = 1;
1715 return;
1717 connection->commandList = COMMAND_LIST;
1718 mpd_executeCommand(connection,"command_list_begin\n");
1721 void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
1722 if(connection->commandList) {
1723 strcpy(connection->errorStr,"already in command list mode");
1724 connection->error = 1;
1725 return;
1727 connection->commandList = COMMAND_LIST_OK;
1728 mpd_executeCommand(connection,"command_list_ok_begin\n");
1729 connection->listOks = 0;
1732 void mpd_sendCommandListEnd(mpd_Connection * connection) {
1733 if(!connection->commandList) {
1734 strcpy(connection->errorStr,"not in command list mode");
1735 connection->error = 1;
1736 return;
1738 connection->commandList = 0;
1739 mpd_executeCommand(connection,"command_list_end\n");
1742 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1743 mpd_executeCommand(connection,"outputs\n");
1746 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1747 if(connection->doneProcessing || (connection->listOks &&
1748 connection->doneListOk))
1750 return NULL;
1753 if(connection->error) return NULL;
1755 mpd_OutputEntity* output = g_slice_new0(mpd_OutputEntity);
1756 output->id = -10;
1758 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1760 while(connection->returnElement) {
1761 mpd_ReturnElement * re = connection->returnElement;
1762 if(strcmp(re->name,"outputid")==0) {
1763 if(output!=NULL && output->id>=0) return output;
1764 output->id = atoi(re->value);
1766 else if(strcmp(re->name,"outputname")==0) {
1767 output->name = strdup(re->value);
1769 else if(strcmp(re->name,"outputenabled")==0) {
1770 output->enabled = atoi(re->value);
1773 mpd_getNextReturnElement(connection);
1774 if(connection->error) {
1775 mpd_freeOutputElement(output);
1776 return NULL;
1780 return output;
1783 void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
1784 int len = strlen("enableoutput")+2+INTLEN+3;
1785 char *string = malloc(len);
1786 snprintf(string, len, "enableoutput \"%i\"\n", outputId);
1787 mpd_executeCommand(connection,string);
1788 free(string);
1791 void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
1792 int len = strlen("disableoutput")+2+INTLEN+3;
1793 char *string = malloc(len);
1794 snprintf(string, len, "disableoutput \"%i\"\n", outputId);
1795 mpd_executeCommand(connection,string);
1796 free(string);
1799 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1800 if(output->name)
1801 free(output->name);
1802 g_slice_free(mpd_OutputEntity, output);
1806 * mpd_sendNotCommandsCommand
1807 * odd naming, but it gets the not allowed commands
1810 void mpd_sendNotCommandsCommand(mpd_Connection * connection)
1812 mpd_executeCommand(connection, "notcommands\n");
1816 * mpd_sendCommandsCommand
1817 * odd naming, but it gets the allowed commands
1819 void mpd_sendCommandsCommand(mpd_Connection * connection)
1821 mpd_executeCommand(connection, "commands\n");
1825 * Get the next returned command
1827 char * mpd_getNextCommand(mpd_Connection * connection)
1829 return mpd_getNextReturnElementNamed(connection, "command");
1832 void mpd_sendUrlHandlersCommand(mpd_Connection * connection)
1834 mpd_executeCommand(connection, "urlhandlers\n");
1837 char * mpd_getNextHandler(mpd_Connection * connection)
1839 return mpd_getNextReturnElementNamed(connection, "handler");
1842 void mpd_sendTagTypesCommand(mpd_Connection * connection)
1844 mpd_executeCommand(connection, "tagtypes\n");
1847 char * mpd_getNextTagType(mpd_Connection * connection)
1849 return mpd_getNextReturnElementNamed(connection, "tagtype");
1852 void mpd_startSearch(mpd_Connection *connection, int exact)
1854 if (connection->request) {
1855 strcpy(connection->errorStr, "search already in progress");
1856 connection->error = 1;
1857 return;
1860 if (exact) connection->request = strdup("find");
1861 else connection->request = strdup("search");
1864 void mpd_startStatsSearch(mpd_Connection *connection)
1866 if (connection->request) {
1867 strcpy(connection->errorStr, "search already in progress");
1868 connection->error = 1;
1869 return;
1872 connection->request = strdup("count");
1875 void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
1877 if (connection->request) {
1878 strcpy(connection->errorStr, "search already in progress");
1879 connection->error = 1;
1880 return;
1883 if (exact) connection->request = strdup("playlistfind");
1884 else connection->request = strdup("playlistsearch");
1887 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1889 char *strtype;
1890 int len;
1892 if (connection->request) {
1893 strcpy(connection->errorStr, "search already in progress");
1894 connection->error = 1;
1895 return;
1898 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1899 strcpy(connection->errorStr, "invalid type specified");
1900 connection->error = 1;
1901 return;
1904 strtype = mpdTagItemKeys[type];
1906 len = 5+strlen(strtype)+1;
1907 connection->request = malloc(len);
1909 snprintf(connection->request, len, "list %c%s",
1910 tolower(strtype[0]), strtype+1);
1913 void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name)
1915 char *strtype;
1916 char *arg;
1917 int len;
1918 char *string;
1920 if (!connection->request) {
1921 strcpy(connection->errorStr, "no search in progress");
1922 connection->error = 1;
1923 return;
1926 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1927 strcpy(connection->errorStr, "invalid type specified");
1928 connection->error = 1;
1929 return;
1932 if (name == NULL) {
1933 strcpy(connection->errorStr, "no name specified");
1934 connection->error = 1;
1935 return;
1938 string = strdup(connection->request);
1939 strtype = mpdTagItemKeys[type];
1940 arg = mpd_sanitizeArg(name);
1942 len = strlen(string)+1+strlen(strtype)+2+strlen(arg)+2;
1943 connection->request = realloc(connection->request, len);
1944 snprintf(connection->request, len, "%s %c%s \"%s\"",
1945 string, tolower(strtype[0]), strtype+1, arg);
1947 free(string);
1948 free(arg);
1951 void mpd_commitSearch(mpd_Connection *connection)
1953 int len;
1955 if (!connection->request) {
1956 strcpy(connection->errorStr, "no search in progress");
1957 connection->error = 1;
1958 return;
1961 len = strlen(connection->request)+2;
1962 connection->request = realloc(connection->request, len);
1963 connection->request[len-2] = '\n';
1964 connection->request[len-1] = '\0';
1965 mpd_sendInfoCommand(connection, connection->request);
1967 free(connection->request);
1968 connection->request = NULL;
1972 * @param connection a MpdConnection
1973 * @param path the path to the playlist.
1975 * List the content, with full metadata, of a stored playlist.
1978 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection,const char *path)
1980 char *arg = mpd_sanitizeArg(path);
1981 int len = strlen("listplaylistinfo")+2+strlen(arg)+3;
1982 char *query = malloc(len);
1983 snprintf(query, len, "listplaylistinfo \"%s\"\n", arg);
1984 mpd_sendInfoCommand(connection, query);
1985 free(arg);
1986 free(query);
1990 * @param connection a MpdConnection
1991 * @param path the path to the playlist.
1993 * List the content of a stored playlist.
1996 void mpd_sendListPlaylistCommand(mpd_Connection *connection,const char *path)
1998 char *arg = mpd_sanitizeArg(path);
1999 int len = strlen("listplaylist")+2+strlen(arg)+3;
2000 char *query = malloc(len);
2001 snprintf(query, len, "listplaylist \"%s\"\n", arg);
2002 mpd_sendInfoCommand(connection, query);
2003 free(arg);
2004 free(query);
2007 void mpd_sendPlaylistClearCommand(mpd_Connection *connection,const char *path)
2009 char *sPath = mpd_sanitizeArg(path);
2010 int len = strlen("playlistclear")+2+strlen(sPath)+3;
2011 char *string = malloc(len);
2012 snprintf(string, len, "playlistclear \"%s\"\n", sPath);
2013 mpd_executeCommand(connection, string);
2014 free(sPath);
2015 free(string);
2018 void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
2019 const char *playlist,const char *path)
2021 char *sPlaylist = mpd_sanitizeArg(playlist);
2022 char *sPath = mpd_sanitizeArg(path);
2023 int len = strlen("playlistadd")+2+strlen(sPlaylist)+3+strlen(sPath)+3;
2024 char *string = malloc(len);
2025 snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath);
2026 mpd_executeCommand(connection, string);
2027 free(sPlaylist);
2028 free(sPath);
2029 free(string);
2032 void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
2033 const char *playlist, int from, int to)
2035 char *sPlaylist = mpd_sanitizeArg(playlist);
2036 int len = strlen("playlistmove")+
2037 2+strlen(sPlaylist)+3+INTLEN+3+INTLEN+3;
2038 char *string = malloc(len);
2039 snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n",
2040 sPlaylist, from, to);
2041 mpd_executeCommand(connection, string);
2042 free(sPlaylist);
2043 free(string);
2046 void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
2047 const char *playlist, int pos)
2049 char *sPlaylist = mpd_sanitizeArg(playlist);
2050 int len = strlen("playlistdelete")+2+strlen(sPlaylist)+3+INTLEN+3;
2051 char *string = malloc(len);
2052 snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos);
2053 mpd_executeCommand(connection, string);
2054 free(sPlaylist);
2055 free(string);
2057 void mpd_sendClearErrorCommand(mpd_Connection * connection) {
2058 mpd_executeCommand(connection,"clearerror\n");
2062 void mpd_sendGetEventsCommand(mpd_Connection *connection) {
2063 mpd_executeCommand(connection, "idle\nnoidle\n");
2064 // mpd_executeCommand(connection, "noidle\n");
2067 char * mpd_getNextEvent(mpd_Connection *connection)
2069 return mpd_getNextReturnElementNamed(connection, "changed");
2072 void mpd_sendListPlaylistsCommand(mpd_Connection * connection) {
2073 mpd_sendInfoCommand(connection, "listplaylists\n");
2076 char * mpd_getNextSticker (mpd_Connection * connection)
2078 return mpd_getNextReturnElementNamed(connection, "sticker");
2080 void mpd_sendGetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker)
2082 char *sSong = mpd_sanitizeArg(song_path);
2083 char *sSticker = mpd_sanitizeArg(sticker);
2084 int len = strlen("sticker get song ")+strlen(sSong)+3+strlen(sSticker)+4;
2085 char *string = malloc(len);
2086 snprintf(string, len, "sticker get song \"%s\" \"%s\"\n", sSong, sSticker);
2087 mpd_executeCommand(connection, string);
2088 free(string);
2089 free(sSong);
2090 free(sSticker);
2093 void mpd_sendSetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker, const char *value)
2096 char *sSong = mpd_sanitizeArg(song_path);
2097 char *sSticker = mpd_sanitizeArg(sticker);
2098 char *sValue = mpd_sanitizeArg(value);
2099 int len = strlen("sticker set song ")+strlen(sSong)+3+strlen(sSticker)+3+strlen(sValue)+4;
2100 char *string = malloc(len);
2101 snprintf(string, len, "sticker set song \"%s\" \"%s\" \"%s\"\n", sSong, sSticker,sValue);
2102 mpd_sendInfoCommand(connection, string);
2103 free(string);
2104 free(sSong);
2105 free(sSticker);
2106 free(sValue);