Skip to content
Snippets Groups Projects
app_confbridge.c 132 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		return 0;
    	}
    	if (!ao2_container_count(conference_bridges)) {
    		astman_send_error(s, m, "No active conferences.");
    		return 0;
    	}
    
    	conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
    
    		astman_send_error(s, m, "No Conference by that name found.");
    		return 0;
    	}
    
    
    	found = !kick_conference_participant(conference, channel);
    	ao2_ref(conference, -1);
    
    		astman_send_ack(s, m, !strcmp("all", channel) ? "All participants kicked" : "User kicked");
    
    	} else {
    		astman_send_error(s, m, "No Channel by that name found in Conference.");
    	}
    	return 0;
    }
    
    static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
    {
    
    	const char *conference_name = astman_get_header(m, "Conference");
    
    	const char *recordfile = astman_get_header(m, "RecordFile");
    
    	struct confbridge_conference *conference;
    
    	if (ast_strlen_zero(conference_name)) {
    
    		astman_send_error(s, m, "No Conference name provided.");
    		return 0;
    	}
    	if (!ao2_container_count(conference_bridges)) {
    		astman_send_error(s, m, "No active conferences.");
    		return 0;
    	}
    
    
    	conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
    
    		astman_send_error(s, m, "No Conference by that name found.");
    		return 0;
    	}
    
    
    	ao2_lock(conference);
    	if (conf_is_recording(conference)) {
    
    		astman_send_error(s, m, "Conference is already being recorded.");
    
    		ao2_unlock(conference);
    		ao2_ref(conference, -1);
    
    		return 0;
    	}
    
    	if (!ast_strlen_zero(recordfile)) {
    
    		ast_copy_string(conference->b_profile.rec_file, recordfile, sizeof(conference->b_profile.rec_file));
    
    		astman_send_error(s, m, "Internal error starting conference recording.");
    
    		ao2_unlock(conference);
    		ao2_ref(conference, -1);
    
    	ao2_ref(conference, -1);
    
    	astman_send_ack(s, m, "Conference Recording Started.");
    	return 0;
    }
    static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
    {
    
    	const char *conference_name = astman_get_header(m, "Conference");
    
    	struct confbridge_conference *conference;
    
    	if (ast_strlen_zero(conference_name)) {
    
    		astman_send_error(s, m, "No Conference name provided.");
    		return 0;
    	}
    	if (!ao2_container_count(conference_bridges)) {
    		astman_send_error(s, m, "No active conferences.");
    		return 0;
    	}
    
    
    	conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
    
    		astman_send_error(s, m, "No Conference by that name found.");
    		return 0;
    	}
    
    
    	ao2_lock(conference);
    	if (conf_stop_record(conference)) {
    		ao2_unlock(conference);
    
    		astman_send_error(s, m, "Internal error while stopping recording.");
    
    		ao2_ref(conference, -1);
    
    	ao2_ref(conference, -1);
    
    	astman_send_ack(s, m, "Conference Recording Stopped.");
    	return 0;
    }
    
    
    static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
    {
    
    	const char *conference_name = astman_get_header(m, "Conference");
    
    	const char *channel = astman_get_header(m, "Channel");
    
    	struct confbridge_user *user;
    	struct confbridge_conference *conference;
    
    	if (ast_strlen_zero(conference_name)) {
    
    		astman_send_error(s, m, "No Conference name provided.");
    		return 0;
    	}
    	if (ast_strlen_zero(channel)) {
    		astman_send_error(s, m, "No channel name provided.");
    		return 0;
    	}
    	if (!ao2_container_count(conference_bridges)) {
    		astman_send_error(s, m, "No active conferences.");
    		return 0;
    	}
    
    
    	conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
    
    		astman_send_error(s, m, "No Conference by that name found.");
    		return 0;
    	}
    
    	/* find channel and set as video src. */
    
    	ao2_lock(conference);
    	AST_LIST_TRAVERSE(&conference->active_list, user, list) {
    		if (!strncmp(channel, ast_channel_name(user->chan), strlen(channel))) {
    			ast_bridge_set_single_src_video_mode(conference->bridge, user->chan);
    
    	ao2_unlock(conference);
    	ao2_ref(conference, -1);
    
    	/* do not access user after conference unlock.  We are just
    
    	 * using this check to see if it was found or not */
    
    		astman_send_error(s, m, "No channel by that name found in conference.");
    		return 0;
    	}
    	astman_send_ack(s, m, "Conference single video source set.");
    	return 0;
    }
    
    
    static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
    {
    
    	char *parse;
    	struct confbridge_conference *conference;
    	struct confbridge_user *user;
    
    	int count = 0;
    	AST_DECLARE_APP_ARGS(args,
    		AST_APP_ARG(type);
    		AST_APP_ARG(confno);
    	);
    
    	/* parse all the required arguments and make sure they exist. */
    	if (ast_strlen_zero(data)) {
    		return -1;
    	}
    	parse = ast_strdupa(data);
    	AST_STANDARD_APP_ARGS(args, parse);
    	if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
    		return -1;
    	}
    
    	conference = ao2_find(conference_bridges, args.confno, OBJ_KEY);
    
    		snprintf(buf, len, "0");
    		return 0;
    
    	}
    
    	/* get the correct count for the type requested */
    
    	if (!strcasecmp(args.type, "parties")) {
    
    		AST_LIST_TRAVERSE(&conference->active_list, user, list) {
    
    		AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
    
    	} else if (!strcasecmp(args.type, "admins")) {
    
    		AST_LIST_TRAVERSE(&conference->active_list, user, list) {
    			if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
    
    	} else if (!strcasecmp(args.type, "marked")) {
    
    		AST_LIST_TRAVERSE(&conference->active_list, user, list) {
    			if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
    
    	} else if (!strcasecmp(args.type, "locked")) {
    
    		count = conference->locked;
    
    	} else if (!strcasecmp(args.type, "muted")) {
    		count = conference->muted;
    
    		ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO.\n", args.type);
    
    	}
    	snprintf(buf, len, "%d", count);
    
    	ao2_unlock(conference);
    	ao2_ref(conference, -1);
    
    void conf_add_user_active(struct confbridge_conference *conference, struct confbridge_user *user)
    
    	AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
    	conference->activeusers++;
    
    void conf_add_user_marked(struct confbridge_conference *conference, struct confbridge_user *user)
    
    	AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
    	conference->activeusers++;
    	conference->markedusers++;
    
    void conf_add_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user)
    
    	AST_LIST_INSERT_TAIL(&conference->waiting_list, user, list);
    	conference->waitingusers++;
    
    void conf_remove_user_active(struct confbridge_conference *conference, struct confbridge_user *user)
    
    	AST_LIST_REMOVE(&conference->active_list, user, list);
    	conference->activeusers--;
    
    void conf_remove_user_marked(struct confbridge_conference *conference, struct confbridge_user *user)
    
    	AST_LIST_REMOVE(&conference->active_list, user, list);
    	conference->activeusers--;
    	conference->markedusers--;
    
    void conf_mute_only_active(struct confbridge_conference *conference)
    
    	struct confbridge_user *only_user = AST_LIST_FIRST(&conference->active_list);
    
    	/* Turn on MOH if the single participant is set up for it */
    
    	if (ast_test_flag(&only_user->u_profile, USER_OPT_MUSICONHOLD)) {
    		conf_moh_start(only_user);
    
    	conf_update_user_mute(only_user);
    
    void conf_remove_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user)
    
    	AST_LIST_REMOVE(&conference->waiting_list, user, list);
    	conference->waitingusers--;
    
    /*!
     * \internal
     * \brief Unregister a ConfBridge channel technology.
     * \since 12.0.0
     *
     * \param tech What to unregister.
     *
     * \return Nothing
     */
    static void unregister_channel_tech(struct ast_channel_tech *tech)
    {
    	ast_channel_unregister(tech);
    
    }
    
    /*!
     * \internal
     * \brief Register a ConfBridge channel technology.
     * \since 12.0.0
     *
     * \param tech What to register.
     *
     * \retval 0 on success.
     * \retval -1 on error.
     */
    static int register_channel_tech(struct ast_channel_tech *tech)
    {
    
    	tech->capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
    
    	ast_format_cap_append_by_type(tech->capabilities, AST_MEDIA_TYPE_UNKNOWN);
    
    	if (ast_channel_register(tech)) {
    		ast_log(LOG_ERROR, "Unable to register channel technology %s(%s).\n",
    			tech->type, tech->description);
    		return -1;
    	}
    	return 0;
    }
    
    
    /*! \brief Called when module is being unloaded */
    static int unload_module(void)
    {
    
    	ast_custom_function_unregister(&confbridge_function);
    
    	ast_custom_function_unregister(&confbridge_info_function);
    
    	ast_cli_unregister_multiple(cli_confbridge, ARRAY_LEN(cli_confbridge));
    
    	ast_manager_unregister("ConfbridgeList");
    	ast_manager_unregister("ConfbridgeListRooms");
    	ast_manager_unregister("ConfbridgeMute");
    	ast_manager_unregister("ConfbridgeUnmute");
    	ast_manager_unregister("ConfbridgeKick");
    	ast_manager_unregister("ConfbridgeUnlock");
    	ast_manager_unregister("ConfbridgeLock");
    	ast_manager_unregister("ConfbridgeStartRecord");
    	ast_manager_unregister("ConfbridgeStopRecord");
    	ast_manager_unregister("ConfbridgeSetSingleVideoSrc");
    
    	/* Unsubscribe from stasis confbridge message type and clean it up. */
    	manager_confbridge_shutdown();
    
    
    	/* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
    
    	ao2_cleanup(conference_bridges);
    	conference_bridges = NULL;
    
    	conf_destroy_config();
    
    
    	unregister_channel_tech(conf_announce_get_tech());
    	unregister_channel_tech(conf_record_get_tech());
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*!
     * \brief Load the module
     *
     * Module loading including tests for configuration or dependencies.
     * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
     * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
    
     * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
     * configuration file or other non-critical problem return
    
    Andrew Latham's avatar
    Andrew Latham committed
     * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
     */
    
    static int load_module(void)
    {
    
    	if (conf_load_config()) {
    
    		ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
    		return AST_MODULE_LOAD_DECLINE;
    	}
    
    	if (register_channel_tech(conf_record_get_tech())
    		|| register_channel_tech(conf_announce_get_tech())) {
    
    		return AST_MODULE_LOAD_DECLINE;
    
    	/* Create a container to hold the conference bridges */
    
    	conference_bridges = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
    		CONFERENCE_BRIDGE_BUCKETS,
    		conference_bridge_hash_cb, NULL, conference_bridge_cmp_cb);
    
    	if (!conference_bridges) {
    		unload_module();
    
    		return AST_MODULE_LOAD_DECLINE;
    
    	/* Setup manager stasis subscriptions */
    	res |= manager_confbridge_init();
    
    
    	res |= ast_register_application_xml(app, confbridge_exec);
    
    
    	res |= ast_custom_function_register_escalating(&confbridge_function, AST_CFE_WRITE);
    
    	res |= ast_custom_function_register(&confbridge_info_function);
    
    	res |= ast_cli_register_multiple(cli_confbridge, ARRAY_LEN(cli_confbridge));
    
    
    	res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
    	res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
    	res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
    	res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
    	res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
    	res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
    	res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
    
    	res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_SYSTEM, action_confbridgestartrecord);
    	res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_SYSTEM, action_confbridgestoprecord);
    
    	res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
    
    		return AST_MODULE_LOAD_DECLINE;
    
    	return AST_MODULE_LOAD_SUCCESS;
    
    static int reload(void)
    {
    
    	return conf_reload_config();
    
    AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
    
    	.load = load_module,
    	.unload = unload_module,
    
    	.reload = reload,
    
    	.load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
    
    	.optional_modules = "codec_speex,func_jitterbuffer",