Initial revision
[python-rwhoisd.git] / rwhoisd / Rwhois.py
1 # This modules contains classes that are fairly general to RWhois
2 # server operation.
3
4 class RwhoisError(Exception):
5     pass
6
7 # The RWhois error codes.  Most of these won't ever be used.
8 error_codes = { 120 : "Registration Deferred",
9                 130 : "Object Not Authoritative",
10                 230 : "No Objects Found",
11                 320 : "Invalid Attribute",
12                 321 : "Invalid Attribute Syntax",
13                 322 : "Required Attribute Missing",
14                 323 : "Object Reference Not Found",
15                 324 : "Primary Key Not Unique",
16                 325 : "Failed to Update Stale Object",
17                 330 : "Exceeded Response Limit",
18                 331 : "Invalid Limit",
19                 332 : "Nothing To Transfer",
20                 333 : "Not Master for Authority Area",
21                 336 : "Object Not Found",
22                 338 : "Invalid Directive Syntax",
23                 340 : "Invalid Authority Area",
24                 341 : "Invalid Class",
25                 342 : "Invalid Host/Port",
26                 350 : "Invalid Query Syntax",
27                 351 : "Query Too Complex",
28                 352 : "Invalid Security Method",
29                 353 : "Authentication Failed",
30                 354 : "Encryption Failed",
31                 360 : "Corrupt Data. Keyadd Failed",
32                 400 : "Directive Not Available",
33                 401 : "Not Authorized For Directive",
34                 402 : "Unidentified Error",
35                 420 : "Registration Not Authorized",
36                 436 : "Invalid Display Format",
37                 500 : "Memory Allocation Problem",
38                 501 : "Service Not Available",
39                 502 : "Unrecoverable Error",
40                 503 : "Idle Time Exceeded",
41                 560 : ""
42                 }
43
44 def error_message(value):
45     try:
46         code, msg = value
47         code = int(code)
48     except (TypeError, ValueError):
49         try:
50             code = int(value)
51             msg  = None
52         except ValueError:
53             msg  = value
54             code = 402
55     if msg:
56         return "%%error %d %s: %s\r\n" % \
57                (code, error_codes.get(code, 402), msg)
58     else:
59         return "%%error %d %s\r\n" % (code, error_codes.get(code, 402))
60
61 def ok():
62     return "%ok\r\n"
63
64 class rwhoisobject:
65     """This is the standard class for RWhois data objects."""
66
67     def __init__(self):
68         self.data = {}
69         self.attr_order = []
70
71     def get_attr(self, attr, default=None):
72         """This returns a list of values associated with a particular
73         attribute.  The default value, if supplied, must be a single
74         (non-sequence) value."""
75         
76         if default:
77             return self.data.get(attr.strip().lower(), [default])
78         return self.data.get(attr.strip().lower(), [])
79
80     def get_attr_value(self, attr, default=None):
81         """This returns a single value associated with the attribute.
82         If the attribute has multiple values, the first is
83         returned."""
84         
85         return self.data.get(attr.strip().lower(), [default])[0]
86     
87     def getid(self):
88         """Return the RWhois ID of this object."""
89         
90         return self.get_attr_value("id")
91
92     def add_attr(self, attr, value):
93         """Adds an attribute to the object."""
94         
95         attr = attr.strip().lower()
96         if self.data.has_key(attr): self.data[attr].append(value)
97         else:
98             self.attr_order.append(attr)
99             self.data.setdefault(attr, []).append(value)
100
101     def add_attrs(self, attr_list):
102         """Adds a list of (attribute, value) tuples to the object."""
103         for attr, value in attr_list:
104             self.add_attr(attr, value)
105         
106     def items(self):
107         """Returns the list of (attribute, value) tuples (actually 2
108         elements lists).  Attributes with multiple values produce
109         multiple tuples.  The items are returned in the same order
110         they were added to the object."""
111         
112         return [ [x, y] for x in self.attr_order for y in self.data[x] ]
113
114     def values(self):
115         """Return the list of values in this object."""
116         
117         return [ x for y in self.data.values() for x in y ]
118     
119     def __str__(self):
120         """A convenient string representation of this object"""
121         return '\n'.join([':'.join(x) for x in self.items()])
122
123     def __repr__(self):
124         return "<rwhoisobject: " + self.getid() + ">"
125     
126     def attrs_to_wire_str(self, attrs, prefix=None):
127         """Return specific attributes in a response formatted string
128         (classname:attr:value)"""
129
130         cn = self.get_attr_value("class-name", "unknown-class")
131         items = [ [cn, x, y] for x in attrs for y in self.data[x] ]
132
133         if prefix:
134             res = '\r\n'.join([ prefix + ':'.join(x) for x in items ])
135         else:
136             res = '\r\n'.join([ ':'.join(x) for x in items ])
137             
138         return res;
139
140     def to_wire_str(self, prefix=None):
141         """Return the response formatted string (classname:attr:value)"""
142
143         return self.attrs_to_wire_str(self.attr_order, prefix)
144     
145
146
147 ## A basic test driver
148 if __name__ == '__main__':
149
150     obj = rwhoisobject()
151     obj.add_attr('id', '001')
152     obj.add_attr("class-name", 'contact')
153     obj.add_attr("class-name", "foo")
154     obj.add_attr('name', 'Aiden Quinn')
155     obj.add_attr('email', 'aquin@yahoo.com')
156     obj.add_attr('org-name', 'YoYoDyne Inc.')
157     obj.add_attr('email', 'aq@aol.net')
158     obj.add_attr('First-Name', 'Aiden ')
159
160     print "obj:\n", obj
161     print "wire:\n", obj.to_wire_str()