All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.antmedia.console.rest.CommonRestService Maven / Gradle / Ivy

Go to download

Ant Media Server supports RTMP, RTSP, MP4, HLS, WebRTC, Adaptive Streaming, etc.

There is a newer version: 2.11.3
Show newest version
package io.antmedia.console.rest;

import static org.slf4j.Logger.ROOT_LOGGER_NAME;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.red5.server.Launcher;
import org.red5.server.api.scope.IScope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

import ch.qos.logback.classic.Level;
import io.antmedia.AntMediaApplicationAdapter;
import io.antmedia.AppSettings;
import io.antmedia.SystemUtils;
import io.antmedia.cluster.IClusterNotifier;
import io.antmedia.console.AdminApplication;
import io.antmedia.console.AdminApplication.ApplicationInfo;
import io.antmedia.console.AdminApplication.BroadcastInfo;
import io.antmedia.console.datastore.AbstractConsoleDataStore;
import io.antmedia.console.datastore.ConsoleDataStoreFactory;
import io.antmedia.datastore.db.types.Licence;
import io.antmedia.datastore.db.types.User;
import io.antmedia.datastore.preference.PreferenceStore;
import io.antmedia.licence.ILicenceService;
import io.antmedia.rest.RestServiceBase;
import io.antmedia.rest.model.Result;
import io.antmedia.rest.model.SslConfigurationType;
import io.antmedia.datastore.db.types.UserType;
import io.antmedia.rest.model.Version;
import io.antmedia.security.SslConfigurator;
import io.antmedia.settings.ServerSettings;
import io.antmedia.statistic.IStatsCollector;
import io.antmedia.statistic.StatsCollector;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;


public class CommonRestService {

	private static final String LOG_TYPE_ERROR = "error";

	private static final String FILE_NOT_EXIST = "There is no log yet";

	private static final String ERROR_LOG_LOCATION = "log/antmedia-error.log";

	private static final String SERVER_LOG_LOCATION = "log/ant-media-server.log";

	private static final String LOG_CONTENT = "logContent";

	private static final String LOG_CONTENT_SIZE = "logContentSize";

	private static final String LOG_FILE_SIZE = "logFileSize";

	private static final int MAX_CHAR_SIZE = 512000;

	private static final String LOG_LEVEL_ALL = "ALL";

	private static final String LOG_LEVEL_TRACE = "TRACE";

	private static final String LOG_LEVEL_DEBUG = "DEBUG";

	private static final String LOG_LEVEL_INFO = "INFO";

	private static final String LOG_LEVEL_WARN = "WARN";

	private static final String LOG_LEVEL_ERROR = "ERROR";

	private static final String LOG_LEVEL_OFF = "OFF";

	public static final String USER_PASSWORD = "user.password";

	public static final String USER_EMAIL = "user.email";

	public static final String IS_AUTHENTICATED = "isAuthenticated";

	public static final String SERVER_NAME = "server.name";

	public static final String LICENSE_KEY = "server.licence_key";

	public static final String MARKET_BUILD = "server.market_build";

	public static final String NODE_GROUP = "nodeGroup";

	Gson gson = new Gson();

	private AbstractConsoleDataStore dataStore;

	private static final String LOG_LEVEL = "logLevel";

	private static final String RED5_PROPERTIES_PATH = "conf/red5.properties";

	protected static final Logger logger = LoggerFactory.getLogger(CommonRestService.class);

	private static final String LICENSE_STATUS = "license";

	protected ApplicationContext applicationContext;

	@Context
	private ServletContext servletContext;

	@Context
	private HttpServletRequest servletRequest;

	private ConsoleDataStoreFactory dataStoreFactory;
	private ServerSettings serverSettings;

	private ILicenceService licenceService;

	private IStatsCollector statsCollector;

	private static final int BLOCKED_LOGIN_TIMEOUT_SECS = 300 ; // in seconds

	private static final int ALLOWED_LOGIN_ATTEMPTS = 2 ;

	public static final String SESSION_SCOPE_KEY = "scope";

	public static final String USER_TYPE = "user-type";

	public static final String SCOPE_SYSTEM = "system";

	public int getAllowedLoginAttempts() {
		return ALLOWED_LOGIN_ATTEMPTS;
	}


	/**
	 * Add user account on db. 
	 * Username must be unique,
	 * if there is a user with the same name, user will not be created
	 * 
	 * userType = 0 means ready only account
	 * userType = 1 means read-write account
	 * 
	 * Post method should be used.
	 * 
	 * application/json
	 * 
	 * form parameters - case sensitive
	 * "userName", "password", "userType
	 * 
	 * @param user: The user to be added
	 * @return JSON data
	 * if user is added success will be true
	 * if user is not added success will be false
	 * 	if user is not added, errorId = 1 means username already exist
	 */

	public Result addUser(User user) {
		boolean result = false;
		String message = "";
		if (user != null) 
		{
			if (!getDataStore().doesUsernameExist(user.getEmail()) && user.getPassword() != null && user.getEmail() != null)
			{
				user.setPassword(getMD5Hash(user.getPassword()));
				result = getDataStore().addUser(user);
				logger.info("added user = {} user type = {} -> {}", user.getEmail() ,user.getUserType(), result);

				new Thread() {
					public void run() {
						sendUserInfo(user.getEmail(), user.getFirstName(), user.getLastName(), user.getScope(), user.getUserType().toString(), user.getAppNameUserType());
					};
				}.start();
			}
			else {
				message = "User with the same e-mail already exists";
			}
		}
		else {
			message = "User object is null";
		}


		Result operationResult = new Result(result);
		operationResult.setMessage(message);

		return operationResult;
	}





