/* Handle command that synchronises the playlists between interfaces * (except forwarding the whole list). Return 0 on error. */ static int plist_sync_cmd (struct client *cli, const int cmd) { if (cmd == CMD_CLI_PLIST_ADD) { struct plist_item *item; debug ("Sending EV_PLIST_ADD"); if (!(item = recv_item(cli->socket))) { logit ("Error while receiving item"); return 0; } add_event_all (EV_PLIST_ADD, item); plist_free_item_fields (item); free (item); } else if (cmd == CMD_CLI_PLIST_DEL) { char *file; debug ("Sending EV_PLIST_DEL"); if (!(file = get_str(cli->socket))) { logit ("Error while receiving file"); return 0; } add_event_all (EV_PLIST_DEL, file); free (file); } else if (cmd == CMD_CLI_PLIST_MOVE) { struct move_ev_data m; if (!(m.from = get_str(cli->socket)) || !(m.to = get_str(cli->socket))) { logit ("Error while receiving file"); return 0; } add_event_all (EV_PLIST_MOVE, &m); free (m.from); free (m.to); } else { /* it can be only CMD_CLI_PLIST_CLEAR */ debug ("Sending EV_PLIST_CLEAR"); add_event_all (EV_PLIST_CLEAR, NULL); } return 1; }
/* Handle CMD_QUEUE_ADD, return 1 if ok or 0 on error. */ static int req_queue_add (const struct client *cli) { char *file; struct plist_item *item; file = get_str (cli->socket); if (!file) return 0; logit ("Adding '%s' to the queue", file); audio_queue_add (file); /* Wrap the filename in struct plist_item. * We don't need tags, because the player gets them * when playing the file. This may change if there is * support for viewing/reordering the queue and here * is the place to read the tags and fill them into * the item. */ item = plist_new_item (); item->file = xstrdup (file); item->type = file_type (file); item->mtime = get_mtime (file); add_event_all (EV_QUEUE_ADD, item); plist_free_item_fields (item); free (item); free (file); return 1; }
/* Report an error logging it and sending a message to the client. */ void server_error (const char *msg) { strncpy (err_msg, msg, sizeof(err_msg) - 1); err_msg[sizeof(err_msg) - 1] = 0; logit ("ERROR: %s", err_msg); add_event_all (EV_SRV_ERROR, NULL); }
/* Handle CMD_QUEUE_MOVE. Return 0 on error. */ static int req_queue_move (const struct client *cli) { /*char *from; char *to; */ struct move_ev_data m; if (!(m.from = get_str(cli->socket))) return 0; if (!(m.to = get_str(cli->socket))) { free (m.from); return 0; } audio_queue_move (m.from, m.to); logit ("Swapping %s with %s in the queue", m.from, m.to); /* Broadcast the event to clients */ add_event_all (EV_QUEUE_MOVE, &m); free (m.from); free (m.to); return 1; }
static int req_queue_del (const struct client *cli) { char *file; if (!(file = get_str(cli->socket))) return 0; debug ("Deleting '%s' from queue", file); audio_queue_delete (file); add_event_all (EV_QUEUE_DEL, file); free (file); return 1; }
/* Get and set an option from the client. Return 1 on error. */ static int get_set_option (struct client *cli) { char *name; int val; if (!(name = get_str (cli->socket))) return 0; if (!valid_sync_option (name)) { logit ("Client requested setting invalid option '%s'", name); return 0; } if (!get_int (cli->socket, &val)) { free (name); return 0; } options_set_bool (name, val ? true : false); free (name); add_event_all (EV_OPTIONS, NULL); return 1; }
/* Notify the client about change of the player state. */ void state_change () { add_event_all (EV_STATE, NULL); }
/* Report an error logging it and sending a message to the client. */ void server_error (const char *file, int line, const char *function, const char *msg) { internal_logit (file, line, function, "ERROR: %s", msg); add_event_all (EV_SRV_ERROR, msg); }
/* XXX: this function is called from player thread and add_event_all * imho doesn't properly lock all shared variables -- possible * race condition??? */ void server_queue_pop (const char *filename) { debug ("Queue pop -- broadcasting EV_QUEUE_DEL"); add_event_all (EV_QUEUE_DEL, filename); }
/* Handle CMD_TOGGLE_MIXER_CHANNEL. */ void req_toggle_mixer_channel () { audio_toggle_mixer_channel (); add_event_all (EV_MIXER_CHANGE, NULL); }
void ev_audio_start () { add_event_all (EV_AUDIO_START, NULL); }
void ev_audio_stop () { add_event_all (EV_AUDIO_STOP, NULL); }
void tags_change () { add_event_all (EV_TAGS, NULL); }
void status_msg (const char *msg) { add_event_all (EV_STATUS_MSG, msg); }
/* Handle CMD_TOGGLE_SOFTMIXER. */ void req_toggle_softmixer () { softmixer_set_active(!softmixer_is_active()); add_event_all (EV_MIXER_CHANGE, NULL); }
void ctime_change () { add_event_all (EV_CTIME, NULL); }
void set_info_avg_bitrate (const int avg_bitrate) { sound_info.avg_bitrate = avg_bitrate; add_event_all (EV_AVG_BITRATE, NULL); }
void set_info_rate (const int rate) { sound_info.rate = rate; add_event_all (EV_RATE, NULL); }
void set_info_channels (const int channels) { sound_info.channels = channels; add_event_all (EV_CHANNELS, NULL); }
void set_info_bitrate (const int bitrate) { sound_info.bitrate = bitrate; add_event_all (EV_BITRATE, NULL); }
/* Receive a command from the client and execute it. */ static void handle_command (const int client_id) { int cmd; int err = 0; struct client *cli = &clients[client_id]; if (!get_int(cli->socket, &cmd)) { logit ("Failed to get command from the client"); close (cli->socket); del_client (cli); return; } switch (cmd) { case CMD_QUIT: logit ("Exit request from the client"); close (cli->socket); del_client (cli); server_quit = 1; break; case CMD_LIST_CLEAR: logit ("Clearing the list"); audio_plist_clear (); break; case CMD_LIST_ADD: if (!req_list_add(cli)) err = 1; break; case CMD_PLAY: if (!req_play(cli)) err = 1; break; case CMD_DISCONNECT: logit ("Client disconnected"); close (cli->socket); del_client (cli); break; case CMD_PAUSE: audio_pause (); break; case CMD_UNPAUSE: audio_unpause (); break; case CMD_STOP: audio_stop (); break; case CMD_GET_CTIME: if (!send_data_int(cli, MAX(0, audio_get_time()))) err = 1; break; case CMD_SEEK: if (!req_seek(cli)) err = 1; break; case CMD_JUMP_TO: if (!req_jump_to(cli)) err = 1; break; case CMD_GET_SNAME: if (!send_sname(cli)) err = 1; break; case CMD_GET_STATE: if (!send_data_int(cli, audio_get_state())) err = 1; break; case CMD_GET_BITRATE: if (!send_data_int(cli, sound_info.bitrate)) err = 1; break; case CMD_GET_AVG_BITRATE: if (!send_data_int(cli, sound_info.avg_bitrate)) err = 1; break; case CMD_GET_RATE: if (!send_data_int(cli, sound_info.rate)) err = 1; break; case CMD_GET_CHANNELS: if (!send_data_int(cli, sound_info.channels)) err = 1; break; case CMD_NEXT: audio_next (); break; case CMD_PREV: audio_prev (); break; case CMD_PING: if (!send_int(cli->socket, EV_PONG)) err = 1; break; case CMD_GET_OPTION: if (!send_option(cli)) err = 1; break; case CMD_SET_OPTION: if (!get_set_option(cli)) err = 1; break; case CMD_GET_MIXER: if (!send_data_int(cli, audio_get_mixer())) err = 1; break; case CMD_SET_MIXER: if (!set_mixer(cli)) err = 1; break; case CMD_DELETE: if (!delete_item(cli)) err = 1; break; case CMD_SEND_PLIST_EVENTS: cli->wants_plist_events = 1; logit ("Request for events"); break; case CMD_GET_PLIST: if (!get_client_plist(cli)) err = 1; break; case CMD_SEND_PLIST: if (!req_send_plist(cli)) err = 1; break; case CMD_CAN_SEND_PLIST: cli->can_send_plist = 1; break; case CMD_CLI_PLIST_ADD: case CMD_CLI_PLIST_DEL: case CMD_CLI_PLIST_CLEAR: case CMD_CLI_PLIST_MOVE: if (!plist_sync_cmd(cli, cmd)) err = 1; break; case CMD_LOCK: if (!client_lock(cli)) err = 1; break; case CMD_UNLOCK: if (!client_unlock(cli)) err = 1; break; case CMD_GET_SERIAL: if (!send_serial(cli)) err = 1; break; case CMD_PLIST_GET_SERIAL: if (!req_plist_get_serial(cli)) err = 1; break; case CMD_PLIST_SET_SERIAL: if (!req_plist_set_serial(cli)) err = 1; break; case CMD_GET_TAGS: if (!req_get_tags(cli)) err = 1; break; case CMD_TOGGLE_MIXER_CHANNEL: req_toggle_mixer_channel (); break; case CMD_TOGGLE_SOFTMIXER: req_toggle_softmixer (); break; case CMD_GET_MIXER_CHANNEL_NAME: if (!req_get_mixer_channel_name(cli)) err = 1; break; case CMD_GET_FILE_TAGS: if (!get_file_tags(client_id)) err = 1; break; case CMD_ABORT_TAGS_REQUESTS: if (!abort_tags_requests(client_id)) err = 1; break; case CMD_LIST_MOVE: if (!req_list_move(cli)) err = 1; break; case CMD_TOGGLE_EQUALIZER: req_toggle_equalizer(); break; case CMD_EQUALIZER_REFRESH: req_equalizer_refresh(); break; case CMD_EQUALIZER_PREV: req_equalizer_prev(); break; case CMD_EQUALIZER_NEXT: req_equalizer_next(); break; case CMD_TOGGLE_MAKE_MONO: req_toggle_make_mono(); break; case CMD_QUEUE_ADD: if (!req_queue_add(cli)) err = 1; break; case CMD_QUEUE_DEL: if (!req_queue_del(cli)) err = 1; break; case CMD_QUEUE_CLEAR: logit ("Clearing the queue"); audio_queue_clear (); add_event_all (EV_QUEUE_CLEAR, NULL); break; case CMD_QUEUE_MOVE: if (!req_queue_move(cli)) err = 1; break; case CMD_GET_QUEUE: if (!req_send_queue(cli)) err = 1; break; default: logit ("Bad command (0x%x) from the client", cmd); err = 1; } if (err) { logit ("Closing client connection due to error"); close (cli->socket); del_client (cli); } }