1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import sys
20 import base64
21 import string
22 try:
23
24 import xmlrpclib
25 except ImportError:
26
27 import xmlrpc.client as xmlrpclib
28 from rhn.rpclib import transports
29
30
31 from spacewalk.common.usix import raise_with_tb
32 from spacewalk.common import apache, rhnFlags
33 from spacewalk.common.rhnConfig import CFG
34 from spacewalk.common import byterange
35 from spacewalk.common.rhnLog import log_debug, log_error
36 from spacewalk.common.rhnException import rhnFault, rhnNotFound,\
37 redirectException
38 from spacewalk.common.rhnTranslate import _
39 from spacewalk.common.rhnLib import setHeaderValue
40 from spacewalk.common.rhnTB import Traceback
41
42
43 import rhnRepository
44 import rhnImport
45 import rhnSQL
46 import rhnCapability
47 import apacheAuth
48
49
50
51
53
55 Exception.__init__(self)
56 self.__value = value
57
59 return _("Invalid request received (%s).") % self.__value
60 __str__ = __repr__
61
62
65
66
67
68
70
71 - def __init__(self, client_version, req):
72 self.client = client_version
73 self.req = req
74
75 self.input = transports.Input(req.headers_in)
76
77 self.parser, self.decoder = xmlrpclib.getparser()
78
79
80 self.decoder._encoding = None
81
82
83
84 req_config = req.get_options()
85
86
87 self.server = req_config["SERVER"]
88
89
90 self.servers = None
91 self._setup_servers()
92
94 self.servers = rhnImport.load("server/handlers",
95 interface_signature='rpcClasses')
96
97
99 raise UnknownXML("Could not find reference definition"
100 "for method '%s'" % method)
101
102
104
105 if CFG.SEND_MESSAGE_TO_ALL:
106
107 if method == 'applet.poll_status':
108 return self.response({
109 'checkin_interval': 3600,
110 'server_status': 'normal'
111 })
112 if method == 'applet.poll_packages':
113 return self.response({'use_cached_copy': 1})
114
115
116 msg = open(CFG.MESSAGE_TO_ALL).read()
117 log_debug(3, "Sending message to all clients: %s" % msg)
118
119 response = xmlrpclib.Fault(
120 -1, _("IMPORTANT MESSAGE FOLLOWS:\n%s") % msg)
121
122 ret = self.response(response)
123 log_debug(4, "Leave with return value", ret)
124 return ret
125
126
127 log_debug(2, method)
128
129
130 force_rollback = 1
131 try:
132 rhnSQL.clear_log_id()
133
134 func = self.method_ref(method)
135 response = func(*params)
136 except (TypeError, ValueError, KeyError, IndexError, UnknownXML):
137
138 fault = 1
139
140 if sys.version_info[0] == 3:
141 exctype = sys.exc_info()[0]
142 else:
143 exctype = sys.exc_type
144
145 if exctype == UnknownXML:
146 fault = -1
147 e_type, e_value = sys.exc_info()[:2]
148 response = xmlrpclib.Fault(fault, _(
149 "While running '%s': caught\n%s : %s\n") % (
150 method, e_type, e_value))
151 Traceback(method, self.req,
152 extra="Response sent back to the caller:\n%s\n" % (
153 response.faultString,),
154 severity="notification")
155 except rhnNotFound:
156 e = sys.exc_info()[1]
157 return apache.HTTP_NOT_FOUND
158
159 except redirectException:
160 re = sys.exc_info()[1]
161 log_debug(3, "redirect exception caught", re.path)
162 response = re.path
163
164 except rhnFault:
165 f = sys.exc_info()[1]
166 response = f.getxml()
167 except rhnSQL.SQLSchemaError:
168 e = sys.exc_info()[1]
169 f = None
170 if e.errno == 20200:
171 log_debug(2, "User Group Membership EXCEEDED")
172 f = rhnFault(43, e.errmsg)
173 if not f:
174 log_error("rhnSQL.SQLSchemaError caught", e)
175 rhnSQL.rollback()
176
177 Traceback(method, self.req,
178 extra="SQL Error generated: %s" % e,
179 severity="schema")
180 return apache.HTTP_INTERNAL_SERVER_ERROR
181 response = f.getxml()
182 except rhnSQL.SQLError:
183 e = sys.exc_info()[1]
184 log_error("rhnSQL.SQLError caught", e)
185 rhnSQL.rollback()
186 Traceback(method, self.req,
187 extra="SQL Error generated: %s" % e,
188 severity="schema")
189 return apache.HTTP_INTERNAL_SERVER_ERROR
190 except Exception:
191 e = sys.exc_info()[1]
192 log_error("Unhandled exception", e)
193 rhnSQL.rollback()
194
195 Traceback(method, self.req, severity="unhandled")
196 return apache.HTTP_INTERNAL_SERVER_ERROR
197 else:
198
199 force_rollback = 0
200 if force_rollback:
201 rhnSQL.rollback()
202 rhnSQL.clear_log_id()
203
204 ret = self.response(response)
205 log_debug(4, "Leave with return value", ret)
206 return ret
207
208
212
213
214
219
220
222 log_debug(3, response.name)
223
224 if rhnFlags.test("Content-Type"):
225 self.req.content_type = rhnFlags.get("Content-Type")
226 else:
227
228 self.req.content_type = "application/octet-stream"
229
230
231 if response.length == 0:
232 response.file_obj.seek(0, 2)
233 file_size = response.file_obj.tell()
234 response.file_obj.seek(0, 0)
235 else:
236 file_size = response.length
237
238 success_response = apache.OK
239 response_size = file_size
240
241
242 if ("If-Modified-Since" in self.req.headers_in and
243 "Last-Modified" in rhnFlags.get("outputTransportOptions") and
244 rhnFlags.get("outputTransportOptions")['Last-Modified'] == self.req.headers_in['If-Modified-Since']):
245 return apache.HTTP_NOT_MODIFIED
246
247
248 if "Range" in self.req.headers_in:
249 try:
250 range_start, range_end = \
251 byterange.parse_byteranges(self.req.headers_in["Range"],
252 file_size)
253 response_size = range_end - range_start
254 self.req.headers_out["Content-Range"] = \
255 byterange.get_content_range(range_start, range_end, file_size)
256 self.req.headers_out["Accept-Ranges"] = "bytes"
257
258 response.file_obj.seek(range_start)
259
260
261
262 self.req.status = apache.HTTP_PARTIAL_CONTENT
263 success_response = apache.HTTP_PARTIAL_CONTENT
264
265
266 except byterange.InvalidByteRangeException:
267 pass
268 except byterange.UnsatisfyableByteRangeException:
269 pass
270
271 self.req.headers_out["Content-Length"] = str(response_size)
272
273
274
275
276
277 if response.name:
278 self.req.headers_out["X-Package-FileName"] = response.name
279
280 xrepcon = "X-Replace-Content-Active" in self.req.headers_in \
281 and rhnFlags.test("Download-Accelerator-Path")
282 if xrepcon:
283 fpath = rhnFlags.get("Download-Accelerator-Path")
284 log_debug(1, "Serving file %s" % fpath)
285 self.req.headers_out["X-Replace-Content"] = fpath
286
287 byte_rate = rhnFlags.get("QOS-Max-Bandwidth")
288 if byte_rate:
289 self.req.headers_out["X-Replace-Content-Throttle"] = str(byte_rate)
290
291
292 self.req.send_http_header()
293
294 if "Range" in self.req.headers_in:
295
296 read = 0
297 while read < response_size:
298
299 if (read + CFG.BUFFER_SIZE > response_size):
300 to_read = read + CFG.BUFFER_SIZE - response_size
301 else:
302 to_read = CFG.BUFFER_SIZE
303 buf = response.read(CFG.BUFFER_SIZE)
304 if not buf:
305 break
306 try:
307 self.req.write(buf)
308 read = read + CFG.BUFFER_SIZE
309 except IOError:
310 if xrepcon:
311
312
313 break
314 return apache.HTTP_BAD_REQUEST
315 response.close()
316 else:
317 if 'wsgi.file_wrapper' in self.req.headers_in:
318 self.req.output = self.req.headers_in['wsgi.file_wrapper'](response, CFG.BUFFER_SIZE)
319 else:
320 self.req.output = iter(lambda: response.read(CFG.BUFFER_SIZE), '')
321
322 return success_response
323
324
326
327 log_debug(3, type(response))
328 needs_xmlrpc_encoding = not rhnFlags.test("XMLRPC-Encoded-Response")
329 compress_response = rhnFlags.test("compress_response")
330
331
332 if isinstance(response, transports.File):
333 if not hasattr(response.file_obj, 'fileno') and compress_response:
334
335
336 response = response.file_obj.read()
337 needs_xmlrpc_encoding = 0
338 else:
339
340 return self.response_file(response)
341
342 output = transports.Output()
343
344
345 output.set_transport_flags(
346 transfer=transports.lookupTransfer(self.input.transfer),
347 encoding=transports.lookupEncoding(self.input.encoding))
348
349 if isinstance(response, xmlrpclib.Fault):
350 log_debug(4, "Return FAULT",
351 response.faultCode, response.faultString)
352
353
354 output.set_transport_flags(output.TRANSFER_NONE, output.ENCODE_NONE)
355 elif compress_response:
356
357 log_debug(4, "Compression on for client version", self.client)
358 if self.client > 0:
359 output.set_transport_flags(output.TRANSFER_BINARY,
360 output.ENCODE_ZLIB)
361 else:
362 output.set_transport_flags(output.TRANSFER_BASE64,
363 output.ENCODE_ZLIB)
364
365
366 output.headers.update(rhnFlags.get('outputTransportOptions').dict())
367
368 if needs_xmlrpc_encoding:
369
370 response = self.normalize(response)
371 try:
372 response = xmlrpclib.dumps(response, methodresponse=1)
373 except TypeError:
374 e = sys.exc_info()[1]
375 log_debug(4, "Error \"%s\" encoding response = %s" % (e, response))
376 Traceback("apacheHandler.response", self.req,
377 extra="Error \"%s\" encoding response = %s" % (e, response),
378 severity="notification")
379 return apache.HTTP_INTERNAL_SERVER_ERROR
380 except:
381
382 Traceback("apacheHandler.response", self.req,
383 severity="unhandled")
384 return apache.HTTP_INTERNAL_SERVER_ERROR
385
386
387 output.process(response)
388
389 for k, v in output.headers.items():
390 if string.lower(k) == 'content-type':
391
392 self.req.content_type = v
393 else:
394 setHeaderValue(self.req.headers_out, k, v)
395
396 if 5 <= CFG.DEBUG < 10:
397 log_debug(5, "The response: %s[...SNIP (for sanity) SNIP...]%s" % (response[:100], response[-100:]))
398 elif CFG.DEBUG >= 10:
399
400 log_debug(10, "The response: %s" % response)
401
402
403 self.req.send_http_header()
404 try:
405
406
407
408 self.req.write(output.data)
409 except IOError:
410
411
412 return apache.HTTP_BAD_REQUEST
413 del output
414 return apache.OK
415
418
421
422
423
424
425 -class apachePOST(apacheRequest):
426
427
428 - def decode(self, data):
429 try:
430 self.parser.feed(data)
431 except IndexError:
432
433 raise_with_tb(xmlrpclib.ResponseError, sys.exc_info()[2])
434
435 self.parser.close()
436
437 params = self.decoder.close()
438 method = self.decoder.getmethodname()
439 return params, method
440
441
442 - def method_ref(self, method):
443
444
445 log_debug(3, self.server, method)
446 if method[-8:] == '.__str__':
447
448
449 log_error("Ignoring call for method", method)
450 raise rhnFault(-1, "Ignoring call for a __str__ method", explain=0)
451 if self.server is None:
452 raise UnknownXML("Method `%s' is not bound to a server "
453 "(server = %s)" % (method, self.server))
454 classes = self.servers[self.server]
455 if classes is None:
456 raise UnknownXML("Server %s is not a valid XML-RPC receiver" %
457 (self.server,))
458
459 try:
460 classname, funcname = string.split(method, '.', 1)
461 except:
462 raise_with_tb(UnknownXML("method '%s' doesn't have a class and function" %
463 (method,)), sys.exc_info()[2])
464 if not classname or not funcname:
465 raise UnknownXML(method)
466
467 log_debug(4, "Class name: %s; function name: %s" % (classname,
468 funcname))
469 c = classes.get(classname)
470 if c is None:
471 raise UnknownXML("class %s.%s is not defined (function = %s)" % (
472 self.server, classname, funcname))
473
474
475 serverHandlers = c()
476
477 serverHandlers.remote_hostname = self.req.get_remote_host(apache.REMOTE_DOUBLE_REV)
478 f = serverHandlers.get_function(funcname)
479 if f is None:
480 raise UnknownXML("function: %s invalid" % (method,))
481
482 rhnCapability.set_server_capabilities()
483 return f
484
485
487 log_debug(3)
488
489
490 try:
491 fd = self.input.decode(self.req)
492 except IOError:
493 return apache.HTTP_BAD_REQUEST
494
495
496 _body = fd.read()
497 fd.close()
498
499
500
501 if _body is None or len(_body) == 0:
502 return apache.HTTP_BAD_REQUEST
503
504
505 try:
506 params, method = self.decode(_body)
507 except xmlrpclib.ResponseError:
508 log_error("Got bad XML-RPC blob of len = %d" % len(_body))
509 return apache.HTTP_BAD_REQUEST
510 else:
511 if params is None:
512 params = ()
513
514 return self.call_function(method, params)
515
516
518
519 - def __init__(self, client_version, req):
539
542
543
545
546
547 - def __init__(self, client_version, req):
550
554
555
571
572
594
596
597
598
599 array = string.split(self.req.path_info, '/')
600 if len(array) < 4:
601 log_error("Invalid URI for GET request", self.req.path_info)
602 raise rhnFault(21, _("Invalid URI %s" % self.req.path_info))
603
604 self.channel, method = (array[2], array[3])
605 params = tuple(array[4:])
606 return method, params
607
608
610 log_debug(3)
611
612 if isinstance(response, str):
613 method, params = self._get_method_params()
614 if method == "getPackage":
615 return self.redirect(self.req, response)
616
617
618
619
620
621 if isinstance(response, xmlrpclib.Fault):
622 log_debug(4, "Return FAULT",
623 response.faultCode, response.faultString)
624 retcode = apache.HTTP_NOT_FOUND
625 if abs(response.faultCode) in (33, 34, 35, 37, 39, 41):
626 retcode = apache.HTTP_UNAUTHORIZED
627
628 self.req.headers_out["X-RHN-Fault-Code"] = \
629 str(response.faultCode)
630 faultString = string.strip(base64.encodestring(
631 response.faultString))
632
633 for line in string.split(faultString, '\n'):
634 self.req.headers_out.add("X-RHN-Fault-String",
635 string.strip(line))
636
637 for k, v in rhnFlags.get('outputTransportOptions').items():
638 setHeaderValue(self.req.headers_out, k, v)
639 return retcode
640
641
642
643
644 for k, v in rhnFlags.get('outputTransportOptions').items():
645 setHeaderValue(self.req.headers_out, k, v)
646
647 return apacheRequest.response(self, response)
648
649
650 - def redirect(self, req, url, temporary=1):
665