]> git.phdru.name Git - bookmarks_db.git/blob - bkmk_objects.py
Fix(Py3): Fix import from `urllib`
[bookmarks_db.git] / bkmk_objects.py
1 """Objects to represent bookmarks.html structure
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-2023 PhiloSoft Design"
9 __license__ = "GNU GPL"
10
11 __all__ = ['Folder', 'Bookmark', 'Ruler', 'Walker', 'Writer', 'Robot',
12            'InverseLinker', 'Linear', 'make_linear', 'make_tree', 'break_tree',
13            'quote_title', 'unquote_title', 'parse_params', 'set_params',
14            ]
15
16
17 import os
18 try:
19     from urllib.parse import quote, \
20         splittype, splithost, splituser, splitpasswd, \
21         splitport
22 except ImportError:
23     from urllib import quote, \
24         splittype, splithost, splituser, splitpasswd, \
25         splitport
26
27 BKMK_FORMAT = os.environ.get("BKMK_FORMAT", "MOZILLA")
28
29
30 class Folder(list):
31     isFolder = 1
32     isBookmark = 0
33
34     def __init__(self, add_date=None, comment='', last_modified=None):
35         super(Folder, self).__init__()
36         self.comment = comment
37         self.add_date = add_date
38         self.last_modified = last_modified
39
40     def walk_depth(self, walker, level=0):
41         if hasattr(self, "header"):  # root folder
42             prune = 0
43             walker.root_folder(self)
44         else:
45             prune = walker.prune_folder(self)
46             if not prune:
47                 walker.start_folder(self, level)
48
49         if not prune:
50             for object in self:
51                 if object.isFolder:
52                     object.walk_depth(walker, level+1)
53                 elif object.isBookmark:
54                     walker.bookmark(object, level)
55                 else:
56                     walker.ruler(object, level)
57
58             walker.end_folder(self, level)
59
60
61 class Bookmark(object):
62     isFolder = 0
63     isBookmark = 1
64
65     def __init__(self, href, add_date, last_visit=None, last_modified=None,
66                  keyword=None, comment='', icon_href=None, icon=None,
67                  charset=None, parser_charset=None):
68         protocol, request = splittype(href)
69         user, password, port = None, None, None
70         host, path = splithost(request)
71         if host:
72             user, host = splituser(host)
73             if user:
74                 user, password = splitpasswd(user)
75             host, port = splitport(host)
76             if port: port = int(port)
77
78         if protocol == 'place':
79             href = protocol + ":"
80         else:
81             href = protocol + "://"
82         if user:
83             href += quote(user)
84             if password:
85                 href += ':' + quote(password)
86             href += '@'
87         if host:
88             href += host.decode(parser_charset or 'utf-8').encode('idna')
89             if port:
90                 href += ':%d' % port
91         if path:
92             href += path
93
94         self.href = href
95         self.add_date = add_date
96         self.last_visit = last_visit
97         self.last_modified = last_modified
98         self.keyword = keyword
99         self.comment = comment
100         self.icon_href = icon_href
101         self.icon = icon
102         self.charset = charset
103
104
105 class Ruler(object):
106     isFolder = 0
107     isBookmark = 0
108
109
110 class Walker(object):
111     """
112        Interface class. Any instance that will be passed to Folder.walk_depth
113        may be derived from this class. It is not mandatory - unlike Java
114        Python does not require interface classes; but it is convenient to have
115        some methods predefined to no-op, in case you do not want to
116        provide end_folder etc.
117     """
118
119     def root_folder(self, r):
120         pass
121
122     def start_folder(self, f, level):
123         pass
124
125     def end_folder(self, f, level):
126         pass
127
128     def bookmark(self, b, level):
129         pass
130
131     def ruler(self, r, level):
132         pass
133
134     def prune_folder(self, folder):
135         return 0
136
137
138 class Writer(Walker):
139     def __init__(self, outfile, prune=None):
140         self.outfile = outfile
141         self.prune = prune
142
143     def prune_folder(self, folder):
144         return self.prune == folder.name
145
146
147 class Robot(object):
148     def __init__(self, log):
149         self.log = log
150
151     def stop(self):
152         pass  # Nothing to do on cleanup
153
154
155 # Helper class to make inverese links (nodes linked to their parent)
156 class InverseLinker(Walker):
157     def root_folder(self, r):
158         self.parent_stack = [r]
159
160     def start_folder(self, f, level):
161         f.parent = self.parent_stack[-1]
162         # Push the folder onto the stack of parents
163         self.parent_stack.append(f)
164
165     def end_folder(self, f, level):
166         del self.parent_stack[-1]   # Pop off the stack
167
168     def bookmark(self, b, level):
169         b.parent = self.parent_stack[-1]
170
171     def ruler(self, r, level):
172         r.parent = self.parent_stack[-1]
173
174
175 # Helper class to make linear represenatation of the tree
176 class Linear(Walker):
177     def root_folder(self, r):
178         r.linear = [r]
179         self.linear = r.linear
180
181     def add_object(self, object):
182         self.linear.append(object)
183
184     def start_folder(self, f, level):
185         self.add_object(f)
186
187     def bookmark(self, b, level):
188         self.add_object(b)
189
190     def ruler(self, r, level):
191         self.add_object(r)
192
193
194 # Helper - make linked linear represenatation of the tree,
195 # suitable to be stored in sequential storage.
196 def make_linear(root_folder):
197     linker = InverseLinker()
198     root_folder.walk_depth(linker)
199
200     linear = Linear()
201     root_folder.walk_depth(linear)
202
203
204 # Helper, opposite of make_linear -
205 # make a tree from the linked linear representation.
206 def make_tree(linear):
207     root_folder = linear[0]
208     del linear[0]
209
210     for object in linear:
211         object.parent.append(object)
212
213     return root_folder
214
215
216 def break_tree(linear):
217     del linear[0]
218
219     for object in linear:
220         del object.parent
221
222
223 def quote_title(title):
224     if BKMK_FORMAT == "MOZILLA":
225         title = title.replace("'", "&#39;")
226     return title
227
228
229 def unquote_title(title):
230     if BKMK_FORMAT == "MOZILLA":
231         from HTMLParser import HTMLParser
232         title = HTMLParser().unescape(
233             title.replace("&amp;", '&').decode('utf-8'))
234         title = title.encode('utf-8').replace("&#39;", "'")
235     return title
236
237
238 def parse_params(param_str):
239     params = param_str.split(':')
240     main_param = params.pop(0)
241     param_list = {}
242     for param in params:
243         key, value = param.split('=', 1)
244         param_list[key] = value
245     return main_param, param_list
246
247
248 def set_params(obj, params):
249     if hasattr(params, "items"):
250         params = params.items()
251     for key, value in params:
252         setattr(obj, key, value)