/*
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.wso2.carbon.device.mgt.ios.services.impl;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.cms.CMSException;
import org.wso2.carbon.apimgt.application.extension.exception.APIManagerException;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.certificate.mgt.core.dto.SCEPResponse;
import org.wso2.carbon.certificate.mgt.core.exception.KeystoreException;
import org.wso2.carbon.certificate.mgt.core.scep.SCEPException;
import org.wso2.carbon.certificate.mgt.core.scep.TenantedDeviceWrapper;
import org.wso2.carbon.certificate.mgt.core.service.CertificateManagementService;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.app.mgt.Application;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.license.mgt.License;
import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException;
import org.wso2.carbon.device.mgt.common.policy.mgt.Policy;
import org.wso2.carbon.device.mgt.common.policy.mgt.ProfileFeature;
import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.ComplianceFeature;
import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.PolicyComplianceException;
import org.wso2.carbon.device.mgt.ios.api.utils.beans.DeviceIdentifierBean;
import org.wso2.carbon.device.mgt.ios.api.utils.beans.LoginBean;
import org.wso2.carbon.device.mgt.ios.api.utils.beans.ProfileBean;
import org.wso2.carbon.device.mgt.ios.api.utils.beans.TokenBean;
import org.wso2.carbon.device.mgt.ios.api.utils.util.ApplicationUtils;
import org.wso2.carbon.device.mgt.ios.api.utils.util.Message;
import org.wso2.carbon.device.mgt.ios.api.utils.util.OperationMapping;
import org.wso2.carbon.device.mgt.ios.apns.exception.APNSException;
import org.wso2.carbon.device.mgt.ios.core.bean.ChallengeTokenBean;
import org.wso2.carbon.device.mgt.ios.core.exception.IOSEnrollmentException;
import org.wso2.carbon.device.mgt.ios.core.exception.TenantConfigurationException;
import org.wso2.carbon.device.mgt.ios.core.impl.SCEPOperation;
import org.wso2.carbon.device.mgt.ios.core.publisher.TokenPersistence;
import org.wso2.carbon.device.mgt.ios.core.service.IOSEnrollmentService;
import org.wso2.carbon.device.mgt.ios.core.util.AppConfigurations;
import org.wso2.carbon.device.mgt.ios.core.util.TenantUtils;
import org.wso2.carbon.device.mgt.ios.dep.utils.TokenUtils;
import org.wso2.carbon.device.mgt.ios.exception.BadRequestException;
import org.wso2.carbon.device.mgt.ios.payload.dto.APNSStatus;
import org.wso2.carbon.device.mgt.ios.payload.dto.CertificateAttributes;
import org.wso2.carbon.device.mgt.ios.payload.dto.CheckInMessageType;
import org.wso2.carbon.device.mgt.ios.payload.dto.DeviceProperties;
import org.wso2.carbon.device.mgt.ios.payload.exception.PListException;
import org.wso2.carbon.device.mgt.ios.payload.exception.ProfileConfigurationException;
import org.wso2.carbon.device.mgt.ios.services.EnrollmentService;
import org.wso2.carbon.device.mgt.ios.util.ContentType;
import org.wso2.carbon.device.mgt.ios.util.DeviceUtils;
import org.wso2.carbon.device.mgt.ios.util.EnrollmentUtils;
import org.wso2.carbon.device.mgt.ios.util.IOSServiceUtils;
import org.wso2.carbon.device.mgt.ios.util.OAuthUtils;
import org.wso2.carbon.device.mgt.ios.util.OperationUtils;
import org.wso2.carbon.device.mgt.ios.util.PolicyUtils;
import org.wso2.carbon.device.mgt.mobile.impl.ios.dto.DEPDevice;
import org.wso2.carbon.policy.mgt.common.PolicyManagementException;
import org.wso2.carbon.policy.mgt.core.util.PolicyManagementConstants;
import org.wso2.carbon.user.api.UserStoreException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.lang.reflect.Type;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

public class EnrollmentServiceImpl implements EnrollmentService {
    private static final Log log = LogFactory.getLog(EnrollmentServiceImpl.class);

    @GET
    @Produces({MediaType.WILDCARD})
    @Path("/ca")
    public void getCA(@Context HttpServletResponse response) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("Requesting CA certificate");
        }

        try {
            CertificateManagementService certificateManagementService = IOSServiceUtils.
                    getCertificateManagementService();
            Certificate certificateCA = certificateManagementService.getCACertificate();

            //TODO Temp fix to get this working. Make this a proper API.
            byte[] data = certificateCA.getEncoded();
            response.setContentType(ContentType.X_X509_CA_CERT);
            response.setContentLength(data.length);
            response.getOutputStream().write(data);

        } catch (CertificateEncodingException e) {
            log.error("Error occurred in certificate encoding", e);
        } catch (KeystoreException e) {
            log.error("Keystore error occurred while enrolling the iOS device", e);
        } catch (IOSEnrollmentException e) {
            log.error("Error occurred while enrolling the iOS device", e);
        }

    }

    @POST
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    @Path("/authenticate")
    public Response authenticate(LoginBean loginBean) {

        if (log.isDebugEnabled()) {
            log.debug("Authentication endpoint called");
        }

        try {
            IOSServiceUtils iosServiceUtils = new IOSServiceUtils();
            if (iosServiceUtils.authenticate(loginBean.getUsername(), loginBean.getPassword(),
                    loginBean.getTenantDomain())) {
                DeviceIdentifierBean deviceIdentifierBean = OAuthUtils.getOAuthToken(loginBean);
                String challengeToken = EnrollmentUtils.generateRandomToken();
                ChallengeTokenBean challengeTokenBean = new ChallengeTokenBean();
                challengeTokenBean.setChallengeToken(challengeToken);
                challengeTokenBean.setDeviceID("");
                challengeTokenBean.setUsername(loginBean.getUsername());
                challengeTokenBean.setDomain(loginBean.getTenantDomain());
                challengeTokenBean.setRefreshToken(deviceIdentifierBean.getRefreshToken());
                challengeTokenBean.setAccessToken(deviceIdentifierBean.getAccessToken());
                challengeTokenBean.setClient(deviceIdentifierBean.getClientCredentials());
                challengeTokenBean.setAgentAvailable(loginBean.isAgentAvailable());
                EnrollmentUtils.persistChallengeToken(challengeTokenBean);

                TokenBean tokenBean = new TokenBean();
                tokenBean.setChallengeToken(challengeToken);
                return Response.ok(tokenBean).build();
            }
        } catch (IOSEnrollmentException e) {
            log.error("Error occurred while enrolling the iOS device", e);
            return Response.serverError().build();
        } catch (APIManagerException e) {
            log.error("Error occurred while authenticating the user "+ loginBean.getUsername(), e);
            return Response.serverError().build();
        } catch (UserStoreException e) {
            log.error("Error occurred while initializing the userstore for the tenant: " + loginBean.getTenantDomain()
                    , e);
            return Response.serverError().build();
        }

        return Response.status(Response.Status.UNAUTHORIZED).build();
    }

    @POST
    @Consumes({ MediaType.APPLICATION_JSON })
    @Produces({ ContentType.APPLE_ASPEN_CONFIG })
    @Path("/enroll")
    public Response getMobileConfigurations(LoginBean loginBean) {

        if (log.isDebugEnabled()) {
            log.debug("Requesting mobile configurations");
        }

        try {
            IOSServiceUtils iosServiceUtils = new IOSServiceUtils();
            if (!iosServiceUtils
                    .authenticate(loginBean.getUsername(), loginBean.getPassword(), loginBean.getTenantDomain())) {
                return Response.status(Response.Status.UNAUTHORIZED).build();
            }

            IOSEnrollmentService enrollmentService = IOSServiceUtils.getEnrollmentService();
            String tenantDomain = loginBean.getTenantDomain();
            if (StringUtils.isEmpty(tenantDomain)) {
                tenantDomain = TenantUtils.getTenantDomainName();
            }
            String mobileConfiguration = enrollmentService
                    .generateMobileConfigurations(loginBean.getChallengeToken(), tenantDomain);
            byte signedData[] = enrollmentService.getSignedData(mobileConfiguration.getBytes());

            return Response.ok(signedData).build();
        } catch (IOSEnrollmentException e) {
            log.error("Error occurred while enrolling the iOS device", e);
        }

        return Response.serverError().build();
    }

    @GET
    @Path("/enrolled")
    public Response isEnrolled(@QueryParam("deviceid") String deviceId) {

        if (log.isDebugEnabled()) {
            log.debug("Invoking isEnrolled method");
        }

        DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
        deviceIdentifier.setId(deviceId);
        deviceIdentifier.setType(DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS);

        try {
            TenantedDeviceWrapper tenantedDeviceWrapper = IOSServiceUtils.getSCEPManager().
                    getValidatedDevice(deviceIdentifier);

            if (tenantedDeviceWrapper != null && tenantedDeviceWrapper.getTenantDomain() != null &&
                    tenantedDeviceWrapper.getTenantId() != MultitenantConstants.INVALID_TENANT_ID) {
                try {
                    PrivilegedCarbonContext.startTenantFlow();
                    PrivilegedCarbonContext privilegedCarbonContext = PrivilegedCarbonContext
                            .getThreadLocalCarbonContext();
                    privilegedCarbonContext.setTenantId(tenantedDeviceWrapper.getTenantId());
                    privilegedCarbonContext.setTenantDomain(tenantedDeviceWrapper.getTenantDomain());
                    privilegedCarbonContext
                            .setUsername(tenantedDeviceWrapper.getDevice().getEnrolmentInfo().getOwner());

                    IOSEnrollmentService enrollmentService = IOSServiceUtils.getEnrollmentService();
                    if (enrollmentService.isEnrolled(deviceIdentifier)) {
                        return Response.ok().build();
                    } else {
                        return Response.status(Response.Status.NOT_FOUND).build();
                    }
                } finally {
                    PrivilegedCarbonContext.endTenantFlow();
                }
            }
        } catch (SCEPException e) {
            log.error("Error occurred while fetching the device", e);
        } catch (IOSEnrollmentException e) {
            log.error("Error occurred while enrolling the iOS device", e);
        }
        return Response.serverError().build();
    }


    @POST
    @Produces({ContentType.APPLE_ASPEN_CONFIG, MediaType.APPLICATION_JSON})
    @Consumes({MediaType.WILDCARD})
    @Path("/profile-dep")
    public Response getDEPProfileRequest(InputStream inputStream) {
        String username = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
        Boolean isAuthenticated = false;
        DEPDevice depDevice;
        InputStream[] clonedInput;
        DeviceProperties deviceProperties;

        try {
            OperationUtils operationUtil = new OperationUtils();
            clonedInput = operationUtil.cloneInputStream(inputStream);
            deviceProperties = IOSServiceUtils.getEnrollmentService().getDeviceProperties(clonedInput[2]);
            depDevice = IOSServiceUtils.getProfileService().getDEPDevice(deviceProperties.getSerial());
        } catch (IOSEnrollmentException e) {
            log.error("Error occurred while enrolling the iOS device", e);
            return Response.serverError().build();
        } catch (ProfileConfigurationException e) {
            log.error("Error occurred while fetching serial number", e);
            return Response.serverError().build();
        }

        if (username == null) {
            try {
                if (depDevice != null && StringUtils.isNotEmpty(depDevice.getSerial()) &&
                        !depDevice.getNeedBasicAuth()) {
                    if (StringUtils.isEmpty(depDevice.getTenantDomain())) {
                        throw new IllegalStateException("The device with serial number: " +
                                deviceProperties.getSerial() + " does not have a associated user." +
                                " A user must be associated with a device ");
                    }
                    int tenantId = MultitenantConstants.SUPER_TENANT_ID;
                    depDevice = IOSServiceUtils.getProfileService()
                            .getDEPDevice(deviceProperties.getSerial());

                    if (StringUtils.isNotEmpty(depDevice.getTenantDomain()) && !MultitenantConstants
                            .SUPER_TENANT_DOMAIN_NAME.
                                    equals(depDevice.getTenantDomain())) {
                        tenantId = IOSServiceUtils.getTenantID(depDevice.getTenantDomain());
                    }
                    isAuthenticated = true;
                    PrivilegedCarbonContext.startTenantFlow();
                    PrivilegedCarbonContext privilegedCarbonContext = PrivilegedCarbonContext.
                            getThreadLocalCarbonContext();
                    if (StringUtils.isNotEmpty(depDevice.getTenantDomain())) {
                        privilegedCarbonContext.setTenantDomain(depDevice.getTenantDomain());
                    } else {
                        privilegedCarbonContext.setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
                    }
                    privilegedCarbonContext.setTenantId(tenantId);
                    privilegedCarbonContext.setUsername(depDevice.getUsername());
                    username = depDevice.getUsername();
                } else {
                    return Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders
                            .WWW_AUTHENTICATE, "Basic").build();
                }
            } catch (IOSEnrollmentException e) {
                log.error("Error occurred while enrolling the iOS device", e);
                return Response.serverError().build();
            } catch (ProfileConfigurationException e) {
                log.error("Error occurred while fetching serial number", e);
                return Response.serverError().build();
            }
        }
        if (username != null || isAuthenticated) {
            IOSEnrollmentService enrollmentService = null;
            try {
                enrollmentService = IOSServiceUtils.getEnrollmentService();
            } catch (IOSEnrollmentException e) {
                log.error("Error occurred while getting enrollment service.", e);
            }

            if (StringUtils.isNotEmpty(username)) {
                try {

                    Message message = new Message();
                    TenantUtils tenantUtils = new TenantUtils();

                    CertificateAttributes certificateAttributes = tenantUtils
                            .getCertificateAttributes();
                    String topicID = tenantUtils.getTopicID();
                    String tenantDisplayName = tenantUtils.getTenantDisplayName();

                    if (tenantDisplayName == null || tenantDisplayName.isEmpty()) {
                        tenantDisplayName = "";
                    }

                    if (certificateAttributes == null) {
                        message.setErrorMessage("Certificate attributes are not configured.");
                        throw new BadRequestException(message, MediaType.APPLICATION_JSON_TYPE);
                    }

                    if (topicID == null) {
                        message.setErrorMessage("Topic ID is not configured.");
                        throw new BadRequestException(message, MediaType.APPLICATION_JSON_TYPE);
                    }

                    if (enrollmentService != null && deviceProperties.getSerial() != null) {
                        byte[] signedData = enrollmentService
                                .handleProfileRequest(clonedInput[1], tenantDisplayName,
                                        topicID, certificateAttributes, true);
                        if (depDevice != null && StringUtils.isEmpty(depDevice
                                .getDeviceIdentifier())) {
                            depDevice.setDeviceIdentifier(deviceProperties.getDeviceIdentifier());
                            IOSServiceUtils.getProfileService().updateDEPDevice(depDevice);
                        }
                        return Response.ok(signedData).build();
                    }
                } catch (IOSEnrollmentException e) {
                    log.error("Error occurred while enrolling the iOS device", e);
                } catch (DeviceManagementException e) {
                    log.error("Error occurred while fetching tenant configurations", e);
                } catch (TenantConfigurationException e) {
                    log.error("Error occurred while reading registry entries", e);
                } catch (ProfileConfigurationException e) {
                    log.error("Error occurred while fetching updating device.", e);
                } finally {
                    // Only when serial number based authentication is done, a tenant flow is
                    // started in the API level.
                    if (isAuthenticated) {
                        PrivilegedCarbonContext.endTenantFlow();
                    }
                }
            }
        }
        return Response.serverError().build();
    }

    @POST
    @Consumes({MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_JSON})
    @Path("/agent-authenticate")
    public Response agentAuthenticate(LoginBean loginBean) {

        if (log.isDebugEnabled()) {
            log.debug("Authentication endpoint called");
        }

        try {
            IOSServiceUtils iosServiceUtils = new IOSServiceUtils();
            if (iosServiceUtils.authenticate(loginBean.getUsername(), loginBean.getPassword(),
                    loginBean.getTenantDomain())) {
                DeviceIdentifierBean deviceIdentifierBean = OAuthUtils.getOAuthToken(loginBean);
                String challengeToken = EnrollmentUtils.generateRandomToken();
                ChallengeTokenBean challengeTokenBean = new ChallengeTokenBean();
                challengeTokenBean.setChallengeToken(challengeToken);
                challengeTokenBean.setDeviceID("");
                challengeTokenBean.setUsername(loginBean.getUsername());
                challengeTokenBean.setDomain(loginBean.getTenantDomain());
                challengeTokenBean.setRefreshToken(deviceIdentifierBean.getRefreshToken());
                challengeTokenBean.setAccessToken(deviceIdentifierBean.getAccessToken());
                challengeTokenBean.setClient(deviceIdentifierBean.getClientCredentials());
                challengeTokenBean.setAgentAvailable(loginBean.isAgentAvailable());
                EnrollmentUtils.persistChallengeToken(challengeTokenBean);
                return Response.ok(challengeTokenBean).build();
            }
        } catch (IOSEnrollmentException e) {
            log.error("Error occurred while enrolling the iOS device", e);
            return Response.serverError().build();
        } catch (APIManagerException e) {
            log.error("Error occurred while getting an access token for the iOS device", e);
            return Response.serverError().build();
        } catch (UserStoreException e) {
            log.error("Error occurred while initializing the userstore for the tenant: " +
                            loginBean.getTenantDomain(), e);
            return Response.serverError().build();
        }

        return Response.status(Response.Status.UNAUTHORIZED).build();
    }

    @POST
    @Produces({ContentType.APPLE_ASPEN_CONFIG, MediaType.APPLICATION_JSON})
    @Consumes({MediaType.WILDCARD})
    @Path("/profile")
    public Response getProfileRequest(InputStream inputStream) {

        if (log.isDebugEnabled()) {
            log.debug("Executing profile request");
        }

        ChallengeTokenBean challengeTokenBean;
        IOSEnrollmentService enrollmentService = null;
        InputStream[] clonedInputStream = null;
        String username = null;
        String tenantDomain = null;
        int tenantId = MultitenantConstants.SUPER_TENANT_ID;
        DeviceProperties deviceProperties = null;
        DEPDevice depDevice;

        try {
            OperationUtils operationUtils = new OperationUtils();
            clonedInputStream = operationUtils.cloneInputStream(inputStream);
            enrollmentService = IOSServiceUtils.getEnrollmentService();
            String challengeToken = enrollmentService.extractChallengeTokenFromProfile(clonedInputStream[0]);
            deviceProperties = enrollmentService.extractDeviceIdentifierFromProfile(clonedInputStream[1]);
            String deviceIdentifier = deviceProperties.getDeviceIdentifier();
            if (StringUtils.isNotEmpty(challengeToken) && StringUtils.isNotEmpty(deviceIdentifier)) {
                challengeTokenBean = new ChallengeTokenBean();
                challengeTokenBean.setChallengeToken(challengeToken);
                challengeTokenBean.setDeviceID(deviceIdentifier);
                EnrollmentUtils.persistChallengeToken(challengeTokenBean);
            }

            if (StringUtils.isNotEmpty(challengeToken)) {
                challengeTokenBean = EnrollmentUtils.getTokenEntry(challengeToken);
            } else {
                challengeTokenBean = EnrollmentUtils.getTokenEntryByDeviceId(deviceIdentifier);
            }

            if (challengeTokenBean != null) {
                username = challengeTokenBean.getUsername();
                tenantDomain = challengeTokenBean.getDomain();

                if (StringUtils.isNotEmpty(tenantDomain) && !MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.
                        equals(tenantDomain)) {
                    tenantId = IOSServiceUtils.getTenantID(tenantDomain);
                }
            }
        } catch (IOSEnrollmentException e) {
            log.error("Error occurred while enrolling the iOS device", e);
        } catch (PListException e) {
            log.error("Error occurred while reading input stream payload", e);
        }

        if (StringUtils.isNotEmpty(username)) {
            try {
                PrivilegedCarbonContext.startTenantFlow();
                PrivilegedCarbonContext privilegedCarbonContext = PrivilegedCarbonContext.
                        getThreadLocalCarbonContext();
                if (StringUtils.isNotEmpty(tenantDomain)) {
                    privilegedCarbonContext.setTenantDomain(tenantDomain);
                } else {
                    privilegedCarbonContext.setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
                }
                privilegedCarbonContext.setTenantId(tenantId);
                privilegedCarbonContext.setUsername(username);

                Message message = new Message();
                TenantUtils tenantUtils = new TenantUtils();

                CertificateAttributes certificateAttributes = tenantUtils.getCertificateAttributes();
                String topicID = tenantUtils.getTopicID();
                String tenantDisplayName = tenantUtils.getTenantDisplayName();

                if (tenantDisplayName == null || tenantDisplayName.isEmpty()) {
                    tenantDisplayName = "";
                }

                if (certificateAttributes == null) {
                    message.setErrorMessage("Certificate attributes are not configured");
                    throw new BadRequestException(message, MediaType.APPLICATION_JSON_TYPE);
                }

                if (topicID == null) {
                    message.setErrorMessage("Topic ID is not configured");
                    throw new BadRequestException(message, MediaType.APPLICATION_JSON_TYPE);
                }

                depDevice = IOSServiceUtils.getProfileService().getDEPDevice(deviceProperties.getSerial());
                if (enrollmentService != null) {
                    byte[] signedData = enrollmentService
                            .handleProfileRequest(clonedInputStream[2], tenantDisplayName, topicID,
                                    certificateAttributes, false);
                    if (depDevice != null && StringUtils.isEmpty(depDevice.getDeviceIdentifier())) {
                        depDevice.setDeviceIdentifier(deviceProperties.getDeviceIdentifier());
                        IOSServiceUtils.getProfileService().updateDEPDevice(depDevice);
                    }
                    return Response.ok(signedData).build();
                }
            } catch (IOSEnrollmentException e) {
                log.error("Error occurred while enrolling the iOS device", e);
            } catch (DeviceManagementException e) {
                log.error("Error occurred while fetching tenant configurations", e);
            } catch (TenantConfigurationException e) {
                log.error("Error occurred while reading registry entries", e);
            } catch (ProfileConfigurationException e) {
                log.error("Error occurred while fetching updating device.", e);
            } finally {
                PrivilegedCarbonContext.endTenantFlow();
            }
        }
        return Response.serverError().build();
    }

    @GET
    @Path("/scep")
    public Response scepRequest(@QueryParam("operation") String operation, @QueryParam("message") String message) {

        if (log.isDebugEnabled()) {
            log.debug("Invoking SCEP operation " + operation);
        }

        if (SCEPOperation.GET_CA_CERT.getValue().equals(operation)) {

            if (log.isDebugEnabled()) {
                log.debug("Invoking GetCACert");
            }

            try {
                CertificateManagementService certificateManagementService = IOSServiceUtils.
                        getCertificateManagementService();
                SCEPResponse scepResponse = certificateManagementService.getCACertSCEP();
                Response.ResponseBuilder responseBuilder;

                switch (scepResponse.getResultCriteria()) {
                    case CA_CERT_FAILED:
                        log.error("CA cert failed");
                        responseBuilder = Response.serverError();
                        break;
                    case CA_CERT_RECEIVED:

                        if (log.isDebugEnabled()) {
                            log.debug("CA certificate received in GetCACert");
                        }

                        responseBuilder = Response.ok(scepResponse.getEncodedResponse(), ContentType.X_X509_CA_CERT);
                        break;
                    case CA_RA_CERT_RECEIVED:

                        if (log.isDebugEnabled()) {
                            log.debug("CA and RA certificates received in GetCACert");
                        }

                        responseBuilder = Response.ok(scepResponse.getEncodedResponse(), ContentType.X_X509_CA_RA_CERT);
                        break;
                    default:
                        log.error("Invalid SCEP request");
                        responseBuilder = Response.serverError();
                        break;
                }

                return responseBuilder.build();
            } catch (IOSEnrollmentException e) {
                log.error("Error occurred while enrolling the iOS device", e);
            } catch (KeystoreException e) {
                log.error("Keystore error occurred while enrolling the iOS device", e);
            }

        } else if (SCEPOperation.GET_CA_CAPS.getValue().equals(operation)) {

            if (log.isDebugEnabled()) {
                log.debug("Invoking GetCACaps");
            }

            try {
                CertificateManagementService certificateManagementService = IOSServiceUtils.
                        getCertificateManagementService();
                byte caCaps[] = certificateManagementService.getCACapsSCEP();

                return Response.ok(caCaps, MediaType.TEXT_PLAIN).build();
            } catch (IOSEnrollmentException e) {
                log.error("Error occurred while enrolling the iOS device", e);
            }

        } else {
            log.error("Invalid SCEP operation " + operation);
        }

        return Response.serverError().build();
    }

    @POST
    @Consumes({ContentType.X_PKI_MESSAGE})
    @Path("/scep")
    public Response scepRequestPost(@QueryParam("operation") String operation, InputStream inputStream) {

        if (log.isDebugEnabled()) {
            log.debug("Invoking SCEP operation " + operation);
        }

        if (SCEPOperation.PKI_OPERATION.getValue().equals(operation)) {

            if (log.isDebugEnabled()) {
                log.debug("Invoking PKIOperation");
            }

            try {
                CertificateManagementService certificateManagementService = IOSServiceUtils.
                        getCertificateManagementService();
                byte pkiMessage[] = certificateManagementService.getPKIMessageSCEP(inputStream);

                return Response.ok(pkiMessage, ContentType.X_PKI_MESSAGE).build();
            } catch (IOSEnrollmentException e) {
                log.error("Error occurred while enrolling the iOS device", e);
            } catch (KeystoreException e) {
                log.error("Keystore error occurred while enrolling the iOS device", e);
            }

        }

        return Response.serverError().build();
    }

    @PUT
    @Produces({MediaType.WILDCARD})
    @Consumes({MediaType.WILDCARD})
    @Path("/checkin")
    public Response checkInRequest(InputStream inputStream) {
        if (log.isDebugEnabled()) {
            log.debug("Invoking check-in url");
        }

        CheckInMessageType checkInMessageType = null;
        try {
            StringWriter stringWriter = new StringWriter();
            IOUtils.copy(inputStream, stringWriter, AppConfigurations.UTF_8);
            String contentString = stringWriter.toString();
            IOSEnrollmentService enrollmentService = IOSServiceUtils.getEnrollmentService();
            checkInMessageType = enrollmentService.extractTokens(contentString);
            if (log.isDebugEnabled()) {
                log.debug("CheckIn url message type is " + checkInMessageType.getMessageType());
            }
        } catch (PListException e) {
            log.error("Error occurred when extracting plist elements in check-in request", e);
        } catch (IOSEnrollmentException e) {
            log.error("Error occurred while enrolling the iOS device", e);
        } catch (IOException e) {
            log.error("Input stream cannot be parsed in check-in request", e);
        }

        if (checkInMessageType != null) {
            DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
            deviceIdentifier.setId(checkInMessageType.getDeviceIdentifier());
            deviceIdentifier.setType(DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS);
            try {
                TenantedDeviceWrapper tenantedDeviceWrapper = IOSServiceUtils.getSCEPManager()
                        .getValidatedDevice(deviceIdentifier);
                if (tenantedDeviceWrapper != null && tenantedDeviceWrapper.getTenantDomain() != null &&
                        tenantedDeviceWrapper.getTenantId() != MultitenantConstants.INVALID_TENANT_ID) {
                    try {
                        PrivilegedCarbonContext.startTenantFlow();
                        PrivilegedCarbonContext privilegedCarbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
                        privilegedCarbonContext.setTenantDomain(tenantedDeviceWrapper.getTenantDomain());
                        privilegedCarbonContext.setTenantId(tenantedDeviceWrapper.getTenantId());
                        privilegedCarbonContext.setUsername(tenantedDeviceWrapper.getDevice().getEnrolmentInfo().getOwner());
                        if (log.isDebugEnabled()) {
                            log.debug("Starting tenant flow with tenant: " + tenantedDeviceWrapper.getTenantDomain());
                        }
                        Message message = new Message();

                        if (checkInMessageType.getMessageType() == CheckInMessageType.MessageType.CHECKOUT) {
                            if (log.isDebugEnabled()) {
                                log.debug("UDID " + checkInMessageType.getDeviceIdentifier());
                            }

                            List<? extends Operation> operations = IOSServiceUtils.getDeviceManagementService().getOperations(deviceIdentifier);
                            if (operations != null) {
                                for (Operation operation : operations) {
                                    operation.setStatus(Operation.Status.COMPLETED);
                                    IOSServiceUtils.getDeviceManagementService()
                                            .updateOperation(deviceIdentifier, operation);
                                }
                            }

                            TokenPersistence tokenPersistence = new TokenPersistence();
                            tokenPersistence.disEnrollDevice(checkInMessageType.getDeviceIdentifier());

                            return Response.ok().build();
                        } else if (checkInMessageType.getMessageType() == CheckInMessageType.MessageType.TOKEN_UPDATE) {
                            if (log.isDebugEnabled()) {
                                log.debug("Magic Token " + checkInMessageType.getPushMagic());
                                log.debug("Token " + checkInMessageType.getToken());
                                log.debug("Unlock Token " + checkInMessageType.getUnlockToken());
                                log.debug("UDID " + checkInMessageType.getDeviceIdentifier());
                            }
                            TokenPersistence tokenPersistence = new TokenPersistence();
                            message.setErrorMessage("Device ID not found");

                            DeviceProperties deviceProperties = new DeviceProperties();
                            deviceProperties.setChallenge(null);
                            deviceProperties.setDeviceIdentifier(checkInMessageType.getDeviceIdentifier());

                            tokenPersistence.savePushTokens(deviceProperties, checkInMessageType.getToken(),
                                    checkInMessageType.getPushMagic(), checkInMessageType.getUnlockToken());

                            // if the agent is installed in the device, no need to apply the effective policy
                            // from here since the agent is implemented to retrieve the policy as the last step
                            // of its enrollment.

                            ChallengeTokenBean challengeTokenBean =
                                    EnrollmentUtils.getTokenEntryByDeviceId(checkInMessageType.getDeviceIdentifier());

                            //In case of DEP enrollment, the challenge token will be null as the
                            // challenge tokens are not being used.
                            if (challengeTokenBean == null || !challengeTokenBean
                                    .isAgentAvailable()) {
                                PolicyUtils policyUtils = new PolicyUtils();
                                policyUtils.enforceEffectivePolicy(checkInMessageType.getDeviceIdentifier());
                            }
                            String agentAppId = TokenUtils.getAgentAppId();
                            DEPDevice depDevice = IOSServiceUtils.getProfileService()
                                    .getDEPDeviceByDeviceId(checkInMessageType.getDeviceIdentifier());
                            // Check if the agent app Id is mentioned in tenant configs and if
                            // the device is a DEP device with an assigned user and if the device
                            // is required to have the agent.
                            if (agentAppId != null && !agentAppId.isEmpty() && depDevice != null
                                    && depDevice.getUsername() != null) {
                                if (log.isDebugEnabled()) {
                                    log.debug("DEP device with agent installation.");
                                }
                                if (depDevice.getAgentRequired()) {
                                    OperationUtils operationUtils = new OperationUtils();
                                    operationUtils.addAgentInstall(deviceIdentifier.getId(),
                                            tenantedDeviceWrapper.getDevice().getEnrolmentInfo().getOwner(),
                                            tenantedDeviceWrapper.getTenantDomain(), agentAppId);
                                }
                            }
                            return Response.ok().build();
                        } else if (checkInMessageType.getMessageType() == CheckInMessageType.MessageType.AUTHENTICATE) {
                            return Response.ok().build();
                        }
                    } catch (DeviceManagementException e) {
                        log.error("Error occurred when saving push token in check-in request", e);
                    } catch (APNSException e) {
                        log.error("Error occurred while sending a push notification message to APNS", e);
                    } catch (OperationManagementException e) {
                        log.error("Error occurred while updating unenroll status", e);
                    } catch (ProfileConfigurationException e) {
                        log.error("Error occurred while getting DEP device", e);
                    } finally {
                        PrivilegedCarbonContext.endTenantFlow();
                    }
                }
            } catch (SCEPException e) {
                log.error("Error occurred while fetching the device", e);
            } catch (IOSEnrollmentException e) {
                log.error("Error occurred while enrolling the iOS device", e);
            }
        }

        return Response.serverError().build();
    }

    @PUT
    @Produces({MediaType.WILDCARD})
    @Consumes({MediaType.WILDCARD})
    @Path("/server")
    public Response serverRequest(@Context HttpServletRequest request, InputStream inputStream) throws CMSException {
        if (log.isDebugEnabled()) {
            log.debug("Invoking server URL");
        }
        OperationUtils operationUtils = new OperationUtils();
        APNSStatus apnsStatus = null;
        DeviceIdentifier deviceIdentifier = null;
        String contentString = null;
        Operation operation = null;

        try {
            String headerMDMSignature = request.getHeader(IOSServiceUtils.HEADER_MDM_SIGNATURE);
            Message message = new Message();

            StringWriter stringWriter = new StringWriter();
            X509Certificate certificate = IOSServiceUtils.getCertificateManagementService().
                    extractCertificateFromSignature(headerMDMSignature);

            IOUtils.copy(inputStream, stringWriter, AppConfigurations.UTF_8);
            contentString = stringWriter.toString();

            IOSEnrollmentService enrollmentService = IOSServiceUtils.getEnrollmentService();
            apnsStatus = enrollmentService.extractAPNSResponse(contentString);

            DeviceUtils deviceUtils = new DeviceUtils();
            deviceIdentifier = deviceUtils.fetchDeviceIdentifierInstance(apnsStatus.getDeviceIdentifier(), message,
                    MediaType.APPLICATION_JSON_TYPE);
            Device device = IOSServiceUtils.getDeviceManagementService().getDevice(deviceIdentifier);

            TenantedDeviceWrapper tenantedDeviceWrapper = IOSServiceUtils.getSCEPManager().
                    getValidatedDevice(deviceIdentifier);
            if (tenantedDeviceWrapper != null && tenantedDeviceWrapper.getTenantDomain() != null &&
                    tenantedDeviceWrapper.getTenantId() != MultitenantConstants.INVALID_TENANT_ID) {
                try {
                    PrivilegedCarbonContext.startTenantFlow();
                    PrivilegedCarbonContext privilegedCarbonContext = PrivilegedCarbonContext
                            .getThreadLocalCarbonContext();
                    privilegedCarbonContext.setTenantDomain(tenantedDeviceWrapper.getTenantDomain());
                    privilegedCarbonContext.setTenantId(tenantedDeviceWrapper.getTenantId());
                    privilegedCarbonContext
                            .setUsername(tenantedDeviceWrapper.getDevice().getEnrolmentInfo().getOwner());
                    if (log.isDebugEnabled()) {
                        log.debug("Starting tenant flow with tenant: " + tenantedDeviceWrapper.getTenantDomain());
                    }

                    if (APNSStatus.APNSResponseStatus.Acknowledged.toString().equals(apnsStatus.getStatus())) {
                        operation = new Operation();
                        String queryResponse = apnsStatus.getResponseData();

                        if (APNSStatus.APNSResponse.QueryResponses.toString().equals(apnsStatus.getOperation())) {
                            TokenPersistence tokenPersistence = new TokenPersistence();
                            tokenPersistence.saveDeviceInformation(deviceIdentifier, queryResponse);
                            DeviceUtils.saveDeviceDetails(deviceIdentifier, queryResponse);
                        } else if (APNSStatus.APNSResponse.InstalledApplicationList.toString().
                                equals(apnsStatus.getOperation())) {
                            ApplicationUtils applicationUtils = new ApplicationUtils();
                            List<Application> applications = applicationUtils.parseApplicationList(queryResponse);

                            operationUtils.updateApplicationList(applications, deviceIdentifier);
                        } else if (APNSStatus.APNSResponse.ProfileList.toString().equals(apnsStatus.getOperation())) {

                            Gson gson = new Gson();
                            Type type = new TypeToken<List<ProfileBean>>() {
                            }.getType();
                            List<ProfileBean> profiles = gson.fromJson(queryResponse, type);

                            boolean isCompliance = false;

                            for (ProfileBean profileBean : profiles) {
                                if (PolicyManagementConstants.POLICY_BUNDLE
                                        .equals(profileBean.getPayloadIdentifier())) {
                                    //TODO check one by one and do the compliance
                                    isCompliance = true;
                                    break;
                                }
                            }

                            Policy policy = IOSServiceUtils.getPolicyManagerService().
                                    getAppliedPolicyToDevice(deviceIdentifier);

                            if (policy != null) {
                                List<ProfileFeature> profileFeatures = policy.getProfile().getProfileFeaturesList();
                                List<ComplianceFeature> complianceFeatures = new ArrayList<>();

                                for (ProfileFeature profileFeature : profileFeatures) {
                                    ComplianceFeature complianceFeature = new ComplianceFeature();
                                    complianceFeature.setFeature(profileFeature);
                                    complianceFeature.setFeatureCode(profileFeature.getFeatureCode());
                                    complianceFeature.setCompliance(isCompliance);
                                    complianceFeatures.add(complianceFeature);
                                }

                                IOSServiceUtils.getPolicyManagerService()
                                        .checkCompliance(deviceIdentifier, complianceFeatures);
                            }

                        } else {
                            operation.setOperationResponse(queryResponse);
                        }

                        if (queryResponse == null) {
                            Operation existingOperation = operationUtils
                                    .getOperation(deviceIdentifier, Integer.parseInt(apnsStatus.getCommandUUID()));
                            if (existingOperation != null && OperationMapping.WIPE_DATA.getCode()
                                    .equals(existingOperation.getCode())) {
                                if (log.isDebugEnabled()) {
                                    log.debug(OperationMapping.WIPE_DATA.getCode()
                                            + " command has been executed in the device. Terminating /server call.");
                                }
                                return Response.ok().build();
                            }
                        }
                        operation.setId(Integer.valueOf(apnsStatus.getCommandUUID()));
                        operation.setStatus(Operation.Status.COMPLETED);
                        operationUtils.updateOperation(operation, deviceIdentifier);
                    } else if (APNSStatus.APNSResponseStatus.Error.toString().equals(apnsStatus.getStatus())) {
                        operation = new Operation();
                        operation.setId(Integer.valueOf(apnsStatus.getCommandUUID()));
                        operation.setStatus(Operation.Status.ERROR);
                        operation.setOperationResponse(apnsStatus.getError());
                        operationUtils.updateOperation(operation, deviceIdentifier);
                    } else if (APNSStatus.APNSResponseStatus.NotNow.toString().equals(apnsStatus.getStatus())) {
                        /*
                        Device returns NotNow when the device is unable to perform the operation, In that case the
                        operation status of the particular operation has to be made PENDING inorder to resend the
                        operation again to the device.
                         */
                        if (log.isDebugEnabled()) {
                            log.debug("NotNow Response received for the operation with OperationID: " + apnsStatus
                                    .getCommandUUID() + ", for " + deviceIdentifier.toString());
                        }
                        operation = new Operation();
                        operation.setId(Integer.valueOf(apnsStatus.getCommandUUID()));
                        operation.setStatus(Operation.Status.PENDING);
                        operationUtils.updateOperation(operation, deviceIdentifier);
                    }

                    operation = operationUtils.getPendingOperation(apnsStatus.getDeviceIdentifier());

                    if (operation != null) {

                        if (operationUtils.isOneWayOperation(operation)) {
                            operation.setStatus(Operation.Status.COMPLETED);
                        } else {
                            operation.setStatus(Operation.Status.IN_PROGRESS);
                        }

                        operationUtils.updateOperation(operation, deviceIdentifier);

                        if (OperationMapping.ENTERPRISE_WIPE.getCode().equals(operation.getCode())) {
                            return Response.status(Response.Status.UNAUTHORIZED).build();
                        } else {
                            String operationPayload = operationUtils
                                    .generateOperationPayload(device, operation, certificate);

                            if (OperationMapping.WIPE_DATA.getCode().equals(operation.getCode())) {
                                //Removing the device from the server if a wipe command is added
                                if (log.isDebugEnabled()) {
                                    log.debug("WIPE_DATA operation (id: " + operation.getId()
                                            + ") is added to the device: " + deviceIdentifier.getId()
                                            + ". Therefore removing the device.");
                                }
                                TokenPersistence tokenPersistence = new TokenPersistence();
                                tokenPersistence.disEnrollDevice(deviceIdentifier.getId());
                            }

                            if (operationPayload != null && !operationPayload.isEmpty()) {
                                return Response.status(Response.Status.OK).entity(operationPayload.
                                        getBytes(AppConfigurations.UTF_8)).type(ContentType.APPLE_ASPEN_CONFIG).build();
                            }
                        }
                    }

                } catch (ProfileConfigurationException e) {
                    String errorMsg = "Error occurred when creating the profile for server request. Discarding the operation.";
                    log.error(errorMsg, e);
                    operationUtils
                            .updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), errorMsg, deviceIdentifier);
                } catch (APNSException e) {
                    String errorMsg = "Error occurred while sending a push notification message to APNS";
                    log.error(errorMsg, e);
                    operationUtils
                            .updateOperationWithErrorStatus(String.valueOf(operation.getId()), errorMsg, deviceIdentifier);
                } catch (PolicyManagementException e) {
                    String errorMsg = "Error occurred while enforcing policies";
                    log.error(errorMsg, e);
                    operationUtils
                            .updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), errorMsg, deviceIdentifier);
                } catch (PolicyComplianceException e) {
                    String errorMsg = "Error occurred while checking policy compliance";
                    log.error(errorMsg, e);
                    operationUtils
                            .updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), errorMsg, deviceIdentifier);
                } catch (OperationManagementException e) {
                    String errorMsg = "Issue in retrieving operation management service instance";
                    log.error(errorMsg, e);
                    operationUtils
                            .updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), errorMsg, deviceIdentifier);
                } catch (ApplicationManagementException e) {
                    String errorMsg = "Issue in updating application list of device";
                    log.error(errorMsg, e);
                    operationUtils
                            .updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), errorMsg, deviceIdentifier);
                } finally {
                    PrivilegedCarbonContext.endTenantFlow();
                }
            }
        } catch (SCEPException e) {
            String errorMsg = "Error occurred when retrieving tenanted device.";
            log.error(errorMsg, e);
            operationUtils.updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), errorMsg, deviceIdentifier);
        } catch (KeystoreException e) {
            String errorMsg = "KeystoreException occurred when verifying signature";
            log.error(errorMsg, e);
            operationUtils.updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), errorMsg, deviceIdentifier);
        } catch (PListException e) { //Removing error log as this exception is a result of bad enrollment and this
            // causes continuous error logs in prod servers.
            try {
                operationUtils.updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), "Error occurred when" +
                        " extracting plist elements in check-in request.", deviceIdentifier);
            } catch (Exception e2) {
                log.error("Error when communicating with an iOS device. Device responded with the payload: " +
                        contentString);
            }
        } catch (IOSEnrollmentException e) {
            String errorMsg = "Error occurred while retrieving enrollment service.";
            log.error(errorMsg, e);
            operationUtils.updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), errorMsg, deviceIdentifier);
        } catch (IOException e) {
            String errorMsg = "IOException occurred when copying input stream.";
            log.error(errorMsg, e);
            operationUtils.updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), errorMsg, deviceIdentifier);
        } catch (DeviceManagementException e) {
            String errorMsg = "Error occurred when retrieving device from device management service";
            log.error(errorMsg, e);
            operationUtils.updateOperationWithErrorStatus(apnsStatus.getCommandUUID(), errorMsg, deviceIdentifier);
        }
        return Response.ok().build();
    }

    @GET
    @Path("/license")
    @Produces({MediaType.APPLICATION_JSON})
    @Consumes({MediaType.WILDCARD})
    public License getLicense() {
        License license = null;

        try {
            license =
                    IOSServiceUtils.getDeviceManagementService().getLicense(
                            DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS,
                            DeviceManagementConstants.LanguageCodes.LANGUAGE_CODE_ENGLISH_US);
            if (license == null || license.getText() == null) {
                license.setName("ios");
                license.setLanguage("en_US");
                license.setVersion("1.0.0");
                license.setText("This End User License Agreement (\"Agreement\") is a legal agreement between you (\"You\") " +
                        "and WSO2, Inc., regarding the enrollment of Your personal mobile device (\"Device\") in SoR's " +
                        "mobile device management program, and the loading to and removal from Your Device and Your use " +
                        "of certain applications and any associated software and user documentation, whether provided in " +
                        "\"online\" or electronic format, used in connection with the operation of or provision of services " +
                        "to WSO2, Inc.,  BY SELECTING \"I ACCEPT\" DURING INSTALLATION, YOU ARE ENROLLING YOUR DEVICE, AND " +
                        "THEREBY AUTHORIZING SOR OR ITS AGENTS TO INSTALL, UPDATE AND REMOVE THE APPS FROM YOUR DEVICE AS " +
                        "DESCRIBED IN THIS AGREEMENT.  YOU ARE ALSO EXPLICITLY ACKNOWLEDGING AND AGREEING THAT (1) THIS IS " +
                        "A BINDING CONTRACT AND (2) YOU HAVE READ AND AGREE TO THE TERMS OF THIS AGREEMENT.\n" +
                        "\n" +
                        "IF YOU DO NOT ACCEPT THESE TERMS, DO NOT ENROLL YOUR DEVICE AND DO NOT PROCEED ANY FURTHER.\n" +
                        "\n" +
                        "You agree that: (1) You understand and agree to be bound by the terms and conditions contained " +
                        "in this Agreement, and (2) You are at least 21 years old and have the legal capacity to enter " +
                        "into this Agreement as defined by the laws of Your jurisdiction.  SoR shall have the right, " +
                        "without prior notice, to terminate or suspend (i) this Agreement, (ii) the enrollment of Your " +
                        "Device, or (iii) the functioning of the Apps in the event of a violation of this Agreement or " +
                        "the cessation of Your relationship with SoR (including termination of Your employment if You are " +
                        "an employee or expiration or termination of Your applicable franchise or supply agreement if You " +
                        "are a franchisee of or supplier to the WSO2 WSO2, Inc., system).  SoR expressly reserves all " +
                        "rights not expressly granted herein.");

            }

        } catch (DeviceManagementException e) {
            String msg = "Error occurred while retrieving the license configured for iOS device enrolment";
            log.error(msg, e);
        }

        return license;
    }
}
