107a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossum"""RPC Client module."""
207a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossum
307a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossumimport sys
407a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossumimport socket
507a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossumimport pickle
607a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossumimport __builtin__
707a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossumimport os
807a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossum
907a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossum
1007a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossum# Default verbosity (0 = silent, 1 = print connections, 2 = print requests too)
1107a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van RossumVERBOSE = 1
1207a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossum
1307a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossum
1407a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossumclass Client:
15e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
16e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    """RPC Client class.  No need to derive a class -- it's fully generic."""
17e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
18e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def __init__(self, address, verbose = VERBOSE):
19e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._pre_init(address, verbose)
20e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._post_init()
21e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
22e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _pre_init(self, address, verbose = VERBOSE):
23e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if type(address) == type(0):
24e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            address = ('', address)
25e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._address = address
26e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._verbose = verbose
27e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if self._verbose: print "Connecting to %s ..." % repr(address)
28e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
29e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._socket.connect(address)
30e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if self._verbose: print "Connected."
31e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._lastid = 0 # Last id for which a reply has been received
32e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._nextid = 1 # Id of next request
33e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._replies = {} # Unprocessed replies
34e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._rf = self._socket.makefile('r')
35e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._wf = self._socket.makefile('w')
36e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
37e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _post_init(self):
38e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._methods = self._call('.methods')
39e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
40e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def __del__(self):
41e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._close()
42e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
43e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _close(self):
44e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if self._rf: self._rf.close()
45e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._rf = None
46e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if self._wf: self._wf.close()
47e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._wf = None
48e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if self._socket: self._socket.close()
49e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._socket = None
50e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
51e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def __getattr__(self, name):
52e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if name in self._methods:
53e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            method = _stub(self, name)
54e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            setattr(self, name, method) # XXX circular reference
55e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            return method
56e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        raise AttributeError, name
57e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
58e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _setverbose(self, verbose):
59e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._verbose = verbose
60e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
61e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _call(self, name, *args):
62e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        return self._vcall(name, args)
63e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
64e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _vcall(self, name, args):
65e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        return self._recv(self._vsend(name, args))
66e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
67e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _send(self, name, *args):
68e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        return self._vsend(name, args)
69e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
70e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _send_noreply(self, name, *args):
71e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        return self._vsend(name, args, 0)
72e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
73e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _vsend_noreply(self, name, args):
74e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        return self._vsend(name, args, 0)
75e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
76e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _vsend(self, name, args, wantreply = 1):
77e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        id = self._nextid
78e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._nextid = id+1
79e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if not wantreply: id = -id
80e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        request = (name, args, id)
81e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if self._verbose > 1: print "sending request: %s" % repr(request)
82e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        wp = pickle.Pickler(self._wf)
83e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        wp.dump(request)
84e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        return id
85e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
86e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _recv(self, id):
87e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        exception, value, rid = self._vrecv(id)
88e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if rid != id:
89e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            raise RuntimeError, "request/reply id mismatch: %d/%d" % (id, rid)
90e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if exception is None:
91e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            return value
92e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        x = exception
93e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if hasattr(__builtin__, exception):
94e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            x = getattr(__builtin__, exception)
95e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        elif exception in ('posix.error', 'mac.error'):
96e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            x = os.error
97e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if x == exception:
98e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            exception = x
99e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        raise exception, value
100e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
101e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _vrecv(self, id):
102e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._flush()
103e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if self._replies.has_key(id):
104e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            if self._verbose > 1: print "retrieving previous reply, id = %d" % id
105e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            reply = self._replies[id]
106e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            del self._replies[id]
107e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            return reply
108e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        aid = abs(id)
109e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        while 1:
110e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            if self._verbose > 1: print "waiting for reply, id = %d" % id
111e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            rp = pickle.Unpickler(self._rf)
112e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            reply = rp.load()
113e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            del rp
114e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            if self._verbose > 1: print "got reply: %s" % repr(reply)
115e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            rid = reply[2]
116e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            arid = abs(rid)
117e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            if arid == aid:
118e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters                if self._verbose > 1: print "got it"
119e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters                return reply
120e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            self._replies[rid] = reply
121e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters            if arid > aid:
122e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters                if self._verbose > 1: print "got higher id, assume all ok"
123e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters                return (None, None, id)
124e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
125e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def _flush(self):
126e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._wf.flush()
12707a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossum
12807a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossum
12945babef8c28f7756bc751bda0f261fc68a7b1e18Guido van Rossumfrom security import Security
13045babef8c28f7756bc751bda0f261fc68a7b1e18Guido van Rossum
13145babef8c28f7756bc751bda0f261fc68a7b1e18Guido van Rossum
13245babef8c28f7756bc751bda0f261fc68a7b1e18Guido van Rossumclass SecureClient(Client, Security):
13345babef8c28f7756bc751bda0f261fc68a7b1e18Guido van Rossum
134e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def __init__(self, *args):
135e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        import string
136e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        apply(self._pre_init, args)
137e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        Security.__init__(self)
138e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._wf.flush()
139e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        line = self._rf.readline()
140e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        challenge = string.atoi(string.strip(line))
141e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        response = self._encode_challenge(challenge)
142e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        line = repr(long(response))
143e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        if line[-1] in 'Ll': line = line[:-1]
144e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._wf.write(line + '\n')
145e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._wf.flush()
146e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._post_init()
14745babef8c28f7756bc751bda0f261fc68a7b1e18Guido van Rossum
14807a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossumclass _stub:
14907a272d9dec67f8eb4f2bf1396a2f13671fdaba2Guido van Rossum
150e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    """Helper class for Client -- each instance serves as a method of the client."""
151e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
152e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def __init__(self, client, name):
153e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._client = client
154e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        self._name = name
155e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters
156e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters    def __call__(self, *args):
157e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters        return self._client._vcall(self._name, args)
158