text: import sys
  
  from winrm.exceptions import WinRMTransportError, UnauthorizedError
  
  
  HAVE_KERBEROS = False
  try:
      import kerberos
      HAVE_KERBEROS = True
  except ImportError:
      pass
  
  is_py2 = sys.version[0] == '2'
  if is_py2:
      from urllib2 import (Request, URLError, HTTPError, HTTPBasicAuthHandler,
                           HTTPPasswordMgrWithDefaultRealm, HTTPSHandler)
      from urllib2 import urlopen, build_opener, install_opener
      from urlparse import urlparse
      from httplib import HTTPSConnection
  else:
      from urllib.request import (Request, URLError, HTTPError,
                                  HTTPBasicAuthHandler,
                                  HTTPPasswordMgrWithDefaultRealm, HTTPSHandler)
      from urllib.request import urlopen, build_opener, install_opener
      from urllib.parse import urlparse
      from http.client import HTTPSConnection
  
  
  class HttpTransport(object):
      def __init__(self, endpoint, username, password):
          self.endpoint = endpoint
          self.username = username
          self.password = password
          self.user_agent = 'Python WinRM client'
          # Set this to an unreasonable amount for now because WinRM has timeouts
          self.timeout = 3600
  
      def basic_auth_only(self):
          # here we should remove handler for any authentication handlers other
          # than basic
          # but maybe leave original credentials
  
          # auths = @httpcli.www_auth.instance_variable_get('@authenticator')
          # auths.delete_if {|i| i.scheme !~ /basic/i}
          # drop all variables in auths if they not contains "basic" as
          # insensitive.
          pass
  
      def no_sspi_auth(self):
          # here we should remove handler for Negotiate/NTLM negotiation
          # but maybe leave original credentials
          pass
  
  
  class HttpPlaintext(HttpTransport):
      def __init__(self, endpoint, username='', password='', disable_sspi=True,
                   basic_auth_only=True):
          super(HttpPlaintext, self).__init__(endpoint, username, password)
          if disable_sspi:
              self.no_sspi_auth()
          if basic_auth_only:
              self.basic_auth_only()
  
          self._headers = {'Content-Type': 'application/soap+xml;charset=UTF-8',
                           'User-Agent': 'Python WinRM client'}
  
      def _setup_opener(self):
          password_manager = HTTPPasswordMgrWithDefaultRealm()
          password_manager.add_password(
              None, self.endpoint, self.username, self.password)
          auth_manager = HTTPBasicAuthHandler(password_manager)
          opener = build_opener(auth_manager)
          install_opener(opener)
  
      def send_message(self, message):
          headers = self._headers.copy()
          headers['Content-Length'] = len(message)
  
          self._setup_opener()
          request = Request(self.endpoint, data=message, headers=headers)
          try:
              response = urlopen(request, timeout=self.timeout)
              # Version 1.1 of WinRM adds the namespaces in the document instead
              # of the envelope so we have to
              # add them ourselves here. This should have no affect version 2.
              response_text = response.read()
              return response_text
              # doc = ElementTree.fromstring(response.read())
              # Ruby
              # doc = Nokogiri::XML(resp.http_body.content)
              # doc.collect_namespaces.each_pair do |k,v|
              #    doc.root.add_namespace((k.split(/:/).last),v)
              #    unless doc.namespaces.has_key?(k)
              # end
              # return doc
              # return doc
          except HTTPError as ex:
              if ex.code == 401:
                  raise UnauthorizedError(transport='plaintext', message=ex.msg)
              response_text = ex.read()
              # Per http://msdn.microsoft.com/en-us/library/cc251676.aspx rule 3,
              # should handle this 500 error and retry receiving command output.
              if 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive' in message and 'Code="2150858793"' in response_text:  # NOQA
                  # TODO raise TimeoutError here instead of just return text
                  return response_text
              error_message = 'Bad HTTP response returned from server. ' \
                              ' Code {0}'.format(ex.code)
              if ex.msg:
                  error_message += ', {0}'.format(ex.msg)
              raise WinRMTransportError('http', error_message)
          except URLError as ex:
              raise WinRMTransportError('http', ex.reason)
  
  
  class HTTPSClientAuthHandler(HTTPSHandler):
      def __init__(self, cert, key):
          HTTPSHandler.__init__(self)
          self.cert = cert
          self.key = key
  
      def https_open(self, req):
          return self.do_open(self.getConnection, req)
  
      def getConnection(self, host, timeout=300):
          return HTTPSConnection(host, key_file=self.key, cert_file=self.cert)
  
  
  class HttpSSL(HttpPlaintext):
      """Uses SSL to secure the transport"""
      def __init__(self, endpoint, username, password, ca_trust_path=None,
                   disable_sspi=True, basic_auth_only=True,
                   cert_pem=None, cert_key_pem=None):
          super(HttpSSL, self).__init__(endpoint, username, password)
  
          self._cert_pem = cert_pem
          self._cert_key_pem = cert_key_pem
  
          # Ruby
          # @httpcli.set_auth(endpoint, user, pass)
          # @httpcli.ssl_config.set_trust_ca(ca_trust_path)
          # unless ca_trust_path.nil?
          if disable_sspi:
              self.no_sspi_auth()
          if basic_auth_only:
              self.basic_auth_only()
  
          if self._cert_pem:
              self._headers['Authorization'] = \
                  "http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual"  # NOQA
  
      def _setup_opener(self):
          if not self._cert_pem:
              super(HttpSSL, self)._setup_opener()
          else:
              opener = build_opener(HTTPSClientAuthHandler(
                  self._cert_pem, self._cert_key_pem))
              install_opener(opener)
  
  
  class KerberosTicket:
      """ Implementation based on:
      http://ncoghlan_devs-python-notes.readthedocs.org/en/latest/python_kerberos.html  # NOQA
      """
      def __init__(self, service):
          ignored_code, krb_context = kerberos.authGSSClientInit(service)
          kerberos.authGSSClientStep(krb_context, '')
          # TODO authGSSClientStep may raise following error:
          # GSSError: (('Unspecified GSS failure.
          # Minor code may provide more information', 851968),
          # ("Credentials cache file '/tmp/krb5cc_1000' not found", -1765328189))
          self._krb_context = krb_context
          gss_response = kerberos.authGSSClientResponse(krb_context)
          self.auth_header = 'Negotiate {0}'.format(gss_response)
  
      def verify_response(self, auth_header):
          # Handle comma-separated lists of authentication fields
          for field in auth_header.split(','):
              kind, ignored_space, details = field.strip().partition(' ')
              if kind.lower() == 'negotiate':
                  auth_details = details.strip()
                  break
          else:
              raise ValueError('Negotiate not found in {0}'.format(auth_header))
              # Finish the Kerberos handshake
          krb_context = self._krb_context
          if krb_context is None:
              raise RuntimeError('Ticket already used for verification')
          self._krb_context = None
          kerberos.authGSSClientStep(krb_context, auth_details)
          # print('User {0} authenticated successfully using Kerberos
          # authentication'.format(kerberos.authGSSClientUserName(krb_context)))
          kerberos.authGSSClientClean(krb_context)
  
  
  class HttpKerberos(HttpTransport):
      def __init__(self, endpoint, realm=None, service='HTTP', keytab=None):
          """
          Uses Kerberos/GSS-API to authenticate and encrypt messages
          @param string endpoint: the WinRM webservice endpoint
          @param string realm: the Kerberos realm we are authenticating to
          @param string service: the service name, default is HTTP
          @param string keytab: the path to a keytab file if you are using one
          """
          if not HAVE_KERBEROS:
              raise WinRMTransportError('kerberos is not installed')
  
          super(HttpKerberos, self).__init__(endpoint, None, None)
          self.krb_service = '{0}@{1}'.format(
              service, urlparse(endpoint).hostname)
          # self.krb_ticket = KerberosTicket(krb_service)
  
      def set_auth(self, username, password):
          raise NotImplementedError
  
      def send_message(self, message):
          # TODO current implementation does negotiation on each HTTP request
          # which is not efficient
          # TODO support kerberos session with message encryption
          krb_ticket = KerberosTicket(self.krb_service)
          headers = {'Authorization': krb_ticket.auth_header,
                     'Connection': 'Keep-Alive',
                     'Content-Type': 'application/soap+xml;charset=UTF-8',
                     'User-Agent': 'Python WinRM client'}
  
          request = Request(self.endpoint, data=message, headers=headers)
          try:
              response = urlopen(request, timeout=self.timeout)
              krb_ticket.verify_response(response.headers['WWW-Authenticate'])
              response_text = response.read()
              return response_text
          except HTTPError as ex:
              response_text = ex.read()
              # Per http://msdn.microsoft.com/en-us/library/cc251676.aspx rule 3,
              # should handle this 500 error and retry receiving command output.
              if 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive' in message and 'Code="2150858793"' in response_text:  # NOQA
                  return response_text
              # if ex.code == 401 and ex.headers['WWW-Authenticate'] == \
              #    'Negotiate, Basic realm="WSMAN"':
              error_message = 'Kerberos-based authentication was failed. ' \
                              'Code {0}'.format(ex.code)
              if ex.msg:
                  error_message += ', {0}'.format(ex.msg)
              raise WinRMTransportError('kerberos', error_message)
          except URLError as ex:
              raise WinRMTransportError('kerberos', ex.reason)
  
      def _winrm_encrypt(self, string):
          """
          @returns the encrypted request string
          @rtype string
          """
          raise NotImplementedError
  
      def _winrm_decrypt(self, string):
          raise NotImplementedError
  