Initialize url_handlers
[libmpd.git] / src / libmpd.c
blob0782696c0fa6368bf154ef7fb7773bf2add49644
1 /* libmpd (high level libmpdclient library)
2 * Copyright (C) 2004-2009 Qball Cow <qball@sarine.nl>
3 * Project homepage: http://gmpcwiki.sarine.nl/
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.
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.
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.
20 #include <stdio.h>
21 #include <stdlib.h>
22 #define __USE_GNU
24 #include <string.h>
25 #include <stdarg.h>
26 #include <config.h>
27 #include <glib.h>
28 #include "debug_printf.h"
30 #include "libmpd.h"
31 #include "libmpd-internal.h"
33 static void mpd_free_queue_ob(MpdObj *mi);
34 static void mpd_server_free_commands(MpdObj *mi);
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
42 char * strndup(const char *s, size_t n)
44 size_t nAvail;
45 char *p;
47 if(!s) {
48 return NULL;
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;
56 memcpy(p, s, nAvail);
57 p[nAvail - 1] = 0;
58 return p;
60 #endif
61 /**
62 * @param state a #MpdServerState to initialize
64 * Initialize #MpdServerState. To stop duplicating code.
66 static void mpd_init_MpdServerState(MpdServerState *state)
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';
95 static MpdObj * mpd_create()
97 MpdObj * mi = malloc(sizeof(*mi));
98 if( mi == NULL )
100 /* should never happen on linux */
101 return NULL;
105 /* set default values */
106 /* we start not connected */
107 mi->connected = FALSE;
108 /* port 6600 is the default mpd port */
109 mi->port = 6600;
110 /* localhost */
111 mi->hostname = strdup("localhost");
112 /* no password */
113 mi->password = NULL;
114 /* 1 second timeout */
115 mi->connection_timeout = 1.0;
116 /* we have no connection pointer */
117 mi->connection = NULL;
118 /* no status */
119 mi->status = NULL;
120 /* no stats */
121 mi->stats = NULL;
122 mi->error = 0;
123 mi->error_mpd_code = 0;
124 mi->error_msg = NULL;
125 mi->CurrentSong = NULL;
126 /* info */
127 mpd_init_MpdServerState(&(mi->CurrentState));
128 mpd_init_MpdServerState(&(mi->OldState));
131 * Set signals to NULL
133 /* connection changed signal */
134 mi->the_connection_changed_callback = NULL;
135 mi->the_connection_changed_signal_userdata = NULL;
137 /* status changed */
138 mi->the_status_changed_callback = NULL;
139 mi->the_status_changed_signal_userdata = NULL;
141 /* error callback */
142 mi->the_error_callback = NULL;
143 mi->the_error_signal_userdata = NULL;
144 /* connection is locked because where not connected */
145 mi->connection_lock = TRUE;
147 /* set queue to NULL */
148 mi->queue = NULL;
149 /* search stuff */
150 mi->search_type = MPD_SEARCH_TYPE_NONE;
151 /* no need to initialize, but set it to anything anyway*/
152 mi->search_field = MPD_TAG_ITEM_ARTIST;
154 /* outputs */
155 mi->num_outputs = 0;
156 mi->output_states = NULL;
158 mi->url_handlers = NULL;
159 /* set 0 */
160 memset((mi->supported_tags), 0,sizeof(mi->supported_tags));
162 mi->has_idle = 0;
163 /* commands */
164 mi->commands = NULL;
165 return mi;
168 void mpd_free(MpdObj *mi)
170 debug_printf(DEBUG_INFO, "destroying MpdObj object\n");
171 if(mi->connected)
173 /* disconnect */
174 debug_printf(DEBUG_WARNING, "Connection still running, disconnecting\n");
175 mpd_disconnect(mi);
177 if(mi->hostname)
179 free(mi->hostname);
181 if(mi->password)
183 free(mi->password);
185 if(mi->error_msg)
187 free(mi->error_msg);
189 if(mi->connection)
191 /* obsolete */
192 mpd_closeConnection(mi->connection);
194 if(mi->status)
196 mpd_freeStatus(mi->status);
198 if(mi->stats)
200 mpd_freeStats(mi->stats);
202 if(mi->CurrentSong)
204 mpd_freeSong(mi->CurrentSong);
206 if(mi->url_handlers)
208 g_strfreev(mi->url_handlers);
209 mi->url_handlers = NULL;
211 mpd_free_queue_ob(mi);
212 mpd_server_free_commands(mi);
213 free(mi);
216 int mpd_check_error(MpdObj *mi)
218 if(mi == NULL)
220 debug_printf(DEBUG_ERROR, "mi == NULL?");
221 return MPD_ARGS_ERROR;
224 /* this shouldn't happen, ever */
225 if(mi->connection == NULL)
227 debug_printf(DEBUG_ERROR, "mi->connection == NULL?");
228 return MPD_FATAL_ERROR;
231 /* TODO: map these errors in the future */
232 mi->error = mi->connection->error;
233 mi->error_mpd_code = mi->connection->errorCode;
234 /*TODO: do I need to strdup this? */
235 if(!g_utf8_validate(mi->connection->errorStr, -1, NULL)){
236 mi->error_msg = g_locale_to_utf8(mi->connection->errorStr, -1, NULL, NULL,NULL);
237 }else{
238 mi->error_msg = g_strdup(mi->connection->errorStr);
241 if(mi->error_msg == NULL) mi->error_msg = g_strdup("Failed to convert error message to utf-8");
243 /* Check for permission */
244 /* First check for an error reported by MPD
245 * Then check what type of error mpd reported
247 if(mi->error == MPD_ERROR_ACK)
250 debug_printf(DEBUG_ERROR,"clearing errors in mpd_Connection: %i-%s", mi->connection->errorCode, mi->connection->errorStr);
251 mpd_clearError(mi->connection);
252 if (mi->the_error_callback)
254 debug_printf(DEBUG_ERROR, "Error callback 1 (ACK)");
255 if(mi->the_error_callback(mi, mi->error_mpd_code, mi->error_msg, mi->the_error_signal_userdata ))
257 debug_printf(DEBUG_ERROR, "Error callback told me to disconnect");
258 mpd_disconnect(mi);
259 free(mi->error_msg);
260 mi->error_msg = NULL;
262 return MPD_SERVER_ERROR;
265 free(mi->error_msg);
266 mi->error_msg = NULL;
267 return TRUE;
269 if(mi->error)
272 debug_printf(DEBUG_ERROR, "Following error occurred: %i: code: %i msg: %s", mi->error,mi->connection->errorCode, mi->error_msg);
274 if (mi->the_error_callback)
276 debug_printf(DEBUG_ERROR, "Error callback 2");
277 mi->the_error_callback(mi, mi->error, mi->error_msg, mi->the_error_signal_userdata );
279 mpd_disconnect(mi);
280 free(mi->error_msg);
281 mi->error_msg = NULL;
283 return MPD_SERVER_ERROR;
285 free(mi->error_msg);
286 mi->error_msg = NULL;
287 return MPD_OK;
292 int mpd_lock_conn(MpdObj *mi)
295 if(mi->connection_lock)
297 debug_printf(DEBUG_WARNING, "Failed to lock connection, already locked\n");
298 return MPD_LOCK_FAILED;
300 mi->connection_lock = TRUE;
301 return MPD_OK;
304 int mpd_unlock_conn(MpdObj *mi)
306 if(!mi->connection_lock)
308 debug_printf(DEBUG_ERROR, "Failed to unlock connection, already unlocked\n");
309 return MPD_LOCK_FAILED;
312 mi->connection_lock = FALSE;
314 return mpd_check_error(mi);
317 MpdObj * mpd_new_default()
319 debug_printf(DEBUG_INFO, "creating a new mpdInt object\n");
320 return mpd_create();
323 MpdObj *mpd_new(char *hostname, int port, char *password)
325 MpdObj *mi = mpd_create();
326 if(mi == NULL)
328 return NULL;
330 if(hostname != NULL)
332 mpd_set_hostname(mi, hostname);
334 if(port != 0)
336 mpd_set_port(mi, port);
338 if(password != NULL)
340 mpd_set_password(mi, password);
342 return mi;
346 const char * mpd_get_hostname(MpdObj *mi)
348 if(mi == NULL)
350 return NULL;
352 return mi->hostname;
355 int mpd_set_hostname(MpdObj *mi, char *hostname)
357 if(mi == NULL)
359 debug_printf(DEBUG_ERROR, "mi == NULL\n");
360 return MPD_ARGS_ERROR;
363 if(mi->hostname != NULL)
365 free(mi->hostname);
367 /* possible location todo some post processing of hostname */
368 mi->hostname = strdup(hostname);
369 return MPD_OK;
372 int mpd_set_password(MpdObj *mi, const char *password)
374 if(mi == NULL)
376 debug_printf(DEBUG_ERROR, "mi == NULL\n");
377 return MPD_ARGS_ERROR;
380 if(mi->password != NULL)
382 free(mi->password);
384 /* possible location todo some post processing of password */
385 mi->password = strdup(password);
386 return MPD_OK;
390 int mpd_send_password(MpdObj *mi)
392 if(!mi) return MPD_ARGS_ERROR;
393 if(mi->password && mpd_check_connected(mi) && strlen(mi->password))
395 if(mpd_lock_conn(mi))
397 debug_printf(DEBUG_WARNING, "failed to lock connection");
398 return MPD_LOCK_FAILED;
400 mpd_sendPasswordCommand(mi->connection, mi->password);
401 mpd_finishCommand(mi->connection);
402 if(mpd_unlock_conn(mi))
404 debug_printf(DEBUG_ERROR, "Failed to unlock connection\n");
405 return MPD_LOCK_FAILED;
407 mpd_server_get_allowed_commands(mi);
408 /*TODO: should I do it here, or in the
409 * mpd_server_get_allowed_command, so it also get's executed on
410 * connect
412 if((mi->the_status_changed_callback != NULL))
414 /** update the supported tags */
416 int i;
417 char **retv = mpd_server_get_tag_types(mi);
418 if(retv){
419 for(i=0;i<MPD_TAG_ITEM_ANY;i++)
421 int j=0;
422 for(j=0;retv[j] && strcasecmp(retv[j],mpdTagItemKeys[i]); j++);
423 if(retv[j]) mi->supported_tags[i] = TRUE;
424 else mi->supported_tags[i] = FALSE;
426 g_strfreev(retv);
428 printf("updated tags\n");
429 /* also always true */
430 mi->supported_tags[MPD_TAG_ITEM_FILENAME] = TRUE;
431 mi->supported_tags[MPD_TAG_ITEM_ANY] = TRUE;
433 /* If permission updates, we should also call an output update, The data might be available now. */
434 mi->the_status_changed_callback( mi,
435 MPD_CST_PERMISSION|MPD_CST_OUTPUT, mi->the_status_changed_signal_userdata );
438 return MPD_OK;
441 int mpd_set_port(MpdObj *mi, int port)
443 if(mi == NULL)
445 debug_printf(DEBUG_ERROR, "mi == NULL\n");
446 return MPD_ARGS_ERROR;
448 mi->port = port;
449 return MPD_OK;
452 int mpd_set_connection_timeout(MpdObj *mi, float timeout)
454 if(mi == NULL)
456 debug_printf(DEBUG_ERROR, "mi == NULL\n");
457 return MPD_ARGS_ERROR;
459 mi->connection_timeout = timeout;
460 if(mpd_check_connected(mi))
462 /*TODO: set timeout */
463 if(mpd_lock_conn(mi))
465 debug_printf(DEBUG_ERROR,"lock failed\n");
466 return MPD_LOCK_FAILED;
468 mpd_setConnectionTimeout(mi->connection, timeout);
469 mpd_finishCommand(mi->connection);
471 mpd_unlock_conn(mi);
474 return MPD_OK;
477 static void mpd_server_free_commands(MpdObj *mi)
479 if(mi->commands)
481 int i=0;
482 while(mi->commands[i].command_name)
484 free(mi->commands[i].command_name);
485 i++;
487 free(mi->commands);
488 mi->commands = NULL;
492 char *mpd_server_get_version(MpdObj *mi)
494 char *retval = NULL;
495 if(!mi || !mpd_check_connected(mi))
496 return NULL;
497 retval = malloc(10*sizeof(char));
498 snprintf(retval,10,"%i.%i.%i", mi->connection->version[0], mi->connection->version[1], mi->connection->version[2]);
499 /* always make sure the string is terminated */
500 retval[9] = '\0';
501 return retval;
504 int mpd_server_get_allowed_commands(MpdObj *mi)
506 char *temp = NULL;
507 int num_commands = 0;
508 if(!mi){
509 debug_printf(DEBUG_ERROR, "mi != NULL failed\n");
510 return MPD_ARGS_ERROR;
512 if(!mpd_check_connected(mi)) {
513 debug_printf(DEBUG_WARNING, "Not Connected");
514 return MPD_NOT_CONNECTED;
516 if(!mpd_server_check_version(mi,0,12,0)){
517 debug_printf(DEBUG_INFO, "Not supported by mpd");
518 return MPD_SERVER_NOT_SUPPORTED;
521 mpd_server_free_commands(mi);
523 if(mpd_lock_conn(mi))
525 debug_printf(DEBUG_ERROR, "lock failed");
526 return MPD_LOCK_FAILED;
528 mpd_sendCommandsCommand(mi->connection);
529 while((temp = mpd_getNextCommand(mi->connection)))
531 num_commands++;
532 mi->commands = realloc(mi->commands, (num_commands+1)*sizeof(MpdCommand));
533 mi->commands[num_commands-1].command_name = temp;
534 mi->commands[num_commands-1].enabled = TRUE;
535 mi->commands[num_commands].command_name = NULL;
536 mi->commands[num_commands].enabled = FALSE;
537 if(strcmp(mi->commands[num_commands-1].command_name, "idle") == 0) {
538 mi->has_idle = TRUE;
541 mpd_finishCommand(mi->connection);
542 mpd_sendNotCommandsCommand(mi->connection);
543 while((temp = mpd_getNextCommand(mi->connection)))
545 num_commands++;
546 mi->commands = realloc(mi->commands, (num_commands+1)*sizeof(MpdCommand));
547 mi->commands[num_commands-1].command_name = temp;
548 mi->commands[num_commands-1].enabled = FALSE;
549 mi->commands[num_commands].command_name = NULL;
550 mi->commands[num_commands].enabled = FALSE;
552 mpd_finishCommand(mi->connection);
554 if(mpd_unlock_conn(mi))
556 return MPD_LOCK_FAILED;
558 return MPD_OK;
563 int mpd_disconnect(MpdObj *mi)
566 /* lock */
567 mpd_lock_conn(mi);
568 debug_printf(DEBUG_INFO, "disconnecting\n");
570 if(mi->connection)
572 mpd_closeConnection(mi->connection);
573 mi->connection = NULL;
575 if(mi->status)
577 mpd_freeStatus(mi->status);
578 mi->status = NULL;
580 if(mi->stats)
582 mpd_freeStats(mi->stats);
583 mi->stats = NULL;
585 if(mi->CurrentSong)
587 mpd_freeSong(mi->CurrentSong);
588 mi->CurrentSong = NULL;
590 if(mi->url_handlers)
592 g_strfreev(mi->url_handlers);
593 mi->url_handlers = NULL;
595 mi->CurrentState.playlistid = -1;
596 mi->CurrentState.storedplaylistid = -1;
597 mi->CurrentState.state = -1;
598 mi->CurrentState.songid = -1;
599 mi->CurrentState.songpos = -1;
600 mi->CurrentState.nextsongid = -1;
601 mi->CurrentState.nextsongpos = -1;
602 mi->CurrentState.dbUpdateTime = 0;
603 mi->CurrentState.updatingDb = 0;
604 mi->CurrentState.repeat = -1;
605 mi->CurrentState.random = -1;
606 mi->CurrentState.volume = -2;
607 mi->CurrentState.xfade = -1;
608 mi->CurrentState.totaltime = 0;
609 mi->CurrentState.elapsedtime = 0;
610 mi->CurrentState.bitrate = 0;
611 mi->CurrentState.samplerate = 0;
612 mi->CurrentState.channels = 0;
613 mi->CurrentState.bits = 0;
614 mi->CurrentState.playlistLength = 0;
615 mi->CurrentState.error[0] = '\0';
616 /* search stuff */
617 mi->search_type = MPD_SEARCH_TYPE_NONE;
618 /* no need to initialize, but set it to anything anyway*/
619 mi->search_field = MPD_TAG_ITEM_ARTIST;
620 /* outputs */
621 mi->num_outputs = 0;
622 if(mi->output_states)
623 g_free(mi->output_states);
624 mi->output_states = NULL;
626 /* set 0 */
627 memset((mi->supported_tags), 0,sizeof(mi->supported_tags));
629 mi->has_idle = 0;
631 memcpy(&(mi->OldState), &(mi->CurrentState) , sizeof(MpdServerState));
633 mpd_free_queue_ob(mi);
634 mpd_server_free_commands(mi);
635 /*don't reset errors */
636 /* Remove this signal, we don't actually disconnect */
637 if(mi->connected)
639 /* set disconnect flag */
640 mi->connected = FALSE;
642 if(mi->the_connection_changed_callback != NULL)
644 mi->the_connection_changed_callback( mi, FALSE, mi->the_connection_changed_signal_userdata );
647 debug_printf(DEBUG_INFO, "Disconnect completed\n");
648 return MPD_OK;
650 int mpd_connect(MpdObj *mi)
652 return mpd_connect_real(mi,NULL);
654 int mpd_connect_real(MpdObj *mi,mpd_Connection *connection)
656 int retv;
657 if(mi == NULL)
659 /* should return some spiffy error here */
660 debug_printf(DEBUG_ERROR, "mi != NULL failed");
661 return MPD_ARGS_ERROR;
663 /* reset errors */
664 mi->error = 0;
665 mi->error_mpd_code = 0;
666 if(mi->error_msg != NULL)
668 free(mi->error_msg);
670 mi->error_msg = NULL;
672 debug_printf(DEBUG_INFO, "connecting\n");
673 mpd_init_MpdServerState(&(mi->CurrentState));
675 memcpy(&(mi->OldState), &(mi->CurrentState), sizeof(MpdServerState));
677 if(mi->connected)
679 /* disconnect */
680 mpd_disconnect(mi);
683 if(mi->hostname == NULL)
685 mpd_set_hostname(mi, "localhost");
687 /* make sure this is locked */
688 if(!mi->connection_lock)
690 mpd_lock_conn(mi);
692 if(connection) {
693 mi->connection = connection;
694 } else {
695 /* make timeout configurable */
696 mi->connection = mpd_newConnection(mi->hostname,mi->port,mi->connection_timeout);
698 if(mi->connection == NULL)
700 /* TODO: make seperate error message? */
701 return MPD_NOT_CONNECTED;
703 if(mpd_check_error(mi) != MPD_OK)
705 /* TODO: make seperate error message? */
706 return MPD_NOT_CONNECTED;
709 /* set connected state */
710 mi->connected = TRUE;
711 if(mpd_unlock_conn(mi))
713 return MPD_LOCK_FAILED;
716 /* get the commands we are allowed to use */
717 retv = mpd_server_get_allowed_commands(mi);
718 if(retv!= MPD_OK)
720 return retv;
722 if(mi->password && strlen(mi->password) > 0)
724 mpd_send_password(mi);
726 else
728 int i;
729 char **retv = mpd_server_get_tag_types(mi);
730 if(retv){
731 for(i=0;i<MPD_TAG_ITEM_ANY;i++)
733 int j=0;
734 for(j=0;retv[j] && strcasecmp(retv[j],mpdTagItemKeys[i]); j++);
735 if(retv[j]) mi->supported_tags[i] = TRUE;
736 else mi->supported_tags[i] = FALSE;
738 g_strfreev(retv);
740 /* also always true */
741 mi->supported_tags[MPD_TAG_ITEM_FILENAME] = TRUE;
742 mi->supported_tags[MPD_TAG_ITEM_ANY] = TRUE;
746 retv = mpd_server_update_outputs(mi);
747 if(retv != MPD_OK)
748 return retv;
751 retv = mpd_server_update_outputs(mi);
752 /** update the supported tags */
753 debug_printf(DEBUG_INFO, "Propagating connection changed");
755 if(mi->the_connection_changed_callback != NULL)
757 mi->the_connection_changed_callback( mi, TRUE, mi->the_connection_changed_signal_userdata );
760 if(retv != MPD_OK)
761 return retv;
763 debug_printf(DEBUG_INFO, "Connected to mpd");
764 return MPD_OK;
767 int mpd_check_connected(MpdObj *mi)
769 if(mi == NULL)
771 return FALSE;
773 return mi->connected;
777 /* SIGNALS */
778 void mpd_signal_connect_status_changed (MpdObj *mi, StatusChangedCallback status_changed, void *userdata)
780 if(mi == NULL)
782 debug_printf(DEBUG_ERROR, "mi != NULL failed");
783 return;
785 mi->the_status_changed_callback = status_changed;
786 mi->the_status_changed_signal_userdata = userdata;
790 void mpd_signal_connect_error(MpdObj *mi, ErrorCallback error_callback, void *userdata)
792 if(mi == NULL)
794 debug_printf(DEBUG_ERROR, "mi != NULL failed");
795 return;
797 mi->the_error_callback = error_callback;
798 mi->the_error_signal_userdata = userdata;
801 void mpd_signal_connect_connection_changed(MpdObj *mi, ConnectionChangedCallback connection_changed, void *userdata)
803 if(mi == NULL)
805 debug_printf(DEBUG_ERROR, "mi != NULL failed");
806 return;
808 mi->the_connection_changed_callback = connection_changed;
809 mi->the_connection_changed_signal_userdata = userdata;
813 /* more playlist */
814 /* MpdData Part */
815 MpdData *mpd_new_data_struct(void)
817 MpdData_real* data;
818 data = g_slice_new(MpdData_real);
819 data->type = 0;
821 data->tag_type = 0;
822 data->tag = NULL;
823 data->song = NULL;
824 data->directory = NULL;
825 data->playlist = NULL;
826 data->output_dev = NULL;
827 data->next = NULL;
828 data->prev = NULL;
829 data->first = NULL;
831 data->userdata = NULL;
832 data->freefunc = NULL;
833 return (MpdData*)data;
836 MpdData *mpd_new_data_struct_append(MpdData * data)
838 MpdData_real *data_real = (MpdData_real*)data;
839 if(data_real == NULL)
841 data_real = (MpdData_real*)mpd_new_data_struct();
842 data_real->first = data_real;
844 else
846 data_real->next = (MpdData_real*)mpd_new_data_struct();
847 data_real->next->prev = data_real;
848 data_real = data_real->next;
849 data_real->next = NULL;
850 data_real->first = data_real->prev->first;
852 return (MpdData*)data_real;
855 MpdData * mpd_data_get_first(MpdData const * const data)
857 MpdData_real const * const data_real = (MpdData_real const * const)data;
858 if(data_real != NULL)
860 return (MpdData*)data_real->first;
862 return NULL;
866 MpdData * mpd_data_get_next(MpdData * const data)
868 return mpd_data_get_next_real(data, TRUE);
871 MpdData * mpd_data_get_next_real(MpdData * const data, int kill_list)
873 MpdData_real *data_real = (MpdData_real*)data;
874 if (data_real != NULL)
876 if (data_real->next != NULL )
878 return (MpdData*)data_real->next;
880 else
882 if (kill_list) mpd_data_free((MpdData*)data_real);
883 return NULL;
886 return (MpdData*)data_real;
889 int mpd_data_is_last(MpdData const * const data)
891 MpdData_real const * const data_real = (MpdData_real const * const)data;
892 if(data_real != NULL)
894 if (data_real->next == NULL)
896 return TRUE;
899 return FALSE;
902 MpdData_head *mpd_data_get_head(MpdData const * const data) {
903 return ((MpdData_real*)data)->head;
906 MpdData* mpd_data_concatenate( MpdData * const first, MpdData * const second)
908 MpdData_real *first_real = (MpdData_real*)first;
909 MpdData_real *second_real = (MpdData_real*)second;
910 MpdData_real *first_head = NULL;
912 if ( first == NULL ) {
913 if ( second != NULL )
914 return (MpdData*)second_real;
915 else
916 return NULL;
917 } else {
918 if ( second == NULL )
919 return (MpdData*)first_real;
922 first_head = (MpdData_real *)mpd_data_get_first(first);
924 /* find last element in first data list */
925 while (!mpd_data_is_last((MpdData*)first_real)) first_real = (MpdData_real*)mpd_data_get_next_real((MpdData*)first_real, FALSE);
926 second_real =(MpdData_real*) mpd_data_get_first((MpdData*)second_real);
928 first_real->next = second_real;
929 second_real->prev = first_real;
931 /* I need to set all the -> first correct */
932 while (second_real)
934 second_real->first = first_head;
935 second_real = (MpdData_real*)mpd_data_get_next_real((MpdData*)second_real, FALSE);
938 return (MpdData*)first_head;
940 /**
941 * Deletes an item from the list. It returns the next item in the list.
942 * if that is not available, it will return the last item
944 MpdData * mpd_data_delete_item(MpdData *data)
946 MpdData_real *temp = NULL, *data_real = (MpdData_real*)data;
947 if(data_real == NULL) return NULL;
948 /* if there is a next item, fix the prev pointer of the next item */
949 if (data_real->next)
951 data_real->next->prev = data_real->prev;
952 temp = data_real->next;
954 /* if there is a previous item, fix the next pointer of the previous item */
955 if (data_real->prev)
957 /* the next item of the previous is the next item of the current */
958 data_real->prev->next = data_real->next;
959 /* temp is the previous item */
960 temp = data_real->prev;
963 /* fix first, if removed item is the first */
964 if(temp && temp->first == data_real)
966 MpdData_real *first,*node = temp;
967 /* get first */
968 for(;node->prev;node = node->prev);
969 first = node;
970 while(node){
971 node->first = first;
972 node = node->next;
975 /* make the removed row a valid list, so I can use the default free function to free it */
976 data_real->next = NULL;
977 data_real->prev = NULL;
978 data_real->first = data_real;
979 /* free it */
980 mpd_data_free((MpdData *)data_real);
982 return (MpdData *)temp;
985 void mpd_data_free(MpdData *data)
987 MpdData_real *data_real,*temp;
988 if(data == NULL)
990 debug_printf(DEBUG_ERROR, "data != NULL Failed");
991 return;
993 data_real = (MpdData_real *)mpd_data_get_first(data);
994 while(data_real){
995 temp = data_real;
996 if (data_real->type == MPD_DATA_TYPE_SONG) {
997 if(data_real->song) mpd_freeSong(data_real->song);
998 } else if (data_real->type == MPD_DATA_TYPE_OUTPUT_DEV) {
999 mpd_freeOutputElement(data_real->output_dev);
1000 } else if(data_real->type == MPD_DATA_TYPE_DIRECTORY) {
1001 if(data_real->directory)free(data_real->directory);
1002 } else if(data_real->type == MPD_DATA_TYPE_PLAYLIST) {
1003 if(data_real->playlist) mpd_freePlaylistFile(data_real->playlist);
1004 } else {
1005 free((void*)(data_real->tag));
1007 if(data_real->freefunc)
1009 if(data_real->userdata)
1010 data_real->freefunc(data_real->userdata);
1012 data_real = data_real->next;
1013 g_slice_free1(sizeof(*temp), temp);
1017 /* clean this up.. make one while loop */
1018 static void mpd_free_queue_ob(MpdObj *mi)
1020 MpdQueue *temp = NULL;
1021 if(mi == NULL)
1023 debug_printf(DEBUG_ERROR, "mi != NULL failed");
1024 return;
1026 if(mi->queue == NULL)
1028 debug_printf(DEBUG_INFO, "mi->queue != NULL failed, nothing to clean.");
1029 return;
1031 mi->queue = mi->queue->first;
1032 while(mi->queue != NULL)
1034 temp = mi->queue->next;
1036 if(mi->queue->path != NULL)
1038 free(mi->queue->path);
1041 free(mi->queue);
1042 mi->queue = temp;
1044 mi->queue = NULL;
1048 MpdQueue *mpd_new_queue_struct()
1050 MpdQueue* queue = malloc(sizeof(MpdQueue));
1052 queue->type = 0;
1053 queue->path = NULL;
1054 queue->id = 0;
1056 return queue;
1060 void mpd_queue_get_next(MpdObj *mi)
1062 if(mi->queue != NULL && mi->queue->next != NULL)
1064 mi->queue = mi->queue->next;
1066 else if(mi->queue->next == NULL)
1068 mpd_free_queue_ob(mi);
1069 mi->queue = NULL;
1073 long unsigned mpd_server_get_database_update_time(MpdObj *mi)
1075 if(!mpd_check_connected(mi))
1077 debug_printf(DEBUG_WARNING,"not connected\n");
1078 return MPD_NOT_CONNECTED;
1080 if(mpd_stats_check(mi) != MPD_OK)
1082 debug_printf(DEBUG_WARNING,"Failed grabbing status\n");
1083 return MPD_STATS_FAILED;
1085 return mi->stats->dbUpdateTime;
1089 MpdData * mpd_server_get_output_devices(MpdObj *mi)
1091 mpd_OutputEntity *output = NULL;
1092 MpdData *data = NULL;
1093 if(!mpd_check_connected(mi))
1095 debug_printf(DEBUG_WARNING,"not connected\n");
1096 return NULL;
1098 /* TODO: Check version */
1099 if(mpd_lock_conn(mi))
1101 debug_printf(DEBUG_ERROR,"lock failed\n");
1102 return NULL;
1105 mpd_sendOutputsCommand(mi->connection);
1106 while (( output = mpd_getNextOutput(mi->connection)) != NULL)
1108 data = mpd_new_data_struct_append(data);
1109 data->type = MPD_DATA_TYPE_OUTPUT_DEV;
1110 data->output_dev = output;
1112 mpd_finishCommand(mi->connection);
1114 /* unlock */
1115 if(mpd_unlock_conn(mi) != MPD_OK)
1117 if(data)mpd_data_free(data);
1118 return NULL;
1120 if(data == NULL)
1122 return NULL;
1124 return mpd_data_get_first(data);
1127 int mpd_server_set_output_device(MpdObj *mi,int device_id,int state)
1129 if(!mpd_check_connected(mi))
1131 debug_printf(DEBUG_WARNING,"not connected\n");
1132 return MPD_NOT_CONNECTED;
1134 if(mpd_lock_conn(mi))
1136 debug_printf(DEBUG_ERROR,"lock failed\n");
1137 return MPD_LOCK_FAILED;
1139 if(state)
1141 mpd_sendEnableOutputCommand(mi->connection, device_id);
1143 else
1145 mpd_sendDisableOutputCommand(mi->connection, device_id);
1147 mpd_finishCommand(mi->connection);
1149 mpd_unlock_conn(mi);
1150 mpd_status_queue_update(mi);
1151 return FALSE;
1154 int mpd_server_check_version(MpdObj *mi, int major, int minor, int micro)
1156 if(!mpd_check_connected(mi))
1158 debug_printf(DEBUG_WARNING,"not connected\n");
1159 return FALSE;
1161 if(major > mi->connection->version[0]) return FALSE;
1162 if(mi->connection->version[0] > major) return TRUE;
1163 if(minor > mi->connection->version[1]) return FALSE;
1164 if(mi->connection->version[1] > minor) return TRUE;
1165 if(micro > mi->connection->version[2]) return FALSE;
1166 if(mi->connection->version[2] > micro) return TRUE;
1167 return TRUE;
1170 int mpd_server_check_command_allowed(MpdObj *mi, const char *command)
1172 int i;
1173 if(!mi || !command) return MPD_SERVER_COMMAND_ERROR;
1174 /* when we are connected to a mpd server that doesn't support commands and not commands
1175 * feature. (like mpd 0.11.5) allow everything
1177 if(!mpd_server_check_version(mi, 0,12,0)) return MPD_SERVER_COMMAND_ALLOWED;
1179 * Also when somehow we failted to get commands
1181 if(mi->commands == NULL) return MPD_SERVER_COMMAND_ALLOWED;
1185 for(i=0;mi->commands[i].command_name;i++)
1187 if(!strcasecmp(mi->commands[i].command_name, command))
1188 return mi->commands[i].enabled;
1190 return MPD_SERVER_COMMAND_NOT_SUPPORTED;
1193 char ** mpd_server_get_url_handlers(MpdObj *mi)
1195 char *temp = NULL;
1196 int i=0;
1197 char **retv = NULL;
1198 if(!mpd_check_connected(mi))
1200 debug_printf(DEBUG_WARNING,"not connected\n");
1201 return FALSE;
1203 if(mi->url_handlers){
1204 return g_strdupv(mi->url_handlers);
1206 if(mpd_lock_conn(mi))
1208 debug_printf(DEBUG_ERROR,"lock failed\n");
1209 return NULL;
1212 * Fetch url handlers and store them
1214 mpd_sendUrlHandlersCommand(mi->connection);
1215 while((temp = mpd_getNextHandler(mi->connection)) != NULL)
1217 mi->url_handlers = realloc(mi->url_handlers,(i+2)*sizeof(*mi->url_handlers));
1218 mi->url_handlers[i] = temp;
1219 mi->url_handlers[i+1] = NULL;
1220 i++;
1222 mpd_finishCommand(mi->connection);
1225 mpd_unlock_conn(mi);
1226 /* Return copy */
1227 return g_strdupv(mi->url_handlers);
1229 char ** mpd_server_get_tag_types(MpdObj *mi)
1231 char *temp = NULL;
1232 int i=0;
1233 char **retv = NULL;
1234 if(!mpd_check_connected(mi))
1236 debug_printf(DEBUG_WARNING,"not connected\n");
1237 return FALSE;
1239 if(mpd_lock_conn(mi))
1241 debug_printf(DEBUG_ERROR,"lock failed\n");
1242 return NULL;
1244 mpd_sendTagTypesCommand(mi->connection);
1245 while((temp = mpd_getNextTagType(mi->connection)) != NULL)
1247 retv = realloc(retv,(i+2)*sizeof(*retv));
1248 retv[i] = temp;
1249 retv[i+1] = NULL;
1250 i++;
1252 mpd_finishCommand(mi->connection);
1255 mpd_unlock_conn(mi);
1256 return retv;
1259 int mpd_misc_get_tag_by_name(char *name)
1261 int i;
1262 if(name == NULL)
1264 return MPD_ARGS_ERROR;
1266 for(i=0; i < MPD_TAG_NUM_OF_ITEM_TYPES; i++)
1268 if(!strcasecmp(mpdTagItemKeys[i], name))
1270 return i;
1273 return MPD_TAG_NOT_FOUND;
1276 int mpd_server_update_outputs(MpdObj *mi)
1278 mpd_OutputEntity *output = NULL;
1279 if(!mpd_check_connected(mi))
1281 debug_printf(DEBUG_WARNING,"not connected\n");
1282 return MPD_NOT_CONNECTED;
1284 if(mpd_lock_conn(mi))
1286 debug_printf(DEBUG_ERROR,"lock failed\n");
1287 return MPD_LOCK_FAILED;
1289 mpd_sendOutputsCommand(mi->connection);
1290 while (( output = mpd_getNextOutput(mi->connection)) != NULL)
1292 mi->num_outputs++;
1293 mi->output_states = realloc(mi->output_states,mi->num_outputs*sizeof(int));
1294 mi->output_states[mi->num_outputs-1] = FALSE;/*output->enabled;*/
1295 mpd_freeOutputElement(output);
1297 mpd_finishCommand(mi->connection);
1298 return mpd_unlock_conn(mi);
1301 int mpd_server_has_idle(MpdObj *mi)
1303 return mi->has_idle;
1306 int mpd_server_tag_supported(MpdObj *mi, int tag)
1308 if(!mi) return FALSE;
1309 if(tag < 0 || tag >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1310 return FALSE;
1312 return mi->supported_tags[tag];