1 /* libmpd (high level libmpdclient library)
2  * Copyright (C) 2004-2009 Qball Cow <qball@sarine.nl>
3  * Project homepage: http://gmpcwiki.sarine.nl/
4 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14 
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #define __USE_GNU
23 
24 #include <string.h>
25 #include <stdarg.h>
26 #include <config.h>
27 #include <glib.h>
28 #include "debug_printf.h"
29 
30 #include "libmpd.h"
31 #include "libmpd-internal.h"
32 
33 static void mpd_free_queue_ob(MpdObj *mi);
34 static void mpd_server_free_commands(MpdObj *mi);
35 
36 
37 char *libmpd_version = LIBMPD_VERSION;
38 #ifndef HAVE_STRNDUP
39 /**
40  * Not every platfarm has strndup, so here we have a nice little custom implementation
41  */
strndup(const char * s,size_t n)42 char * strndup(const char *s, size_t n)
43 {
44 	size_t nAvail;
45 	char *p;
46 
47 	if(!s) {
48 		return NULL;
49 	}
50 
51 	/*  nAvail = min( strlen(s)+1, n+1 ); */
52 	nAvail=((strlen(s)+1) > (n+1)) ? n+1 : strlen(s)+1;
53 	if(!(p=malloc(nAvail))) {
54 		return NULL;
55 	}
56 	memcpy(p, s, nAvail);
57 	p[nAvail - 1] = 0;
58 	return p;
59 }
60 #endif
61 /**
62  * @param state a #MpdServerState to initialize
63  *
64  * Initialize #MpdServerState. To stop duplicating code.
65  */
mpd_init_MpdServerState(MpdServerState * state)66 static void mpd_init_MpdServerState(MpdServerState *state)
67 {
68 	state->playlistid 	= -1;
69 	state->storedplaylistid = -1;
70 	state->state 		= -1;
71 	state->songid 		= -1;
72 	state->songpos 		= -1;
73     state->nextsongpos  = -1;
74     state->nextsongid   = -1;
75 	state->dbUpdateTime 	= 0;
76 	state->updatingDb 	= 0;
77 	state->repeat 		= -1;
78 	state->random 		= -1;
79 	state->volume 		= -2;
80 	state->xfade		= -1;
81 	state->totaltime 	= 0;
82 	state->elapsedtime 	= 0;
83 	state->bitrate 		= 0;
84 	state->samplerate 	= 0;
85 	state->channels 	= 0;
86 	state->bits 		= 0;
87     state->consume      = 0;
88     state->single       = 0;
89     state->playlistLength = 0;
90     state->error[0]            = '\0';
91 
92 }
93 
94 
mpd_create()95 static MpdObj * mpd_create()
96 {
97 	MpdObj * mi = g_slice_new0(MpdObj);
98 	g_return_val_if_fail(mi != NULL, NULL); /* should never happen on linux */
99 
100 	/* set default values */
101 	/* we start not connected */
102 	mi->connected = FALSE;
103 	/* port 6600 is the default mpd port */
104 	mi->port = 6600;
105 	/* localhost */
106 	mi->hostname = strdup("localhost");
107 	/* 1 second timeout */
108 	mi->connection_timeout = 1.0;
109 	/* info */
110 	mpd_init_MpdServerState(&(mi->CurrentState));
111 	mpd_init_MpdServerState(&(mi->OldState));
112 
113 	/* connection is locked because where not connected */
114 	mi->connection_lock = TRUE;
115 
116 	/* search stuff */
117 	mi->search_type = MPD_SEARCH_TYPE_NONE;
118 	/* no need to initialize, but set it to anything anyway*/
119 	mi->search_field = MPD_TAG_ITEM_ARTIST;
120 
121 	return mi;
122 }
123 
mpd_free(MpdObj * mi)124 void mpd_free(MpdObj *mi)
125 {
126 	debug_printf(DEBUG_INFO, "destroying MpdObj object\n");
127 	if(mi->connected)
128 	{
129 		/* disconnect */
130 		debug_printf(DEBUG_WARNING, "Connection still running, disconnecting\n");
131 		mpd_disconnect(mi);
132 	}
133 	if(mi->hostname)
134 	{
135 		free(mi->hostname);
136 	}
137 	if(mi->password)
138 	{
139 		free(mi->password);
140 	}
141 	if(mi->error_msg)
142 	{
143 		free(mi->error_msg);
144 	}
145 	if(mi->connection)
146 	{
147 		/* obsolete */
148 		mpd_closeConnection(mi->connection);
149 	}
150 	if(mi->status)
151 	{
152 		mpd_freeStatus(mi->status);
153 	}
154 	if(mi->stats)
155 	{
156 		mpd_freeStats(mi->stats);
157 	}
158 	if(mi->CurrentSong)
159 	{
160 		mpd_freeSong(mi->CurrentSong);
161 	}
162     if(mi->url_handlers)
163     {
164         g_strfreev(mi->url_handlers);
165         mi->url_handlers = NULL;
166     }
167 	mpd_free_queue_ob(mi);
168 	mpd_server_free_commands(mi);
169 	g_slice_free(MpdObj, mi);
170 }
171 
mpd_check_error(MpdObj * mi)172 int mpd_check_error(MpdObj *mi)
173 {
174 	if(mi == NULL)
175 	{
176 		debug_printf(DEBUG_ERROR, "mi == NULL?");
177 		return MPD_ARGS_ERROR;
178 	}
179 
180 	/* this shouldn't happen, ever */
181 	if(mi->connection == NULL)
182 	{
183 		debug_printf(DEBUG_ERROR, "mi->connection == NULL?");
184 		return MPD_FATAL_ERROR;
185 	}
186 
187 	/* TODO: map these errors in the future */
188 	mi->error = mi->connection->error;
189 	mi->error_mpd_code = mi->connection->errorCode;
190 	/*TODO: do I need to strdup this? */
191     if(!g_utf8_validate(mi->connection->errorStr, -1, NULL)){
192         mi->error_msg = g_locale_to_utf8(mi->connection->errorStr, -1, NULL, NULL,NULL);
193     }else{
194         mi->error_msg = g_strdup(mi->connection->errorStr);
195     }
196 
197     if(mi->error_msg == NULL) mi->error_msg = g_strdup("Failed to convert error message to utf-8");
198 
199 	/* Check for permission */
200 	/* First check for an error reported by MPD
201 	 * Then check what type of error mpd reported
202 	 */
203 	if(mi->error == MPD_ERROR_ACK)
204 	{
205 
206 		debug_printf(DEBUG_ERROR,"clearing errors in mpd_Connection: %i-%s", mi->connection->errorCode, mi->connection->errorStr);
207 		mpd_clearError(mi->connection);
208 		if (mi->the_error_callback)
209 		{
210             debug_printf(DEBUG_ERROR, "Error callback 1 (ACK)");
211 			if(mi->the_error_callback(mi, mi->error_mpd_code, mi->error_msg, mi->the_error_signal_userdata ))
212             {
213                 debug_printf(DEBUG_ERROR, "Error callback told me to disconnect");
214                 mpd_disconnect(mi);
215                 free(mi->error_msg);
216                 mi->error_msg = NULL;
217 
218                 return MPD_SERVER_ERROR;
219             }
220 		}
221 		free(mi->error_msg);
222 		mi->error_msg = NULL;
223 		return TRUE;
224 	}
225 	if(mi->error)
226 	{
227 
228 		debug_printf(DEBUG_ERROR, "Following error occurred: %i: code: %i msg: %s", mi->error,mi->connection->errorCode, mi->error_msg);
229 
230 		if (mi->the_error_callback)
231 		{
232             debug_printf(DEBUG_ERROR, "Error callback 2");
233 			mi->the_error_callback(mi, mi->error, mi->error_msg, mi->the_error_signal_userdata );
234 		}
235 		mpd_disconnect(mi);
236 		free(mi->error_msg);
237 		mi->error_msg = NULL;
238 
239 		return MPD_SERVER_ERROR;
240 	}
241 	free(mi->error_msg);
242 	mi->error_msg = NULL;
243 	return MPD_OK;
244 }
245 
246 
247 
mpd_lock_conn(MpdObj * mi)248 int mpd_lock_conn(MpdObj *mi)
249 {
250 
251 	if(mi->connection_lock)
252 	{
253 		debug_printf(DEBUG_WARNING, "Failed to lock connection, already locked\n");
254 		return MPD_LOCK_FAILED;
255 	}
256 	mi->connection_lock = TRUE;
257 	return MPD_OK;
258 }
259 
mpd_unlock_conn(MpdObj * mi)260 int mpd_unlock_conn(MpdObj *mi)
261 {
262 	if(!mi->connection_lock)
263 	{
264 		debug_printf(DEBUG_ERROR, "Failed to unlock connection, already unlocked\n");
265 		return MPD_LOCK_FAILED;
266 	}
267 
268 	mi->connection_lock = FALSE;
269 
270 	return mpd_check_error(mi);
271 }
272 
mpd_new_default()273 MpdObj * mpd_new_default()
274 {
275 	debug_printf(DEBUG_INFO, "creating a new mpdInt object\n");
276 	return mpd_create();
277 }
278 
mpd_new(char * hostname,int port,char * password)279 MpdObj *mpd_new(char *hostname,  int port, char *password)
280 {
281 	MpdObj *mi = mpd_create();
282 	if(mi == NULL)
283 	{
284 		return NULL;
285 	}
286 	if(hostname != NULL)
287 	{
288 		mpd_set_hostname(mi, hostname);
289 	}
290 	if(port != 0)
291 	{
292 		mpd_set_port(mi, port);
293 	}
294 	if(password != NULL)
295 	{
296 		mpd_set_password(mi, password);
297 	}
298 	return mi;
299 }
300 
301 
mpd_get_hostname(MpdObj * mi)302 const char * mpd_get_hostname(MpdObj *mi)
303 {
304 	if(mi == NULL)
305 	{
306 		return NULL;
307 	}
308 	return mi->hostname;
309 }
310 
mpd_set_hostname(MpdObj * mi,char * hostname)311 int mpd_set_hostname(MpdObj *mi, char *hostname)
312 {
313 	if(mi == NULL)
314 	{
315 		debug_printf(DEBUG_ERROR, "mi == NULL\n");
316 		return MPD_ARGS_ERROR;
317 	}
318 
319 	if(mi->hostname != NULL)
320 	{
321 		free(mi->hostname);
322 	}
323 	/* possible location todo some post processing of hostname */
324 	mi->hostname = strdup(hostname);
325 	return MPD_OK;
326 }
327 
mpd_set_password(MpdObj * mi,const char * password)328 int mpd_set_password(MpdObj *mi, const char *password)
329 {
330 	if(mi == NULL)
331 	{
332 		debug_printf(DEBUG_ERROR, "mi == NULL\n");
333 		return MPD_ARGS_ERROR;
334 	}
335 
336 	if(mi->password != NULL)
337 	{
338 		free(mi->password);
339 	}
340 	/* possible location todo some post processing of password */
341 	mi->password = strdup(password);
342 	return MPD_OK;
343 }
344 
345 
mpd_send_password(MpdObj * mi)346 int mpd_send_password(MpdObj *mi)
347 {
348 	if(!mi) return MPD_ARGS_ERROR;
349 	if(mi->password && mpd_check_connected(mi) && strlen(mi->password))
350 	{
351 		if(mpd_lock_conn(mi))
352 		{
353 			debug_printf(DEBUG_WARNING, "failed to lock connection");
354 			return MPD_LOCK_FAILED;
355 		}
356 		mpd_sendPasswordCommand(mi->connection, mi->password);
357 		mpd_finishCommand(mi->connection);
358 		if(mpd_unlock_conn(mi))
359 		{
360 			debug_printf(DEBUG_ERROR, "Failed to unlock connection\n");
361 			return MPD_LOCK_FAILED;
362 		}
363 		mpd_server_get_allowed_commands(mi);
364 		/*TODO: should I do it here, or in the
365 		 * mpd_server_get_allowed_command, so it also get's executed on
366 		 * connect
367 		 */
368 		if((mi->the_status_changed_callback != NULL))
369         {
370             /** update the supported tags */
371             {
372                 int i;
373                 char **retv = mpd_server_get_tag_types(mi);
374                 if(retv){
375                     for(i=0;i<MPD_TAG_ITEM_ANY;i++)
376                     {
377                         int j=0;
378                         for(j=0;retv[j] && strcasecmp(retv[j],mpdTagItemKeys[i]); j++);
379                         if(retv[j]) mi->supported_tags[i] = TRUE;
380                         else mi->supported_tags[i] = FALSE;
381                     }
382                     g_strfreev(retv);
383                 }
384                 /* also always true */
385                 mi->supported_tags[MPD_TAG_ITEM_FILENAME] = TRUE;
386                 mi->supported_tags[MPD_TAG_ITEM_ANY] = TRUE;
387             }
388             /* If permission updates, we should also call an output update, The data might be available now. */
389             mi->the_status_changed_callback( mi,
390                     MPD_CST_PERMISSION|MPD_CST_OUTPUT, mi->the_status_changed_signal_userdata );
391         }
392 	}
393 	return MPD_OK;
394 }
395 
mpd_set_port(MpdObj * mi,int port)396 int mpd_set_port(MpdObj *mi, int port)
397 {
398 	if(mi == NULL)
399 	{
400 		debug_printf(DEBUG_ERROR, "mi == NULL\n");
401 		return MPD_ARGS_ERROR;
402 	}
403 	mi->port = port;
404 	return MPD_OK;
405 }
406 
mpd_set_connection_timeout(MpdObj * mi,float timeout)407 int mpd_set_connection_timeout(MpdObj *mi, float timeout)
408 {
409 	if(mi == NULL)
410 	{
411 		debug_printf(DEBUG_ERROR, "mi == NULL\n");
412 		return MPD_ARGS_ERROR;
413 	}
414 	mi->connection_timeout = timeout;
415 	if(mpd_check_connected(mi))
416 	{
417 		/*TODO: set timeout */
418 		if(mpd_lock_conn(mi))
419 		{
420 			debug_printf(DEBUG_ERROR,"lock failed\n");
421 			return MPD_LOCK_FAILED;
422 		}
423 		mpd_setConnectionTimeout(mi->connection, timeout);
424 		mpd_finishCommand(mi->connection);
425 
426 		mpd_unlock_conn(mi);
427 
428 	}
429 	return MPD_OK;
430 }
431 
mpd_server_free_commands(MpdObj * mi)432 static void mpd_server_free_commands(MpdObj *mi)
433 {
434 	if(mi->commands)
435 	{
436 		int i=0;
437 		while(mi->commands[i].command_name)
438 		{
439 			free(mi->commands[i].command_name);
440 			i++;
441 		}
442 		free(mi->commands);
443 		mi->commands = NULL;
444 	}
445 }
446 
mpd_server_get_version(MpdObj * mi)447 char *mpd_server_get_version(MpdObj *mi)
448 {
449 	char *retval = NULL;
450 	if(!mi || !mpd_check_connected(mi))
451 		return NULL;
452 	retval = malloc(10*sizeof(char));
453 	snprintf(retval,10,"%i.%i.%i", mi->connection->version[0], mi->connection->version[1], mi->connection->version[2]);
454 	/* always make sure the string is terminated */
455 	retval[9] = '\0';
456 	return retval;
457 }
458 
mpd_server_get_allowed_commands(MpdObj * mi)459 int mpd_server_get_allowed_commands(MpdObj *mi)
460 {
461 	char *temp = NULL;
462 	int num_commands = 0;
463 	if(!mi){
464 		debug_printf(DEBUG_ERROR, "mi != NULL failed\n");
465 	       	return MPD_ARGS_ERROR;
466 	}
467 	if(!mpd_check_connected(mi)) {
468 		debug_printf(DEBUG_WARNING, "Not Connected");
469 		return MPD_NOT_CONNECTED;
470 	}
471 	if(!mpd_server_check_version(mi,0,12,0)){
472 		debug_printf(DEBUG_INFO, "Not supported by mpd");
473 	       	return MPD_SERVER_NOT_SUPPORTED;
474 	}
475 
476 	mpd_server_free_commands(mi);
477 
478 	if(mpd_lock_conn(mi))
479 	{
480 		debug_printf(DEBUG_ERROR, "lock failed");
481 		return MPD_LOCK_FAILED;
482 	}
483 	mpd_sendCommandsCommand(mi->connection);
484 	while((temp = mpd_getNextCommand(mi->connection)))
485 	{
486 		num_commands++;
487 		mi->commands = realloc(mi->commands, (num_commands+1)*sizeof(MpdCommand));
488 		mi->commands[num_commands-1].command_name = temp;
489 		mi->commands[num_commands-1].enabled = TRUE;
490 		mi->commands[num_commands].command_name = NULL;
491 		mi->commands[num_commands].enabled = FALSE;
492         if(strcmp(mi->commands[num_commands-1].command_name, "idle") == 0) {
493             mi->has_idle = TRUE;
494         }
495 	}
496 	mpd_finishCommand(mi->connection);
497 	mpd_sendNotCommandsCommand(mi->connection);
498 	while((temp = mpd_getNextCommand(mi->connection)))
499 	{
500 		num_commands++;
501 		mi->commands = realloc(mi->commands, (num_commands+1)*sizeof(MpdCommand));
502 		mi->commands[num_commands-1].command_name = temp;
503 		mi->commands[num_commands-1].enabled = FALSE;
504 		mi->commands[num_commands].command_name = NULL;
505 		mi->commands[num_commands].enabled = FALSE;
506 	}
507 	mpd_finishCommand(mi->connection);
508 
509 	if(mpd_unlock_conn(mi))
510     {
511         return MPD_LOCK_FAILED;
512     }
513 	return MPD_OK;
514 }
515 
516 
517 
mpd_disconnect(MpdObj * mi)518 int mpd_disconnect(MpdObj *mi)
519 {
520 
521 	/* lock */
522 	mpd_lock_conn(mi);
523 	debug_printf(DEBUG_INFO, "disconnecting\n");
524 
525 	if(mi->connection)
526 	{
527 		mpd_closeConnection(mi->connection);
528 		mi->connection = NULL;
529 	}
530 	if(mi->status)
531 	{
532 		mpd_freeStatus(mi->status);
533 		mi->status = NULL;
534 	}
535 	if(mi->stats)
536 	{
537 		mpd_freeStats(mi->stats);
538 		mi->stats = NULL;
539 	}
540 	if(mi->CurrentSong)
541 	{
542 		mpd_freeSong(mi->CurrentSong);
543 		mi->CurrentSong = NULL;
544 	}
545     if(mi->url_handlers)
546     {
547         g_strfreev(mi->url_handlers);
548         mi->url_handlers = NULL;
549     }
550 	mi->CurrentState.playlistid = -1;
551 	mi->CurrentState.storedplaylistid = -1;
552 	mi->CurrentState.state = -1;
553 	mi->CurrentState.songid = -1;
554 	mi->CurrentState.songpos = -1;
555     mi->CurrentState.nextsongid = -1;
556     mi->CurrentState.nextsongpos = -1;
557 	mi->CurrentState.dbUpdateTime = 0;
558 	mi->CurrentState.updatingDb = 0;
559 	mi->CurrentState.repeat = -1;
560 	mi->CurrentState.random = -1;
561 	mi->CurrentState.volume = -2;
562 	mi->CurrentState.xfade	= -1;
563 	mi->CurrentState.totaltime = 0;
564 	mi->CurrentState.elapsedtime = 0;
565 	mi->CurrentState.bitrate = 0;
566 	mi->CurrentState.samplerate = 0;
567 	mi->CurrentState.channels = 0;
568 	mi->CurrentState.bits = 0;
569     mi->CurrentState.playlistLength = 0;
570     mi->CurrentState.error[0] = '\0';
571 	/* search stuff */
572 	mi->search_type = MPD_SEARCH_TYPE_NONE;
573 	/* no need to initialize, but set it to anything anyway*/
574 	mi->search_field = MPD_TAG_ITEM_ARTIST;
575     /* outputs */
576     mi->num_outputs = 0;
577     if(mi->output_states)
578         g_free(mi->output_states);
579     mi->output_states = NULL;
580 
581     /* set 0 */
582     memset((mi->supported_tags), 0,sizeof(mi->supported_tags));
583 
584     mi->has_idle = 0;
585 
586 	memcpy(&(mi->OldState), &(mi->CurrentState) , sizeof(MpdServerState));
587 
588 	mpd_free_queue_ob(mi);
589 	mpd_server_free_commands(mi);
590 	/*don't reset errors */
591 	/* Remove this signal, we don't actually disconnect */
592 	if(mi->connected)
593 	{
594 		/* set disconnect flag */
595 		mi->connected = FALSE;
596 
597 		if(mi->the_connection_changed_callback != NULL)
598 		{
599 			mi->the_connection_changed_callback( mi, FALSE, mi->the_connection_changed_signal_userdata );
600 		}
601 	}
602 	debug_printf(DEBUG_INFO, "Disconnect completed\n");
603 	return MPD_OK;
604 }
mpd_connect(MpdObj * mi)605 int mpd_connect(MpdObj *mi)
606 {
607 	return mpd_connect_real(mi,NULL);
608 }
mpd_connect_real(MpdObj * mi,mpd_Connection * connection)609 int mpd_connect_real(MpdObj *mi,mpd_Connection *connection)
610 {
611     int retv;
612     if(mi == NULL)
613 	{
614 		/* should return some spiffy error here */
615 		debug_printf(DEBUG_ERROR, "mi != NULL failed");
616 		return MPD_ARGS_ERROR;
617 	}
618 	/* reset errors */
619 	mi->error = 0;
620 	mi->error_mpd_code = 0;
621 	if(mi->error_msg != NULL)
622 	{
623 		free(mi->error_msg);
624 	}
625 	mi->error_msg = NULL;
626 
627 	debug_printf(DEBUG_INFO, "connecting\n");
628 	mpd_init_MpdServerState(&(mi->CurrentState));
629 
630 	memcpy(&(mi->OldState), &(mi->CurrentState), sizeof(MpdServerState));
631 
632 	if(mi->connected)
633 	{
634 		/* disconnect */
635 		mpd_disconnect(mi);
636 	}
637 
638 	if(mi->hostname == NULL)
639 	{
640 		mpd_set_hostname(mi, "localhost");
641 	}
642 	/* make sure this is locked */
643 	if(!mi->connection_lock)
644 	{
645 		mpd_lock_conn(mi);
646 	}
647 	if(connection) {
648 		mi->connection = connection;
649 	} else {
650 		/* make timeout configurable */
651 		mi->connection = mpd_newConnection(mi->hostname,mi->port,mi->connection_timeout);
652 	}
653 	if(mi->connection == NULL)
654 	{
655 		/* TODO: make seperate error message? */
656 		return MPD_NOT_CONNECTED;
657 	}
658 	if(mpd_check_error(mi) != MPD_OK)
659 	{
660 		/* TODO: make seperate error message? */
661 		return MPD_NOT_CONNECTED;
662 	}
663 
664 	/* set connected state */
665 	mi->connected = TRUE;
666 	if(mpd_unlock_conn(mi))
667 	{
668 		return MPD_LOCK_FAILED;
669 	}
670 
671 	/* get the commands we are allowed to use */
672 	retv = mpd_server_get_allowed_commands(mi);
673     if(retv!= MPD_OK)
674     {
675         return retv;
676     }
677     /* Trying to send password, this is needed to get right outputs and tag_types */
678     if(mi->password && strlen(mi->password) > 0)
679     {
680         mpd_send_password(mi);
681     }
682     else
683     {
684         /* Update tag types, this is done in send_password.
685          * mpd_send_password() does this.
686          * So only do it when no password is send.
687          */
688         int i;
689         char **retv = mpd_server_get_tag_types(mi);
690         if(retv){
691             for(i=0;i<MPD_TAG_ITEM_ANY;i++)
692             {
693                 int j=0;
694                 for(j=0;retv[j] && strcasecmp(retv[j],mpdTagItemKeys[i]); j++);
695                 if(retv[j]) mi->supported_tags[i] = TRUE;
696                 else mi->supported_tags[i] = FALSE;
697             }
698             g_strfreev(retv);
699         }
700         /* also always true */
701         mi->supported_tags[MPD_TAG_ITEM_FILENAME] = TRUE;
702         mi->supported_tags[MPD_TAG_ITEM_ANY] = TRUE;
703     }
704 /*
705 
706     retv = mpd_server_update_outputs(mi);
707     if(retv != MPD_OK)
708         return retv;
709 */
710 
711     retv = mpd_server_update_outputs(mi);
712     /** update the supported tags */
713     debug_printf(DEBUG_INFO,  "Propagating connection changed");
714 
715     if(mi->the_connection_changed_callback != NULL)
716 	{
717 		mi->the_connection_changed_callback( mi, TRUE, mi->the_connection_changed_signal_userdata );
718 	}
719     /*
720     if(retv != MPD_OK)
721         return retv;
722         */
723 	debug_printf(DEBUG_INFO, "Connected to mpd");
724 	return MPD_OK;
725 }
726 
mpd_check_connected(MpdObj * mi)727 int mpd_check_connected(MpdObj *mi)
728 {
729 	if(mi == NULL)
730 	{
731 		return FALSE;
732 	}
733 	return mi->connected;
734 }
735 
736 
737 /* SIGNALS */
mpd_signal_connect_status_changed(MpdObj * mi,StatusChangedCallback status_changed,void * userdata)738 void	mpd_signal_connect_status_changed        (MpdObj *mi, StatusChangedCallback status_changed, void *userdata)
739 {
740 	if(mi == NULL)
741 	{
742 		debug_printf(DEBUG_ERROR, "mi != NULL failed");
743 		return;
744 	}
745 	mi->the_status_changed_callback = status_changed;
746 	mi->the_status_changed_signal_userdata = userdata;
747 }
748 
749 
mpd_signal_connect_error(MpdObj * mi,ErrorCallback error_callback,void * userdata)750 void	mpd_signal_connect_error(MpdObj *mi, ErrorCallback error_callback, void *userdata)
751 {
752 	if(mi == NULL)
753 	{
754 		debug_printf(DEBUG_ERROR, "mi != NULL failed");
755 		return;
756 	}
757 	mi->the_error_callback = error_callback;
758 	mi->the_error_signal_userdata = userdata;
759 }
760 
mpd_signal_connect_connection_changed(MpdObj * mi,ConnectionChangedCallback connection_changed,void * userdata)761 void	mpd_signal_connect_connection_changed(MpdObj *mi, ConnectionChangedCallback connection_changed, void *userdata)
762 {
763 	if(mi == NULL)
764 	{
765 		debug_printf(DEBUG_ERROR, "mi != NULL failed");
766 		return;
767 	}
768 	mi->the_connection_changed_callback = connection_changed;
769 	mi->the_connection_changed_signal_userdata = userdata;
770 }
771 
772 
773 /* more playlist */
774 /* MpdData Part */
mpd_new_data_struct(void)775 MpdData *mpd_new_data_struct(void)
776 {
777 	return (MpdData*) g_slice_new0(MpdData_real);
778 }
779 
mpd_new_data_struct_append(MpdData * data)780 MpdData *mpd_new_data_struct_append(MpdData  * data)
781 {
782 	MpdData_real *data_real = (MpdData_real*)data;
783 	if(data_real == NULL)
784 	{
785 		data_real = (MpdData_real*)mpd_new_data_struct();
786         data_real->first = data_real;
787 	}
788 	else
789 	{
790 		data_real->next = (MpdData_real*)mpd_new_data_struct();
791 		data_real->next->prev = data_real;
792 		data_real = data_real->next;
793 		data_real->next = NULL;
794         data_real->first = data_real->prev->first;
795 	}
796 	return (MpdData*)data_real;
797 }
798 
mpd_data_get_first(MpdData const * const data)799 MpdData * mpd_data_get_first(MpdData const * const data)
800 {
801 	MpdData_real const * const data_real = (MpdData_real const * const)data;
802 	if(data_real != NULL)
803 	{
804         return (MpdData*)data_real->first;
805     }
806 	return NULL;
807 }
808 
809 
mpd_data_get_next(MpdData * const data)810 MpdData * mpd_data_get_next(MpdData * const data)
811 {
812 	return mpd_data_get_next_real(data, TRUE);
813 }
814 
mpd_data_get_next_real(MpdData * const data,int kill_list)815 MpdData * mpd_data_get_next_real(MpdData * const data, int kill_list)
816 {
817 	MpdData_real *data_real = (MpdData_real*)data;
818 	if (data_real != NULL)
819 	{
820 		if (data_real->next != NULL )
821 		{
822 			return (MpdData*)data_real->next;
823 		}
824 		else
825 		{
826 			if (kill_list) mpd_data_free((MpdData*)data_real);
827 			return NULL;
828 		}
829 	}
830 	return (MpdData*)data_real;
831 }
832 
mpd_data_is_last(MpdData const * const data)833 int mpd_data_is_last(MpdData const * const data)
834 {
835 	MpdData_real const * const data_real = (MpdData_real const * const)data;
836 	if(data_real != NULL)
837 	{
838 		if (data_real->next == NULL)
839 		{
840 			return TRUE;
841 		}
842 	}
843 	return FALSE;
844 }
845 /*
846 MpdData_head *mpd_data_get_head(MpdData const * const data) {
847 	return ((MpdData_real*)data)->head;
848 }
849 */
mpd_data_concatenate(MpdData * const first,MpdData * const second)850 MpdData* mpd_data_concatenate( MpdData  * const first, MpdData  * const second)
851 {
852 	MpdData_real *first_real  = (MpdData_real*)first;
853 	MpdData_real *second_real = (MpdData_real*)second;
854 	MpdData_real *first_head  = NULL;
855 
856 	if ( first == NULL ) {
857 		if ( second != NULL )
858 			return (MpdData*)second_real;
859 		else
860 			return NULL;
861 	} else {
862 		if ( second == NULL )
863 			return (MpdData*)first_real;
864 	}
865 
866 	first_head = (MpdData_real *)mpd_data_get_first(first);
867 
868 	/* find last element in first data list */
869 	while (!mpd_data_is_last((MpdData*)first_real)) first_real = (MpdData_real*)mpd_data_get_next_real((MpdData*)first_real, FALSE);
870 	second_real =(MpdData_real*) mpd_data_get_first((MpdData*)second_real);
871 
872 	first_real->next = second_real;
873 	second_real->prev = first_real;
874 
875 	/* I need to set all the -> first correct */
876 	while (second_real)
877 	{
878 		second_real->first = first_head;
879 		second_real = (MpdData_real*)mpd_data_get_next_real((MpdData*)second_real, FALSE);
880 	}
881 
882 	return (MpdData*)first_head;
883 }
884 /**
885  * Deletes an item from the list. It returns the next item in the list.
886  * if that is not available, it will return the last item
887  */
mpd_data_delete_item(MpdData * data)888 MpdData * mpd_data_delete_item(MpdData *data)
889 {
890     MpdData_real *temp = NULL, *data_real = (MpdData_real*)data;
891     if(data_real == NULL) return NULL;
892     /* if there is a next item, fix the prev pointer of the next item */
893     if (data_real->next)
894     {
895         data_real->next->prev = data_real->prev;
896         temp = data_real->next;
897     }
898     /* if there is a previous item, fix the next pointer of the previous item */
899     if (data_real->prev)
900     {
901         /* the next item of the previous is the next item of the current */
902         data_real->prev->next = data_real->next;
903         /* temp is the previous item */
904         temp = data_real->prev;
905     }
906 
907     /* fix first,  if removed item is the first */
908     if(temp && temp->first == data_real)
909     {
910         MpdData_real *first,*node = temp;
911         /* get first */
912         for(;node->prev;node = node->prev);
913         first = node;
914         while(node){
915             node->first = first;
916             node = node->next;
917         }
918     }
919     /* make the removed row a valid list, so I can use the default free function to free it */
920     data_real->next = NULL;
921     data_real->prev = NULL;
922     data_real->first = data_real;
923     /* free it */
924     mpd_data_free((MpdData *)data_real);
925 
926     return (MpdData *)temp;
927 }
928 
mpd_data_free(MpdData * data)929 void mpd_data_free(MpdData *data)
930 {
931     MpdData_real *data_real,*temp;
932     if(data == NULL)
933     {
934         debug_printf(DEBUG_ERROR, "data != NULL Failed");
935         return;
936     }
937     data_real = (MpdData_real *)mpd_data_get_first(data);
938     while(data_real){
939         temp = data_real;
940         if (data_real->type == MPD_DATA_TYPE_SONG) {
941             if(data_real->song) mpd_freeSong(data_real->song);
942         } else if (data_real->type == MPD_DATA_TYPE_OUTPUT_DEV) {
943             mpd_freeOutputElement(data_real->output_dev);
944         } else if(data_real->type == MPD_DATA_TYPE_DIRECTORY) {
945             if(data_real->directory)free(data_real->directory);
946         } else if(data_real->type == MPD_DATA_TYPE_PLAYLIST) {
947             if(data_real->playlist) mpd_freePlaylistFile(data_real->playlist);
948         } else {
949             free((void*)(data_real->tag));
950         }
951         if(data_real->freefunc)
952         {
953             if(data_real->userdata)
954                 data_real->freefunc(data_real->userdata);
955         }
956         data_real = data_real->next;
957         g_slice_free(MpdData_real, temp);
958     }
959 }
960 
961 /* clean this up.. make one while loop */
mpd_free_queue_ob(MpdObj * mi)962 static void mpd_free_queue_ob(MpdObj *mi)
963 {
964     MpdQueue *temp = NULL;
965     if(mi == NULL)
966     {
967         debug_printf(DEBUG_ERROR, "mi != NULL failed");
968         return;
969     }
970     if(mi->queue == NULL)
971     {
972         debug_printf(DEBUG_INFO, "mi->queue != NULL failed, nothing to clean.");
973         return;
974     }
975     mi->queue = mi->queue->first;
976     while(mi->queue != NULL)
977     {
978         temp = mi->queue->next;
979 
980         if(mi->queue->path != NULL)
981         {
982             free(mi->queue->path);
983         }
984 
985         g_slice_free(MpdQueue, mi->queue);
986         mi->queue = temp;
987     }
988     mi->queue = NULL;
989 
990 }
991 
mpd_new_queue_struct()992 MpdQueue *mpd_new_queue_struct()
993 {
994     return g_slice_new0(MpdQueue);
995 }
996 
997 
mpd_queue_get_next(MpdObj * mi)998 void mpd_queue_get_next(MpdObj *mi)
999 {
1000     if(mi->queue != NULL && mi->queue->next != NULL)
1001     {
1002         mi->queue = mi->queue->next;
1003     }
1004     else if(mi->queue->next == NULL)
1005     {
1006         mpd_free_queue_ob(mi);
1007         mi->queue = NULL;
1008     }
1009 }
1010 
mpd_server_get_database_update_time(MpdObj * mi)1011 long unsigned mpd_server_get_database_update_time(MpdObj *mi)
1012 {
1013     if(!mpd_check_connected(mi))
1014     {
1015         debug_printf(DEBUG_WARNING,"not connected\n");
1016         return MPD_NOT_CONNECTED;
1017     }
1018     if(mpd_stats_check(mi) != MPD_OK)
1019     {
1020         debug_printf(DEBUG_WARNING,"Failed grabbing status\n");
1021         return MPD_STATS_FAILED;
1022     }
1023     return mi->stats->dbUpdateTime;
1024 }
1025 
1026 
mpd_server_get_output_devices(MpdObj * mi)1027 MpdData * mpd_server_get_output_devices(MpdObj *mi)
1028 {
1029     mpd_OutputEntity *output = NULL;
1030     MpdData *data = NULL;
1031     if(!mpd_check_connected(mi))
1032     {
1033         debug_printf(DEBUG_WARNING,"not connected\n");
1034         return NULL;
1035     }
1036     /* TODO: Check version */
1037     if(mpd_lock_conn(mi))
1038     {
1039         debug_printf(DEBUG_ERROR,"lock failed\n");
1040         return NULL;
1041     }
1042 
1043     mpd_sendOutputsCommand(mi->connection);
1044     while (( output = mpd_getNextOutput(mi->connection)) != NULL)
1045     {
1046         data = mpd_new_data_struct_append(data);
1047         data->type = MPD_DATA_TYPE_OUTPUT_DEV;
1048         data->output_dev = output;
1049     }
1050     mpd_finishCommand(mi->connection);
1051 
1052     /* unlock */
1053     if(mpd_unlock_conn(mi) != MPD_OK)
1054     {
1055         if(data)mpd_data_free(data);
1056         return NULL;
1057     }
1058     if(data == NULL)
1059     {
1060         return NULL;
1061     }
1062     return mpd_data_get_first(data);
1063 }
1064 
mpd_server_set_output_device(MpdObj * mi,int device_id,int state)1065 int mpd_server_set_output_device(MpdObj *mi,int device_id,int state)
1066 {
1067     if(!mpd_check_connected(mi))
1068     {
1069         debug_printf(DEBUG_WARNING,"not connected\n");
1070         return MPD_NOT_CONNECTED;
1071     }
1072     if(mpd_lock_conn(mi))
1073     {
1074         debug_printf(DEBUG_ERROR,"lock failed\n");
1075         return MPD_LOCK_FAILED;
1076     }
1077     if(state)
1078     {
1079         mpd_sendEnableOutputCommand(mi->connection, device_id);
1080     }
1081     else
1082     {
1083         mpd_sendDisableOutputCommand(mi->connection, device_id);
1084     }
1085     mpd_finishCommand(mi->connection);
1086 
1087     mpd_unlock_conn(mi);
1088     mpd_status_queue_update(mi);
1089     return FALSE;
1090 }
1091 
mpd_server_check_version(MpdObj * mi,int major,int minor,int micro)1092 int mpd_server_check_version(MpdObj *mi, int major, int minor, int micro)
1093 {
1094     if(!mpd_check_connected(mi))
1095     {
1096         debug_printf(DEBUG_WARNING,"not connected\n");
1097         return FALSE;
1098     }
1099     if(major > mi->connection->version[0]) return FALSE;
1100     if(mi->connection->version[0] > major) return TRUE;
1101     if(minor > mi->connection->version[1]) return FALSE;
1102     if(mi->connection->version[1] > minor) return TRUE;
1103     if(micro > mi->connection->version[2]) return FALSE;
1104     if(mi->connection->version[2] > micro) return TRUE;
1105     return TRUE;
1106 }
1107 
mpd_server_check_command_allowed(MpdObj * mi,const char * command)1108 int mpd_server_check_command_allowed(MpdObj *mi, const char *command)
1109 {
1110     int i;
1111     if(!mi || !command) return MPD_SERVER_COMMAND_ERROR;
1112     /* when we are connected to a mpd server that doesn't support commands and not commands
1113      * feature. (like mpd 0.11.5) allow everything
1114      */
1115     if(!mpd_server_check_version(mi, 0,12,0)) return MPD_SERVER_COMMAND_ALLOWED;
1116     /*
1117      * Also when somehow we failted to get commands
1118      */
1119     if(mi->commands == NULL) return MPD_SERVER_COMMAND_ALLOWED;
1120 
1121 
1122 
1123     for(i=0;mi->commands[i].command_name;i++)
1124     {
1125         if(!strcasecmp(mi->commands[i].command_name, command))
1126             return mi->commands[i].enabled;
1127     }
1128     return MPD_SERVER_COMMAND_NOT_SUPPORTED;
1129 }
1130 
mpd_server_get_url_handlers(MpdObj * mi)1131 char ** mpd_server_get_url_handlers(MpdObj *mi)
1132 {
1133     char *temp = NULL;
1134     int i=0;
1135     if(!mpd_check_connected(mi))
1136     {
1137         debug_printf(DEBUG_WARNING,"not connected\n");
1138         return FALSE;
1139     }
1140     if(mi->url_handlers){
1141         return g_strdupv(mi->url_handlers);
1142     }
1143     if(mpd_lock_conn(mi))
1144     {
1145         debug_printf(DEBUG_ERROR,"lock failed\n");
1146         return NULL;
1147     }
1148     /**
1149      * Fetch url handlers and store them
1150      */
1151     mpd_sendUrlHandlersCommand(mi->connection);
1152     while((temp = mpd_getNextHandler(mi->connection)) != NULL)
1153     {
1154         mi->url_handlers = realloc(mi->url_handlers,(i+2)*sizeof(*mi->url_handlers));
1155         mi->url_handlers[i]   = temp;
1156         mi->url_handlers[i+1] = NULL;
1157         i++;
1158     }
1159     mpd_finishCommand(mi->connection);
1160 
1161 
1162     mpd_unlock_conn(mi);
1163     /* Return copy */
1164     return g_strdupv(mi->url_handlers);
1165 }
mpd_server_get_tag_types(MpdObj * mi)1166 char ** mpd_server_get_tag_types(MpdObj *mi)
1167 {
1168     char *temp = NULL;
1169     int i=0;
1170     char **retv = NULL;
1171     if(!mpd_check_connected(mi))
1172     {
1173         debug_printf(DEBUG_WARNING,"not connected\n");
1174         return FALSE;
1175     }
1176     if(mpd_lock_conn(mi))
1177     {
1178         debug_printf(DEBUG_ERROR,"lock failed\n");
1179         return NULL;
1180     }
1181     mpd_sendTagTypesCommand(mi->connection);
1182     while((temp = mpd_getNextTagType(mi->connection)) != NULL)
1183     {
1184         retv = realloc(retv,(i+2)*sizeof(*retv));
1185         retv[i]   = temp;
1186         retv[i+1] = NULL;
1187         i++;
1188     }
1189     mpd_finishCommand(mi->connection);
1190 
1191 
1192     mpd_unlock_conn(mi);
1193     return retv;
1194 }
1195 
mpd_misc_get_tag_by_name(char * name)1196 int mpd_misc_get_tag_by_name(char *name)
1197 {
1198     int i;
1199     if(name == NULL)
1200     {
1201         return MPD_ARGS_ERROR;
1202     }
1203     for(i=0; i < MPD_TAG_NUM_OF_ITEM_TYPES; i++)
1204     {
1205         if(!strcasecmp(mpdTagItemKeys[i], name))
1206         {
1207             return i;
1208         }
1209     }
1210     return MPD_TAG_NOT_FOUND;
1211 }
1212 
mpd_server_update_outputs(MpdObj * mi)1213 int mpd_server_update_outputs(MpdObj *mi)
1214 {
1215     mpd_OutputEntity *output = NULL;
1216     if(!mpd_check_connected(mi))
1217     {
1218         debug_printf(DEBUG_WARNING,"not connected\n");
1219         return MPD_NOT_CONNECTED;
1220     }
1221     if(mpd_lock_conn(mi))
1222     {
1223         debug_printf(DEBUG_ERROR,"lock failed\n");
1224         return MPD_LOCK_FAILED;
1225     }
1226     mpd_sendOutputsCommand(mi->connection);
1227     while (( output = mpd_getNextOutput(mi->connection)) != NULL)
1228     {
1229         mi->num_outputs++;
1230         mi->output_states = realloc(mi->output_states,mi->num_outputs*sizeof(int));
1231         mi->output_states[mi->num_outputs-1] = FALSE;/*output->enabled;*/
1232         mpd_freeOutputElement(output);
1233     }
1234     mpd_finishCommand(mi->connection);
1235     return mpd_unlock_conn(mi);
1236 }
1237 
mpd_server_has_idle(MpdObj * mi)1238 int mpd_server_has_idle(MpdObj *mi)
1239 {
1240     return mi->has_idle;
1241 }
1242 
mpd_server_tag_supported(MpdObj * mi,int tag)1243 int mpd_server_tag_supported(MpdObj *mi, int tag)
1244 {
1245     if(!mi) return FALSE;
1246     if(tag < 0 || tag >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1247         return FALSE;
1248     }
1249     return mi->supported_tags[tag];
1250 }
1251 
mpd_server_set_replaygain_mode(MpdObj * mi,MpdServerReplaygainMode mode)1252 int mpd_server_set_replaygain_mode(MpdObj *mi, MpdServerReplaygainMode mode)
1253 {
1254     if(!mpd_check_connected(mi))
1255     {
1256         debug_printf(DEBUG_WARNING,"not connected\n");
1257         return MPD_NOT_CONNECTED;
1258     }
1259     if(mpd_lock_conn(mi))
1260     {
1261         debug_printf(DEBUG_ERROR,"lock failed\n");
1262         return MPD_LOCK_FAILED;
1263     }
1264     switch(mode){
1265         case MPD_SERVER_REPLAYGAIN_MODE_AUTO:
1266             mpd_sendSetReplayGainMode(mi->connection, "auto");
1267             break;
1268         case MPD_SERVER_REPLAYGAIN_MODE_TRACK:
1269             mpd_sendSetReplayGainMode(mi->connection, "track");
1270             break;
1271         case MPD_SERVER_REPLAYGAIN_MODE_ALBUM:
1272             mpd_sendSetReplayGainMode(mi->connection, "album");
1273             break;
1274         default:
1275             mpd_sendSetReplayGainMode(mi->connection, "off");
1276             break;
1277     }
1278     mpd_finishCommand(mi->connection);
1279     return mpd_unlock_conn(mi);
1280 }
1281 
mpd_server_get_replaygain_mode(MpdObj * mi)1282 MpdServerReplaygainMode mpd_server_get_replaygain_mode(MpdObj *mi)
1283 {
1284     char *var;
1285     MpdServerReplaygainMode retv = MPD_SERVER_REPLAYGAIN_MODE_OFF;
1286 	if(!mpd_check_connected(mi))
1287 	{
1288 		debug_printf(DEBUG_ERROR, "Not Connected\n");
1289 		return retv;
1290 	}
1291 
1292 	if(mpd_lock_conn(mi))
1293 	{
1294 		return retv;
1295 	}
1296 
1297     mpd_sendReplayGainModeCommand(mi->connection);
1298 
1299     var = mpd_getReplayGainMode(mi->connection);
1300     if(var)
1301     {
1302         if(strcmp(var, "track")==0) {
1303             retv = MPD_SERVER_REPLAYGAIN_MODE_TRACK;
1304         }else if(strcmp(var, "album")==0) {
1305             retv = MPD_SERVER_REPLAYGAIN_MODE_ALBUM;
1306         }else if (strcmp(var, "auto") == 0) {
1307             retv = MPD_SERVER_REPLAYGAIN_MODE_AUTO;
1308         }
1309         free(var);
1310     }
1311     mpd_finishCommand(mi->connection);
1312 
1313 	mpd_unlock_conn(mi);
1314 
1315     return retv;
1316 }
1317