	public Result addInitialUser(User user) {
		boolean result = false;
		int errorId = -1;
		user.setPassword(getMD5Hash(user.getPassword()));
		user.setUserType(UserType.ADMIN);
		user.setScope(SCOPE_SYSTEM);
		Map appNameUserType = new HashMap<>();
		appNameUserType.put(SCOPE_SYSTEM, UserType.ADMIN.toString());
		user.setAppNameUserType(appNameUserType);

		if (getDataStore().getNumberOfUserRecords() == 0) {
			result = getDataStore().addUser(user);
		}

		Result operationResult = new Result(result);
		operationResult.setErrorId(errorId);

		new Thread() {

			@Override
			public void run() 
			{
				sendUserInfo(user.getEmail(), user.getFirstName(), user.getLastName(), user.getScope(), user.getUserType().toString(), user.getAppNameUserType());
			}
		}.start();


		return operationResult;
	}

	public CloseableHttpClient getHttpClient() {
		return  HttpClients.createDefault();
	}

	public boolean sendUserInfo(String email, String firstname, String lastname, String scope, String userType, Map appNameUserTypeMap)
	{
		boolean success = false;

		try (CloseableHttpClient httpClient = getHttpClient()) 
		{
			Version version = RestServiceBase.getSoftwareVersion();

			HttpPost httpPost = new HttpPost("https://antmedia.io/livedemo/ams_web_panel_registration.php");

			RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(2 * 1000).setSocketTimeout(5*1000).build();

			httpPost.setConfig(requestConfig);

			// if args are null, set them to empty string
			firstname = Objects.requireNonNullElse(firstname, "");
			lastname = Objects.requireNonNullElse(lastname, "");
			email = Objects.requireNonNullElse(email, "");
			String isEnterprise = Objects.requireNonNullElse(RestServiceBase.isEnterprise(), "") + "";
			String licenseKey = Objects.requireNonNullElse(getServerSettings().getLicenceKey(), "") + "";
			String versionStr = Objects.requireNonNullElse(version.getVersionType(), "")+" "+Objects.requireNonNullElse(version.getVersionName(), "")+" "+Objects.requireNonNullElse(version.getBuildNumber(), "");
			String marketplace = Objects.requireNonNullElse(getServerSettings().getMarketplace(), "")+"";
			String instanceId = Objects.requireNonNullElse(Launcher.getInstanceId(), "");
			scope = Objects.requireNonNullElse(scope, "");
			userType = Objects.requireNonNullElse(userType, "");

			MultipartEntityBuilder builder = MultipartEntityBuilder.create();
			builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

			builder.addTextBody("firstname", firstname);
			builder.addTextBody("lastname", lastname);
			builder.addTextBody("email", email);
			builder.addTextBody("isEnterprise", isEnterprise);
			builder.addTextBody("licenseKey", licenseKey);
			builder.addTextBody("version", versionStr);
			builder.addTextBody("marketplace", marketplace);
			builder.addTextBody("instanceId", instanceId);
			builder.addTextBody("userScope", scope);
			builder.addTextBody("userType", userType);
			String jsonAppNameUserType = gson.toJson(appNameUserTypeMap);
			builder.addTextBody("appNameUserType", jsonAppNameUserType);




			HttpEntity httpEntity = builder.build();

			httpPost.setEntity(httpEntity);

			CloseableHttpResponse response = httpClient.execute(httpPost);

			try {
				if (response.getStatusLine().getStatusCode() == 200) 
				{
					success = true;
				}	
			} finally {
				response.close();
			}
		}catch (Exception e) {
			logger.error(ExceptionUtils.getStackTrace(e));
		}


		return success;		
	}



	protected static String getWebAppsDirectory() {
		return String.format("%s/webapps", System.getProperty("red5.root"));
	}

	protected static String getTmpDirectory() {
		return String.format("%s/tmp", System.getProperty("red5.root"));
	}

	public Result isFirstLogin() 
	{
		boolean result = false;
		if (getDataStore().getNumberOfUserRecords() == 0) {
			result = true;
		}
		return new Result(result);
	}


	/**
	 * Authenticates user with userName and password
	 * 
	 * 
	 * @param user: The User object to be authenticated 
	 * @return json that shows user is authenticated or not
	 */
	public Result authenticateUser(User user) 
	{

		String message = "";

		boolean tryToAuthenticate = false;
		if (user != null && user.getEmail() != null) 
		{
			if (getDataStore().isUserBlocked(user.getEmail())) 
			{
				if ((Instant.now().getEpochSecond() - getDataStore().getBlockTime(user.getEmail())) > BLOCKED_LOGIN_TIMEOUT_SECS) 
				{
					logger.info("Unblocking the user -> {}", user.getEmail());
					getDataStore().setUnBlocked(user.getEmail());
					getDataStore().resetInvalidLoginCount(user.getEmail());
					tryToAuthenticate = true;
				}
				else {
					message = "Too many login attempts. User is blocked for " + BLOCKED_LOGIN_TIMEOUT_SECS + " secs";
				}


			}
			else {
				tryToAuthenticate = true;
			}
		}

		boolean result = false;
		if (tryToAuthenticate) 
		{
			result = getDataStore().doesUserExist(user.getEmail(), user.getPassword()) ||
					getDataStore().doesUserExist(user.getEmail(), getMD5Hash(user.getPassword()));

			if (result) 
			{
				HttpSession session = servletRequest.getSession();
				session.setAttribute(IS_AUTHENTICATED, true);
				session.setAttribute(USER_EMAIL, user.getEmail());
				session.setAttribute(USER_PASSWORD, getMD5Hash(user.getPassword()));
				user = getDataStore().getUser(user.getEmail());
				if(user.getScope() != null && user.getUserType() != null){
					message = user.getScope() + "/" + user.getUserType();

				}else if(user.getAppNameUserType() != null){
					JsonObject appNameUserTypeJson = new JsonObject();
					for (Map.Entry entry : user.getAppNameUserType().entrySet()) {
						String appName = entry.getKey();
						String userType = entry.getValue();

						appNameUserTypeJson.addProperty(appName, userType);
					}

					message = appNameUserTypeJson.toString();
				}
				getDataStore().resetInvalidLoginCount(user.getEmail());
			} 
			else 
			{
				getDataStore().incrementInvalidLoginCount(user.getEmail());
				logger.info("Increased invalid login count to: {}", getDataStore().getInvalidLoginCount(user.getEmail()));
				if (getDataStore().getInvalidLoginCount(user.getEmail()) > ALLOWED_LOGIN_ATTEMPTS) {
					getDataStore().setBlocked(user.getEmail());
					getDataStore().setBlockTime(user.getEmail(), Instant.now().getEpochSecond());
					logger.info("User is blocked: {}", getDataStore().doesUsernameExist(user.getEmail()));
				}

			}
		}

		return new Result(result, message);

	}
	
	
	public Result deleteSession() {
		HttpSession session = servletRequest.getSession();
		session.invalidate();
		
		return new Result(true);
	}
	
	
	public void setRequestForTest(HttpServletRequest testRequest){
		servletRequest = testRequest;
	}

