pull req by EagleTM (retyped manually)
[electrum-server.git] / backends / irc / __init__.py
1 import threading, socket, traceback, time, sys
2
3 def random_string(N):
4     import random, string
5     return ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(N))
6
7 from processor import Processor
8 from version import VERSION
9
10 class IrcThread(threading.Thread):
11
12     def __init__(self, processor, config):
13         threading.Thread.__init__(self)
14         self.processor = processor
15         self.daemon = True
16         self.stratum_tcp_port = config.get('server','stratum_tcp_port')
17         self.stratum_http_port = config.get('server','stratum_http_port')
18         self.stratum_tcp_ssl_port = config.get('server','stratum_tcp_ssl_port')
19         self.stratum_http_ssl_port = config.get('server','stratum_http_ssl_port')
20         self.report_stratum_tcp_port = config.get('server','report_stratum_tcp_port')
21         self.report_stratum_http_port = config.get('server','report_stratum_http_port')
22         self.report_stratum_tcp_ssl_port = config.get('server','report_stratum_tcp_ssl_port')
23         self.report_stratum_http_ssl_port = config.get('server','report_stratum_http_ssl_port')
24         self.peers = {}
25         self.host = config.get('server','host')
26         self.report_host = config.get('server','report_host')
27         self.nick = config.get('server', 'irc_nick')
28         if self.report_stratum_tcp_port: self.stratum_tcp_port = self.report_stratum_tcp_port
29         if self.report_stratum_http_port: self.stratum_http_port = self.report_stratum_http_port
30         if self.report_stratum_tcp_ssl_port: self.stratum_tcp_ssl_port = self.report_stratum_tcp_ssl_port
31         if self.report_stratum_http_ssl_port: self.stratum_http_ssl_port = self.report_stratum_http_ssl_port
32         if self.report_host: self.host = self.report_host
33         if not self.nick: self.nick = random_string(10)
34         self.prepend = 'E_'
35         if config.get('server', 'coin') == 'litecoin':
36             self.prepend = 'EL_'
37         self.pruning = config.get('server', 'backend') == 'leveldb'
38         self.nick = self.prepend + self.nick
39
40     def get_peers(self):
41         return self.peers.values()
42
43
44     def getname(self):
45         s = 'v' + VERSION + ' '
46         if self.pruning: s += 'p '
47         if self.stratum_tcp_port:
48             s += 't' + self.stratum_tcp_port + ' ' 
49         if self.stratum_http_port:
50             s += 'h' + self.stratum_http_port + ' '
51         if self.stratum_tcp_port:
52             s += 's' + self.stratum_tcp_ssl_port + ' ' 
53         if self.stratum_http_port:
54             s += 'g' + self.stratum_http_ssl_port + ' '
55         return s
56
57
58     def run(self):
59         ircname = self.getname()
60
61         while not self.processor.shared.stopped():
62             try:
63                 s = socket.socket()
64                 s.connect(('irc.freenode.net', 6667))
65                 s.settimeout(260)
66             except:
67                 s.close()
68                 time.sleep(10)
69                 continue
70
71             try:
72                 s.send('USER electrum 0 * :' + self.host + ' ' + ircname + '\n')
73                 s.send('NICK ' + self.nick + '\n')
74                 s.send('JOIN #electrum\n')
75                 sf = s.makefile('r', 0)
76                 t = 0
77                 while not self.processor.shared.stopped():
78                     line = sf.readline()
79                     line = line.rstrip('\r\n')
80                     line = line.split()
81                     if not line: continue
82                     if line[0]=='PING': 
83                         s.send('PONG '+line[1]+'\n')
84                     elif '353' in line: # answer to /names
85                         k = line.index('353')
86                         for item in line[k+1:]:
87                             if item.startswith(self.prepend):
88                                 s.send('WHO %s\n'%item)
89                     elif '352' in line: # answer to /who
90                         # warning: this is a horrible hack which apparently works
91                         k = line.index('352')
92                         ip = line[k+4]
93                         ip = socket.gethostbyname(ip)
94                         name = line[k+6]
95                         host = line[k+9]
96                         ports  = line[k+10:]
97                         self.peers[name] = (ip, host, ports)
98                     if time.time() - t > 5*60:
99                         self.processor.push_response({'method':'server.peers', 'params':[self.get_peers()]})
100                         s.send('NAMES #electrum\n')
101                         t = time.time()
102                         self.peers = {}
103             except:
104                 traceback.print_exc(file=sys.stdout)
105             finally:
106                 sf.close()
107                 s.close()
108
109         print "quitting IRC"
110
111
112
113 class ServerProcessor(Processor):
114
115     def __init__(self, config):
116         Processor.__init__(self)
117         self.daemon = True
118         self.banner = config.get('server','banner')
119         self.password = config.get('server','password')
120
121         if config.get('server', 'irc') == 'yes':
122             self.irc = IrcThread(self, config)
123         else: 
124             self.irc = None
125
126
127     def get_peers(self):
128         if self.irc:
129             return self.irc.get_peers()
130         else:
131             return []
132
133
134     def run(self):
135         if self.irc:
136             self.irc.start()
137         Processor.run(self)
138
139     def process(self, request):
140         method = request['method']
141         params = request['params']
142         result = None
143
144         if method in ['server.stop', 'server.info']:
145             try:
146                 password = request['params'][0]
147             except:
148                 password = None
149
150             if password != self.password:
151                 response = { 'id':request['id'], 'result':None,  'error':'incorrect password'}
152                 self.push_response(response)
153                 return
154
155         if method == 'server.banner':
156             result = self.banner.replace('\\n','\n')
157
158         elif method == 'server.peers.subscribe':
159             result = self.get_peers()
160
161         elif method == 'server.version':
162             result = VERSION
163
164         elif method == 'server.stop':
165             self.shared.stop()
166             result = 'stopping, please wait until all threads terminate.'
167
168         elif method == 'server.info':
169             result = map(lambda s: { "time":s.time, 
170                                      "name":s.name,
171                                      "address":s.address, 
172                                      "version":s.version, 
173                                      "subscriptions":len(s.subscriptions)}, 
174                          self.dispatcher.request_dispatcher.get_sessions())
175
176         elif method == 'server.cache':
177             p = self.dispatcher.request_dispatcher.processors['blockchain']
178             result = len(repr(p.store.tx_cache))
179
180         elif method == 'server.load':
181             p = self.dispatcher.request_dispatcher.processors['blockchain']
182             result = p.queue.qsize()
183
184         else:
185             print "unknown method", request
186
187         if result!='':
188             response = { 'id':request['id'], 'result':result }
189             self.push_response(response)
190