2020# SOFTWARE.
2121
2222import base64
23+ import httplib
2324import json
2425import socket
2526import sys
2829import shared
2930import tr # translate
3031
32+ # FIXME: from debug import logger crashes PyBitmessage due to a circular
33+ # dependency. The debug module will also override/disable logging.getLogger()
34+ # loggers so module level logging functions are used instead
35+ import logging as logger
36+
3137configSection = "bitmessagesettings"
3238
3339# Error thrown when the RPC call returns an error.
@@ -36,6 +42,9 @@ class RPCError (Exception):
3642
3743 def __init__ (self , data ):
3844 self .error = data
45+
46+ def __str__ (self ):
47+ return '{0}: {1}' .format (type (self ).__name__ , self .error )
3948
4049# This class handles the Namecoin identity integration.
4150class namecoinConnection (object ):
@@ -46,6 +55,7 @@ class namecoinConnection (object):
4655 nmctype = None
4756 bufsize = 4096
4857 queryid = 1
58+ con = None
4959
5060 # Initialise. If options are given, take the connection settings from
5161 # them instead of loading from the configs. This can be used to test
@@ -55,18 +65,20 @@ def __init__ (self, options = None):
5565 if options is None :
5666 self .nmctype = shared .config .get (configSection , "namecoinrpctype" )
5767 self .host = shared .config .get (configSection , "namecoinrpchost" )
58- self .port = shared .config .get (configSection , "namecoinrpcport" )
68+ self .port = int ( shared .config .get (configSection , "namecoinrpcport" ) )
5969 self .user = shared .config .get (configSection , "namecoinrpcuser" )
6070 self .password = shared .config .get (configSection ,
6171 "namecoinrpcpassword" )
6272 else :
6373 self .nmctype = options ["type" ]
6474 self .host = options ["host" ]
65- self .port = options ["port" ]
75+ self .port = int ( options ["port" ])
6676 self .user = options ["user" ]
6777 self .password = options ["password" ]
6878
6979 assert self .nmctype == "namecoind" or self .nmctype == "nmcontrol"
80+ if self .nmctype == "namecoind" :
81+ self .con = httplib .HTTPConnection (self .host , self .port , timeout = 3 )
7082
7183 # Query for the bitmessage address corresponding to the given identity
7284 # string. If it doesn't contain a slash, id/ is prepended. We return
@@ -85,21 +97,24 @@ def query (self, string):
8597 res = self .callRPC ("data" , ["getValue" , string ])
8698 res = res ["reply" ]
8799 if res == False :
88- raise RPCError ({ "code" : - 4 } )
100+ return ( tr . _translate ( "MainWindow" , 'The name %1 was not found.' ). arg ( unicode ( string )), None )
89101 else :
90102 assert False
91103 except RPCError as exc :
92- if exc .error ["code" ] == - 4 :
93- return (tr ._translate ("MainWindow" ,'The name %1 was not found.' ).arg (unicode (string )), None )
104+ logger .exception ("Namecoin query RPC exception" )
105+ if isinstance (exc .error , dict ):
106+ errmsg = exc .error ["message" ]
94107 else :
95- return (tr ._translate ("MainWindow" ,'The namecoin query failed (%1)' ).arg (unicode (exc .error ["message" ])), None )
108+ errmsg = exc .error
109+ return (tr ._translate ("MainWindow" ,'The namecoin query failed (%1)' ).arg (unicode (errmsg )), None )
96110 except Exception as exc :
97- print "Namecoin query exception: %s" % str ( exc )
111+ logger . exception ( "Namecoin query exception" )
98112 return (tr ._translate ("MainWindow" ,'The namecoin query failed.' ), None )
99113
100114 try :
101115 val = json .loads (res )
102116 except :
117+ logger .exception ("Namecoin query json exception" )
103118 return (tr ._translate ("MainWindow" ,'The name %1 has no valid JSON data.' ).arg (unicode (string )), None )
104119
105120 if "bitmessage" in val :
@@ -132,14 +147,14 @@ def test (self):
132147 if ("reply" in res ) and res ["reply" ][:len (prefix )] == prefix :
133148 return ('success' , tr ._translate ("MainWindow" ,'Success! NMControll is up and running.' ))
134149
135- print "Unexpected nmcontrol reply: %s" % res
150+ logger . error ( "Unexpected nmcontrol reply: %s" , res )
136151 return ('failed' , tr ._translate ("MainWindow" ,'Couldn\' t understand NMControl.' ))
137152
138153 else :
139154 assert False
140155
141- except Exception as exc :
142- print "Namecoin connection test: %s" % str ( exc )
156+ except Exception :
157+ logger . exception ( "Namecoin connection test failure" )
143158 return ('failed' , "The connection to namecoin failed." )
144159
145160 # Helper routine that actually performs an JSON RPC call.
@@ -155,35 +170,43 @@ def callRPC (self, method, params):
155170
156171 if val ["id" ] != self .queryid :
157172 raise Exception ("ID mismatch in JSON RPC answer." )
158- self .queryid = self .queryid + 1
173+
174+ if self .nmctype == "namecoind" :
175+ self .queryid = self .queryid + 1
159176
160- if val ["error" ] is not None :
161- raise RPCError (val ["error" ])
177+ error = val ["error" ]
178+ if error is None :
179+ return val ["result" ]
162180
163- return val ["result" ]
181+ if isinstance (error , bool ):
182+ raise RPCError (val ["result" ])
183+ raise RPCError (error )
164184
165185 # Query the server via HTTP.
166186 def queryHTTP (self , data ):
167- header = "POST / HTTP/1.1\n "
168- header += "User-Agent: bitmessage\n "
169- header += "Host: %s\n " % self .host
170- header += "Content-Type: application/json\n "
171- header += "Content-Length: %d\n " % len (data )
172- header += "Accept: application/json\n "
173- authstr = "%s:%s" % (self .user , self .password )
174- header += "Authorization: Basic %s\n " % base64 .b64encode (authstr )
175-
176- resp = self .queryServer ("%s\n %s" % (header , data ))
177- lines = resp .split ("\r \n " )
178187 result = None
179- body = False
180- for line in lines :
181- if line == "" and not body :
182- body = True
183- elif body :
184- if result is not None :
185- raise Exception ("Expected a single line in HTTP response." )
186- result = line
188+
189+ try :
190+ self .con .putrequest ("POST" , "/" )
191+ self .con .putheader ("Connection" , "Keep-Alive" )
192+ self .con .putheader ("User-Agent" , "bitmessage" )
193+ self .con .putheader ("Host" , self .host )
194+ self .con .putheader ("Content-Type" , "application/json" )
195+ self .con .putheader ("Content-Length" , str (len (data )))
196+ self .con .putheader ("Accept" , "application/json" )
197+ authstr = "%s:%s" % (self .user , self .password )
198+ self .con .putheader ("Authorization" , "Basic %s" % base64 .b64encode (authstr ))
199+ self .con .endheaders ()
200+ self .con .send (data )
201+ try :
202+ resp = self .con .getresponse ()
203+ result = resp .read ()
204+ if resp .status != 200 :
205+ raise Exception ("Namecoin returned status %i: %s" , resp .status , resp .reason )
206+ except :
207+ logger .error ("HTTP receive error" )
208+ except :
209+ logger .error ("HTTP connection error" )
187210
188211 return result
189212
@@ -193,7 +216,7 @@ def queryServer (self, data):
193216 s = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
194217 s .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 )
195218 s .settimeout (3 )
196- s .connect ((self .host , int ( self .port ) ))
219+ s .connect ((self .host , self .port ))
197220 s .sendall (data )
198221 result = ""
199222
@@ -270,7 +293,7 @@ def ensureNamecoinOptions ():
270293 nmc .close ()
271294
272295 except Exception as exc :
273- print "Could not read the Namecoin config file probably because you don't have Namecoin installed. That's ok; we don't really need it. Detailed error message: %s" % str ( exc )
296+ logger . warning ( "Error processing namecoin.conf" , exc_info = True )
274297
275298 # If still nothing found, set empty at least.
276299 if (not hasUser ):
0 commit comments