	public Result isAdmin() {
		HttpSession session = servletRequest.getSession();
		if(isAuthenticated(session)) {
			User currentUser = getDataStore().getUser(session.getAttribute(USER_EMAIL).toString());
			if (currentUser.getUserType().equals(UserType.ADMIN)) {
				return new Result(true, "User is admin");
			}
		}
		return new Result(false, "User is not admin");
	}


	public Result editUser(User user) 
	{
		boolean result = false;
		String message = "";
		HttpSession session = servletRequest.getSession();
		String userEmail = (String)session.getAttribute(USER_EMAIL);

		if (user != null && user.getEmail() != null && getDataStore().doesUsernameExist(user.getEmail())) 
		{
			if (!userEmail.equals(user.getEmail()))
			{

				if(user.getNewPassword() != null && !user.getNewPassword().isEmpty()) 
				{
					logger.info("Changing password of user: {}",  user.getEmail());
					user.setPassword(getMD5Hash(user.getNewPassword()));
					user.setNewPassword(null);
				}
				else {
					//just keep the password same
					User userOriginal = getDataStore().getUser(user.getEmail());
					user.setPassword(userOriginal.getPassword());
				}

				result = getDataStore().editUser(user);

			}
			else {
				message = "User cannot edit itself";
			}

		} 
		else {
			message = "Edited user is not found in database";
		}

		return new Result(result, message);
	}

	public Result deleteUser(String userName) {
		HttpSession session = servletRequest.getSession();
		String userEmail = (String) session.getAttribute(USER_EMAIL);
		boolean result = false;
		String message = "";

		if (!userEmail.equals(userName)) {
			result = getDataStore().deleteUser(userName);
			if (!result) {
				logger.info("Could not delete the user: {}" , userName);
			}
		}
		else {
			message = "You cannot delete yourself";
		}

		if(result) {
			logger.info("Deleted user: {} ", userName);
		}	

		return new Result(result, message);
	}


	public List getUserList() {
		return getDataStore().getUserList();
	}


	public Result changeUserPassword(User user) {

		String userMail = (String)servletRequest.getSession().getAttribute(USER_EMAIL);

		return changeUserPasswordInternal(userMail, user);

	}

	public Result changeUserPasswordInternal(String userMail, User user) {
		boolean result = false;
		String message = null;
		if (userMail != null && user.getNewPassword() != null) {
			result = getDataStore().doesUserExist(userMail, user.getPassword()) || getDataStore().doesUserExist(userMail, getMD5Hash(user.getPassword()));
			if (result) {
				User userFromDB = getDataStore().getUser(userMail);
				userFromDB.setPassword(getMD5Hash(user.getNewPassword()));
				userFromDB.setNewPassword(null);
				result = getDataStore().editUser(userFromDB);

				if (result) {
					message = "Success";
					HttpSession session = servletRequest.getSession();
					if (session != null) {
						session.setAttribute(IS_AUTHENTICATED, true);
						session.setAttribute(USER_EMAIL, userMail);
						session.setAttribute(USER_PASSWORD, getMD5Hash(user.getPassword()));
					}
				}
			}
			else {
				message = "User not exist with that name and pass";
			}
		}
		else {
			message = "User name does not exist or there is no new password";
		}

		return new Result(result, message);
	}

	public Result isAuthenticatedRest(){
		return new Result(isAuthenticated(servletRequest.getSession()));
	}

	public static boolean isAuthenticated(HttpSession session) 
	{
		Object isAuthenticated = session.getAttribute(IS_AUTHENTICATED);
		Object userEmail = session.getAttribute(USER_EMAIL);
		Object userPassword = session.getAttribute(USER_PASSWORD);
		boolean result = false;
		if (isAuthenticated != null && userEmail != null && userPassword != null) {
			result = true;
		}
		return result;
	}

