1 /* libmpdclient
2 (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
3 This project's homepage is: http://www.musicpd.org
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
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.
15
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.
19
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 */
32
33
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>
40
41 #include <string.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
45 #include <limits.h>
46
47 #include <glib.h>
48
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
58
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)
62
63 #define COMMAND_LIST 1
64 #define COMMAND_LIST_OK 2
65
66 #ifndef MPD_NO_GAI
67 # ifdef AI_ADDRCONFIG
68 # define MPD_HAVE_GAI
69 # endif
70 #endif
71
72 #ifndef WIN32
73 #include <sys/un.h>
74 #endif
75
76 #ifndef MSG_DONTWAIT
77 # define MSG_DONTWAIT 0
78 #endif
79
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
90
91 #ifdef WIN32
winsock_dll_error(mpd_Connection * connection)92 static int winsock_dll_error(mpd_Connection *connection)
93 {
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;
102 }
103 return 0;
104 }
105
do_connect_fail(mpd_Connection * connection,const struct sockaddr * serv_addr,int addrlen)106 static int do_connect_fail(mpd_Connection *connection,
107 const struct sockaddr *serv_addr, int addrlen)
108 {
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;
114 }
115 #elif defined(__solaris__) || defined(__sun__)
do_connect_fail(mpd_Connection * connection,const struct sockaddr * serv_addr,int addrlen)116 static int do_connect_fail(mpd_Connection *connection,
117 const struct sockaddr *serv_addr, int addrlen)
118 {
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;
125 }
126 #else /* !WIN32 (sane operating systems) */
do_connect_fail(mpd_Connection * connection,const struct sockaddr * serv_addr,int addrlen)127 static int do_connect_fail(mpd_Connection *connection,
128 const struct sockaddr *serv_addr, int addrlen)
129 {
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;
153 }
154 }
155 else {
156 /* Connecting timed out. */
157 fprintf(stderr, "Timeout or error() %d - %s\n", valopt, strerror(valopt));
158 return 1;
159 }
160 }
161 else {
162 /* Failed to connect */
163 fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno));
164 return 1;
165 }
166 }
167
168 return 0;
169 }
170 #endif /* !WIN32 */
171
172 #ifdef MPD_HAVE_GAI
mpd_connect(mpd_Connection * connection,const char * host,int port,float timeout)173 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
174 float timeout)
175 {
176 int error;
177 char service[INTLEN+1];
178 struct addrinfo hints;
179 struct addrinfo *res = NULL;
180 struct addrinfo *addrinfo = NULL;
181
182 /**
183 * Setup hints
184 */
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;
193
194 snprintf(service, sizeof(service), "%i", port);
195
196 error = getaddrinfo(host, service, &hints, &addrinfo);
197
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;
204 }
205
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;
219 }
220
221 mpd_setConnectionTimeout(connection, timeout);
222
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;
230 }
231
232 break;
233 }
234
235 freeaddrinfo(addrinfo);
236
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;
242
243 return -1;
244 }
245
246 return 0;
247 }
248 #else /* !MPD_HAVE_GAI */
mpd_connect(mpd_Connection * connection,const char * host,int port,float timeout)249 static int mpd_connect(mpd_Connection * connection, const char * host, int port,
250 float timeout)
251 {
252 struct hostent * he;
253 struct sockaddr * dest;
254 int destlen;
255 struct sockaddr_in sin;
256
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;
262 }
263
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);
268
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;
281 }
282
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;
289 }
290
291 mpd_setConnectionTimeout(connection,timeout);
292
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;
300 }
301
302 return 0;
303 }
304 #endif /* !MPD_HAVE_GAI */
305
306 char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
307 {
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"
322 };
323
mpd_sanitizeArg(const char * arg)324 static char * mpd_sanitizeArg(const char * arg) {
325 size_t i;
326 char * ret;
327 register const char *c;
328 register char *rc;
329
330 /* instead of counting in that loop above, just
331 * use a bit more memory and half running time
332 */
333 ret = malloc(strlen(arg) * 2 + 1);
334
335 c = arg;
336 rc = ret;
337 for(i = strlen(arg)+1; i != 0; --i) {
338 if(*c=='"' || *c=='\\')
339 *rc++ = '\\';
340 *(rc++) = *(c++);
341 }
342
343 return ret;
344 }
345
mpd_newReturnElement(const char * name,const char * value)346 static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
347 {
348 mpd_ReturnElement* ret = g_slice_new(mpd_ReturnElement);
349
350 ret->name = strdup(name);
351 ret->value = strdup(value);
352
353 return ret;
354 }
355
mpd_freeReturnElement(mpd_ReturnElement * re)356 static void mpd_freeReturnElement(mpd_ReturnElement * re) {
357 free(re->name);
358 free(re->value);
359 g_slice_free(mpd_ReturnElement, re);
360 }
361
mpd_setConnectionTimeout(mpd_Connection * connection,float timeout)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);
367 }
368
mpd_parseWelcome(mpd_Connection * connection,const char * host,int port,char * output)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;
374
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;
381 }
382
383 tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
384
385 for(i=0;i<3;i++) {
386 if(tmp) connection->version[i] = strtol(tmp,&test,10);
387
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;
396 }
397 tmp = ++test;
398 }
399
400 return 0;
401 }
402
403 #ifndef WIN32
mpd_connect_un(mpd_Connection * connection,const char * host,float timeout)404 static int mpd_connect_un(mpd_Connection * connection,
405 const char * host, float timeout)
406 {
407 int error, flags;
408 size_t path_length;
409 struct sockaddr_un saun;
410
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;
416 }
417
418 saun.sun_family = AF_UNIX;
419 memcpy(saun.sun_path, host, path_length + 1);
420
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;
426 }
427
428 mpd_setConnectionTimeout(connection, timeout);
429
430 flags = fcntl(connection->sock, F_GETFL, 0);
431 fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
432
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;
438
439 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
440 "problems connecting to \"%s\": %s",
441 host, strerror(errno));
442 connection->error = MPD_ERROR_CONNPORT;
443 return -1;
444 }
445
446 return 0;
447 }
448 #endif /* WIN32 */
449
mpd_newConnection(const char * host,int port,float timeout)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,"");
460
461 if (winsock_dll_error(connection))
462 return connection;
463
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;
472
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;
490 }
491 connection->buflen+=readed;
492 connection->buffer[connection->buflen] = '\0';
493 }
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;
503 }
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;
510 }
511 }
512
513 *rt = '\0';
514 output = strdup(connection->buffer);
515 strcpy(connection->buffer,rt+1);
516 connection->buflen = strlen(connection->buffer);
517
518 if(mpd_parseWelcome(connection,host,port,output) == 0) connection->doneProcessing = 1;
519
520 free(output);
521
522 return connection;
523 }
524
mpd_clearError(mpd_Connection * connection)525 void mpd_clearError(mpd_Connection * connection) {
526 connection->error = 0;
527 connection->errorStr[0] = '\0';
528 }
529
mpd_closeConnection(mpd_Connection * connection)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();
536 }
537
mpd_executeCommand(mpd_Connection * connection,const char * command)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);
544
545 if(!connection->doneProcessing && !connection->commandList) {
546 strcpy(connection->errorStr,"not done processing current command");
547 connection->error = 1;
548 return;
549 }
550
551 mpd_clearError(connection);
552
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)
562 {
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;
568 }
569 else {
570 commandPtr+=ret;
571 commandLen-=ret;
572 }
573
574 if(commandLen<=0) break;
575 }
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;
582 }
583
584 if(!connection->commandList) connection->doneProcessing = 0;
585 else if(connection->commandList == COMMAND_LIST_OK) {
586 connection->listOks++;
587 }
588 }
589
mpd_getNextReturnElement(mpd_Connection * connection)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;
602
603 if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
604 connection->returnElement = NULL;
605
606 if(connection->doneProcessing || (connection->listOks &&
607 connection->doneListOk))
608 {
609 strcpy(connection->errorStr,"already done processing current command");
610 connection->error = 1;
611 return;
612 }
613
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;
625 }
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;
632 }
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;
645 }
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;
653 }
654 connection->buflen+=readed;
655 connection->buffer[connection->buflen] = '\0';
656 }
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;
664 }
665 }
666
667 *rt = '\0';
668 output = connection->buffer+connection->bufstart;
669 connection->bufstart = rt - connection->buffer + 1;
670
671 if(strcmp(output,"OK")==0) {
672 if(connection->listOks > 0) {
673 strcpy(connection->errorStr, "expected more list_OK's");
674 connection->error = 1;
675 }
676 connection->listOks = 0;
677 connection->doneProcessing = 1;
678 connection->doneListOk = 0;
679 return;
680 }
681
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;
687 }
688 else {
689 connection->doneListOk = 1;
690 connection->listOks--;
691 }
692 return;
693 }
694
695 if(strncmp(output,"ACK",strlen("ACK"))==0) {
696 char * test;
697 char * needle;
698 int val;
699
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;
706
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;
716 }
717
718 tok = strchr(output, ':');
719 if (!tok) return;
720 pos = tok - output;
721 value = ++tok;
722 name = output;
723 name[pos] = '\0';
724
725 if(value[0]==' ') {
726 connection->returnElement = mpd_newReturnElement(name,&(value[1]));
727 }
728 else {
729 snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
730 "error parsing: %s:%s",name,value);
731 connection->error = 1;
732 }
733 }
734
mpd_finishCommand(mpd_Connection * connection)735 void mpd_finishCommand(mpd_Connection * connection) {
736 while(!connection->doneProcessing) {
737 if(connection->doneListOk) connection->doneListOk = 0;
738 mpd_getNextReturnElement(connection);
739 }
740 }
741
mpd_finishListOkCommand(mpd_Connection * connection)742 static void mpd_finishListOkCommand(mpd_Connection * connection) {
743 while(!connection->doneProcessing && connection->listOks &&
744 !connection->doneListOk)
745 {
746 mpd_getNextReturnElement(connection);
747 }
748 }
749
mpd_nextListOkCommand(mpd_Connection * 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;
755 }
756
mpd_sendStatusCommand(mpd_Connection * connection)757 void mpd_sendStatusCommand(mpd_Connection * connection) {
758 mpd_executeCommand(connection,"status\n");
759 }
760
mpd_getStatus(mpd_Connection * connection)761 mpd_Status * mpd_getStatus(mpd_Connection * connection) {
762 /*mpd_executeCommand(connection,"status\n");
763
764 if(connection->error) return NULL;*/
765
766 if(connection->doneProcessing || (connection->listOks &&
767 connection->doneListOk))
768 {
769 return NULL;
770 }
771
772 if(!connection->returnElement) mpd_getNextReturnElement(connection);
773
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;
783
784 if(connection->error) {
785 g_slice_free(mpd_Status, status);
786 return NULL;
787 }
788 while(connection->returnElement) {
789 mpd_ReturnElement * re = connection->returnElement;
790 if(strcmp(re->name,"volume")==0) {
791 status->volume = atoi(re->value);
792 }
793 else if(strcmp(re->name,"repeat")==0) {
794 status->repeat = atoi(re->value);
795 }
796 else if(strcmp(re->name,"single")==0) {
797 status->single = atoi(re->value);
798 }
799 else if(strcmp(re->name,"consume")==0) {
800 status->consume = atoi(re->value);
801 }
802 else if(strcmp(re->name,"random")==0) {
803 status->random = atoi(re->value);
804 }
805 else if(strcmp(re->name,"playlist")==0) {
806 status->playlist = strtol(re->value,NULL,10);
807 }
808 else if(strcmp(re->name,"playlistlength")==0) {
809 status->playlistLength = atoi(re->value);
810 }
811 else if(strcmp(re->name,"bitrate")==0) {
812 status->bitRate = atoi(re->value);
813 }
814 else if(strcmp(re->name,"state")==0) {
815 if(strcmp(re->value,"play")==0) {
816 status->state = MPD_STATUS_STATE_PLAY;
817 }
818 else if(strcmp(re->value,"stop")==0) {
819 status->state = MPD_STATUS_STATE_STOP;
820 }
821 else if(strcmp(re->value,"pause")==0) {
822 status->state = MPD_STATUS_STATE_PAUSE;
823 }
824 else {
825 status->state = MPD_STATUS_STATE_UNKNOWN;
826 }
827 }
828 else if(strcmp(re->name,"song")==0) {
829 status->song = atoi(re->value);
830 }
831 else if(strcmp(re->name,"songid")==0) {
832 status->songid = atoi(re->value);
833 }
834 else if(strcmp(re->name,"nextsong")==0) {
835 status->nextsong = atoi(re->value);
836 }
837 else if(strcmp(re->name,"nextsongid")==0) {
838 status->nextsongid = atoi(re->value);
839 }
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);
847 }
848 }
849 else if(strcmp(re->name,"error")==0) {
850 status->error = strdup(re->value);
851 }
852 else if(strcmp(re->name,"xfade")==0) {
853 status->crossfade = atoi(re->value);
854 }
855 else if(strcmp(re->name,"updating_db")==0) {
856 status->updatingDb = atoi(re->value);
857 }
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);
866 }
867 }
868
869 mpd_getNextReturnElement(connection);
870 if(connection->error) {
871 g_slice_free(mpd_Status, status);
872 return NULL;
873 }
874 }
875
876 if(connection->error) {
877 g_slice_free(mpd_Status, status);
878 return NULL;
879 }
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;
885 }
886
887 return status;
888 }
889
mpd_freeStatus(mpd_Status * status)890 void mpd_freeStatus(mpd_Status * status) {
891 if(status->error) free(status->error);
892 g_slice_free(mpd_Status, status);
893 }
894
mpd_sendStatsCommand(mpd_Connection * connection)895 void mpd_sendStatsCommand(mpd_Connection * connection) {
896 mpd_executeCommand(connection,"stats\n");
897 }
898
mpd_getStats(mpd_Connection * connection)899 mpd_Stats * mpd_getStats(mpd_Connection * connection) {
900 /*mpd_executeCommand(connection,"stats\n");
901
902 if(connection->error) return NULL;*/
903
904 if(connection->doneProcessing || (connection->listOks &&
905 connection->doneListOk))
906 {
907 return NULL;
908 }
909
910 if(!connection->returnElement) mpd_getNextReturnElement(connection);
911
912 mpd_Stats* stats = g_slice_new0(mpd_Stats);
913
914 if(connection->error) {
915 mpd_freeStats(stats);
916 return NULL;
917 }
918 while(connection->returnElement) {
919 mpd_ReturnElement * re = connection->returnElement;
920 if(strcmp(re->name,"artists")==0) {
921 stats->numberOfArtists = atoi(re->value);
922 }
923 else if(strcmp(re->name,"albums")==0) {
924 stats->numberOfAlbums = atoi(re->value);
925 }
926 else if(strcmp(re->name,"songs")==0) {
927 stats->numberOfSongs = atoi(re->value);
928 }
929 else if(strcmp(re->name,"uptime")==0) {
930 stats->uptime = strtol(re->value,NULL,10);
931 }
932 else if(strcmp(re->name,"db_update")==0) {
933 stats->dbUpdateTime = strtol(re->value,NULL,10);
934 }
935 else if(strcmp(re->name,"playtime")==0) {
936 stats->playTime = strtol(re->value,NULL,10);
937 }
938 else if(strcmp(re->name,"db_playtime")==0) {
939 stats->dbPlayTime = strtol(re->value,NULL,10);
940 }
941
942 mpd_getNextReturnElement(connection);
943 if(connection->error) {
944 mpd_freeStats(stats);
945 return NULL;
946 }
947 }
948
949 if(connection->error) {
950 mpd_freeStats(stats);
951 return NULL;
952 }
953
954 return stats;
955 }
956
mpd_freeStats(mpd_Stats * stats)957 void mpd_freeStats(mpd_Stats * stats) {
958 g_slice_free(mpd_Stats, stats);
959 }
960
mpd_getSearchStats(mpd_Connection * connection)961 mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection)
962 {
963 if (connection->doneProcessing ||
964 (connection->listOks && connection->doneListOk)) {
965 return NULL;
966 }
967
968 if (!connection->returnElement) mpd_getNextReturnElement(connection);
969
970 if (connection->error)
971 return NULL;
972
973 mpd_ReturnElement* re;
974 mpd_SearchStats* stats = g_slice_new0(mpd_SearchStats);
975
976 while (connection->returnElement) {
977 re = connection->returnElement;
978
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);
983 }
984
985 mpd_getNextReturnElement(connection);
986 if (connection->error) {
987 mpd_freeSearchStats(stats);
988 return NULL;
989 }
990 }
991
992 if (connection->error) {
993 mpd_freeSearchStats(stats);
994 return NULL;
995 }
996
997 return stats;
998 }
999
mpd_freeSearchStats(mpd_SearchStats * stats)1000 void mpd_freeSearchStats(mpd_SearchStats * stats)
1001 {
1002 g_slice_free(mpd_SearchStats, stats);
1003 }
1004
mpd_finishSong(mpd_Song * song)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);
1019 }
1020
mpd_newSong(void)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;
1027 }
1028
mpd_freeSong(mpd_Song * song)1029 void mpd_freeSong(mpd_Song * song) {
1030 mpd_finishSong(song);
1031 g_slice_free(mpd_Song, song);
1032 }
1033
mpd_songDup(const mpd_Song * song)1034 mpd_Song * mpd_songDup(const mpd_Song * song) {
1035 mpd_Song * ret = mpd_newSong();
1036
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;
1053
1054 return ret;
1055 }
1056
1057
mpd_finishDirectory(mpd_Directory * directory)1058 static void mpd_finishDirectory(mpd_Directory * directory) {
1059 if(directory->path) free(directory->path);
1060 }
1061
mpd_newDirectory(void)1062 mpd_Directory * mpd_newDirectory(void) {
1063 return g_slice_new0(mpd_Directory);
1064 }
1065
mpd_freeDirectory(mpd_Directory * directory)1066 void mpd_freeDirectory(mpd_Directory * directory) {
1067 mpd_finishDirectory(directory);
1068 g_slice_free(mpd_Directory, directory);
1069 }
1070
mpd_directoryDup(mpd_Directory * directory)1071 mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
1072 mpd_Directory * ret = mpd_newDirectory();
1073
1074 if(directory->path) ret->path = strdup(directory->path);
1075
1076 return ret;
1077 }
1078
mpd_finishPlaylistFile(mpd_PlaylistFile * playlist)1079 static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
1080 if(playlist->path) free(playlist->path);
1081 if(playlist->mtime) free(playlist->mtime);
1082 }
1083
mpd_newPlaylistFile(void)1084 mpd_PlaylistFile * mpd_newPlaylistFile(void) {
1085 return g_slice_new0(mpd_PlaylistFile);
1086 }
1087
mpd_freePlaylistFile(mpd_PlaylistFile * playlist)1088 void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
1089 mpd_finishPlaylistFile(playlist);
1090 g_slice_free(mpd_PlaylistFile, playlist);
1091 }
1092
mpd_playlistFileDup(mpd_PlaylistFile * playlist)1093 mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
1094 mpd_PlaylistFile * ret = mpd_newPlaylistFile();
1095
1096 if(playlist->path) ret->path = strdup(playlist->path);
1097 if(playlist->mtime) ret->mtime = strdup(playlist->mtime);
1098
1099 return ret;
1100 }
1101
mpd_finishInfoEntity(mpd_InfoEntity * entity)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);
1106 }
1107 else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
1108 mpd_freeSong(entity->info.song);
1109 }
1110 else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
1111 mpd_freePlaylistFile(entity->info.playlistFile);
1112 }
1113 }
1114 }
1115
mpd_newInfoEntity(void)1116 mpd_InfoEntity * mpd_newInfoEntity(void) {
1117 return g_slice_new0(mpd_InfoEntity);
1118 }
1119
mpd_freeInfoEntity(mpd_InfoEntity * entity)1120 void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
1121 mpd_finishInfoEntity(entity);
1122 g_slice_free(mpd_InfoEntity, entity);
1123 }
1124
mpd_sendInfoCommand(mpd_Connection * connection,const char * command)1125 static void mpd_sendInfoCommand(mpd_Connection * connection,const char * command) {
1126 mpd_executeCommand(connection,command);
1127 }
1128
mpd_getNextInfoEntity(mpd_Connection * connection)1129 mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
1130 mpd_InfoEntity * entity = NULL;
1131
1132 if(connection->doneProcessing || (connection->listOks &&
1133 connection->doneListOk))
1134 {
1135 return NULL;
1136 }
1137
1138 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1139
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);
1147 }
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);
1155 }
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);
1162 }
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);
1168 }
1169 else {
1170 connection->error = 1;
1171 strcpy(connection->errorStr,"problem parsing song info");
1172 return NULL;
1173 }
1174 }
1175 else return NULL;
1176
1177 mpd_getNextReturnElement(connection);
1178 while(connection->returnElement) {
1179 mpd_ReturnElement * re = connection->returnElement;
1180
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;
1185
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);
1195 }
1196 else {
1197 entity->info.song->artist = strdup(re->value);
1198 }
1199 }
1200 else if(!entity->info.song->album &&
1201 strcmp(re->name,"Album")==0) {
1202 entity->info.song->album = strdup(re->value);
1203 }
1204 else if(!entity->info.song->title &&
1205 strcmp(re->name,"Title")==0) {
1206 entity->info.song->title = strdup(re->value);
1207 }
1208 else if(!entity->info.song->track &&
1209 strcmp(re->name,"Track")==0) {
1210 entity->info.song->track = strdup(re->value);
1211 }
1212 else if(!entity->info.song->name &&
1213 strcmp(re->name,"Name")==0) {
1214 entity->info.song->name = strdup(re->value);
1215 }
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);
1219 }
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);
1223 }
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);
1227 }
1228 else if(!entity->info.song->date &&
1229 strcmp(re->name, "Date") == 0) {
1230 entity->info.song->date = strdup(re->value);
1231 }
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);
1240 }
1241 else {
1242 entity->info.song->genre = strdup(re->value);
1243 }
1244 }
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);
1252 }
1253 else {
1254 entity->info.song->composer = strdup(re->value);
1255 }
1256 }
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);
1264 }
1265 else {
1266 entity->info.song->performer = strdup(re->value);
1267 }
1268 }
1269 else if(!entity->info.song->disc &&
1270 strcmp(re->name, "Disc") == 0) {
1271 entity->info.song->disc = strdup(re->value);
1272 }
1273 else if(!entity->info.song->comment &&
1274 strcmp(re->name, "Comment") == 0) {
1275 entity->info.song->comment = strdup(re->value);
1276 }
1277
1278 else if(!entity->info.song->albumartist &&
1279 strcmp(re->name, "AlbumArtist") == 0) {
1280 entity->info.song->albumartist = strdup(re->value);
1281 }
1282 }
1283 else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
1284 }
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);
1289 }
1290 }
1291
1292 mpd_getNextReturnElement(connection);
1293 }
1294
1295 return entity;
1296 }
1297
mpd_getNextReturnElementNamed(mpd_Connection * connection,const char * name)1298 static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
1299 const char * name)
1300 {
1301 if(connection->doneProcessing || (connection->listOks &&
1302 connection->doneListOk))
1303 {
1304 return NULL;
1305 }
1306
1307 mpd_getNextReturnElement(connection);
1308 while(connection->returnElement) {
1309 mpd_ReturnElement * re = connection->returnElement;
1310
1311 if(strcmp(re->name,name)==0) return strdup(re->value);
1312 mpd_getNextReturnElement(connection);
1313 }
1314
1315 return NULL;
1316 }
1317
mpd_getNextTag(mpd_Connection * connection,int type)1318 char *mpd_getNextTag(mpd_Connection *connection, int type)
1319 {
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]);
1326 }
1327
mpd_getNextArtist(mpd_Connection * connection)1328 char * mpd_getNextArtist(mpd_Connection * connection) {
1329 return mpd_getNextReturnElementNamed(connection,"Artist");
1330 }
1331
mpd_getNextAlbum(mpd_Connection * connection)1332 char * mpd_getNextAlbum(mpd_Connection * connection) {
1333 return mpd_getNextReturnElementNamed(connection,"Album");
1334 }
1335
mpd_sendPlaylistInfoCommand(mpd_Connection * connection,int songPos)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);
1342 }
1343
mpd_sendPlaylistIdCommand(mpd_Connection * connection,int id)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);
1350 }
1351
mpd_sendPlChangesCommand(mpd_Connection * connection,long long playlist)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);
1358 }
1359
mpd_sendPlChangesPosIdCommand(mpd_Connection * connection,long long playlist)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);
1366 }
1367
mpd_sendListallCommand(mpd_Connection * connection,const char * dir)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);
1376 }
1377
mpd_sendListallInfoCommand(mpd_Connection * connection,const char * dir)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);
1386 }
1387
mpd_sendLsInfoCommand(mpd_Connection * connection,const char * dir)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);
1396 }
1397
mpd_sendCurrentSongCommand(mpd_Connection * connection)1398 void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
1399 mpd_executeCommand(connection,"currentsong\n");
1400 }
1401
mpd_sendSearchCommand(mpd_Connection * connection,int table,const char * str)1402 void mpd_sendSearchCommand(mpd_Connection * connection, int table,
1403 const char * str)
1404 {
1405 mpd_startSearch(connection, 0);
1406 mpd_addConstraintSearch(connection, table, str);
1407 mpd_commitSearch(connection);
1408 }
1409
mpd_sendFindCommand(mpd_Connection * connection,int table,const char * str)1410 void mpd_sendFindCommand(mpd_Connection * connection, int table,
1411 const char * str)
1412 {
1413 mpd_startSearch(connection, 1);
1414 mpd_addConstraintSearch(connection, table, str);
1415 mpd_commitSearch(connection);
1416 }
1417
mpd_sendListCommand(mpd_Connection * connection,int table,const char * arg1)1418 void mpd_sendListCommand(mpd_Connection * connection, int table,
1419 const char * arg1)
1420 {
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;
1430 }
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);
1437 }
1438 else {
1439 len = strlen("list")+1+strlen(st)+2;
1440 string = malloc(len);
1441 snprintf(string, len, "list %s\n", st);
1442 }
1443 mpd_sendInfoCommand(connection,string);
1444 free(string);
1445 }
1446
mpd_sendAddCommand(mpd_Connection * connection,const char * file)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);
1455 }
1456
mpd_sendAddIdCommand(mpd_Connection * connection,const char * file)1457 int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
1458 {
1459 int retval = -1;
1460 char *sFile = mpd_sanitizeArg(file);
1461 int len = strlen("addid")+2+strlen(sFile)+3;
1462 char *string = malloc(len);
1463
1464 snprintf(string, len, "addid \"%s\"\n", sFile);
1465 mpd_sendInfoCommand(connection, string);
1466 free(string);
1467 free(sFile);
1468
1469 string = mpd_getNextReturnElementNamed(connection, "Id");
1470 if (string) {
1471 retval = atoi(string);
1472 free(string);
1473 }
1474
1475 return retval;
1476 }
1477
mpd_sendDeleteCommand(mpd_Connection * connection,int songPos)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);
1484 }
1485
mpd_sendDeleteIdCommand(mpd_Connection * connection,int id)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);
1492 }
1493
mpd_sendSaveCommand(mpd_Connection * connection,const char * name)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);
1502 }
1503
mpd_sendLoadCommand(mpd_Connection * connection,const char * name)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);
1512 }
1513
mpd_sendRmCommand(mpd_Connection * connection,const char * name)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);
1522 }
1523
mpd_sendRenameCommand(mpd_Connection * connection,const char * from,const char * to)1524 void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
1525 const char *to)
1526 {
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);
1536 }
1537
mpd_sendShuffleCommand(mpd_Connection * connection)1538 void mpd_sendShuffleCommand(mpd_Connection * connection) {
1539 mpd_executeCommand(connection,"shuffle\n");
1540 }
1541
mpd_sendClearCommand(mpd_Connection * connection)1542 void mpd_sendClearCommand(mpd_Connection * connection) {
1543 mpd_executeCommand(connection,"clear\n");
1544 }
1545
mpd_sendPlayCommand(mpd_Connection * connection,int songPos)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);
1552 }
1553
mpd_sendPlayIdCommand(mpd_Connection * connection,int id)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);
1560 }
1561
mpd_sendStopCommand(mpd_Connection * connection)1562 void mpd_sendStopCommand(mpd_Connection * connection) {
1563 mpd_executeCommand(connection,"stop\n");
1564 }
1565
mpd_sendPauseCommand(mpd_Connection * connection,int pauseMode)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);
1572 }
1573
mpd_sendNextCommand(mpd_Connection * connection)1574 void mpd_sendNextCommand(mpd_Connection * connection) {
1575 mpd_executeCommand(connection,"next\n");
1576 }
1577
mpd_sendMoveCommand(mpd_Connection * connection,int from,int to)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);
1584 }
1585
mpd_sendMoveIdCommand(mpd_Connection * connection,int id,int to)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);
1592 }
1593
mpd_sendSwapCommand(mpd_Connection * connection,int song1,int song2)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);
1600 }
1601
mpd_sendSwapIdCommand(mpd_Connection * connection,int id1,int id2)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);
1608 }
1609
mpd_sendSeekCommand(mpd_Connection * connection,int song,int seek_time)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);
1616 }
1617
mpd_sendSeekIdCommand(mpd_Connection * connection,int id,int seek_time)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);
1624 }
1625
mpd_sendUpdateCommand(mpd_Connection * connection,const char * path)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);
1634 }
1635
mpd_getUpdateId(mpd_Connection * connection)1636 int mpd_getUpdateId(mpd_Connection * connection) {
1637 char * jobid;
1638 int ret = 0;
1639
1640 jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
1641 if(jobid) {
1642 ret = atoi(jobid);
1643 free(jobid);
1644 }
1645
1646 return ret;
1647 }
1648
mpd_sendPrevCommand(mpd_Connection * connection)1649 void mpd_sendPrevCommand(mpd_Connection * connection) {
1650 mpd_executeCommand(connection,"previous\n");
1651 }
1652
mpd_sendSingleCommand(mpd_Connection * connection,int singleMode)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);
1659 }
1660
mpd_sendConsumeCommand(mpd_Connection * connection,int consumeMode)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);
1667 }
1668
mpd_sendRepeatCommand(mpd_Connection * connection,int repeatMode)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);
1675 }
1676
mpd_sendRandomCommand(mpd_Connection * connection,int randomMode)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);
1683 }
1684
mpd_sendSetvolCommand(mpd_Connection * connection,int volumeChange)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);
1691 }
1692
mpd_sendCrossfadeCommand(mpd_Connection * connection,int seconds)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);
1699 }
1700
mpd_sendPasswordCommand(mpd_Connection * connection,const char * pass)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);
1709 }
1710
mpd_sendCommandListBegin(mpd_Connection * connection)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;
1716 }
1717 connection->commandList = COMMAND_LIST;
1718 mpd_executeCommand(connection,"command_list_begin\n");
1719 }
1720
mpd_sendCommandListOkBegin(mpd_Connection * connection)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;
1726 }
1727 connection->commandList = COMMAND_LIST_OK;
1728 mpd_executeCommand(connection,"command_list_ok_begin\n");
1729 connection->listOks = 0;
1730 }
1731
mpd_sendCommandListEnd(mpd_Connection * connection)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;
1737 }
1738 connection->commandList = 0;
1739 mpd_executeCommand(connection,"command_list_end\n");
1740 }
1741
mpd_sendOutputsCommand(mpd_Connection * connection)1742 void mpd_sendOutputsCommand(mpd_Connection * connection) {
1743 mpd_executeCommand(connection,"outputs\n");
1744 }
1745
mpd_getNextOutput(mpd_Connection * connection)1746 mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
1747 if(connection->doneProcessing || (connection->listOks &&
1748 connection->doneListOk))
1749 {
1750 return NULL;
1751 }
1752
1753 if(connection->error) return NULL;
1754
1755 mpd_OutputEntity* output = g_slice_new0(mpd_OutputEntity);
1756 output->id = -10;
1757
1758 if(!connection->returnElement) mpd_getNextReturnElement(connection);
1759
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);
1765 }
1766 else if(strcmp(re->name,"outputname")==0) {
1767 output->name = strdup(re->value);
1768 }
1769 else if(strcmp(re->name,"outputenabled")==0) {
1770 output->enabled = atoi(re->value);
1771 }
1772
1773 mpd_getNextReturnElement(connection);
1774 if(connection->error) {
1775 mpd_freeOutputElement(output);
1776 return NULL;
1777 }
1778 }
1779
1780 return output;
1781 }
1782
mpd_sendEnableOutputCommand(mpd_Connection * connection,int outputId)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);
1789 }
1790
mpd_sendDisableOutputCommand(mpd_Connection * connection,int outputId)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);
1797 }
1798
mpd_freeOutputElement(mpd_OutputEntity * output)1799 void mpd_freeOutputElement(mpd_OutputEntity * output) {
1800 if(output->name)
1801 free(output->name);
1802 g_slice_free(mpd_OutputEntity, output);
1803 }
1804
1805 /**
1806 * mpd_sendNotCommandsCommand
1807 * odd naming, but it gets the not allowed commands
1808 */
1809
mpd_sendNotCommandsCommand(mpd_Connection * connection)1810 void mpd_sendNotCommandsCommand(mpd_Connection * connection)
1811 {
1812 mpd_executeCommand(connection, "notcommands\n");
1813 }
1814
1815 /**
1816 * mpd_sendCommandsCommand
1817 * odd naming, but it gets the allowed commands
1818 */
mpd_sendCommandsCommand(mpd_Connection * connection)1819 void mpd_sendCommandsCommand(mpd_Connection * connection)
1820 {
1821 mpd_executeCommand(connection, "commands\n");
1822 }
1823
1824 /**
1825 * Get the next returned command
1826 */
mpd_getNextCommand(mpd_Connection * connection)1827 char * mpd_getNextCommand(mpd_Connection * connection)
1828 {
1829 return mpd_getNextReturnElementNamed(connection, "command");
1830 }
1831
mpd_sendUrlHandlersCommand(mpd_Connection * connection)1832 void mpd_sendUrlHandlersCommand(mpd_Connection * connection)
1833 {
1834 mpd_executeCommand(connection, "urlhandlers\n");
1835 }
1836
mpd_getNextHandler(mpd_Connection * connection)1837 char * mpd_getNextHandler(mpd_Connection * connection)
1838 {
1839 return mpd_getNextReturnElementNamed(connection, "handler");
1840 }
1841
mpd_sendTagTypesCommand(mpd_Connection * connection)1842 void mpd_sendTagTypesCommand(mpd_Connection * connection)
1843 {
1844 mpd_executeCommand(connection, "tagtypes\n");
1845 }
1846
mpd_getNextTagType(mpd_Connection * connection)1847 char * mpd_getNextTagType(mpd_Connection * connection)
1848 {
1849 return mpd_getNextReturnElementNamed(connection, "tagtype");
1850 }
1851
mpd_startSearch(mpd_Connection * connection,int exact)1852 void mpd_startSearch(mpd_Connection *connection, int exact)
1853 {
1854 if (connection->request) {
1855 strcpy(connection->errorStr, "search already in progress");
1856 connection->error = 1;
1857 return;
1858 }
1859
1860 if (exact) connection->request = strdup("find");
1861 else connection->request = strdup("search");
1862 }
1863
mpd_startStatsSearch(mpd_Connection * connection)1864 void mpd_startStatsSearch(mpd_Connection *connection)
1865 {
1866 if (connection->request) {
1867 strcpy(connection->errorStr, "search already in progress");
1868 connection->error = 1;
1869 return;
1870 }
1871
1872 connection->request = strdup("count");
1873 }
1874
mpd_startPlaylistSearch(mpd_Connection * connection,int exact)1875 void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
1876 {
1877 if (connection->request) {
1878 strcpy(connection->errorStr, "search already in progress");
1879 connection->error = 1;
1880 return;
1881 }
1882
1883 if (exact) connection->request = strdup("playlistfind");
1884 else connection->request = strdup("playlistsearch");
1885 }
1886
mpd_startFieldSearch(mpd_Connection * connection,int type)1887 void mpd_startFieldSearch(mpd_Connection *connection, int type)
1888 {
1889 char *strtype;
1890 int len;
1891
1892 if (connection->request) {
1893 strcpy(connection->errorStr, "search already in progress");
1894 connection->error = 1;
1895 return;
1896 }
1897
1898 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1899 strcpy(connection->errorStr, "invalid type specified");
1900 connection->error = 1;
1901 return;
1902 }
1903
1904 strtype = mpdTagItemKeys[type];
1905
1906 len = 5+strlen(strtype)+1;
1907 connection->request = malloc(len);
1908
1909 snprintf(connection->request, len, "list %c%s",
1910 tolower(strtype[0]), strtype+1);
1911 }
1912
mpd_addConstraintSearch(mpd_Connection * connection,int type,const char * name)1913 void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name)
1914 {
1915 char *strtype;
1916 char *arg;
1917 int len;
1918 char *string;
1919
1920 if (!connection->request) {
1921 strcpy(connection->errorStr, "no search in progress");
1922 connection->error = 1;
1923 return;
1924 }
1925
1926 if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1927 strcpy(connection->errorStr, "invalid type specified");
1928 connection->error = 1;
1929 return;
1930 }
1931
1932 if (name == NULL) {
1933 strcpy(connection->errorStr, "no name specified");
1934 connection->error = 1;
1935 return;
1936 }
1937
1938 string = strdup(connection->request);
1939 strtype = mpdTagItemKeys[type];
1940 arg = mpd_sanitizeArg(name);
1941
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);
1946
1947 free(string);
1948 free(arg);
1949 }
1950
mpd_commitSearch(mpd_Connection * connection)1951 void mpd_commitSearch(mpd_Connection *connection)
1952 {
1953 int len;
1954
1955 if (!connection->request) {
1956 strcpy(connection->errorStr, "no search in progress");
1957 connection->error = 1;
1958 return;
1959 }
1960
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);
1966
1967 free(connection->request);
1968 connection->request = NULL;
1969 }
1970
1971 /**
1972 * @param connection a MpdConnection
1973 * @param path the path to the playlist.
1974 *
1975 * List the content, with full metadata, of a stored playlist.
1976 *
1977 */
mpd_sendListPlaylistInfoCommand(mpd_Connection * connection,const char * path)1978 void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection,const char *path)
1979 {
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);
1987 }
1988
1989 /**
1990 * @param connection a MpdConnection
1991 * @param path the path to the playlist.
1992 *
1993 * List the content of a stored playlist.
1994 *
1995 */
mpd_sendListPlaylistCommand(mpd_Connection * connection,const char * path)1996 void mpd_sendListPlaylistCommand(mpd_Connection *connection,const char *path)
1997 {
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);
2005 }
2006
mpd_sendPlaylistClearCommand(mpd_Connection * connection,const char * path)2007 void mpd_sendPlaylistClearCommand(mpd_Connection *connection,const char *path)
2008 {
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);
2016 }
2017
mpd_sendPlaylistAddCommand(mpd_Connection * connection,const char * playlist,const char * path)2018 void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
2019 const char *playlist,const char *path)
2020 {
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);
2030 }
2031
mpd_sendPlaylistMoveCommand(mpd_Connection * connection,const char * playlist,int from,int to)2032 void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
2033 const char *playlist, int from, int to)
2034 {
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);
2044 }
2045
mpd_sendPlaylistDeleteCommand(mpd_Connection * connection,const char * playlist,int pos)2046 void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
2047 const char *playlist, int pos)
2048 {
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);
2056 }
mpd_sendClearErrorCommand(mpd_Connection * connection)2057 void mpd_sendClearErrorCommand(mpd_Connection * connection) {
2058 mpd_executeCommand(connection,"clearerror\n");
2059 }
2060
2061
mpd_sendGetEventsCommand(mpd_Connection * connection)2062 void mpd_sendGetEventsCommand(mpd_Connection *connection) {
2063 mpd_executeCommand(connection, "idle\nnoidle\n");
2064 // mpd_executeCommand(connection, "noidle\n");
2065 }
2066
mpd_getNextEvent(mpd_Connection * connection)2067 char * mpd_getNextEvent(mpd_Connection *connection)
2068 {
2069 return mpd_getNextReturnElementNamed(connection, "changed");
2070 }
2071
mpd_sendListPlaylistsCommand(mpd_Connection * connection)2072 void mpd_sendListPlaylistsCommand(mpd_Connection * connection) {
2073 mpd_sendInfoCommand(connection, "listplaylists\n");
2074 }
2075
mpd_getNextSticker(mpd_Connection * connection)2076 char * mpd_getNextSticker (mpd_Connection * connection)
2077 {
2078 return mpd_getNextReturnElementNamed(connection, "sticker");
2079 }
mpd_sendGetSongSticker(mpd_Connection * connection,const char * song_path,const char * sticker)2080 void mpd_sendGetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker)
2081 {
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);
2091 }
2092
mpd_sendSetSongSticker(mpd_Connection * connection,const char * song_path,const char * sticker,const char * value)2093 void mpd_sendSetSongSticker(mpd_Connection *connection, const char *song_path, const char *sticker, const char *value)
2094 {
2095
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);
2107 }
2108
mpd_sendSetReplayGainMode(mpd_Connection * connection,const char * mode)2109 void mpd_sendSetReplayGainMode(mpd_Connection *connection, const char *mode)
2110 {
2111 char *smode= mpd_sanitizeArg(mode);
2112 int len = strlen("replay_gain_mode ")+strlen(smode)+4;
2113 char *string = malloc(len);
2114 snprintf(string, len, "replay_gain_mode \"%s\"\n", smode);
2115 mpd_sendInfoCommand(connection, string);
2116 free(smode);
2117 free(string);
2118 }
mpd_sendReplayGainModeCommand(mpd_Connection * connection)2119 void mpd_sendReplayGainModeCommand(mpd_Connection *connection)
2120 {
2121 mpd_executeCommand(connection, "replay_gain_status\n");
2122 }
mpd_getReplayGainMode(mpd_Connection * connection)2123 char *mpd_getReplayGainMode(mpd_Connection *connection)
2124 {
2125 return mpd_getNextReturnElementNamed(connection, "replay_gain_mode");
2126 }
2127