Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

# Licensed under the MIT license 

# http://opensource.org/licenses/mit-license.php 

 

# Copyright 2007 - Frank Scholz <coherence@beebits.net> 

from lxml import etree 

from twisted.python import failure 

 

from coherence import log 

from coherence.upnp.core import soap_lite 

from coherence.upnp.core.utils import getPage 

 

 

class SOAPProxy(log.LogAble): 

    """ A Proxy for making remote SOAP calls. 

 

        Based upon twisted.web.soap.Proxy and 

        extracted to remove the SOAPpy dependency 

 

        Pass the URL of the remote SOAP server to the constructor. 

 

        Use proxy.callRemote('foobar', 1, 2) to call remote method 

        'foobar' with args 1 and 2, proxy.callRemote('foobar', x=1) 

        will call foobar with named argument 'x'. 

    """ 

 

    logCategory = 'soap' 

 

    def __init__(self, url, namespace=None, envelope_attrib=None, header=None, 

                 soapaction=None): 

        log.LogAble.__init__(self) 

        if not isinstance(url, bytes): 

            self.warning('SOAPProxy.__init__: ' 

                         'url is not string bytes...modifying') 

            url = url.encode('ascii') 

        self.url = url 

        self.namespace = namespace 

        self.header = header 

        self.action = None 

        self.soapaction = soapaction 

        self.envelope_attrib = envelope_attrib 

 

    def callRemote(self, soapmethod, arguments): 

        soapaction = soapmethod or self.soapaction 

        if '#' not in soapaction: 

            soapaction = '#'.join((self.namespace[1], soapaction)) 

        self.action = soapaction.split('#')[1].encode('ascii') 

 

        self.info("callRemote %r %r %r %r", self.soapaction, soapmethod, 

                  self.namespace, self.action) 

        self.debug('\t- arguments: {}'.format(arguments)) 

        self.debug('\t- action: {}'.format(self.action)) 

        self.debug('\t- namespace: {}'.format(self.namespace)) 

 

        headers = {'content-type': 'text/xml ;charset="utf-8"', 

                   'SOAPACTION': '"{}"'.format(soapaction), } 

        if 'headers' in arguments: 

            headers.update(arguments['headers']) 

            del arguments['headers'] 

 

        payload = soap_lite.build_soap_call(self.action, arguments, 

                                            ns=self.namespace[1]) 

        self.debug('\t- payload: {}'.format(payload)) 

 

        self.info("callRemote soapaction:  %s %s", self.action, self.url) 

        self.debug("callRemote payload:  %s", payload) 

 

        def gotError(error, url): 

            self.warning("error requesting url %r", url) 

            self.debug(error) 

            try: 

                # TODO: Must deal with error handling 

                self.error('\t-> callRemote [type: {}]: {} => {}'.format( 

                    type(error.value.__traceback__), 

                    'error.value.__traceback__', 

                    error.value.__traceback__)) 

                tree = etree.fromstring(error.value.__traceback__) 

                body = tree.find( 

                    '{http://schemas.xmlsoap.org/soap/envelope/}Body') 

                return failure.Failure(Exception("%s - %s" % ( 

                    body.find( 

                        './/{urn:schemas-upnp-org:control-1-0}' 

                        'errorCode').text, 

                    body.find( 

                        './/{urn:schemas-upnp-org:control-1-0}' 

                        'errorDescription').text))) 

            except Exception as e: 

                self.error('callRemote error on getting traceback: %r' % e) 

                import traceback 

                self.debug(traceback.format_exc()) 

            return error 

 

        return getPage( 

            self.url, 

            postdata=payload, 

            method=b"POST", 

            headers=headers).addCallbacks( 

            self._cbGotResult, gotError, None, None, [self.url], None) 

 

    def _cbGotResult(self, result): 

        page, headers = result 

 

        def print_c(e): 

            for c in e.getchildren(): 

                print(c, c.tag) 

                print_c(c) 

 

        self.debug("_cbGotResult.action: %r", self.action) 

        self.debug("_cbGotResult.result: %r", page) 

 

        a = self.action.decode('utf-8') 

        tree = etree.fromstring(page) 

        body = tree.find('{http://schemas.xmlsoap.org/soap/envelope/}Body') 

        response = body.find( 

            '{%s}%sResponse' % (self.namespace[1], a)) 

        if response is None: 

            """ fallback for improper SOAP action responses """ 

            response = body.find('%sResponse' % a) 

        self.debug("callRemote response  %s", response) 

        result = {} 

        if response is not None: 

            for elem in response: 

                result[elem.tag] = self.decode_result(elem) 

 

        return result 

 

    def decode_result(self, element): 

        self.debug('decode_result [element]: {}'.format(element)) 

        type = element.get('{http://www.w3.org/1999/XMLSchema-instance}type') 

        if type is not None: 

            try: 

                prefix, local = type.split(":") 

                if prefix == 'xsd': 

                    type = local 

            except ValueError: 

                pass 

 

        if type == "integer" or type == "int": 

            return int(element.text) 

        if type == "float" or type == "double": 

            return float(element.text) 

        if type == "boolean": 

            return element.text == "true" 

 

        return element.text or ""