	/*
	 * 	os.name						:Operating System Name
	 * 	os.arch						: x86/x64/...
	 * 	java.specification.version	: Java Version (Required 1.5 or 1.6 and higher to run Red5)
	 * 	-------------------------------
	 * 	Runtime.getRuntime()._____  (Java Virtual Machine Memory)
	 * 	===============================
	 * 	maxMemory()					: Maximum limitation
	 * 	totalMemory()				: Total can be used
	 * 	freeMemory()				: Availability
	 * 	totalMemory()-freeMemory()	: In Use
	 * 	availableProcessors()		: Total Processors available
	 * 	-------------------------------
	 *  getOperatingSystemMXBean()	(Actual Operating System RAM)
	 *	===============================
	 *  osCommittedVirtualMemory()	: Virtual Memory
	 *  osTotalPhysicalMemory()		: Total Physical Memory
	 *  osFreePhysicalMemory()		: Available Physical Memory
	 *  osInUsePhysicalMemory()		: In Use Physical Memory
	 *  osTotalSwapSpace()			: Total Swap Space
	 *  osFreeSwapSpace()			: Available Swap Space
	 *  osInUseSwapSpace()			: In Use Swap Space
	 *  -------------------------------
	 *  File						(Actual Harddrive Info: Supported for JRE 1.6)
	 *	===============================
	 *	osHDUsableSpace()			: Usable Space
	 *	osHDTotalSpace()			: Total Space
	 *	osHDFreeSpace()				: Available Space
	 *	osHDInUseSpace()			: In Use Space
	 **/



	public String getSystemInfo() {
		return gson.toJson(StatsCollector.getSystemInfoJSObject());
	}


	/*
	 * 	Runtime.getRuntime()._____  (Java Virtual Machine Memory)
	 * 	===============================
	 * 	maxMemory()					: Maximum limitation
	 * 	totalMemory()				: Total can be used
	 * 	freeMemory()				: Availability
	 * 	totalMemory()-freeMemory()	: In Use
	 * 	availableProcessors()		: Total Processors available
	 */

	public String getJVMMemoryInfo() {
		return gson.toJson(StatsCollector.getJVMMemoryInfoJSObject());
	}


	/*
	 *  osCommittedVirtualMemory()	: Virtual Memory
	 *  osTotalPhysicalMemory()		: Total Physical Memory
	 *  osFreePhysicalMemory()		: Available Physical Memory
	 *  osInUsePhysicalMemory()		: In Use Physical Memory
	 *  osTotalSwapSpace()			: Total Swap Space
	 *  osFreeSwapSpace()			: Available Swap Space
	 *  osInUseSwapSpace()			: In Use Swap Space
	 */

	public String getSystemMemoryInfo() {
		return gson.toJson(StatsCollector.getSysteMemoryInfoJSObject());
	}


	/*
	 *  File						(Actual Harddrive Info: Supported for JRE 1.6)
	 *	===============================
	 *	osHDUsableSpace()			: Usable Space
	 *	osHDTotalSpace()			: Total Space
	 *	osHDFreeSpace()				: Available Space
	 *	osHDInUseSpace()			: In Use Space
	 **/

	public String getFileSystemInfo() {
		return gson.toJson(StatsCollector.getFileSystemInfoJSObject());
	}

	/**
	 * getProcessCpuTime:  microseconds CPU time used by the process
	 * 
	 * getSystemCpuLoad:	"% recent cpu usage" for the whole system. 
	 * 
	 * getProcessCpuLoad: "% recent cpu usage" for the Java Virtual Machine process. 
	 * @return the CPU load info
	 */

	public String getCPUInfo() {
		return gson.toJson(StatsCollector.getCPUInfoJSObject());
	}


	public String getThreadDump() {
		return Arrays.toString(StatsCollector.getThreadDump());
	}


	public String getThreadDumpJSON() {
		return gson.toJson(StatsCollector.getThreadDumpJSON());
	}



	public String getThreadsInfo() {
		return gson.toJson(StatsCollector.getThreadInfoJSONObject());
	}


	public Response getHeapDump() {
		SystemUtils.getHeapDump(SystemUtils.HEAPDUMP_HPROF);
		File file = new File(SystemUtils.HEAPDUMP_HPROF);
		return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM)
				.header("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"" ) //optional
				.build();
	}



	/**
	 * Return server uptime and startime in milliseconds
	 * @return JSON object contains the server uptime and start time
	 */

	public String getServerTime() {
		return gson.toJson(StatsCollector.getServerTime());
	}


	public String getSystemResourcesInfo() {

		AdminApplication application = getApplication();
		IScope rootScope = application.getRootScope();

		//add live stream size
		int totalLiveStreams = 0;
		Queue scopes = new LinkedList<>();
		List appNames = application.getApplications();
		for (String name : appNames) 
		{
			IScope scope = rootScope.getScope(name);
			scopes.add(scope);
			totalLiveStreams += application.getAppLiveStreamCount(scope);
		}

		JsonObject jsonObject = StatsCollector.getSystemResourcesInfo(scopes);

		jsonObject.addProperty(StatsCollector.TOTAL_LIVE_STREAMS, totalLiveStreams);

		jsonObject.add(LICENSE_STATUS, gson.toJsonTree(getLicenceStatus()));

		return gson.toJson(jsonObject);
	}


	public String getGPUInfo() 
	{
		return gson.toJson(StatsCollector.getGPUInfoJSObject());
	}



	public String getVersion() {
		return gson.toJson(RestServiceBase.getSoftwareVersion());
	}



	public String getApplications() {
		List applications = getApplication().getApplications();
		JsonObject jsonObject = new JsonObject();
		JsonArray jsonArray = new JsonArray();

		for (String appName : applications) {
			if (!appName.equals(AdminApplication.APP_NAME)) {
				jsonArray.add(appName);
			}
		}
		jsonObject.add("applications", jsonArray);
		return gson.toJson(jsonObject);
	}

	/**
	 * Refactor name getTotalLiveStreamSize
	 * only return totalLiveStreamSize
	 * @return the number of live clients
	 */

	public String getLiveClientsSize() 
	{
		int totalConnectionSize = getApplication().getTotalConnectionSize();
		int totalLiveStreamSize = getApplication().getTotalLiveStreamSize();
		JsonObject jsonObject = new JsonObject();
		jsonObject.addProperty("totalConnectionSize", totalConnectionSize);
		jsonObject.addProperty(StatsCollector.TOTAL_LIVE_STREAMS, totalLiveStreamSize);

		return gson.toJson(jsonObject);
	}


