Update TODO based on work for 0.4.1
[python-rwhoisd.git] / rwhoisd / RwhoisServer.py
1 # This file is part of python-rwhoisd
2 #
3 # Copyright (C) 2003, 2008 David E. Blacka
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 # USA
19
20 import sys, socket, SocketServer
21
22 import config, Session
23 import QueryParser, QueryProcessor, DirectiveProcessor, Rwhois
24
25
26 # server-wide variables
27
28 query_processor     = None
29 directive_processor = None
30
31 class RwhoisTCPServer(SocketServer.ThreadingTCPServer):
32     def __init__(self, server_address, RequestHandlerClass):
33         self.allow_reuse_address = True
34         SocketServer.TCPServer.__init__(self, server_address,
35                                         RequestHandlerClass)
36
37     def verify_request(self, request, client_address):
38         # implement access control here
39         return True
40
41 class RwhoisHandler(SocketServer.StreamRequestHandler):
42
43     def readline(self):
44         """Read a line of input from the client."""
45         # a simple way of doing this
46         # return self.rfile.readline()
47
48         data = self.request.recv(1024)
49         if not data: return None
50
51         lines = data.splitlines(True)
52
53         # ugh. this totally defeats any pipelining, not that rwhois
54         # clients should be doing that.
55         if len(lines) > 1 and config.verbose:
56             print "%s discarding additional input lines: %r" \
57                   % (self.client_address, lines)
58         return lines[0]
59         
60     def handle(self):
61
62         self.quit_flag = False
63
64         # output a banner
65         self.wfile.write(config.banner_string);
66         self.wfile.write("\r\n");
67
68         # get a session.
69         session = Session.Context()
70         session.rfile = self.rfile
71         session.wfile = self.wfile
72
73         if config.verbose:
74             print "%s accepted connection" % (self.client_address,)
75
76         c = 0
77         while 1:
78             line = self.readline()
79             if not line: break
80
81             line = line.strip()
82             # we can skip blank lines.
83             if not line:
84                 continue
85         
86             try:
87                 if line.startswith("-"):
88                     self.handle_directive(session, line)
89                 else:
90                     self.handle_query(session, line)
91                     if not session.holdconnect:
92                         self.quit_flag = True
93             except Rwhois.RwhoisError, e:
94                 self.handle_error(session, e)
95
96             self.wfile.flush()
97
98             # check to see if we were asked to quit
99             if self.quit_flag: break
100
101         if config.verbose:
102             print "%s disconnected" %  (self.client_address,)
103
104     def handle_directive(self, session, line):
105         if config.verbose:
106             print "%s directive %s" % (self.client_address, line)
107         if (line.startswith("-quit")):
108             self.quit_flag = True
109             self.wfile.write(Rwhois.ok())
110             return
111         directive_processor.process_directive(session, line)
112
113     def handle_query(self, session, line):
114         if config.verbose:
115             print "%s query %s" % (self.client_address, line)
116         query_processor.process_query(session, line)
117
118     def handle_error(self, session, error):
119         code = error[0]
120         msg = error[1]
121         session.wfile.write(Rwhois.error_message((code, msg)))
122
123 def usage(pname):
124     print """\
125 usage: %s [-v] schema_file data_file [data_file ...]
126        -v: verbose """ % pname
127     sys.exit(64)
128     
129 def init(argv):
130     import MemDB
131     import getopt
132
133     pname = argv[0]
134     opts, argv = getopt.getopt(argv[1:], 'v')
135     for o, a in opts:
136         if o == "-v":
137             config.verbose = True
138     
139     if len(argv) < 2: usage(pname)
140     schema_file = argv[0]
141     data_files  = argv[1:]
142
143
144     db = MemDB.MemDB()
145
146     db.init_schema(schema_file)
147     for df in data_files:
148         db.load_data(df)
149     db.index_data()
150
151     QueryParser.db = db
152
153     global query_processor, directive_processor
154     
155     query_processor     = QueryProcessor.QueryProcessor(db)
156     directive_processor = DirectiveProcessor.DirectiveProcessor(db)
157
158 def serve():
159     # initialize the TCP server
160     server = RwhoisTCPServer((config.server_address, config.port),
161                              RwhoisHandler)
162
163     # and handle incoming connections
164     if config.verbose:
165         if not config.server_address:
166             print "listening on port %d" % config.port
167         else:
168             print "listening on %s port %d" % \
169                   (config.server_address, config.port)
170     server.serve_forever()
171
172     sys.exit(0)
173
174 if __name__ == "__main__":
175
176     init(sys.argv)
177     serve()
178