1 #include "libmpdclient.h"
2 
3 #include "gbemol-mpd.h"
4 #include "gbemol-marshal.h"
5 
6 /* Properties */
7 enum {
8 	GBEMOL_MPD_HOST = 1,
9 	GBEMOL_MPD_PASS,
10 	GBEMOL_MPD_PORT,
11 	GBEMOL_MPD_TIMEOUT
12 };
13 
14 /* Signals */
15 typedef enum {
16 	STATE_CHANGED_SIGNAL,
17 	SONG_CHANGED_SIGNAL,
18 	PLAYLIST_CHANGED_SIGNAL,
19 	REFRESH_SIGNAL,
20 	ERROR_SIGNAL,
21 	LAST_SIGNAL
22 } GbemolMpdSignalType;
23 
24 struct _GbemolMpdPrivate {
25 	mpd_Connection *conn;
26 
27 	gchar *host;		/* MPD Host */
28 	gchar *pass;		/* MPD Pass */
29 	guint port;		/* MPD TCP Port */
30 
31 	float timeout;		/* Timeout, in seconds */
32 
33 	gboolean connected;     /* Connected to the daemon? */
34 
35 	int error;		/* Error code, 0 for no error */
36 
37 	GList* not_commands;	/* List of not allowed commands */
38 };
39 
40 static guint gbemol_mpd_signals [LAST_SIGNAL] = { 0 };
41 
42 static void gbemol_mpd_class_init (GObjectClass *g_class);
43 static void gbemol_mpd_init (GbemolMpd *obj);
44 static void gbemol_mpd_finalize (GObject *object);
45 static gboolean gbemol_mpd_refresh (GbemolMpd *obj);
46 static void gbemol_mpd_get_not_commands_list (GbemolMpd *obj);
47 gboolean gbemol_mpd_check_permission (GbemolMpd *obj, gchar *command);
48 
49 /* Utils */
50 static gboolean gbemol_mpd_finish_and_check (GbemolMpd *obj);
51 static gboolean gbemol_mpd_finish_and_handle (GbemolMpd *obj);
52 
53 
54 /* Finishes a command and checks if an error has ocurred, true if yes */
55 
56 GType
gbemol_mpd_get_type(void)57 gbemol_mpd_get_type (void)
58 {
59 	static GType type = 0;
60 	if (type == 0) {
61 		static const GTypeInfo info = {
62 			sizeof (GbemolMpdClass),
63 			NULL,   /* base_init */
64 			NULL,   /* base_finalize */
65 			(GClassInitFunc)gbemol_mpd_class_init,   /* class_init */
66 			NULL,   /* class_finalize */
67 			NULL,   /* class_data */
68 			sizeof (GbemolMpd),
69 			0,      /* n_preallocs */
70 			(GInstanceInitFunc)gbemol_mpd_init    /* instance_init */
71 		};
72 
73 		type = g_type_register_static (G_TYPE_OBJECT,
74 				"GbemolMpd",
75 				&info, 0);
76 	}
77 	return type;
78 }
79 
80 
81 static void
gbemol_mpd_init(GbemolMpd * obj)82 gbemol_mpd_init (GbemolMpd *obj)
83 {
84 	obj->priv = g_new0 (GbemolMpdPrivate, 1);
85 
86 	/* Default values */
87 	obj->status = NULL;
88 
89 	obj->priv->host = g_strdup ("localhost");
90 	obj->priv->pass = NULL;
91 	obj->priv->port = 6600;
92 	obj->priv->timeout = 1.0;
93 
94 	obj->priv->connected = FALSE;
95 
96 	obj->priv->conn = NULL;
97 
98 	obj->priv->not_commands = NULL;
99 }
100 
101 static void
gbemol_mpd_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)102 gbemol_mpd_set_property (GObject      *object,
103                         guint         property_id,
104                         const GValue *value,
105                         GParamSpec   *pspec)
106 {
107 	GbemolMpd *obj = GBEMOL_MPD (object);
108 
109 	switch (property_id)
110 	{
111 		case GBEMOL_MPD_HOST:
112 			g_free (obj->priv->host);
113 			obj->priv->host = g_value_dup_string (value);
114 			break;
115 		case GBEMOL_MPD_PASS:
116 			g_free (obj->priv->pass);
117 			obj->priv->pass = g_value_dup_string (value);
118 			break;
119 		case GBEMOL_MPD_PORT:
120 			obj->priv->port = g_value_get_int (value);
121 			break;
122 		case GBEMOL_MPD_TIMEOUT:
123 			obj->priv->timeout = g_value_get_float (value);
124 			break;
125 		default:
126 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
127     			break;
128 	}
129 }
130 
131 static void
gbemol_mpd_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)132 gbemol_mpd_get_property (GObject      *object,
133                         guint         property_id,
134                         GValue *value,
135                         GParamSpec   *pspec)
136 {
137 	GbemolMpd *obj = GBEMOL_MPD (object);
138 
139 	switch (property_id)
140 	{
141 		case GBEMOL_MPD_HOST:
142 			g_value_set_string (value, obj->priv->host);
143 			break;
144 		case GBEMOL_MPD_PASS:
145 			g_value_set_string (value, obj->priv->pass);
146 			break;
147 		case GBEMOL_MPD_PORT:
148 			g_value_set_int (value, obj->priv->port);
149 			break;
150 		case GBEMOL_MPD_TIMEOUT:
151 			g_value_set_float (value, obj->priv->timeout);
152 			break;
153 		default:
154 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
155     			break;
156 	}
157 }
158 
159 static void
gbemol_mpd_class_init(GObjectClass * g_class)160 gbemol_mpd_class_init (GObjectClass *g_class)
161 {
162 	GParamSpec *pspec;
163 
164   	g_class->set_property = gbemol_mpd_set_property;
165   	g_class->get_property = gbemol_mpd_get_property;
166 	g_class->finalize = gbemol_mpd_finalize;
167 
168 	pspec = g_param_spec_string ("host",
169                                "MPD Host",
170                                "Set MPD server host name",
171                                "localhost" /* default value */,
172                                G_PARAM_READWRITE);
173 
174 	g_object_class_install_property (g_class,
175                                    GBEMOL_MPD_HOST,
176                                    pspec);
177 
178 	pspec = g_param_spec_string ("pass",
179                                "MPD Password",
180                                "Set MPD server password",
181                                NULL /* default value */,
182                                G_PARAM_READWRITE);
183 
184 	g_object_class_install_property (g_class,
185                                    GBEMOL_MPD_PASS,
186                                    pspec);
187 
188 	pspec = g_param_spec_int ("port",
189                                "MPD Port",
190                                "Set MPD TCP/IP port to use",
191                                0,
192 			       65535,
193 			       6600,
194 			       G_PARAM_READWRITE);
195 
196 	g_object_class_install_property (g_class,
197                                    GBEMOL_MPD_PORT,
198                                    pspec);
199 
200 	pspec = g_param_spec_float ("timeout",
201                                "MPD Timeout",
202                                "Set MPD connection timeout",
203                                0,
204 			       65535,
205 			       1.0,
206 			       G_PARAM_READWRITE);
207 
208 	g_object_class_install_property (g_class,
209                                    GBEMOL_MPD_TIMEOUT,
210                                    pspec);
211 
212 	/* Signals */
213 	gbemol_mpd_signals [STATE_CHANGED_SIGNAL] =
214 		g_signal_new ("state_changed",
215 			      G_TYPE_FROM_CLASS (g_class),
216 			      G_SIGNAL_RUN_LAST,
217 			      0,
218 			      NULL,
219 			      NULL,
220 			      g_cclosure_marshal_VOID__VOID,
221 			      G_TYPE_NONE,
222 			      0);
223 
224 	gbemol_mpd_signals [SONG_CHANGED_SIGNAL] =
225 		g_signal_new ("song_changed",
226 			      G_TYPE_FROM_CLASS (g_class),
227 			      G_SIGNAL_RUN_LAST,
228 			      0,
229 			      NULL,
230 			      NULL,
231 			      g_cclosure_marshal_VOID__VOID,
232 			      G_TYPE_NONE,
233 			      0);
234 	gbemol_mpd_signals [PLAYLIST_CHANGED_SIGNAL] =
235 		g_signal_new ("playlist_changed",
236 			      G_TYPE_FROM_CLASS (g_class),
237 			      G_SIGNAL_RUN_LAST,
238 			      0,
239 			      NULL,
240 			      NULL,
241 			      g_cclosure_marshal_VOID__VOID,
242 			      G_TYPE_NONE,
243 			      0);
244 	gbemol_mpd_signals [REFRESH_SIGNAL] =
245 		g_signal_new ("refresh",
246 			      G_TYPE_FROM_CLASS (g_class),
247 			      G_SIGNAL_RUN_LAST,
248 			      0,
249 			      NULL,
250 			      NULL,
251 			      g_cclosure_marshal_VOID__VOID,
252 			      G_TYPE_NONE,
253 			      0);
254 		gbemol_mpd_signals [ERROR_SIGNAL] =
255 		g_signal_new ("error",
256 			      G_TYPE_FROM_CLASS (g_class),
257 			      G_SIGNAL_RUN_LAST,
258 			      0,
259 			      NULL,
260 			      NULL,
261 			      gbemol_cclosure_VOID__INT_INT_STRING,
262 			      G_TYPE_NONE,
263 			      3,
264 			      G_TYPE_INT,
265 			      G_TYPE_INT,
266 			      G_TYPE_STRING);
267 
268 }
269 
gbemol_mpd_get_status(GbemolMpd * obj)270 static GbemolMpdStatus* gbemol_mpd_get_status (GbemolMpd* obj)
271 {
272 	if (!gbemol_mpd_check_permission (obj, "status"))
273 		return NULL;
274 
275 	mpd_sendCurrentSongCommand (obj->priv->conn);
276 
277 	if (gbemol_mpd_finish_and_handle (obj))
278 		return NULL;
279 
280 	mpd_sendStatusCommand (obj->priv->conn);
281 	return mpd_getStatus (obj->priv->conn);
282 }
283 
284 
285 
286 static void
gbemol_mpd_finalize(GObject * obj)287 gbemol_mpd_finalize (GObject *obj)
288 {
289 	GbemolMpd *mpd = GBEMOL_MPD (obj);
290 
291 	mpd_closeConnection (mpd->priv->conn);
292 	g_free (mpd->priv->host);
293 	g_free (mpd->priv->pass);
294 
295 	g_free (GBEMOL_MPD (obj)->priv);
296 }
297 
gbemol_mpd_refresh(GbemolMpd * obj)298 static gboolean gbemol_mpd_refresh (GbemolMpd *obj)
299 {
300 	GbemolMpdStatus* status;
301 	gboolean signals [LAST_SIGNAL] = { FALSE }; /* Signals to be emitted */
302 	int i;
303 
304 	if (!gbemol_mpd_check_permission (obj, "status"))
305 		return TRUE;
306 
307 	status = gbemol_mpd_get_status (obj);
308 
309 	signals [REFRESH_SIGNAL] = TRUE;
310 
311 	if (status)
312 	{
313 		/* If there wasn't a status before, update everything */
314 		if (!obj->status)
315 		{
316 			signals [SONG_CHANGED_SIGNAL] = TRUE;
317 			signals [PLAYLIST_CHANGED_SIGNAL] = TRUE;
318 			signals [STATE_CHANGED_SIGNAL] = TRUE;
319 		}
320 		else
321 		{
322 			/* Check if song has changed */
323 			if ((status->song < 0) || (status->songid != obj->status->songid))
324 				signals [SONG_CHANGED_SIGNAL] = TRUE;
325 
326 			/* Check if playlist has changed */
327 			if (status->playlist != obj->status->playlist)
328 				signals [PLAYLIST_CHANGED_SIGNAL] = TRUE;
329 
330 			/* State has changed? */
331 			if (status->state != obj->status->state)
332 				signals [STATE_CHANGED_SIGNAL] = TRUE;
333 
334 			mpd_freeStatus (obj->status);
335 		}
336 	}
337 
338 	obj->status = status;
339 
340 	/* Emit the signals */
341 	for (i = 0; i < LAST_SIGNAL; i++)
342 		if (signals[i])
343 			g_signal_emit (obj, gbemol_mpd_signals [i], 0, NULL);
344 
345 	return TRUE;
346 
347 }
348 
gbemol_mpd_new(const gchar * host,const gchar * pass,int port,float timeout)349 GbemolMpd* gbemol_mpd_new (const gchar *host, const gchar *pass, int port, float timeout)
350 {
351 	GbemolMpd *obj;
352 
353 	obj = gbemol_mpd_new_with_defaults ();
354 
355 	g_object_set (G_OBJECT (obj),
356 			"host", host,
357 			"pass", pass,
358 			"port", port,
359 			"timeout", timeout,
360 			NULL);
361 	return obj;
362 }
363 
364 GbemolMpd*
gbemol_mpd_new_with_defaults(void)365 gbemol_mpd_new_with_defaults (void)
366 {
367 	GbemolMpd* obj;
368 
369 	obj = GBEMOL_MPD (g_object_new (GBEMOL_TYPE_MPD, NULL));
370 
371 	/* Add refresh function to the main loop */
372 	g_timeout_add (500, (GSourceFunc) gbemol_mpd_refresh, (gpointer) obj);
373 
374 	return obj;
375 }
376 
gbemol_mpd_is_connected(GbemolMpd * obj)377 gboolean gbemol_mpd_is_connected (GbemolMpd *obj)
378 {
379 	return obj->priv->connected;
380 }
381 
gbemol_mpd_get_version(GbemolMpd * obj)382 gchar* gbemol_mpd_get_version (GbemolMpd* obj)
383 {
384 	gchar* str;
385 
386 	str = g_strdup_printf ("%d.%d.%d", obj->priv->conn->version[0], obj->priv->conn->version[1],
387 			obj->priv->conn->version[2]);
388 	return str;
389 }
390 
gbemol_mpd_free_song(GbemolMpdSong * song)391 void gbemol_mpd_free_song (GbemolMpdSong *song)
392 {
393 	if (song)
394 		mpd_freeSong (song);
395 	song = NULL;
396 }
397 
gbemol_mpd_free_song_list(GList * songs)398 void gbemol_mpd_free_song_list (GList *songs)
399 {
400 	if (songs)
401 	{
402 		g_list_foreach (songs, (GFunc) mpd_freeSong, NULL);
403 		g_list_free (songs);
404 	}
405 	songs = NULL;
406 }
407 
gbemol_mpd_free_char_list(GList * l)408 void gbemol_mpd_free_char_list (GList *l)
409 {
410 	if (l)
411 	{
412 		g_list_foreach (l, (GFunc) g_free, NULL);
413 		g_list_free (l);
414 	}
415 	l = NULL;
416 }
417 
gbemol_mpd_free_output_list(GList * l)418 void gbemol_mpd_free_output_list (GList *l)
419 {
420 	if (l)
421 	{
422 		g_list_foreach (l, (GFunc) mpd_freeOutputElement, NULL);
423 		g_list_free (l);
424 	}
425 	l = NULL;
426 }
427 
428 /* Property related methods */
gbemol_mpd_set_host(GbemolMpd * obj,const gchar * host)429 void gbemol_mpd_set_host (GbemolMpd *obj, const gchar *host)
430 {
431 	g_object_set (G_OBJECT (obj), "host", host, NULL);
432 }
433 
gbemol_mpd_set_pass(GbemolMpd * obj,const gchar * pass)434 void gbemol_mpd_set_pass (GbemolMpd *obj, const gchar *pass)
435 {
436 	g_object_set (G_OBJECT (obj), "pass", pass, NULL);
437 }
438 
gbemol_mpd_set_port(GbemolMpd * obj,guint port)439 void gbemol_mpd_set_port (GbemolMpd *obj, guint port)
440 {
441 	g_object_set (G_OBJECT (obj), "port", port, NULL);
442 }
443 
gbemol_mpd_set_timeout(GbemolMpd * obj,gfloat timeout)444 void gbemol_mpd_set_timeout (GbemolMpd *obj, gfloat timeout)
445 {
446 	g_object_set (G_OBJECT (obj), "timeout", timeout, NULL);
447 }
448 
gbemol_mpd_connect(GbemolMpd * obj)449 gboolean gbemol_mpd_connect (GbemolMpd* obj)
450 {
451 	if (obj->priv->conn)
452 		mpd_closeConnection (obj->priv->conn);
453 
454 	obj->priv->conn = mpd_newConnection (obj->priv->host, obj->priv->port, obj->priv->timeout);
455 
456 	if (gbemol_mpd_finish_and_handle (obj))
457 		obj->priv->connected = FALSE;
458 	else
459 	{
460 		gbemol_mpd_get_not_commands_list (obj);
461 		if (obj->status)
462 			mpd_freeStatus (obj->status);
463 		obj->status = gbemol_mpd_get_status (obj);
464 		obj->priv->connected = TRUE;
465 		/* Emit a song-changed signal */
466 		g_signal_emit (obj, gbemol_mpd_signals [SONG_CHANGED_SIGNAL], 0, NULL);
467 	}
468 
469 	return obj->priv->connected;
470 }
471 
472 /* Connect */
gbemol_mpd_connect_and_authenticate(GbemolMpd * obj)473 gboolean gbemol_mpd_connect_and_authenticate (GbemolMpd* obj)
474 {
475 	if (obj->priv->conn)
476 		mpd_closeConnection (obj->priv->conn);
477 
478 	obj->priv->conn = mpd_newConnection (obj->priv->host, obj->priv->port, obj->priv->timeout);
479 
480 	if (gbemol_mpd_finish_and_handle (obj))
481 	{
482 		obj->priv->connected = FALSE;
483 		return FALSE;
484 	}
485 	else
486 		obj->priv->connected = TRUE;
487 
488 	/* Not authenticated, yet connected, so return TRUE, but mark as disconnected */
489 	if (!gbemol_mpd_authenticate (obj))
490 		obj->priv->connected = FALSE;
491 	else
492 	{
493 		gbemol_mpd_get_not_commands_list (obj);
494 		if (obj->status)
495 			mpd_freeStatus (obj->status);
496 		obj->status = gbemol_mpd_get_status (obj);
497 		/* Emit a song-changed signal */
498 		g_signal_emit (obj, gbemol_mpd_signals [SONG_CHANGED_SIGNAL], 0, NULL);
499 	}
500 
501 	return TRUE;
502 }
503 
gbemol_mpd_disconnect(GbemolMpd * obj)504 void gbemol_mpd_disconnect (GbemolMpd *obj)
505 {
506 	if (obj->priv->not_commands)
507 		gbemol_mpd_free_char_list (obj->priv->not_commands);
508 	obj->priv->not_commands = NULL;
509 
510 	obj->priv->connected = FALSE;
511 	if (obj->priv->conn)
512 		mpd_closeConnection (obj->priv->conn);
513 	obj->priv->conn = NULL;
514 }
515 
gbemol_mpd_authenticate(GbemolMpd * obj)516 gboolean gbemol_mpd_authenticate (GbemolMpd *obj)
517 {
518 	mpd_sendPasswordCommand (obj->priv->conn, obj->priv->pass);
519 	return !gbemol_mpd_finish_and_handle (obj);
520 }
521 
522 
523 /*
524  * Get the list of not allowed commands
525  */
gbemol_mpd_get_not_commands_list(GbemolMpd * obj)526 static void gbemol_mpd_get_not_commands_list (GbemolMpd *obj)
527 {
528 	gchar* command;
529 
530 	/* if there's a previous list, free it first */
531 	mpd_sendNotCommandsCommand (obj->priv->conn);
532 	while ((command = mpd_getNextCommand (obj->priv->conn)))
533 		obj->priv->not_commands = g_list_append (obj->priv->not_commands, command);
534 
535 	gbemol_mpd_finish_and_handle (obj);
536 }
537 
538 /*
539  * Finish a command and checks for errors, emits a signal for the error
540  */
gbemol_mpd_finish_and_handle(GbemolMpd * obj)541 gboolean gbemol_mpd_finish_and_handle (GbemolMpd* obj)
542 {
543 	if (gbemol_mpd_is_connected (obj))
544 		mpd_finishCommand (obj->priv->conn);
545 
546 	if (obj->priv->conn->error)
547 	{
548 		switch (obj->priv->conn->error)
549 		{
550 			case MPD_ERROR_CONNCLOSED:
551 				obj->priv->connected = FALSE;
552 				break;
553 			case MPD_ERROR_TIMEOUT:
554 				obj->priv->connected = FALSE;
555 				break;
556 			case MPD_ERROR_UNKHOST:
557 				obj->priv->connected = FALSE;
558 				break;
559 			case MPD_ERROR_NORESPONSE:
560 				obj->priv->connected = FALSE;
561 				break;
562 			case MPD_ERROR_CONNPORT:
563 				obj->priv->connected = FALSE;
564 				break;
565 			case MPD_ERROR_NOTMPD:
566 				obj->priv->connected = FALSE;
567 				break;
568 			case MPD_ERROR_SYSTEM:
569 				obj->priv->connected = FALSE;
570 				break;
571 		}
572 		g_signal_emit (obj, gbemol_mpd_signals [ERROR_SIGNAL], 0,
573 				obj->priv->conn->error, obj->priv->conn->errorCode, obj->priv->conn->errorStr, NULL);
574 		mpd_clearError (obj->priv->conn);
575 		return TRUE;
576 	}
577 	else
578 	{
579 		mpd_finishCommand (obj->priv->conn);
580 		return FALSE;
581 	}
582 }
583 
584 /*
585  * Finishes a command and return TRUE if there are no errors, FALSE if there are
586  * but doesn't treat them
587  */
gbemol_mpd_finish_and_check(GbemolMpd * obj)588 gboolean gbemol_mpd_finish_and_check (GbemolMpd *obj)
589 {
590 	mpd_finishCommand (obj->priv->conn);
591 
592 	if (obj->priv->conn->error)
593 		return FALSE;
594 	else
595 		return TRUE;
596 }
597 
598 /*
599  * Check if user is allowed to execute command
600  */
gbemol_mpd_check_permission(GbemolMpd * obj,gchar * command)601 gboolean gbemol_mpd_check_permission (GbemolMpd *obj, gchar *command)
602 {
603 	GList* l = g_list_first(obj->priv->not_commands);
604 
605 	if (!gbemol_mpd_is_connected (obj))
606 		return FALSE;
607 
608 	while ((l = g_list_next (l)))
609 	{
610 		if (g_str_equal ((gchar *) l->data, command))
611 		{
612 			g_message ("DENIED: %s", (gchar *) l->data);
613 			return FALSE;
614 		}
615 	}
616 	return TRUE;
617 }
618 
gbemol_mpd_player_next(GbemolMpd * obj)619 void gbemol_mpd_player_next (GbemolMpd *obj)
620 {
621 	if (!gbemol_mpd_check_permission (obj, "next"))
622 		return;
623 
624 	mpd_sendNextCommand (obj->priv->conn);
625 	gbemol_mpd_finish_and_handle (obj);
626 }
627 
gbemol_mpd_player_previous(GbemolMpd * obj)628 void gbemol_mpd_player_previous (GbemolMpd *obj)
629 {
630 	if (!gbemol_mpd_check_permission (obj, "previous"))
631 			return;
632 
633 	mpd_sendPrevCommand (obj->priv->conn);
634 	gbemol_mpd_finish_and_handle (obj);
635 }
636 
gbemol_mpd_player_stop(GbemolMpd * obj)637 void gbemol_mpd_player_stop (GbemolMpd *obj)
638 {
639 	if (!gbemol_mpd_check_permission (obj, "stop"))
640 			return;
641 
642 	mpd_sendStopCommand (obj->priv->conn);
643 	gbemol_mpd_finish_and_handle (obj);
644 }
645 
gbemol_mpd_player_pause(GbemolMpd * obj)646 void gbemol_mpd_player_pause (GbemolMpd *obj)
647 {
648 	if (!gbemol_mpd_check_permission (obj, "pause"))
649 			return;
650 
651 	if (obj->status->state == MPD_STATUS_STATE_PLAY)
652 		mpd_sendPauseCommand (obj->priv->conn, 1);
653 	else
654 		mpd_sendPauseCommand (obj->priv->conn, 0);
655 	gbemol_mpd_finish_and_handle (obj);
656 }
657 
gbemol_mpd_player_play_song_by_id(GbemolMpd * obj,int id)658 void gbemol_mpd_player_play_song_by_id (GbemolMpd *obj, int id)
659 {
660 
661 	if (!gbemol_mpd_check_permission (obj, "playid"))
662 			return;
663 
664 	mpd_sendPlayIdCommand (obj->priv->conn, id);
665 	gbemol_mpd_finish_and_handle (obj);
666 }
667 
gbemol_mpd_player_play_song_by_pos(GbemolMpd * obj,int pos)668 void gbemol_mpd_player_play_song_by_pos (GbemolMpd *obj, int pos)
669 {
670 	if (!gbemol_mpd_check_permission (obj, "play"))
671 		return;
672 
673 	mpd_sendPlayCommand (obj->priv->conn, pos);
674 	gbemol_mpd_finish_and_handle (obj);
675 }
676 
gbemol_mpd_set_random(GbemolMpd * obj,gboolean random)677 void gbemol_mpd_set_random (GbemolMpd *obj, gboolean random)
678 {
679 	if (!gbemol_mpd_check_permission (obj, "random"))
680 		return;
681 
682 	mpd_sendRandomCommand (obj->priv->conn, random);
683 	gbemol_mpd_finish_and_handle (obj);
684 }
685 
gbemol_mpd_set_repeat(GbemolMpd * obj,gboolean repeat)686 void gbemol_mpd_set_repeat (GbemolMpd *obj, gboolean repeat)
687 {
688 	if (!gbemol_mpd_check_permission (obj, "repeat"))
689 			return;
690 
691 	mpd_sendRepeatCommand (obj->priv->conn, repeat);
692 	gbemol_mpd_finish_and_handle (obj);
693 }
694 
gbemol_mpd_seek(GbemolMpd * obj,int id,int sec)695 void gbemol_mpd_seek (GbemolMpd *obj, int id, int sec)
696 {
697 	if (!gbemol_mpd_check_permission (obj, "seekid"))
698 			return;
699 
700 	mpd_sendSeekIdCommand (obj->priv->conn, id, sec);
701 	gbemol_mpd_finish_and_handle (obj);
702 }
703 
gbemol_mpd_crossfade(GbemolMpd * obj,int fade)704 void gbemol_mpd_crossfade (GbemolMpd *obj, int fade)
705 {
706 	if (!gbemol_mpd_check_permission (obj, "crossfade"))
707 		return;
708 
709 	mpd_sendCrossfadeCommand (obj->priv->conn, fade);
710 	gbemol_mpd_finish_and_handle (obj);
711 }
712 
gbemol_mpd_set_volume(GbemolMpd * obj,int volume)713 void gbemol_mpd_set_volume (GbemolMpd *obj, int volume)
714 {
715 	if (!gbemol_mpd_check_permission (obj, "setvol"))
716 		return;
717 	mpd_sendSetvolCommand (obj->priv->conn, volume);
718 	gbemol_mpd_finish_and_handle (obj);
719 }
720 
721 GbemolMpdSong*
gbemol_mpd_get_current_song(GbemolMpd * obj)722 gbemol_mpd_get_current_song (GbemolMpd* obj)
723 {
724 	if (!gbemol_mpd_check_permission (obj, "status") || !obj->status)
725 		return NULL;
726 
727 	if (obj->status->state == MPD_STATUS_STATE_STOP)
728 		return NULL;
729 
730 	if (obj->status->songid >= 0)
731 		return gbemol_mpd_playlist_get_song_by_id (obj, obj->status->songid);
732 	return NULL;
733 }
734 
gbemol_mpd_playlist_get_songs(GbemolMpd * obj)735 GList* gbemol_mpd_playlist_get_songs (GbemolMpd *obj)
736 {
737 	GList* songs = NULL;
738 	GbemolMpdInfo *info;
739 	GbemolMpdSong *song;
740 
741 	if (!gbemol_mpd_check_permission (obj, "playlistinfo"))
742 			return NULL;
743 
744 	mpd_sendPlaylistInfoCommand (obj->priv->conn, -1);
745 
746 	while ((info = mpd_getNextInfoEntity (obj->priv->conn)))
747 	{
748 		if (info->type == MPD_INFO_ENTITY_TYPE_SONG)
749 		{
750 			song = mpd_songDup (info->info.song);
751 			songs = g_list_prepend (songs, (gpointer) song);
752 		}
753 		mpd_freeInfoEntity (info);
754 	}
755 
756 	if (gbemol_mpd_finish_and_handle (obj))
757 		return NULL;
758 	else
759 		return g_list_reverse (songs);
760 }
761 
gbemol_mpd_playlist_get_song_by_id(GbemolMpd * obj,gint id)762 GbemolMpdSong* gbemol_mpd_playlist_get_song_by_id (GbemolMpd *obj, gint id)
763 {
764 	GbemolMpdInfo *info;
765 	GbemolMpdSong *song;
766 
767 	if ((id < 0) || !gbemol_mpd_check_permission (obj, "playlistid"))
768 		return NULL;
769 
770 	mpd_sendPlaylistIdCommand (obj->priv->conn, id);
771 	info = mpd_getNextInfoEntity (obj->priv->conn);
772 
773 	if (gbemol_mpd_finish_and_handle (obj) || !info)
774 		return NULL;
775 
776 	if (info->type == MPD_INFO_ENTITY_TYPE_SONG)
777 	{
778 		song = info->info.song;
779 		info->info.song = NULL;
780 	} else
781 		song = NULL;
782 
783 	mpd_freeInfoEntity (info);
784 
785 	return song;
786 }
787 
788 /*
789  * Return the current selected song (referenced by status->song)
790  */
gbemol_mpd_playlist_get_selected_song(GbemolMpd * obj)791 GbemolMpdSong* gbemol_mpd_playlist_get_selected_song (GbemolMpd *obj)
792 {
793 	GbemolMpdSong *song = NULL;
794 	GbemolMpdInfo *info;
795 
796 	if (!gbemol_mpd_check_permission (obj, "status"))
797 			return NULL;
798 
799 	mpd_sendCurrentSongCommand (obj->priv->conn);
800 	info = mpd_getNextInfoEntity (obj->priv->conn);
801 
802 	if (info && (info->type == MPD_INFO_ENTITY_TYPE_SONG))
803 		song = mpd_songDup (info->info.song);
804 
805 	mpd_freeInfoEntity (info);
806 
807 	gbemol_mpd_finish_and_handle (obj);
808 
809 	return song;
810 }
811 
gbemol_mpd_playlist_clear(GbemolMpd * obj)812 void gbemol_mpd_playlist_clear (GbemolMpd *obj)
813 {
814 	if (!gbemol_mpd_check_permission (obj, "clear"))
815 		return;
816 
817 	mpd_sendClearCommand (obj->priv->conn);
818 	gbemol_mpd_finish_and_handle (obj);
819 }
820 
gbemol_mpd_playlist_shuffle(GbemolMpd * obj)821 void gbemol_mpd_playlist_shuffle (GbemolMpd *obj)
822 {
823 	if (!gbemol_mpd_check_permission (obj, "shuffle"))
824 		return;
825 
826 	mpd_sendShuffleCommand (obj->priv->conn);
827 	gbemol_mpd_finish_and_handle (obj);
828 }
829 
gbemol_mpd_playlist_remove_song_by_id(GbemolMpd * obj,gint id)830 void gbemol_mpd_playlist_remove_song_by_id (GbemolMpd *obj, gint id)
831 {
832 	if (!gbemol_mpd_check_permission (obj, "deleteid"))
833 		return;
834 
835 	mpd_sendDeleteIdCommand (obj->priv->conn, id);
836 	gbemol_mpd_finish_and_handle (obj);
837 }
838 
gbemol_mpd_playlist_remove_song_by_pos(GbemolMpd * obj,gint pos)839 void gbemol_mpd_playlist_remove_song_by_pos (GbemolMpd *obj, gint pos)
840 {
841 	if (!gbemol_mpd_check_permission (obj, "delete"))
842 			return;
843 
844 	mpd_sendDeleteCommand (obj->priv->conn, pos);
845 	gbemol_mpd_finish_and_handle (obj);
846 }
847 
gbemol_mpd_playlist_add_song(GbemolMpd * obj,gchar * path)848 void gbemol_mpd_playlist_add_song (GbemolMpd* obj, gchar* path)
849 {
850 	if (!gbemol_mpd_check_permission (obj, "add"))
851 		return;
852 
853 	mpd_sendAddCommand (obj->priv->conn, path);
854 	gbemol_mpd_finish_and_handle (obj);
855 }
856 
857 /*
858  * TRUE if the song is actually a stream
859  */
gbemol_mpd_song_is_stream(GbemolMpdSong * song)860 gboolean gbemol_mpd_song_is_stream (GbemolMpdSong* song)
861 {
862 	if (song->name && !song->artist && (song->time == MPD_SONG_NO_TIME))
863 		return TRUE;
864 	else
865 		return FALSE;
866 }
867 
gbemol_mpd_preview_playlist(GbemolMpd * obj,gchar * path)868 GList* gbemol_mpd_preview_playlist (GbemolMpd *obj, gchar *path)
869 {
870 	GList *songs = NULL;
871 	GbemolMpdSong* song;
872 	GbemolMpdInfo* info;
873 
874 	if (!gbemol_mpd_check_permission (obj, "listplaylistinfo"))
875 			return NULL;
876 
877 	mpd_sendListPlaylistInfoCommand (obj->priv->conn, path);
878 
879 	while ((info = mpd_getNextInfoEntity (obj->priv->conn)))
880 	{
881 		if (info->type == MPD_INFO_ENTITY_TYPE_SONG)
882 		{
883 			song = info->info.song;
884 			info->info.song = NULL;
885 			songs = g_list_prepend (songs, song);
886 		}
887 		mpd_freeInfoEntity (info);
888 	}
889 
890 	if (gbemol_mpd_finish_and_handle (obj))
891 		return NULL;
892 	else
893 		return g_list_reverse (songs);
894 
895 }
896 
gbemol_mpd_load_playlist(GbemolMpd * obj,gchar * playlist)897 void gbemol_mpd_load_playlist (GbemolMpd *obj, gchar *playlist)
898 {
899 	if (!gbemol_mpd_check_permission (obj, "load"))
900 		return;
901 
902 	mpd_sendLoadCommand (obj->priv->conn, playlist);
903 	gbemol_mpd_finish_and_handle (obj);
904 }
905 
gbemol_mpd_delete_playlist(GbemolMpd * obj,gchar * path)906 void gbemol_mpd_delete_playlist (GbemolMpd *obj, gchar *path)
907 {
908 	if (!gbemol_mpd_check_permission (obj, "rm"))
909 		return;
910 
911 	mpd_sendRmCommand (obj->priv->conn, path);
912 	gbemol_mpd_finish_and_handle (obj);
913 }
914 
gbemol_mpd_save_playlist(GbemolMpd * obj,gchar * path)915 gboolean gbemol_mpd_save_playlist (GbemolMpd *obj, gchar *path)
916 {
917 	if (!gbemol_mpd_check_permission (obj, "save"))
918 		return FALSE;
919 
920 	mpd_sendSaveCommand (obj->priv->conn, path);
921 	if (!gbemol_mpd_finish_and_check (obj))
922 	{
923 		/* Playlist already exists (probably) */
924 		gbemol_mpd_delete_playlist (obj, path);
925 		mpd_sendSaveCommand (obj->priv->conn, path);
926 		return gbemol_mpd_finish_and_handle (obj);
927 	}
928 
929 	return TRUE;
930 }
931 
gbemol_mpd_get_playlists(GbemolMpd * obj)932 GList* gbemol_mpd_get_playlists (GbemolMpd *obj)
933 {
934 	GList* pls = NULL;
935 	GbemolMpdInfo *info;
936 	gchar *list;
937 
938 	if (!gbemol_mpd_check_permission (obj, "lsinfo"))
939 			return NULL;
940 
941 	mpd_sendLsInfoCommand (obj->priv->conn, "/");
942 
943 	while ((info = mpd_getNextInfoEntity (obj->priv->conn)))
944 	{
945 		if (info->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
946 		{
947 			list = g_strdup (info->info.playlistFile->path);
948 			pls = g_list_prepend (pls, (gpointer) list);
949 		}
950 		mpd_freeInfoEntity (info);
951 	}
952 
953 	if (gbemol_mpd_finish_and_handle (obj))
954 		return NULL;
955 	else
956 		return g_list_reverse (pls);
957 }
958 
959 /*
960  * Begin a queue of commands
961  */
gbemol_mpd_queue_start(GbemolMpd * obj)962 void gbemol_mpd_queue_start (GbemolMpd* obj)
963 {
964 	if (!gbemol_mpd_is_connected (obj))
965 		return;
966 
967 	mpd_sendCommandListOkBegin (obj->priv->conn);
968 }
969 
970 /*
971  * Execute the queue
972  */
gbemol_mpd_queue_finish(GbemolMpd * obj)973 void gbemol_mpd_queue_finish (GbemolMpd* obj)
974 {
975 	if (!gbemol_mpd_is_connected (obj))
976 		return;
977 
978 	mpd_sendCommandListEnd (obj->priv->conn);
979 	gbemol_mpd_finish_and_handle (obj);
980 }
981 
982 /*
983  * Queue removing a song
984  */
gbemol_mpd_queue_remove_song(GbemolMpd * obj,gint id)985 void gbemol_mpd_queue_remove_song (GbemolMpd* obj, gint id)
986 {
987 	if (!gbemol_mpd_check_permission (obj, "deleteid"))
988 		return;
989 
990 	mpd_sendDeleteIdCommand (obj->priv->conn, id);
991 }
992 
gbemol_mpd_queue_add_song(GbemolMpd * obj,gchar * path)993 void gbemol_mpd_queue_add_song (GbemolMpd* obj, gchar* path)
994 {
995 	if (!gbemol_mpd_check_permission (obj, "add"))
996 		return;
997 
998 	mpd_sendAddCommand (obj->priv->conn, path);
999 }
1000 
1001 /*
1002  * Start field search
1003  */
gbemol_mpd_search_field_start(GbemolMpd * obj,gint field)1004 void gbemol_mpd_search_field_start (GbemolMpd* obj, gint field)
1005 {
1006 	if (!gbemol_mpd_is_connected (obj))
1007 		return;
1008 
1009 	mpd_startFieldSearch (obj->priv->conn, field);
1010 }
1011 
gbemol_mpd_search_start(GbemolMpd * obj)1012 void gbemol_mpd_search_start (GbemolMpd* obj)
1013 {
1014 	if (!gbemol_mpd_is_connected (obj))
1015 		return;
1016 
1017 	mpd_startSearch (obj->priv->conn, TRUE);
1018 }
1019 
1020 
1021 /*
1022  * Add constraint to the search
1023  */
gbemol_mpd_search_add_constraint(GbemolMpd * obj,gint tag,gchar * value)1024 void gbemol_mpd_search_add_constraint (GbemolMpd* obj, gint tag, gchar* value)
1025 {
1026 	if (!gbemol_mpd_is_connected (obj))
1027 		return;
1028 
1029 	mpd_addConstraintSearch (obj->priv->conn, tag, value);
1030 }
1031 
1032 /*
1033  * Get the search results
1034  */
gbemol_mpd_search_field_get_results(GbemolMpd * obj,gint tag)1035 GList* gbemol_mpd_search_field_get_results (GbemolMpd* obj, gint tag)
1036 {
1037 	GList* results = NULL;
1038 	gchar* str;
1039 
1040 	if (!gbemol_mpd_is_connected (obj))
1041 		return NULL;
1042 
1043 	mpd_commitSearch (obj->priv->conn);
1044 	while ((str = mpd_getNextTag (obj->priv->conn, tag)) != NULL)
1045 		results = g_list_append (results, str);
1046 
1047 	return results;
1048 }
1049 
gbemol_mpd_search_get_results(GbemolMpd * obj)1050 GList* gbemol_mpd_search_get_results (GbemolMpd* obj)
1051 {
1052 	GList* results = NULL;
1053 	GbemolMpdInfo *info;
1054 	GbemolMpdSong *song;
1055 
1056 	if (!gbemol_mpd_is_connected (obj))
1057 		return NULL;
1058 
1059 	gbemol_mpd_search_commit (obj);
1060 
1061 	while ((info = mpd_getNextInfoEntity (obj->priv->conn)) != NULL)
1062 	{
1063 		if (info->type == MPD_INFO_ENTITY_TYPE_SONG)
1064 		{
1065 			song = mpd_songDup (info->info.song);
1066 			results = g_list_append (results, song);
1067 		}
1068 		mpd_freeInfoEntity (info);
1069 	}
1070 
1071 	return results;
1072 }
1073 
gbemol_mpd_search_commit(GbemolMpd * obj)1074 void gbemol_mpd_search_commit (GbemolMpd* obj)
1075 {
1076 	if (!gbemol_mpd_is_connected (obj))
1077 		return;
1078 
1079 	mpd_commitSearch (obj->priv->conn);
1080 }
1081 
gbemol_mpd_database_get_all_songs(GbemolMpd * obj)1082 GList* gbemol_mpd_database_get_all_songs (GbemolMpd* obj)
1083 {
1084 	GList* results = NULL;
1085 	GbemolMpdInfo *info;
1086 	GbemolMpdSong *song;
1087 
1088 	if (!gbemol_mpd_is_connected (obj))
1089 		return NULL;
1090 
1091 	mpd_sendListallInfoCommand (obj->priv->conn, "/");
1092 
1093 	while ((info = mpd_getNextInfoEntity (obj->priv->conn)) != NULL)
1094 	{
1095 		if (info->type == MPD_INFO_ENTITY_TYPE_SONG)
1096 		{
1097 			song = mpd_songDup (info->info.song);
1098 			results = g_list_append (results, song);
1099 		}
1100 		mpd_freeInfoEntity (info);
1101 	}
1102 
1103 	if (gbemol_mpd_finish_and_handle (obj))
1104 		return NULL;
1105 	else
1106 		return results;
1107 }
1108 
gbemol_mpd_database_update(GbemolMpd * obj,gchar * path)1109 void gbemol_mpd_database_update (GbemolMpd* obj, gchar *path)
1110 {
1111 	mpd_sendUpdateCommand (obj->priv->conn, path);
1112 	gbemol_mpd_finish_and_handle (obj);
1113 }
1114 
gbemol_mpd_output_get_list(GbemolMpd * obj)1115 GList* gbemol_mpd_output_get_list (GbemolMpd* obj)
1116 {
1117 	GList *l = NULL;
1118 	GbemolMpdOutput* out;
1119 
1120 	mpd_sendOutputsCommand (obj->priv->conn);
1121 
1122 	while ((out = mpd_getNextOutput (obj->priv->conn)))
1123 		l = g_list_append (l, out);
1124 
1125 	if (gbemol_mpd_finish_and_handle (obj))
1126 		return NULL;
1127 	else
1128 		return l;
1129 }
1130 
gbemol_mpd_output_set_active(GbemolMpd * obj,gint id,gboolean active)1131 void gbemol_mpd_output_set_active (GbemolMpd* obj, gint id, gboolean active)
1132 {
1133 	if (active)
1134 		mpd_sendEnableOutputCommand (obj->priv->conn, id);
1135 	else
1136 		mpd_sendDisableOutputCommand (obj->priv->conn, id);
1137 
1138 	gbemol_mpd_finish_and_handle (obj);
1139 }
1140 
gbemol_mpd_get_crossfade(GbemolMpd * obj)1141 gint gbemol_mpd_get_crossfade (GbemolMpd* obj)
1142 {
1143 	if (obj->status && gbemol_mpd_check_permission (obj, "status"))
1144 		return obj->status->crossfade;
1145 	else
1146 		return -1;
1147 }
1148 
gbemol_mpd_get_state(GbemolMpd * obj)1149 gint gbemol_mpd_get_state (GbemolMpd* obj)
1150 {
1151 	if (obj->status && gbemol_mpd_check_permission (obj, "status"))
1152 		return obj->status->state;
1153 	else
1154 		return -1;
1155 }
1156 
gbemol_mpd_get_current_id(GbemolMpd * obj)1157 gint gbemol_mpd_get_current_id (GbemolMpd* obj)
1158 {
1159 	if (obj->status && gbemol_mpd_check_permission (obj, "status"))
1160 		return obj->status->songid;
1161 	else
1162 		return -1;
1163 }
1164 
gbemol_mpd_get_total_time(GbemolMpd * obj)1165 gint gbemol_mpd_get_total_time (GbemolMpd* obj)
1166 {
1167 	if (obj->status && gbemol_mpd_check_permission (obj, "status"))
1168 		return obj->status->totalTime;
1169 	else
1170 		return -1;
1171 }
1172 
gbemol_mpd_songdup(GbemolMpdSong * song)1173 GbemolMpdSong* gbemol_mpd_songdup(GbemolMpdSong* song)
1174 {
1175 	return mpd_songDup(song);
1176 }
1177