	public String getApplicationInfo() {
		List info = getApplication().getApplicationInfo();
		return gson.toJson(info);
	}

	/**
	 * Refactor remove this function and use ProxyServlet to get this info
	 * Before deleting check web panel does not use it
	 * @param name: application name 
	 * @return live streams in the application
	 */

	public String getAppLiveStreams(@PathParam("appname") String name) {
		List appLiveStreams = getApplication().getAppLiveStreams(name);
		return gson.toJson(appLiveStreams);
	}


	/**
	 * Refactor remove this function and use ProxyServlet to get this info
	 * Before deleting check web panel does not use it
	 * @param name application name
	 * @param streamName the stream name to be deleted
	 * @return operation value
	 */

	public String deleteVoDStream(@PathParam("appname") String name, @FormParam("streamName") String streamName) {
		boolean deleteVoDStream = getApplication().deleteVoDStream(name, streamName);
		return gson.toJson(new Result(deleteVoDStream));
	}



	public String changeSettings(@PathParam("appname") String appname, AppSettings newSettings){
		AntMediaApplicationAdapter adapter = (AntMediaApplicationAdapter) getApplication().getApplicationContext(appname).getBean(AntMediaApplicationAdapter.BEAN_NAME);
		return gson.toJson(new Result(adapter.updateSettings(newSettings, true, false)));
	}

	public boolean getShutdownStatus(@QueryParam("appNames") String appNamesArray){

		boolean appShutdownProblemExists = false;
		if (appNamesArray != null) 
		{
			String[] appNames = appNamesArray.split(",");

			if (appNames != null) 
			{
				for (String appName : appNames) 
				{
					//Check apps shutdown properly
					AntMediaApplicationAdapter appAdaptor = getAppAdaptor(appName);
					if (!appAdaptor.isShutdownProperly()) {
						appShutdownProblemExists = true;
						break;
					}

				}
			}
		}

		return !appShutdownProblemExists;
	}

	public AntMediaApplicationAdapter getAppAdaptor(String appName) {

		AntMediaApplicationAdapter appAdaptor = null;
		AdminApplication application = getApplication();
		if (application != null) 
		{
			ApplicationContext context = application.getApplicationContext(appName);
			if (context != null) 
			{
				appAdaptor = (AntMediaApplicationAdapter) context.getBean(AntMediaApplicationAdapter.BEAN_NAME);
			}
		}
		return appAdaptor;
	}

	public Response isShutdownProperly(@QueryParam("appNames") String appNamesArray)
	{
		boolean appShutdownProblemExists = false;
		Response response = null;

		if (appNamesArray != null) 
		{
			String[] appNames = appNamesArray.split(",");

			if (appNames != null) 
			{
				for (String appName : appNames) 
				{
					//Check apps shutdown properly
					AntMediaApplicationAdapter appAdaptor = getAppAdaptor(appName);
					if (appAdaptor != null) 
					{
						if (!appAdaptor.isShutdownProperly()) {
							appShutdownProblemExists = true;
							break;
						}
					}
					else {
						response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(new Result(false, "Either server may not be initialized or application name that does not exist is requested. ")).build();
						break;
					}
				}
			}
			else {
				response = Response.status(Status.BAD_REQUEST).entity(new Result(false, "Bad parameter for appNames. ")).build();
			}

		}
		else {
			response = Response.status(Status.BAD_REQUEST).entity(new Result(false, "Bad parameter for appNames. ")).build();
		}

		if (response == null) {
			response = Response.status(Status.OK).entity(new Result(!appShutdownProblemExists)).build();
		}

		return response; 
	}



	public boolean setShutdownStatus(@QueryParam("appNames") String appNamesArray){

		String[] appNames = appNamesArray.split(",");
		boolean result = true;

		for (String appName : appNames) {
			//Check apps shutdown properly
			AntMediaApplicationAdapter appAdaptor = getAppAdaptor(appName);
			if (appAdaptor != null) {
				appAdaptor.setShutdownProperly(true);
			}
		}

		return result;
	}

	public static String extractFQDN(String domainName) {
		// regular expression pattern for fully qualified domain name
		String regex = "([A-Za-z0-9-]{1,63}\\.){1,10}[A-Za-z]{2,6}";
		Pattern pattern = Pattern.compile(regex);
		if (domainName != null) {
			Matcher matcher = pattern.matcher(domainName);

			if (matcher.find()) {
				return matcher.group();
			} else {
				return null;
			}
		}
		return null;
	}


