#!/usr/bin/env python
import argparse
import requests
import re
import sys
from termcolor import colored
from tabulate import tabulate
from requests import Session
from zeep import Client
from zeep.transports import Transport

"""
Script to extract the list of SOAP services from a WSDL descriptor.

Sources:
    https://docs.python-zeep.org
    https://github.com/mvantellingen/python-zeep

Dependencies:
    pip install zeep colorama termcolor requests tabulate
"""
# Constants
TIMEOUT = 30


def format_response(ws_response):
    formatted_string = "No response data"
    if ws_response is not None:
        expr = re.compile(r'\s+')
        formatted_string = str(ws_response).replace("\n", "").replace("\r", "").replace("\t", "")
        formatted_string = expr.sub(" ", formatted_string)
    return formatted_string[:120]


def test_service(ws_client, ws_name):
    availablility_status = None
    try:
        response = ws_client.service[ws_name]()
        availablility_status = (colored("Yes", "green"), format_response(response))
    except Exception as e:
        err = str(e)
        if "missing element" in err.lower():
            availablility_status = (colored("Maybe", "cyan"), err)
        else:
            availablility_status = (colored("No", "red"), err)
    return availablility_status


if __name__ == "__main__":
    requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
    parser = argparse.ArgumentParser(description="Script to extract the list of SOAP services from a WSDL descriptor.")
    parser.add_argument("-d", action="store", dest="wsdl", help="WSDL descriptor location (URL).", required=True)
    args = parser.parse_args()
    wsdl_location = args.wsdl
    session = Session()
    session.verify = False
    print(colored("[+] Verify the WSDL...", "yellow"))
    response = session.get(url=wsdl_location, timeout=TIMEOUT)
    if "<wsdl:definitions" not in response.text.lower():
        print(colored("Invalid WSDL => Try adding '?WSDL' at the end of the URL!", "red"))
        sys.exit(1)
    print("Done.")
    print(colored("[+] Generate a dynamic SOAP web services client from the WSDL...", "yellow"))
    my_transport = Transport(session=session, timeout=TIMEOUT)
    client = Client(wsdl=wsdl_location, transport=my_transport)
    print("Done.")
    print(colored("[+] Enumerate the services by calling them...", "yellow"))
    table_rows = []
    for service in client.service:
        ws_name = service[0]
        availablility_status = test_service(client, ws_name)
        table_rows.append([ws_name, availablility_status[0], availablility_status[1]])
    print(tabulate(table_rows, headers=[f"Services ({len(table_rows)})", "IsAvailable", "Response"]))
