/* ############################################################################### # # # Copyright (C) 2011-2016 OpenMEAP, Inc. # # Credits to Jonathan Schang & Rob Thacher # # # # Released under the LGPLv3 # # # # OpenMEAP is free software: you can redistribute it and/or modify # # it under the terms of the GNU Lesser General Public License as published # # by the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # OpenMEAP is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU Lesser General Public License for more details. # # # # You should have received a copy of the GNU Lesser General Public License # # along with OpenMEAP. If not, see <http://www.gnu.org/licenses/>. # # # ############################################################################### */ package com.openmeap.services; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.FileNameMap; import java.net.URLConnection; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.net.URLCodec; import com.openmeap.thirdparty.org.json.me.JSONException; import com.openmeap.thirdparty.org.json.me.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import com.openmeap.constants.UrlParamConstants; import com.openmeap.digest.DigestException; import com.openmeap.json.JSONObjectBuilder; import com.openmeap.model.ModelManager; import com.openmeap.model.dto.ApplicationArchive; import com.openmeap.model.dto.ApplicationVersion; import com.openmeap.model.dto.ClusterNode; import com.openmeap.model.dto.GlobalSettings; import com.openmeap.protocol.ApplicationManagementService; import com.openmeap.protocol.WebServiceException; import com.openmeap.protocol.dto.Application; import com.openmeap.protocol.dto.ApplicationInstallation; import com.openmeap.protocol.dto.ConnectionOpenRequest; import com.openmeap.protocol.dto.ConnectionOpenResponse; import com.openmeap.protocol.dto.Error; import com.openmeap.protocol.dto.ErrorCode; import com.openmeap.protocol.dto.Result; import com.openmeap.protocol.dto.SLIC; import com.openmeap.util.AuthTokenProvider; import com.openmeap.util.GenericRuntimeException; import com.openmeap.util.Utils; public class ApplicationManagementServlet extends HttpServlet { private static final long serialVersionUID = -4989132152150822192L; private Logger logger = LoggerFactory.getLogger(ApplicationManagementServlet.class); private ModelManager modelManager = null; private WebApplicationContext context = null; @Override public void init() { context = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); modelManager = (ModelManager)context.getBean("modelManager"); } @Override public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Result result = new Result(); Error err = new Error(); String action = req.getParameter(UrlParamConstants.ACTION); if( action==null ) action=""; if( action.equals("connection-open-request") ) { result = connectionOpenRequest(req); } else if ( action.equals("archiveDownload") ) { result = handleArchiveDownload(req,resp); if( result==null ) { return; } } else { err.setCode(ErrorCode.MISSING_PARAMETER); err.setMessage("The \"action\" parameter is not recognized, missing, or empty."); result.setError(err); } try { JSONObjectBuilder builder = new JSONObjectBuilder(); resp.setContentType("text/javascript"); JSONObject jsonResult = builder.toJSON(result); resp.getOutputStream().write(jsonResult.toString().getBytes()); } catch (JSONException e) { throw new RuntimeException(e); } } private Result handleArchiveDownload(HttpServletRequest request, HttpServletResponse response) { Result res = new Result(); Error err = new Error(); res.setError(err); GlobalSettings settings = modelManager.getGlobalSettings(); Map properties = this.getServicesWebProperties(); String nodeKey = (String)properties.get("clusterNodeUrlPrefix"); ClusterNode clusterNode = settings.getClusterNode(nodeKey); if( nodeKey==null || clusterNode==null ) { // TODO: create a configuration error code err.setCode(ErrorCode.UNDEFINED); err.setMessage("A configuration is missing. Please consult the error logs."); logger.error("For each node in the cluster, the property or environment variable OPENMEAP_CLUSTER_NODE_URL_PREFIX must match the \"Service Url Prefix\" value configured in the administrative interface. This value is currently "+nodeKey+"."); return res; } String pathValidation = clusterNode.validateFileSystemStoragePathPrefix(); if( pathValidation!=null ) { err.setCode(ErrorCode.UNDEFINED); err.setMessage("A configuration is missing. Please consult the error logs."); logger.error("There is an issue with the location at \"File-system Storage Prefix\". "+pathValidation); return res; } String hash = request.getParameter(UrlParamConstants.APPARCH_HASH); String hashAlg = request.getParameter(UrlParamConstants.APPARCH_HASH_ALG); String fileName = null; if( hash==null || hashAlg==null ) { // look in the apps directory for the archive specified String appName = request.getParameter(UrlParamConstants.APP_NAME); String versionId = request.getParameter(UrlParamConstants.APP_VERSION); ApplicationVersion appVersion = modelManager.getModelService().findAppVersionByNameAndId(appName, versionId); if( appVersion==null ) { String mesg = "The application version "+versionId+" was not found for application "+appName; err.setCode(ErrorCode.APPLICATION_VERSION_NOTFOUND); err.setMessage(mesg); logger.warn(mesg); return res; } String auth = request.getParameter(UrlParamConstants.AUTH_TOKEN); com.openmeap.model.dto.Application app = appVersion.getApplication(); try { if( auth==null || ! AuthTokenProvider.validateAuthToken(app.getProxyAuthSalt(), auth) ) { err.setCode(ErrorCode.AUTHENTICATION_FAILURE); err.setMessage("The \"auth\" token presented is not recognized, missing, or empty."); return res; } } catch (DigestException e) { throw new GenericRuntimeException(e); } hash = appVersion.getArchive().getHash(); hashAlg = appVersion.getArchive().getHashAlgorithm(); fileName = app.getName()+" - "+appVersion.getIdentifier(); } else { fileName = hashAlg+"-"+hash; } File file = ApplicationArchive.getFile(clusterNode.getFileSystemStoragePathPrefix(),hashAlg,hash); if( ! file.exists() ) { String mesg = "The application archive with "+hashAlg+" hash "+hash+" was not found."; // TODO: create an enumeration for this error err.setCode(ErrorCode.UNDEFINED); err.setMessage(mesg); logger.warn(mesg); return res; } try { FileNameMap fileNameMap = URLConnection.getFileNameMap(); String mimeType = fileNameMap.getContentTypeFor(file.toURL().toString()); response.setContentType(mimeType); response.setContentLength(Long.valueOf(file.length()).intValue()); URLCodec codec = new URLCodec(); response.setHeader("Content-Disposition", "attachment; filename=\""+fileName+".zip\";"); InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = new BufferedInputStream(new FileInputStream(file)); outputStream = response.getOutputStream(); Utils.pipeInputStreamIntoOutputStream(inputStream, outputStream); } finally { if(inputStream!=null) {inputStream.close();} //if(outputStream!=null) {outputStream.close();} } response.flushBuffer(); } catch (FileNotFoundException e) { logger.error("Exception {}",e); } catch (IOException ioe) { logger.error("Exception {}",ioe); } return null; } /** * Pulls parameters out of the request and passes them to the ApplicationManagementPortType bean pulled from the WebApplicationContext * * @param req * @return */ public Result connectionOpenRequest(HttpServletRequest req) { WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); ConnectionOpenRequest request = createConnectionOpenRequest(req); Result result = new Result(); try { ConnectionOpenResponse response = ((ApplicationManagementService)context.getBean("applicationManagementService")).connectionOpen(request); result.setConnectionOpenResponse(response); } catch( WebServiceException wse ) { Error err = new Error(); err.setCode(wse.getType().asErrorCode()); err.setMessage(wse.getMessage()); result.setError(err); } return result; } private ConnectionOpenRequest createConnectionOpenRequest(HttpServletRequest req) { ConnectionOpenRequest request = new ConnectionOpenRequest(); request.setApplication(new Application()); Application app = request.getApplication(); app.setVersionId(req.getParameter(UrlParamConstants.APP_VERSION)); app.setName(req.getParameter(UrlParamConstants.APP_NAME)); app.setHashValue(req.getParameter(UrlParamConstants.APPARCH_HASH)); app.setInstallation(new ApplicationInstallation()); app.getInstallation().setUuid(req.getParameter(UrlParamConstants.DEVICE_UUID)); request.setSlic(new SLIC()); SLIC slic = request.getSlic(); slic.setVersionId(req.getParameter(UrlParamConstants.SLIC_VERSION)); return request; } public Map getServicesWebProperties() { return (Map)context.getBean("openmeapServicesWebPropertiesMap"); } // ACCESSORS public void setModelManager(ModelManager manager) { modelManager = manager; } public ModelManager getModelManager() { return modelManager; } }