	public Result configureSsl(String domainName, String type,
			InputStream fullChainFile,  FormDataContentDisposition fullChainFileDetail,
			InputStream privateKeyFile, FormDataContentDisposition privateKeyFileDetail,
			InputStream chainFile, FormDataContentDisposition chainFileDetail)
	{
		//check if the parameters are valid
		boolean parametersValid = false;
		String responseMessage = null;

		SslConfigurator sslConfigurator = new SslConfigurator();

		File sslTempDir = null;

		SslConfigurationType sslConfigurationType = getSSLConfigurationType(type);

		String fqdn = extractFQDN(domainName);
		if (fqdn == null && sslConfigurationType != SslConfigurationType.ANTMEDIA_SUBDOMAIN) 
		{
			//return invalid if there is no fqdn except for antmedia.cloud subdomain
			responseMessage = "Invalid domain name parameter";
		}
		else if (sslConfigurationType == SslConfigurationType.CUSTOM_CERTIFICATE) 
		{

			if (isCustomCertificateParamsValid(fullChainFile, fullChainFileDetail, privateKeyFile,
					privateKeyFileDetail, chainFile, chainFileDetail)
					) 
			{
				//save the files to the tmp directory
				String systemTempDir = System.getProperty("java.io.tmpdir");
				String sslTempDirName = "ssl-tmp-ant-media";
				sslTempDir = new File(systemTempDir, sslTempDirName);
				sslTempDir.mkdirs();

				File fullChainOutputFile = new File(sslTempDir, FilenameUtils.getName(fullChainFileDetail.getFileName()));
				File privateKeyOutputFile = new File(sslTempDir, FilenameUtils.getName(privateKeyFileDetail.getFileName()));
				File chainOutputFile = new File(sslTempDir, FilenameUtils.getName(chainFileDetail.getFileName()));
				sslConfigurator.setFullChainFile(fullChainOutputFile);
				sslConfigurator.setPrivateKeyFile(privateKeyOutputFile);
				sslConfigurator.setChainFile(chainOutputFile);

				Result tmp = prepareCertificateFiles(fullChainFile, fullChainOutputFile, privateKeyFile,
						privateKeyOutputFile, chainFile, chainOutputFile);

				parametersValid = tmp.isSuccess();
				responseMessage = tmp.getMessage();

			}
			else {
				responseMessage = "Missing parameters for custom SSL certificate. Please provide domain name, fullChain, private key, and chain";
			}

		}
		else if (sslConfigurationType == SslConfigurationType.CUSTOM_DOMAIN || sslConfigurationType == SslConfigurationType.ANTMEDIA_SUBDOMAIN) 
		{
			parametersValid = true;	
		}
		else 
		{
			responseMessage = "Unknown SSL configuration type";
		}

		Result result = new Result(false);
		if (parametersValid) 
		{
			sslConfigurator.setDomain(fqdn);
			sslConfigurator.setType(sslConfigurationType);

			String command = sslConfigurator.getCommand();
			if (command != null) 
			{
				AdminApplication adminApplication = getApplication();
				if (adminApplication != null) 
				{
					result.setSuccess(adminApplication.runCommand(command));
				}
			}
			else {
				result.setMessage("Undefined configuration type");
			}
		}
		else {
			result.setMessage(responseMessage);


		}
		deleteSSLTempDirIfExists(sslTempDir);

		return result;
	}


	private SslConfigurationType getSSLConfigurationType(String type) {
		if (type != null) {
			try {
				return SslConfigurationType.valueOf(type);
			}
			catch (IllegalArgumentException e) {
				logger.error(ExceptionUtils.getStackTrace(e));
			}
		}
		return null;
	}


	private boolean isCustomCertificateParamsValid(InputStream fullChainFile,
			FormDataContentDisposition fullChainFileDetail, InputStream privateKeyFile,
			FormDataContentDisposition privateKeyFileDetail, InputStream chainFile,
			FormDataContentDisposition chainFileDetail) {
		return fullChainFile != null && fullChainFileDetail != null && fullChainFileDetail.getFileName() != null && !fullChainFileDetail.getFileName().isEmpty() &&
				privateKeyFile != null && privateKeyFileDetail != null && privateKeyFileDetail.getFileName() != null && !privateKeyFileDetail.getFileName().isEmpty() &&
				chainFile != null && chainFileDetail != null && chainFileDetail.getFileName() != null && !chainFileDetail.getFileName().isEmpty();
	}


