Update supported tags before telling the world that/if we support it
[libmpd.git] / src / libmpd.c
blobcb61cbe0d98d99fcfcce21476f62e3b592895bc7
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->dbUpdateTime = 0;
74 state->updatingDb = 0;
75 state->repeat = -1;
76 state->random = -1;
77 state->volume = -2;
78 state->xfade = -1;
79 state->totaltime = 0;
80 state->elapsedtime = 0;
81 state->bitrate = 0;
82 state->samplerate = 0;
83 state->channels = 0;
84 state->bits = 0;
85 state->playlistLength = 0;
86 state->error[0] = '\0';
91 static MpdObj * mpd_create()
93 MpdObj * mi = malloc(sizeof(*mi));
94 if( mi == NULL )
96 /* should never happen on linux */
97 return NULL;
101 /* set default values */
102 /* we start not connected */
103 mi->connected = FALSE;
104 /* port 6600 is the default mpd port */
105 mi->port = 6600;
106 /* localhost */
107 mi->hostname = strdup("localhost");
108 /* no password */
109 mi->password = NULL;
110 /* 1 second timeout */
111 mi->connection_timeout = 1.0;
112 /* we have no connection pointer */
113 mi->connection = NULL;
114 /* no status */
115 mi->status = NULL;
116 /* no stats */
117 mi->stats = NULL;
118 mi->error = 0;
119 mi->error_mpd_code = 0;
120 mi->error_msg = NULL;
121 mi->CurrentSong = NULL;
122 /* info */
123 mpd_init_MpdServerState(&(mi->CurrentState));
124 mpd_init_MpdServerState(&(mi->OldState));
127 * Set signals to NULL
129 /* connection changed signal */
130 mi->the_connection_changed_callback = NULL;
131 mi->the_connection_changed_signal_userdata = NULL;
133 /* status changed */
134 mi->the_status_changed_callback = NULL;
135 mi->the_status_changed_signal_userdata = NULL;
137 /* error callback */
138 mi->the_error_callback = NULL;
139 mi->the_error_signal_userdata = NULL;
140 /* connection is locked because where not connected */
141 mi->connection_lock = TRUE;
143 /* set queue to NULL */
144 mi->queue = NULL;
145 /* search stuff */
146 mi->search_type = MPD_SEARCH_TYPE_NONE;
147 /* no need to initialize, but set it to anything anyway*/
148 mi->search_field = MPD_TAG_ITEM_ARTIST;
150 /* outputs */
151 mi->num_outputs = 0;
152 mi->output_states = NULL;
153 /* set 0 */
154 memset((mi->supported_tags), 0,sizeof(mi->supported_tags));
156 mi->has_idle = 0;
157 /* commands */
158 mi->commands = NULL;
159 return mi;
162 void mpd_free(MpdObj *mi)
164 debug_printf(DEBUG_INFO, "destroying MpdObj object\n");
165 if(mi->connected)
167 /* disconnect */
168 debug_printf(DEBUG_WARNING, "Connection still running, disconnecting\n");
169 mpd_disconnect(mi);
171 if(mi->hostname)
173 free(mi->hostname);
175 if(mi->password)
177 free(mi->password);
179 if(mi->error_msg)
181 free(mi->error_msg);
183 if(mi->connection)
185 /* obsolete */
186 mpd_closeConnection(mi->connection);
188 if(mi->status)
190 mpd_freeStatus(mi->status);
192 if(mi->stats)
194 mpd_freeStats(mi->stats);
196 if(mi->CurrentSong)
198 mpd_freeSong(mi->CurrentSong);
200 mpd_free_queue_ob(mi);
201 mpd_server_free_commands(mi);
202 free(mi);
205 int mpd_check_error(MpdObj *mi)
207 if(mi == NULL)
209 debug_printf(DEBUG_ERROR, "mi == NULL?");
210 return MPD_ARGS_ERROR;
213 /* this shouldn't happen, ever */
214 if(mi->connection == NULL)
216 debug_printf(DEBUG_ERROR, "mi->connection == NULL?");
217 return MPD_FATAL_ERROR;
220 /* TODO: map these errors in the future */
221 mi->error = mi->connection->error;
222 mi->error_mpd_code = mi->connection->errorCode;
223 /*TODO: do I need to strdup this? */
224 if(!g_utf8_validate(mi->connection->errorStr, -1, NULL)){
225 mi->error_msg = g_locale_to_utf8(mi->connection->errorStr, -1, NULL, NULL,NULL);
226 }else{
227 mi->error_msg = g_strdup(mi->connection->errorStr);
230 if(mi->error_msg == NULL) mi->error_msg = g_strdup("Failed to convert error message to utf-8");
232 /* Check for permission */
233 /* First check for an error reported by MPD
234 * Then check what type of error mpd reported
236 if(mi->error == MPD_ERROR_ACK)
239 debug_printf(DEBUG_ERROR,"clearing errors in mpd_Connection: %i-%s", mi->connection->errorCode, mi->connection->errorStr);
240 mpd_clearError(mi->connection);
241 if (mi->the_error_callback)
243 debug_printf(DEBUG_ERROR, "Error callback 1 (ACK)");
244 if(mi->the_error_callback(mi, mi->error_mpd_code, mi->error_msg, mi->the_error_signal_userdata ))
246 debug_printf(DEBUG_ERROR, "Error callback told me to disconnect");
247 mpd_disconnect(mi);
248 free(mi->error_msg);
249 mi->error_msg = NULL;
251 return MPD_SERVER_ERROR;
254 free(mi->error_msg);
255 mi->error_msg = NULL;
256 return TRUE;
258 if(mi->error)
261 debug_printf(DEBUG_ERROR, "Following error occurred: %i: code: %i msg: %s", mi->error,mi->connection->errorCode, mi->error_msg);
263 if (mi->the_error_callback)
265 debug_printf(DEBUG_ERROR, "Error callback 2");
266 mi->the_error_callback(mi, mi->error, mi->error_msg, mi->the_error_signal_userdata );
268 mpd_disconnect(mi);
269 free(mi->error_msg);
270 mi->error_msg = NULL;
272 return MPD_SERVER_ERROR;
274 free(mi->error_msg);
275 mi->error_msg = NULL;
276 return MPD_OK;
281 int mpd_lock_conn(MpdObj *mi)
284 if(mi->connection_lock)
286 debug_printf(DEBUG_WARNING, "Failed to lock connection, already locked\n");
287 return MPD_LOCK_FAILED;
289 mi->connection_lock = TRUE;
290 return MPD_OK;
293 int mpd_unlock_conn(MpdObj *mi)
295 if(!mi->connection_lock)
297 debug_printf(DEBUG_ERROR, "Failed to unlock connection, already unlocked\n");
298 return MPD_LOCK_FAILED;
301 mi->connection_lock = FALSE;
303 return mpd_check_error(mi);
306 MpdObj * mpd_new_default()
308 debug_printf(DEBUG_INFO, "creating a new mpdInt object\n");
309 return mpd_create();
312 MpdObj *mpd_new(char *hostname, int port, char *password)
314 MpdObj *mi = mpd_create();
315 if(mi == NULL)
317 return NULL;
319 if(hostname != NULL)
321 mpd_set_hostname(mi, hostname);
323 if(port != 0)
325 mpd_set_port(mi, port);
327 if(password != NULL)
329 mpd_set_password(mi, password);
331 return mi;
335 const char * mpd_get_hostname(MpdObj *mi)
337 if(mi == NULL)
339 return NULL;
341 return mi->hostname;
344 int mpd_set_hostname(MpdObj *mi, char *hostname)
346 if(mi == NULL)
348 debug_printf(DEBUG_ERROR, "mi == NULL\n");
349 return MPD_ARGS_ERROR;
352 if(mi->hostname != NULL)
354 free(mi->hostname);
356 /* possible location todo some post processing of hostname */
357 mi->hostname = strdup(hostname);
358 return MPD_OK;
361 int mpd_set_password(MpdObj *mi, const char *password)
363 if(mi == NULL)
365 debug_printf(DEBUG_ERROR, "mi == NULL\n");
366 return MPD_ARGS_ERROR;
369 if(mi->password != NULL)
371 free(mi->password);
373 /* possible location todo some post processing of password */
374 mi->password = strdup(password);
375 return MPD_OK;
379 int mpd_send_password(MpdObj *mi)
381 if(!mi) return MPD_ARGS_ERROR;
382 if(mi->password && mpd_check_connected(mi) && strlen(mi->password))
384 if(mpd_lock_conn(mi))
386 debug_printf(DEBUG_WARNING, "failed to lock connection");
387 return MPD_LOCK_FAILED;
389 mpd_sendPasswordCommand(mi->connection, mi->password);
390 mpd_finishCommand(mi->connection);
391 if(mpd_unlock_conn(mi))
393 debug_printf(DEBUG_ERROR, "Failed to unlock connection\n");
394 return MPD_LOCK_FAILED;
396 mpd_server_get_allowed_commands(mi);
397 /*TODO: should I do it here, or in the
398 * mpd_server_get_allowed_command, so it also get's executed on
399 * connect
401 if((mi->the_status_changed_callback != NULL))
403 mi->the_status_changed_callback( mi,
404 MPD_CST_PERMISSION, mi->the_status_changed_signal_userdata );
407 return MPD_OK;
410 int mpd_set_port(MpdObj *mi, int port)
412 if(mi == NULL)
414 debug_printf(DEBUG_ERROR, "mi == NULL\n");
415 return MPD_ARGS_ERROR;
417 mi->port = port;
418 return MPD_OK;
421 int mpd_set_connection_timeout(MpdObj *mi, float timeout)
423 if(mi == NULL)
425 debug_printf(DEBUG_ERROR, "mi == NULL\n");
426 return MPD_ARGS_ERROR;
428 mi->connection_timeout = timeout;
429 if(mpd_check_connected(mi))
431 /*TODO: set timeout */
432 if(mpd_lock_conn(mi))
434 debug_printf(DEBUG_ERROR,"lock failed\n");
435 return MPD_LOCK_FAILED;
437 mpd_setConnectionTimeout(mi->connection, timeout);
438 mpd_finishCommand(mi->connection);
440 mpd_unlock_conn(mi);
443 return MPD_OK;
446 static void mpd_server_free_commands(MpdObj *mi)
448 if(mi->commands)
450 int i=0;
451 while(mi->commands[i].command_name)
453 free(mi->commands[i].command_name);
454 i++;
456 free(mi->commands);
457 mi->commands = NULL;
461 char *mpd_server_get_version(MpdObj *mi)
463 char *retval = NULL;
464 if(!mi || !mpd_check_connected(mi))
465 return NULL;
466 retval = malloc(10*sizeof(char));
467 snprintf(retval,10,"%i.%i.%i", mi->connection->version[0], mi->connection->version[1], mi->connection->version[2]);
468 /* always make sure the string is terminated */
469 retval[9] = '\0';
470 return retval;
473 int mpd_server_get_allowed_commands(MpdObj *mi)
475 char *temp = NULL;
476 int num_commands = 0;
477 if(!mi){
478 debug_printf(DEBUG_ERROR, "mi != NULL failed\n");
479 return MPD_ARGS_ERROR;
481 if(!mpd_check_connected(mi)) {
482 debug_printf(DEBUG_WARNING, "Not Connected");
483 return MPD_NOT_CONNECTED;
485 if(!mpd_server_check_version(mi,0,12,0)){
486 debug_printf(DEBUG_INFO, "Not supported by mpd");
487 return MPD_SERVER_NOT_SUPPORTED;
490 mpd_server_free_commands(mi);
492 if(mpd_lock_conn(mi))
494 debug_printf(DEBUG_ERROR, "lock failed");
495 return MPD_LOCK_FAILED;
497 mpd_sendCommandsCommand(mi->connection);
498 while((temp = mpd_getNextCommand(mi->connection)))
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 = TRUE;
504 mi->commands[num_commands].command_name = NULL;
505 mi->commands[num_commands].enabled = FALSE;
506 if(strcmp(mi->commands[num_commands-1].command_name, "idle") == 0) {
507 mi->has_idle = TRUE;
510 mpd_finishCommand(mi->connection);
511 mpd_sendNotCommandsCommand(mi->connection);
512 while((temp = mpd_getNextCommand(mi->connection)))
514 num_commands++;
515 mi->commands = realloc(mi->commands, (num_commands+1)*sizeof(MpdCommand));
516 mi->commands[num_commands-1].command_name = temp;
517 mi->commands[num_commands-1].enabled = FALSE;
518 mi->commands[num_commands].command_name = NULL;
519 mi->commands[num_commands].enabled = FALSE;
521 mpd_finishCommand(mi->connection);
523 if(mpd_unlock_conn(mi))
525 return MPD_LOCK_FAILED;
527 return MPD_OK;
532 int mpd_disconnect(MpdObj *mi)
535 /* lock */
536 mpd_lock_conn(mi);
537 debug_printf(DEBUG_INFO, "disconnecting\n");
539 if(mi->connection)
541 mpd_closeConnection(mi->connection);
542 mi->connection = NULL;
544 if(mi->status)
546 mpd_freeStatus(mi->status);
547 mi->status = NULL;
549 if(mi->stats)
551 mpd_freeStats(mi->stats);
552 mi->stats = NULL;
554 if(mi->CurrentSong)
556 mpd_freeSong(mi->CurrentSong);
557 mi->CurrentSong = NULL;
559 mi->CurrentState.playlistid = -1;
560 mi->CurrentState.storedplaylistid = -1;
561 mi->CurrentState.state = -1;
562 mi->CurrentState.songid = -1;
563 mi->CurrentState.songpos = -1;
564 mi->CurrentState.dbUpdateTime = 0;
565 mi->CurrentState.updatingDb = 0;
566 mi->CurrentState.repeat = -1;
567 mi->CurrentState.random = -1;
568 mi->CurrentState.volume = -2;
569 mi->CurrentState.xfade = -1;
570 mi->CurrentState.totaltime = 0;
571 mi->CurrentState.elapsedtime = 0;
572 mi->CurrentState.bitrate = 0;
573 mi->CurrentState.samplerate = 0;
574 mi->CurrentState.channels = 0;
575 mi->CurrentState.bits = 0;
576 mi->CurrentState.playlistLength = 0;
577 mi->CurrentState.error[0] = '\0';
578 /* search stuff */
579 mi->search_type = MPD_SEARCH_TYPE_NONE;
580 /* no need to initialize, but set it to anything anyway*/
581 mi->search_field = MPD_TAG_ITEM_ARTIST;
582 /* outputs */
583 mi->num_outputs = 0;
584 if(mi->output_states)
585 g_free(mi->output_states);
586 mi->output_states = NULL;
588 /* set 0 */
589 memset((mi->supported_tags), 0,sizeof(mi->supported_tags));
591 mi->has_idle = 0;
593 memcpy(&(mi->OldState), &(mi->CurrentState) , sizeof(MpdServerState));
595 mpd_free_queue_ob(mi);
596 mpd_server_free_commands(mi);
597 /*don't reset errors */
598 /* Remove this signal, we don't actually disconnect */
599 if(mi->connected)
601 /* set disconnect flag */
602 mi->connected = FALSE;
604 if(mi->the_connection_changed_callback != NULL)
606 mi->the_connection_changed_callback( mi, FALSE, mi->the_connection_changed_signal_userdata );
609 debug_printf(DEBUG_INFO, "Disconnect completed\n");
610 return MPD_OK;
612 int mpd_connect(MpdObj *mi)
614 return mpd_connect_real(mi,NULL);
616 int mpd_connect_real(MpdObj *mi,mpd_Connection *connection)
618 int retv;
619 if(mi == NULL)
621 /* should return some spiffy error here */
622 debug_printf(DEBUG_ERROR, "mi != NULL failed");
623 return MPD_ARGS_ERROR;
625 /* reset errors */
626 mi->error = 0;
627 mi->error_mpd_code = 0;
628 if(mi->error_msg != NULL)
630 free(mi->error_msg);
632 mi->error_msg = NULL;
634 debug_printf(DEBUG_INFO, "connecting\n");
635 mpd_init_MpdServerState(&(mi->CurrentState));
637 memcpy(&(mi->OldState), &(mi->CurrentState), sizeof(MpdServerState));
639 if(mi->connected)
641 /* disconnect */
642 mpd_disconnect(mi);
645 if(mi->hostname == NULL)
647 mpd_set_hostname(mi, "localhost");
649 /* make sure this is locked */
650 if(!mi->connection_lock)
652 mpd_lock_conn(mi);
654 if(connection) {
655 mi->connection = connection;
656 } else {
657 /* make timeout configurable */
658 mi->connection = mpd_newConnection(mi->hostname,mi->port,mi->connection_timeout);
660 if(mi->connection == NULL)
662 /* TODO: make seperate error message? */
663 return MPD_NOT_CONNECTED;
665 if(mpd_check_error(mi) != MPD_OK)
667 /* TODO: make seperate error message? */
668 return MPD_NOT_CONNECTED;
671 /* set connected state */
672 mi->connected = TRUE;
673 if(mpd_unlock_conn(mi))
675 return MPD_LOCK_FAILED;
678 /* get the commands we are allowed to use */
679 retv = mpd_server_get_allowed_commands(mi);
680 if(retv!= MPD_OK)
682 return retv;
686 retv = mpd_server_update_outputs(mi);
687 if(retv != MPD_OK)
688 return retv;
691 retv = mpd_server_update_outputs(mi);
692 /** update the supported tags */
694 int i;
695 char **retv = mpd_server_get_tag_types(mi);
696 if(retv){
697 for(i=0;i<MPD_TAG_ITEM_ANY;i++)
699 int j=0;
700 for(j=0;retv[j] && strcasecmp(retv[j],mpdTagItemKeys[i]); j++);
701 if(retv[j]) mi->supported_tags[i] = TRUE;
702 else mi->supported_tags[i] = FALSE;
704 g_strfreev(retv);
706 /* also always true */
707 mi->supported_tags[MPD_TAG_ITEM_FILENAME] = TRUE;
708 mi->supported_tags[MPD_TAG_ITEM_ANY] = TRUE;
709 printf("Supported tags by server\n");
710 for(i=0;i< MPD_TAG_NUM_OF_ITEM_TYPES;i++){
711 printf("%20s: %s\n", mpdTagItemKeys[i], (mi->supported_tags[i])?"true":"false");
714 debug_printf(DEBUG_INFO, "Propagating connection changed");
716 if(mi->the_connection_changed_callback != NULL)
718 mi->the_connection_changed_callback( mi, TRUE, mi->the_connection_changed_signal_userdata );
721 if(retv != MPD_OK)
722 return retv;
724 debug_printf(DEBUG_INFO, "Connected to mpd");
725 return MPD_OK;
728 int mpd_check_connected(MpdObj *mi)
730 if(mi == NULL)
732 return FALSE;
734 return mi->connected;
738 /* SIGNALS */
739 void mpd_signal_connect_status_changed (MpdObj *mi, StatusChangedCallback status_changed, void *userdata)
741 if(mi == NULL)
743 debug_printf(DEBUG_ERROR, "mi != NULL failed");
744 return;
746 mi->the_status_changed_callback = status_changed;
747 mi->the_status_changed_signal_userdata = userdata;
751 void mpd_signal_connect_error(MpdObj *mi, ErrorCallback error_callback, void *userdata)
753 if(mi == NULL)
755 debug_printf(DEBUG_ERROR, "mi != NULL failed");
756 return;
758 mi->the_error_callback = error_callback;
759 mi->the_error_signal_userdata = userdata;
762 void mpd_signal_connect_connection_changed(MpdObj *mi, ConnectionChangedCallback connection_changed, void *userdata)
764 if(mi == NULL)
766 debug_printf(DEBUG_ERROR, "mi != NULL failed");
767 return;
769 mi->the_connection_changed_callback = connection_changed;
770 mi->the_connection_changed_signal_userdata = userdata;
774 /* more playlist */
775 /* MpdData Part */
776 MpdData *mpd_new_data_struct(void)
778 MpdData_real* data;
779 data = g_slice_new(MpdData_real);
780 data->type = 0;
782 data->tag_type = 0;
783 data->tag = NULL;
784 data->song = NULL;
785 data->directory = NULL;
786 data->playlist = NULL;
787 data->output_dev = NULL;
788 data->next = NULL;
789 data->prev = NULL;
790 data->first = NULL;
792 data->userdata = NULL;
793 data->freefunc = NULL;
794 return (MpdData*)data;
797 MpdData *mpd_new_data_struct_append(MpdData * data)
799 MpdData_real *data_real = (MpdData_real*)data;
800 if(data_real == NULL)
802 data_real = (MpdData_real*)mpd_new_data_struct();
803 data_real->first = data_real;
805 else
807 data_real->next = (MpdData_real*)mpd_new_data_struct();
808 data_real->next->prev = data_real;
809 data_real = data_real->next;
810 data_real->next = NULL;
811 data_real->first = data_real->prev->first;
813 return (MpdData*)data_real;
816 MpdData * mpd_data_get_first(MpdData const * const data)
818 MpdData_real const * const data_real = (MpdData_real const * const)data;
819 if(data_real != NULL)
821 return (MpdData*)data_real->first;
823 return NULL;
827 MpdData * mpd_data_get_next(MpdData * const data)
829 return mpd_data_get_next_real(data, TRUE);
832 MpdData * mpd_data_get_next_real(MpdData * const data, int kill_list)
834 MpdData_real *data_real = (MpdData_real*)data;
835 if (data_real != NULL)
837 if (data_real->next != NULL )
839 return (MpdData*)data_real->next;
841 else
843 if (kill_list) mpd_data_free((MpdData*)data_real);
844 return NULL;
847 return (MpdData*)data_real;
850 int mpd_data_is_last(MpdData const * const data)
852 MpdData_real const * const data_real = (MpdData_real const * const)data;
853 if(data_real != NULL)
855 if (data_real->next == NULL)
857 return TRUE;
860 return FALSE;
863 MpdData_head *mpd_data_get_head(MpdData const * const data) {
864 return ((MpdData_real*)data)->head;
867 MpdData* mpd_data_concatenate( MpdData * const first, MpdData * const second)
869 MpdData_real *first_real = (MpdData_real*)first;
870 MpdData_real *second_real = (MpdData_real*)second;
871 MpdData_real *first_head = NULL;
873 if ( first == NULL ) {
874 if ( second != NULL )
875 return (MpdData*)second_real;
876 else
877 return NULL;
878 } else {
879 if ( second == NULL )
880 return (MpdData*)first_real;
883 first_head = (MpdData_real *)mpd_data_get_first(first);
885 /* find last element in first data list */
886 while (!mpd_data_is_last((MpdData*)first_real)) first_real = (MpdData_real*)mpd_data_get_next_real((MpdData*)first_real, FALSE);
887 second_real =(MpdData_real*) mpd_data_get_first((MpdData*)second_real);
889 first_real->next = second_real;
890 second_real->prev = first_real;
892 /* I need to set all the -> first correct */
893 while (second_real)
895 second_real->first = first_head;
896 second_real = (MpdData_real*)mpd_data_get_next_real((MpdData*)second_real, FALSE);
899 return (MpdData*)first_head;
901 /**
902 * Deletes an item from the list. It returns the next item in the list.
903 * if that is not available, it will return the last item
905 MpdData * mpd_data_delete_item(MpdData *data)
907 MpdData_real *temp = NULL, *data_real = (MpdData_real*)data;
908 if(data_real == NULL) return NULL;
909 /* if there is a next item, fix the prev pointer of the next item */
910 if (data_real->next)
912 data_real->next->prev = data_real->prev;
913 temp = data_real->next;
915 /* if there is a previous item, fix the next pointer of the previous item */
916 if (data_real->prev)
918 /* the next item of the previous is the next item of the current */
919 data_real->prev->next = data_real->next;
920 /* temp is the previous item */
921 temp = data_real->prev;
924 /* fix first, if removed item is the first */
925 if(temp && temp->first == data_real)
927 MpdData_real *first,*node = temp;
928 /* get first */
929 for(;node->prev;node = node->prev);
930 first = node;
931 while(node){
932 node->first = first;
933 node = node->next;
936 /* make the removed row a valid list, so I can use the default free function to free it */
937 data_real->next = NULL;
938 data_real->prev = NULL;
939 data_real->first = data_real;
940 /* free it */
941 mpd_data_free((MpdData *)data_real);
943 return (MpdData *)temp;
946 void mpd_data_free(MpdData *data)
948 MpdData_real *data_real,*temp;
949 if(data == NULL)
951 debug_printf(DEBUG_ERROR, "data != NULL Failed");
952 return;
954 data_real = (MpdData_real *)mpd_data_get_first(data);
955 while(data_real){
956 temp = data_real;
957 if (data_real->type == MPD_DATA_TYPE_SONG) {
958 if(data_real->song) mpd_freeSong(data_real->song);
959 } else if (data_real->type == MPD_DATA_TYPE_OUTPUT_DEV) {
960 mpd_freeOutputElement(data_real->output_dev);
961 } else if(data_real->type == MPD_DATA_TYPE_DIRECTORY) {
962 if(data_real->directory)free(data_real->directory);
963 } else if(data_real->type == MPD_DATA_TYPE_PLAYLIST) {
964 if(data_real->playlist) mpd_freePlaylistFile(data_real->playlist);
965 } else {
966 free((void*)(data_real->tag));
968 if(data_real->freefunc)
970 if(data_real->userdata)
971 data_real->freefunc(data_real->userdata);
973 data_real = data_real->next;
974 g_slice_free1(sizeof(*temp), temp);
978 /* clean this up.. make one while loop */
979 static void mpd_free_queue_ob(MpdObj *mi)
981 MpdQueue *temp = NULL;
982 if(mi == NULL)
984 debug_printf(DEBUG_ERROR, "mi != NULL failed");
985 return;
987 if(mi->queue == NULL)
989 debug_printf(DEBUG_INFO, "mi->queue != NULL failed, nothing to clean.");
990 return;
992 mi->queue = mi->queue->first;
993 while(mi->queue != NULL)
995 temp = mi->queue->next;
997 if(mi->queue->path != NULL)
999 free(mi->queue->path);
1002 free(mi->queue);
1003 mi->queue = temp;
1005 mi->queue = NULL;
1009 MpdQueue *mpd_new_queue_struct()
1011 MpdQueue* queue = malloc(sizeof(MpdQueue));
1013 queue->type = 0;
1014 queue->path = NULL;
1015 queue->id = 0;
1017 return queue;
1021 void mpd_queue_get_next(MpdObj *mi)
1023 if(mi->queue != NULL && mi->queue->next != NULL)
1025 mi->queue = mi->queue->next;
1027 else if(mi->queue->next == NULL)
1029 mpd_free_queue_ob(mi);
1030 mi->queue = NULL;
1034 long unsigned mpd_server_get_database_update_time(MpdObj *mi)
1036 if(!mpd_check_connected(mi))
1038 debug_printf(DEBUG_WARNING,"not connected\n");
1039 return MPD_NOT_CONNECTED;
1041 if(mpd_stats_check(mi) != MPD_OK)
1043 debug_printf(DEBUG_WARNING,"Failed grabbing status\n");
1044 return MPD_STATS_FAILED;
1046 return mi->stats->dbUpdateTime;
1050 MpdData * mpd_server_get_output_devices(MpdObj *mi)
1052 mpd_OutputEntity *output = NULL;
1053 MpdData *data = NULL;
1054 if(!mpd_check_connected(mi))
1056 debug_printf(DEBUG_WARNING,"not connected\n");
1057 return NULL;
1059 /* TODO: Check version */
1060 if(mpd_lock_conn(mi))
1062 debug_printf(DEBUG_ERROR,"lock failed\n");
1063 return NULL;
1066 mpd_sendOutputsCommand(mi->connection);
1067 while (( output = mpd_getNextOutput(mi->connection)) != NULL)
1069 data = mpd_new_data_struct_append(data);
1070 data->type = MPD_DATA_TYPE_OUTPUT_DEV;
1071 data->output_dev = output;
1073 mpd_finishCommand(mi->connection);
1075 /* unlock */
1076 mpd_unlock_conn(mi);
1077 if(data == NULL)
1079 return NULL;
1081 return mpd_data_get_first(data);
1084 int mpd_server_set_output_device(MpdObj *mi,int device_id,int state)
1086 if(!mpd_check_connected(mi))
1088 debug_printf(DEBUG_WARNING,"not connected\n");
1089 return MPD_NOT_CONNECTED;
1091 if(mpd_lock_conn(mi))
1093 debug_printf(DEBUG_ERROR,"lock failed\n");
1094 return MPD_LOCK_FAILED;
1096 if(state)
1098 mpd_sendEnableOutputCommand(mi->connection, device_id);
1100 else
1102 mpd_sendDisableOutputCommand(mi->connection, device_id);
1104 mpd_finishCommand(mi->connection);
1106 mpd_unlock_conn(mi);
1107 mpd_status_queue_update(mi);
1108 return FALSE;
1111 int mpd_server_check_version(MpdObj *mi, int major, int minor, int micro)
1113 if(!mpd_check_connected(mi))
1115 debug_printf(DEBUG_WARNING,"not connected\n");
1116 return FALSE;
1118 if(major > mi->connection->version[0]) return FALSE;
1119 if(mi->connection->version[0] > major) return TRUE;
1120 if(minor > mi->connection->version[1]) return FALSE;
1121 if(mi->connection->version[1] > minor) return TRUE;
1122 if(micro > mi->connection->version[2]) return FALSE;
1123 if(mi->connection->version[2] > micro) return TRUE;
1124 return TRUE;
1127 int mpd_server_check_command_allowed(MpdObj *mi, const char *command)
1129 int i;
1130 if(!mi || !command) return MPD_SERVER_COMMAND_ERROR;
1131 /* when we are connected to a mpd server that doesn't support commands and not commands
1132 * feature. (like mpd 0.11.5) allow everything
1134 if(!mpd_server_check_version(mi, 0,12,0)) return MPD_SERVER_COMMAND_ALLOWED;
1136 * Also when somehow we failted to get commands
1138 if(mi->commands == NULL) return MPD_SERVER_COMMAND_ALLOWED;
1142 for(i=0;mi->commands[i].command_name;i++)
1144 if(!strcasecmp(mi->commands[i].command_name, command))
1145 return mi->commands[i].enabled;
1147 return MPD_SERVER_COMMAND_NOT_SUPPORTED;
1150 char ** mpd_server_get_url_handlers(MpdObj *mi)
1152 char *temp = NULL;
1153 int i=0;
1154 char **retv = NULL;
1155 if(!mpd_check_connected(mi))
1157 debug_printf(DEBUG_WARNING,"not connected\n");
1158 return FALSE;
1160 if(mpd_lock_conn(mi))
1162 debug_printf(DEBUG_ERROR,"lock failed\n");
1163 return NULL;
1165 mpd_sendUrlHandlersCommand(mi->connection);
1166 while((temp = mpd_getNextHandler(mi->connection)) != NULL)
1168 retv = realloc(retv,(i+2)*sizeof(*retv));
1169 retv[i] = temp;
1170 retv[i+1] = NULL;
1171 i++;
1173 mpd_finishCommand(mi->connection);
1176 mpd_unlock_conn(mi);
1177 return retv;
1179 char ** mpd_server_get_tag_types(MpdObj *mi)
1181 char *temp = NULL;
1182 int i=0;
1183 char **retv = NULL;
1184 if(!mpd_check_connected(mi))
1186 debug_printf(DEBUG_WARNING,"not connected\n");
1187 return FALSE;
1189 if(mpd_lock_conn(mi))
1191 debug_printf(DEBUG_ERROR,"lock failed\n");
1192 return NULL;
1194 mpd_sendTagTypesCommand(mi->connection);
1195 while((temp = mpd_getNextTagType(mi->connection)) != NULL)
1197 retv = realloc(retv,(i+2)*sizeof(*retv));
1198 retv[i] = temp;
1199 retv[i+1] = NULL;
1200 i++;
1202 mpd_finishCommand(mi->connection);
1205 mpd_unlock_conn(mi);
1206 return retv;
1209 int mpd_misc_get_tag_by_name(char *name)
1211 int i;
1212 if(name == NULL)
1214 return MPD_ARGS_ERROR;
1216 for(i=0; i < MPD_TAG_NUM_OF_ITEM_TYPES; i++)
1218 if(!strcasecmp(mpdTagItemKeys[i], name))
1220 return i;
1223 return MPD_TAG_NOT_FOUND;
1226 int mpd_server_update_outputs(MpdObj *mi)
1228 mpd_OutputEntity *output = NULL;
1229 if(!mpd_check_connected(mi))
1231 debug_printf(DEBUG_WARNING,"not connected\n");
1232 return MPD_NOT_CONNECTED;
1234 if(mpd_lock_conn(mi))
1236 debug_printf(DEBUG_ERROR,"lock failed\n");
1237 return MPD_LOCK_FAILED;
1239 mpd_sendOutputsCommand(mi->connection);
1240 while (( output = mpd_getNextOutput(mi->connection)) != NULL)
1242 mi->num_outputs++;
1243 mi->output_states = g_realloc(mi->output_states,mi->num_outputs*sizeof(int));
1244 mi->output_states[mi->num_outputs-1] = FALSE;/*output->enabled;*/
1245 mpd_freeOutputElement(output);
1247 mpd_finishCommand(mi->connection);
1248 return mpd_unlock_conn(mi);
1251 int mpd_server_has_idle(MpdObj *mi)
1253 return mi->has_idle;
1256 int mpd_server_tag_supported(MpdObj *mi, int tag)
1258 if(!mi) return FALSE;
1259 if(tag < 0 || tag >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1260 return FALSE;
1262 return mi->supported_tags[tag];