# This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Copyright 2005 Dan Williams and Red Hat, Inc. import string import urllib import os import sys import SSLCommon from OpenSSL import SSL import mimetypes import mimetools import stat import httplib import shutil import socket def get_content_type(filename): return mimetypes.guess_type(filename)[0] or 'application/octet-stream' # if sock is None, juste return the estimate size def send_data(v_vars, v_files, boundary, sock=None): l = 0 for (k, v) in v_vars: buffer='' buffer += '--%s\r\n' % boundary buffer += 'Content-Disposition: form-data; name="%s"\r\n' % k buffer += '\r\n' buffer += str(v) + '\r\n' if sock: sock.send(buffer) l += len(buffer) for (k, v) in v_files: fd = v file_size = os.fstat(fd.fileno())[stat.ST_SIZE] name = os.path.basename(fd.name) buffer='' buffer += '--%s\r\n' % boundary buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' \ % (k, name) buffer += 'Content-Type: %s\r\n' % get_content_type(name) buffer += 'Content-Length: %s\r\n' % file_size buffer += '\r\n' l += len(buffer) if sock: sock.send(buffer) fd.seek(0) bufsize = 8*1024 # I/O buffering size for copy to file todo = file_size if todo >= 0: while todo > 0: data = fd.read(min(todo, bufsize)) if not data: break sock.send(data) todo = todo - len(data) l += file_size buffer='\r\n' buffer += '--%s--\r\n' % boundary buffer += '\r\n' if sock: sock.send(buffer) l += len(buffer) return l class PlgURLopener(urllib.URLopener): def __init__(self, certs=None, timeout=None): self.certs = certs self._timeout = timeout urllib.URLopener.__init__(self) def do_open(self, url, protocol, data=None, connection_func=None): if not connection_func: connection_func = self.make_http_connection # Copied and modified from urllib.py to support HTTPS with # multipart mime POST support import httplib user_passwd = None if isinstance(url, str): host, selector = urllib.splithost(url) if host: user_passwd, host = urllib.splituser(host) host = urllib.unquote(host) realhost = host else: host, selector = url urltype, rest = urllib.splittype(selector) url = rest user_passwd = None if urltype.lower() != protocol: realhost = None else: realhost, rest = urllib.splithost(rest) if realhost: user_passwd, realhost = urllib.splituser(realhost) if user_passwd: selector = "%s://%s%s" % (urltype, realhost, rest) if urllib.proxy_bypass(realhost): host = realhost if not host: raise IOError, ('http error', 'no host given') if user_passwd: import base64 auth = base64.encodestring(user_passwd).strip() else: auth = None h = connection_func(host) v_files = [] v_vars = [] if data and type(data) != str: for (k, v) in data: if type(v) == file: v_files.append((k, v)) else: v_vars.append((k, v)) # no file? convert to string if len(v_vars) > 0 and len(v_files) == 0: data = urllib.urlencode(v_vars) v_files=[] v_vars=[] if data is not None: h.putrequest('POST', selector) if len(v_files) > 0: boundary = mimetools.choose_boundary() l = send_data(v_vars, v_files, boundary) h.putheader('Content-Type', 'multipart/form-data; boundary=%s' % boundary) h.putheader('Content-length', str(l)) else: h.putheader('Content-type', 'application/x-www-form-urlencoded') h.putheader('Content-length', '%d' % len(data)) else: h.putrequest('GET', selector) if auth: h.putheader('Authorization', 'Basic %s' % auth) if realhost: h.putheader('Host', realhost) for args in self.addheaders: h.putheader(*args) # httplib will attempt to connect here try: h.endheaders() except (SSL.Error, socket.error), e: # Convert errors into an IOError so the call fails raise IOError(e) if data is not None: if len(v_files) > 0: l = send_data(v_vars, v_files, boundary, h) elif len(v_vars) > 0: # if data is passed as dict ... data = urllib.urlencode(v_vars) h.send(data) else: h.send(data) # close files if data is not None: for (ignored, f) in v_files: f.close() errcode, errmsg, headers = h.getreply() fp = h.getfile() if errcode == 200: return urllib.addinfourl(fp, headers, protocol + url) else: if data is None: return self.http_error(url, fp, errcode, errmsg, headers) else: return self.http_error(url, fp, errcode, errmsg, headers, data) def make_http_connection(self, host): return httplib.HTTP(host) def open_http(self, url, data=None): return self.do_open(url, 'http', data, self.make_http_connection) def make_https_connection(self, host): if not self.certs or len(self.certs) == 0: raise Exception("HTTPS requested, but no certificates provided.") self.ctx = SSLCommon.CreateSSLContext(self.certs) return SSLCommon.PlgHTTPS(host=host, ssl_context=self.ctx, timeout=self._timeout) def open_https(self, url, data=None): return self.do_open(url, 'https', data, self.make_https_connection)