	private Result prepareCertificateFiles(InputStream fullChainFile, File fullChainOutputFile,
			InputStream privateKeyFile, File privateKeyOutputFile, InputStream chainFile,
			File chainOutputFile) {
		boolean result = false;
		String message = null;
		try {
			Files.copy(fullChainFile, fullChainOutputFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
			Files.copy(privateKeyFile, privateKeyOutputFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
			Files.copy(chainFile, chainOutputFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
			result = true;

		} catch (IOException e) {
			logger.error(ExceptionUtils.getStackTrace(e));
			message = "Certificate file operation is not succesful due to " + e.getMessage();

		}
		return new Result(result, message);
	}


	private void deleteSSLTempDirIfExists(File sslTempDir) {
		if (sslTempDir != null && sslTempDir.exists()) {
			try {
				FileUtils.forceDelete(sslTempDir);
			} catch (IOException e) {
				logger.error(ExceptionUtils.getStackTrace(e));
			}
		}
	}

	public String changeServerSettings(ServerSettings serverSettings){
		PreferenceStore store = new PreferenceStore(RED5_PROPERTIES_PATH);

		String serverName = "";
		String licenceKey = "";

		if(serverSettings.getServerName() != null) {
			serverName = serverSettings.getServerName();
		}

		store.put(SERVER_NAME, serverName);
		getServerSettingsInternal().setServerName(serverName);

		if (serverSettings.getLicenceKey() != null) {
			licenceKey = serverSettings.getLicenceKey();
		}

		store.put(LICENSE_KEY, licenceKey);
		getServerSettingsInternal().setLicenceKey(licenceKey);

		store.put(NODE_GROUP, String.valueOf(serverSettings.getNodeGroup()));
		getServerSettingsInternal().setNodeGroup(serverSettings.getNodeGroup());

		ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ROOT_LOGGER_NAME);

		if(LOG_LEVEL_ALL.equals(serverSettings.getLogLevel()) || LOG_LEVEL_TRACE.equals(serverSettings.getLogLevel()) 
				|| LOG_LEVEL_DEBUG.equals(serverSettings.getLogLevel()) || LOG_LEVEL_INFO.equals(serverSettings.getLogLevel()) 
				|| LOG_LEVEL_WARN.equals(serverSettings.getLogLevel())  || LOG_LEVEL_ERROR.equals(serverSettings.getLogLevel())
				|| LOG_LEVEL_OFF.equals(serverSettings.getLogLevel())) 
		{

			rootLogger.setLevel(currentLevelDetect(serverSettings.getLogLevel()));

			store.put(LOG_LEVEL, serverSettings.getLogLevel());
			getServerSettingsInternal().setLogLevel(serverSettings.getLogLevel());
		}

		return gson.toJson(new Result(store.save()));


	}


	public Result isEnterpriseEdition(){
		boolean isEnterprise = RestServiceBase.isEnterprise();
		return new Result(isEnterprise, "");
	}


	public AppSettings getSettings(String appname) 
	{
		AntMediaApplicationAdapter appAdaptor = getAppAdaptor(appname);
		if (appAdaptor != null) {
			return appAdaptor.getAppSettings();
		}
		logger.warn("getSettings for app: {} returns null. It's likely not initialized.", appname);
		return null;
	}


	public IStatsCollector getStatsCollector () {
		if(statsCollector == null) 
		{
			WebApplicationContext ctxt =getContext();
			if (ctxt != null) {
				statsCollector = (IStatsCollector)ctxt.getBean(IStatsCollector.BEAN_NAME);
			}
		}
		return statsCollector;
	}

	public ServerSettings getServerSettings() 
	{
		return getServerSettingsInternal();
	}

	public Licence getLicenceStatus(@QueryParam("key") String key) 
	{
		if(key == null) {
			return null;
		}
		return getLicenceServiceInstance().checkLicence(key);
	}


	public Licence getLicenceStatus() 
	{
		return getLicenceServiceInstance().getLastLicenseStatus();
	}

	/**
	 * This method resets the viewers counts and broadcast status in the db. 
	 * This should be used to recover db after server crashes. 
	 * It's not intended to use to ignore the crash
	 * @param appname the application name that broadcasts will be reset
	 * @return
	 */

	public Result resetBroadcast(@PathParam("appname") String appname) 
	{
		AntMediaApplicationAdapter appAdaptor = getAppAdaptor(appname);
		if (appAdaptor != null) {
			return appAdaptor.resetBroadcasts();
		}
		return new Result(false, "No application adaptor with this name " + appname);
	}

	public void setDataStore(AbstractConsoleDataStore dataStore) {
		this.dataStore = dataStore;
	}

	public AbstractConsoleDataStore getDataStore() {
		if (dataStore == null) {
			dataStore = getDataStoreFactory().getDataStore();
		}
		return dataStore;
	}

	public WebApplicationContext getContext() {
		return WebApplicationContextUtils.getWebApplicationContext(servletContext);
	}

	public ServerSettings getServerSettingsInternal() {

		if(serverSettings == null) 
		{
			WebApplicationContext ctxt = getContext();
			if (ctxt != null) {
				serverSettings = (ServerSettings)ctxt.getBean(ServerSettings.BEAN_NAME);
			}
		}
		return serverSettings;
	}



	public ILicenceService getLicenceServiceInstance () {
		if(licenceService == null) {

			WebApplicationContext ctxt = getContext();
			if (ctxt != null) {
				licenceService = (ILicenceService)ctxt.getBean(ILicenceService.BeanName.LICENCE_SERVICE.toString());
			}
		}
		return licenceService;
	}


	public AdminApplication getApplication() {
		WebApplicationContext ctxt = getContext();
		if (ctxt != null) {
			return (AdminApplication)ctxt.getBean("web.handler");
		}
		return null;
	}

	public ConsoleDataStoreFactory getDataStoreFactory() {
		if(dataStoreFactory == null)
		{
			WebApplicationContext ctxt = getContext();
			if (ctxt != null) {
				dataStoreFactory = (ConsoleDataStoreFactory) ctxt.getBean("dataStoreFactory");
			}
		}
		return dataStoreFactory;
	}

	public void setDataStoreFactory(ConsoleDataStoreFactory dataStoreFactory) {
		this.dataStoreFactory = dataStoreFactory;
	}


	public Result isInClusterMode()
	{
		return new Result(isClusterMode(), "");
	}

	public String changeLogSettings(@PathParam("level") String logLevel){

		ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ROOT_LOGGER_NAME);

		PreferenceStore store = new PreferenceStore(RED5_PROPERTIES_PATH);

		if(logLevel.equals(LOG_LEVEL_ALL) || logLevel.equals(LOG_LEVEL_TRACE) 
				|| logLevel.equals(LOG_LEVEL_DEBUG) || logLevel.equals(LOG_LEVEL_INFO) 
				|| logLevel.equals(LOG_LEVEL_WARN)  || logLevel.equals(LOG_LEVEL_ERROR)
				|| logLevel.equals(LOG_LEVEL_OFF)) {

			rootLogger.setLevel(currentLevelDetect(logLevel));

			store.put(LOG_LEVEL, logLevel);
		}

		return gson.toJson(new Result(store.save()));
	}

	public Level currentLevelDetect(String logLevel) {

		Level currentLevel;
		if( logLevel.equals(LOG_LEVEL_OFF)) {
			currentLevel = Level.OFF;
			return currentLevel;
		}
		if( logLevel.equals(LOG_LEVEL_ERROR)) {
			currentLevel = Level.ERROR;
			return currentLevel;
		}
		if( logLevel.equals(LOG_LEVEL_WARN)) {
			currentLevel = Level.WARN;
			return currentLevel;
		}
		if( logLevel.equals(LOG_LEVEL_DEBUG)) {
			currentLevel = Level.DEBUG;
			return currentLevel;
		}
		if( logLevel.equals(LOG_LEVEL_TRACE)) {
			currentLevel = Level.ALL;
			return currentLevel;
		}
		if( logLevel.equals(LOG_LEVEL_ALL)) {
			currentLevel = Level.ALL;
			return currentLevel;
		}
		else {
			currentLevel = Level.INFO;
			return currentLevel;
		}

	}

	public String getLogFile(@PathParam("charSize") int charSize, @QueryParam("logType") String logType,
			@PathParam("offsetSize") long offsetSize) throws IOException {

		long skipValue = 0;
		int countKb = 0;
		int maxCount = 500;
		//default log 
		String logLocation = SERVER_LOG_LOCATION;

		if (logType.equals(LOG_TYPE_ERROR)) {
			logLocation = ERROR_LOG_LOCATION;
		} 

		JsonObject jsonObject = new JsonObject();
		String logContent = "";
		File file = new File(logLocation);

		if (!file.isFile()) {
			logContent = FILE_NOT_EXIST;

			jsonObject.addProperty(LOG_CONTENT, logContent);

			return jsonObject.toString();
		}

		// check charSize > 500kb
		if (charSize > MAX_CHAR_SIZE) {
			charSize = MAX_CHAR_SIZE;
		}

		if (offsetSize != -1) { 			
			skipValue = offsetSize;
			maxCount = charSize / 1024;
		} 
		else if (file.length() > charSize) {
			skipValue = file.length() - charSize;
		}

		int contentSize = 0;
		if (file.length() > skipValue) {

			ByteArrayOutputStream ous = null;
			InputStream ios = null;


			try {

				byte[] buffer = new byte[1024];
				ous = new ByteArrayOutputStream();
				ios = new FileInputStream(file);

				ios.skip(skipValue);

				int read = 0;

				while ((read = ios.read(buffer)) != -1) {

					ous.write(buffer, 0, read);
					countKb++;
					contentSize += read;
					if (countKb == maxCount) { // max read 500kb
						break;
					}

				}
			} finally {
				try {
					if (ous != null)
						ous.close();
				} catch (IOException e) { 
					logger.error(e.toString());
				}

				try {
					if (ios != null)
						ios.close();
				} catch (IOException e) {
					logger.error(e.toString());
				}
			}

			logContent = ous.toString("UTF-8");
		}
		jsonObject.addProperty(LOG_CONTENT, logContent);
		jsonObject.addProperty(LOG_CONTENT_SIZE, contentSize);
		jsonObject.addProperty(LOG_FILE_SIZE, file.length());

		return jsonObject.toString();
	}

	public String getMD5Hash(String pass){
		String passResult= "";
		try {
			MessageDigest m=MessageDigest.getInstance("MD5");
			m.reset();
			m.update(pass.getBytes(Charset.forName("UTF8")));
			byte[] digestResult=m.digest();
			passResult= Hex.encodeHexString(digestResult);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return passResult;
	}


	public Result createApplication(String appName, InputStream inputStream) 
	{
		appName = appName.replaceAll("[\n\r\t]", "_");

		File warFile = null;
		if (inputStream != null) 
		{
			warFile = AdminApplication.saveWARFile(appName, inputStream);

			if (warFile == null) 
			{
				return new Result(false, "Cannot save the WAR file for appName:{}", appName);
			}
		}

		if (isClusterMode())
		{
			//If there is a record in database, just delete it in order to start from scratch
			IClusterNotifier clusterNotifier = getApplication().getClusterNotifier();
			long deletedRecordCount = clusterNotifier.getClusterStore().deleteAppSettings(appName);
			if (deletedRecordCount > 0) 
			{
				logger.info("App detected in the database. It's likely the app with the same name {} is re-creating. ", appName);
			}

			if (warFile != null) 
			{
				AppSettings tempSetting = new AppSettings();
				tempSetting.setAppName(appName);
				tempSetting.setPullWarFile(true);
				tempSetting.setWarFileOriginServerAddress(getServerSettings().getHostAddress());

				clusterNotifier.getClusterStore().saveSettings(tempSetting);
			}
		}
		
		Result result = new Result(false);
		
		if (getApplication().createApplication(appName, warFile != null ? warFile.getAbsolutePath() : null)) 
		{
			result.setSuccess(true);

			boolean applicationCreated = false;
			for (int i = 0; i < 20; i++) 
			{
				try {
					Thread.sleep(1000);

					IScope scope = getApplication().getRootScope().getScope(appName);
					if (scope != null) {
						applicationCreated = true;
						break;
					}


				} 
				catch (InterruptedException e) {
					logger.error(ExceptionUtils.getStackTrace(e));
				    Thread.currentThread().interrupt();
				}
			}
			
			if (!applicationCreated) {
				result.setSuccess(applicationCreated);
				result.setMessage("Application " + appName + "is not created in the 20 seconds.");
			}

		}
		
		return result;
	}


	public Result deleteApplication(String appName, boolean deleteDB) {
		appName = appName.replaceAll("[\n\r\t]", "_");
		boolean result = false;
		String message = "";
		if (appName != null && appName.matches("^[a-zA-Z0-9]*$")) {
			logger.info("delete application http request:{}", appName);
			AppSettings appSettings = getSettings(appName);
			
			if (appSettings != null) {
				appSettings.setToBeDeleted(true);
				//change settings on the db to let undeploy the app
				changeSettings(appName, appSettings);

				result = getApplication().deleteApplication(appName, deleteDB);
			}
			else {
				logger.info("App settings is not available for app name:{}. App may be initializing", appName);
				message = "AppSettings is not available for app: " + appName + ". It's not available or it's being initialized";
			}
		}
		else {
			message = "appname contains invalid character and does not match regexp ^[a-zA-Z0-9]*$";
		}
		return new Result(result, message);
	}

	public boolean isClusterMode() 
	{
		boolean result = false;
		WebApplicationContext ctxt = getContext();
		if (ctxt != null) {
			result = ctxt.containsBean(IClusterNotifier.BEAN_NAME);
		}
		return result;
	}

	public Result getBlockedStatus(String usermail) {
		return new Result(getDataStore().isUserBlocked(usermail));
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy