]> git.phdru.name Git - bookmarks_db.git/blob - Robots/bkmk_rurllib_py3.py
Refactor(Robots): Refactor request headers
[bookmarks_db.git] / Robots / bkmk_rurllib_py3.py
1 """Simple, strightforward robot based on urllib
2
3 This file is a part of Bookmarks database and Internet robot.
4
5 """
6
7 __author__ = "Oleg Broytman <phd@phdru.name>"
8 __copyright__ = "Copyright (C) 2000-2024 PhiloSoft Design"
9 __license__ = "GNU GPL"
10
11 __all__ = ['robot_urllib_py3']
12
13
14 import http.client
15 import socket
16 import sys
17 import urllib
18 import urllib.request
19
20 from Robots.bkmk_robot_base import robot_base, get_error
21
22 # Fake to import 'add_headers'
23 urllib.URLopener = urllib.request.URLopener
24 urllib.ftpwrapper = urllib.request.ftpwrapper
25 from Robots.bkmk_rurllib import add_headers  # noqa: E402 import not at top
26
27
28 class RedirectException(Exception):
29     def __init__(self, errcode, newurl):
30         Exception.__init__(self)
31         self.errcode = errcode
32         self.newurl = newurl
33
34
35 class MyURLopener(urllib.request.URLopener):
36     # Error 301 -- relocated (permanently)
37     def http_error_301(self, url, fp, errcode, errmsg, headers, data=None):
38         if 'location' in headers:
39             newurl = headers['location']
40         elif 'uri' in headers:
41             newurl = headers['uri']
42         else:
43             newurl = "Nowhere"
44         raise RedirectException(errcode, newurl)
45
46     # Error 302 -- relocated (temporarily)
47     http_error_302 = http_error_301
48     # Error 303 -- relocated (see other)
49     http_error_303 = http_error_301
50     # Error 307 -- relocated (temporarily)
51     http_error_307 = http_error_301
52     # Error 308 -- relocated (permanently)
53     http_error_308 = http_error_301
54
55     # Error 401 -- authentication required
56     def http_error_401(self, url, fp, errcode, errmsg, headers, data=None):
57         raise IOError(
58             ('http error', errcode, "Authentication required ", headers))
59
60     def http_error_default(self, url, fp, errcode, errmsg, headers):
61         if fp:
62             fp.read()
63             fp.close()
64         raise IOError(('http error', errcode, errmsg, headers))
65
66     def open(self, fullurl, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
67         return urllib.request.URLopener.open(self, fullurl, data)
68
69
70 urllib.request._opener = opener = MyURLopener()
71 add_headers(opener)
72
73 urllib_ftpwrapper = urllib.request.ftpwrapper
74 ftpcache_key = None
75
76
77 class myftpwrapper(urllib_ftpwrapper):
78     def __init__(self, user, passwd, host, port, dirs):
79         urllib_ftpwrapper.__init__(self, user, passwd, host, port, dirs)
80         global ftpcache_key
81         ftpcache_key = (user, host, port, '/'.join(dirs))
82
83
84 urllib.request.ftpwrapper = myftpwrapper
85
86
87 class robot_urllib_py3(robot_base):
88     def get(self, bookmark, url, accept_charset=False):
89         try:
90             # Set fake referer to the base URL
91             opener.addheaders[2] = ('Referer', url)
92
93             if accept_charset and bookmark.charset:
94                 opener.addheader('Accept-Charset', bookmark.charset)
95             try:
96                 fname, headers = urllib.request.urlretrieve(url)
97             finally:
98                 if accept_charset and bookmark.charset:
99                     # Remove Accept-Charset
100                     del opener.addheaders[-1]
101
102             possible_encodings = []
103             for encoding in (
104                     bookmark.charset,
105                     sys.getfilesystemencoding(),
106                     'utf-8',
107             ):
108                 if encoding and encoding not in possible_encodings:
109                     possible_encodings.append(encoding)
110             content = e = None
111             infile = open(fname, 'rb')
112             try:
113                 content = infile.read()
114             except Exception:
115                 content = None
116             finally:
117                 infile.close()
118
119             if content is None:
120                 e = str(e)
121                 return (
122                     'ERROR: ' + e,
123                     None, None, None, None
124                 )
125             return None, None, None, headers, content
126
127         except RedirectException as e:
128             return None, e.errcode, e.newurl, None, None
129
130         except (OSError, http.client.IncompleteRead) as e:
131             error = str(e)
132             self.log('   Error: %s' % error)
133             return error, None, None, None, None
134
135         except IOError as e:
136             if (e[0] == "http error") and (e[1] == -1):
137                 error = None
138                 bookmark.no_error = "The server did not return any header - "
139                 "it is not an error, actually"
140                 self.log('   no headers: %s' % bookmark.no_error)
141             else:
142                 error = get_error(e)
143                 self.log('   Error: %s' % error)
144
145             return error, None, None, None, None
146
147     def get_ftp_welcome(self):
148         global ftpcache_key
149         _welcome = opener.ftpcache[ftpcache_key].ftp.welcome
150         # I am assuming there are no duplicate ftp URLs in db.
151         # If there are - ftpcache_key in next line is invalid.
152         ftpcache_key = None
153         return _welcome
154
155     def finish_check_url(self, bookmark):
156         robot_base.finish_check_url(self, bookmark)
157         urllib.request.urlcleanup()
158         urllib.request._opener = opener