/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Build
* IBM UrbanCode Deploy
* IBM UrbanCode Release
* IBM AnthillPro
* (c) Copyright IBM Corporation 2016. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*/

/*
 * BigFix REST API Documentation: https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/Tivoli+Endpoint+Manager/page/REST+API
 * BigFix Swagger.io Documentation: https://bigfix.me/restapi
 */

package com.urbancode.air.plugin.bigfix

import groovy.util.XmlSlurper
import org.apache.http.client.methods.HttpPost
import org.apache.http.client.utils.URIBuilder
import org.apache.http.entity.StringEntity
import org.apache.http.HttpResponse
import org.apache.http.impl.client.DefaultHttpClient
import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder

/**
 * Helper class for the BigFix Plugin. Makes basic REST calls that assist in
 * BigFix actions. In addition, several functions are made to help format the correct
 * URI for the REST calls.
 */
public class BigFixHelper {

    /**
     * Empty constructor
     */
    public BigFixHelper() {
    }

    /**
     * Constructs and returns the formatted address for the specified REST call. BigFix REST calls
     * constructed in this function will have the following structure:
     * <host>://<port>/api/<resource>
     *
     * @param hostname The full URL of the BigFix hostname
     * @param resource  The data entity that will be defined by the target
     * @return          The fully constructed address for the REST call
     */
    public String createAPIAddress(String hostname, String resource) {
        return hostname + "/api/" + resource
    }

    /**
     * Confirm that the Hostname is properly formatted. All URIs must have a Scheme (http/https),
     * Host (www.ibm.com), and Port (8080). Otherwise, crash with invalid Hostname error
     *
     * @param hostname The String representation of the BigFix's hostname/URI
     * @return         String that begins with http, otherwise https:// will be appended to the front. If hostname
     *                 and port cannot be found, then crash
     */
    public String checkHostname(String hostname) {
        URI uri
        try {
            uri = new URI(hostname)
        } catch (URISyntaxException ex) {
            throw new URISyntaxException("[Error]  Invalid syntax for the Hostname property. Please confirm Hostname follows this example: 'https://bigfix-server:8080'")
        }
        URIBuilder builder = new URIBuilder()
        // Confirm valid Scheme was given
        if (uri.getScheme() == "http" || uri.getScheme() == "https"){
            builder.setScheme(uri.getScheme())
        }
        else {
            builder.setScheme("https")
            println "[Warning]  Valid Scheme was not found. 'https' will be used as the default."
        }
        // Confirm valid Host was given
        if (uri.getHost()) {
            builder.setHost(uri.getHost())
        }
        else {
            throw new URISyntaxException("[Error]  No Host was given (ex. www.ibm.com:8080). Please confirm within the Hostname property.")
        }
        // Confirm valid Port was given
        if (uri.getPort()) {
            builder.setPort(uri.getPort())
        }
        else {
            throw new URISyntaxException("[Error]  No Port was given (ex. www.ibm.com:8080). Please confirm within the Hostname property.")
        }
        def result = builder.toString()

        // Return only the hostname, ignore all extra paths
        println "[Ok]  Hostname found: ${result}"
        return result
    }

    /**
     * Returns the DefaultHttpClient object that will connect and authenticate with the BigFix Server
     * Unless switching accounts, this object can be used for all REST calls. In addition, it is
     * set with Preemptive Authentication to send the authenticating message early to reduce overhead.
     * All certificates are accepted.
     *
     * @param username String representation of the username used to login into BigFix
     * @param password String representation of the password used to login into BigFix
     * @return         Object contains the authentication information to connect with BigFix
     * @see            DefaultHttpClient
     */
    public DefaultHttpClient createClient(String username, String password) {
        DefaultHttpClient client = new DefaultHttpClient()
        HttpClientBuilder clientBuilder = new HttpClientBuilder()
        clientBuilder.setUsername(username)
        clientBuilder.setPassword(password)
        clientBuilder.setPreemptiveAuthentication(true)
        com.urbancode.air.XTrustProvider.install()
        clientBuilder.setTrustAllCerts(true)

        client = clientBuilder.buildClient()
        return client
    }

     /**
      * Calls a REST POST request and returns the HTTPResponse
      *
      * @param client    The DefaultHttpClient that contains the necessary authentication information.
      * @param fullUrl   The complete URL address of the REST POST call.
      * @param xmlData   The data structure that contains XML information for the post request.
      * @return          The HTTPResponse for the POST REST Call.
      */
     public HttpResponse doHttpPost(DefaultHttpClient client, String fullUrl, def xmlData){
        // Form XML for the POST REST call
        StringEntity issueEntity
        if (xmlData) {
             issueEntity = new StringEntity(xmlData, "UTF-8")
        }
        else {
            throw new IllegalArgumentException("[Error]  No XML input was given.")
        }

        issueEntity.setContentType("application/xml")

        // Create HttpPost Request and append XML
        URIBuilder builder = new URIBuilder(fullUrl)
        builder.setParameter("Accept", "application/xml")
        builder.setParameter("Accept-Charset", "UTF-8")
        builder.setParameter("Content-Type", "application/xml")
        URI uri = builder.build()
        HttpPost postRequest = new HttpPost(uri)
        postRequest.setEntity(issueEntity)

        // Execute the REST Call
        HttpResponse response
        try {
            response = client.execute(postRequest)
        }
        catch (IOException ex) {
            throw new IOException("[Error]  Unable to access the BigFix Server. Check the hostname: ${ex.getMessage()}")
        }
        return response
     }

    /**
     * Uses the REST Post call to begin an Action. A status code of 200s is
     * expected when the add comment is successful. The call will fail if a non-200s status code
     * is returned.
     *
     * @param client    The DefaultHttpClient that contains the necessary authentication information.
     * @param hostname  The URL address of the BigFix server.
     * @param postData  The String data structure that contains the comment
     * @return          Return REST Status Code
     */
     public int startAction(DefaultHttpClient client, String hostname, String xmlData){
         // Construct the issue specific address and postRequest
         String fullAddress = createAPIAddress(hostname, "actions")
         HttpResponse response = doHttpPost(client, fullAddress, xmlData)
         def statusLine = response.getStatusLine()
         def statusCode = statusLine.getStatusCode()
         println "[Ok]  StatusCode: " + statusCode
         // Analyze returned status code...
         if (Math.floor(statusCode/100) == 2) {
            println "[Ok]  Creating new Action was successful."
         }
         else if (statusCode == 400) {
            println "[Error]  Skipping Action: Bad Request caused by malformed hostname."
            println "[Error]  Status Code: ${statusCode} - Message:\n${response.entity?.content?.text}"
         }
         else if (statusCode == 401) {
            println "[Error]  Skipping Action: Unauthorization caused by invalid username or password."
            println "[Error]  Status Code: ${statusCode} - Message:\n${response.entity?.content?.text}"
         }
         else if ((statusCode == 302) || (statusCode == 404)) {
            println "[Error]  Skipping Action: Not Found caused by invalid hostname."
            println "[Error]  Status Code: ${statusCode} - Message:\n${response.entity?.content?.text}"
         }
         else if (statusCode == 500) {
            println "[Error]  Skipping Action: BigFix received unexpected XML code. Review the below error output."
            println "[Error]  Status Code: ${statusCode} - Message:\n${response.entity?.content?.text}"
         }
         else {
            println "[Error]  Skipping Action: Unexpected Status Code."
            println "[Error]  Status Code: ${statusCode} - Message:\n${response.entity?.content?.text}"
         }
         return statusCode
     }
}