Merge lp:~hernejj/ubuntu-qa-tools/better_hugday into lp:ubuntu-qa-tools
- better_hugday
- Merge into master
Status: | Needs review |
---|---|
Proposed branch: | lp:~hernejj/ubuntu-qa-tools/better_hugday |
Merge into: | lp:ubuntu-qa-tools |
Diff against target: |
1040 lines (+834/-56) 11 files modified
README (+19/-6) debian/changelog (+11/-0) hugday-tools/hugday (+113/-49) hugday_lib/cookie_lib/ChromeCookieLoader.py (+63/-0) hugday_lib/cookie_lib/CookieFinder.py (+149/-0) hugday_lib/cookie_lib/CookieLoader.py (+209/-0) hugday_lib/cookie_lib/EpiphanyCookieLoader.py (+53/-0) hugday_lib/cookie_lib/FirefoxCookieLoader.py (+60/-0) hugday_lib/cookie_lib/KonquerorCookieLoader.py (+111/-0) hugday_lib/cookie_lib/SeamonkeyCookieLoader.py (+43/-0) setup.py (+3/-1) |
To merge this branch: | bzr merge lp:~hernejj/ubuntu-qa-tools/better_hugday |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Markus Korn | Pending | ||
Ubuntu Bug Control | Pending | ||
Review via email: mp+23257@code.launchpad.net |
Commit message
Adds code to do the following:
- Supported browsers: Firefox, Epiphany, Seamonkey, Google Chrome, Konqueror
- Automatic cookie detection for all supported browsers.
- User no longer has to close the browser to find cookies.
- Cookie file format automatically detected when --cookie is given
- User can specify a browser (--browser) and cookie is automatically found within that browser's cookie files.
- Some simple code cleanup and extra commenting in hugday tool.
Description of the change
I've been working on some improvements to the hugday tool (part of the ubuntu-qa-tools) that should make hugday easier to use. Making hugday easier to use will lower the "barrier of entry" for new comers who are interested in participating in the Ubuntu Bug Days. And anything we can do to help people help Ubuntu is a good idea in my book ;).
I've added code to allow for more robust cookie handling by the "hugday init" sub-command. Here is a list of features currently implemented in my branch:
- Supported browsers: Firefox, Epiphany, Seamonkey, Google Chrome, Konqueror
- Automatic cookie detection for all supported browsers.
- User no longer has to close the browser to find cookies.
- Cookie file format automatically detected when --cookie is given
- User can specify a browser (--browser) and cookie is automatically found within that browser's cookie files.
- Some simple code cleanup and extra commenting in hugday tool.
If this is something that bug-control is interested in I'd be happy to have these features merged back into the official ubuntu-
Jason J. Herne (hernejj) wrote : | # |
> This sounds really exciting! You have followed the proper process for
> requesting such a merge but unfortunately I don't think we can get this into
> Lucid since we are past feature freeze. I'm requesting that the original
> author look at your changes too.
Hi Brian. Thanks for your interest. It's not problem for me if this has to wait for Maverick Meerkat.. I understand how the schedule works :). Once code review is complete I can always put an updated package in my PPA. Then others can test the changes if they are interested.
- 339. By Jason J. Herne
-
- Added Google Chromium support
Jason J. Herne (hernejj) wrote : | # |
One note I'd like to make for the reviewers.
The hugday code that I replaced was searching for two cookies: MOIN_SESSION and MOIN_ID. My new code only looks for MOIN_SESSION. I'm not sure what MOIN_ID is or where it comes from but I could not find any instances of it in my testing.
If there is a valid reason to continue to search for MOIN_ID as well as MOIN_SESSION then please let me know and I'll modify this branch to look for MOIN_ID if MOIN_SESSION is not found.
Jason J. Herne (hernejj) wrote : | # |
Just a friendly poke to find out what the status is on this review. :)
Markus Korn (thekorn) wrote : | # |
Hey Jason,
I'm sorry, I totally forgot about this merge proposal. Reviewing this huge diff will take some time, but I hope to get it done until next weekend.
Just a side note, have you ever thought about moving your cookielib into a separate project/package? Because it seems to be useful for other python scripts too, and as a side effect it would reduce the size of this diff.
Sorry again,
and thanks for the reminder,
have a nice day,
Markus
Jason J. Herne (hernejj) wrote : | # |
On Mon, Aug 2, 2010 at 5:14 PM, Markus Korn <email address hidden> wrote:
> Hey Jason,
> I'm sorry, I totally forgot about this merge proposal. Reviewing this huge
> diff will take some time, but I hope to get it done until next weekend.
>
> Just a side note, have you ever thought about moving your cookielib into a
> separate project/package? Because it seems to be useful for other python
> scripts too, and as a side effect it would reduce the size of this diff.
>
> Sorry again,
> and thanks for the reminder,
> have a nice day,
> Markus
> --
>
> https:/
> You are the owner of lp:~hernejj/ubuntu-qa-tools/better_hugday.
>
I had considered separating out the cookielib code but I'm not sure what
other use it has at the moment. Seems preemptive to do it without another
project that can use it.
It can always be done as follow on work when another need arises. It would
not be that hard to do.
--
- Jason J. Herne (<email address hidden>)
Unmerged revisions
- 339. By Jason J. Herne
-
- Added Google Chromium support
- 338. By Jason J. Herne
-
- Fixed errors in usage information for hugday
- Added previously forgotten cookie loader file
- Updated readme and changelog - 337. By Jason J. Herne
-
- ChromeCookieLoa
der.py: Fixed bug, need to check that cookies were loaded BEFORE we iterate through them ;)
- CookieFinder.py: Added missing import of os module
- CookieFinder.py: Added Konqueror support
- CookieFinder.py: Change browser string comparison to always use lower case
- CookieFinder.py: Fixed bug, In get_cookeies(), don't return if a cookie loader has no cookies, need to check ALL cookie loaders.
- KonquerorCookieLoader. py: Initial import. - 336. By Jason J. Herne
-
- User can now specify exactly which browser to pull cookie file from: --browser=
<firefox| seamonkey| chrome| epiphany> - 335. By Jason J. Herne
-
- bug fix: Expand ~ in path names given by user
- Fixed error mesasge text
- Added print stmt to output the CookieLoader we're using - 334. By jason <jason@foobarbaz>
-
- Fixed timestamp handling in Chrome
- 333. By jason <jason@foobarbaz>
-
- Removed unneeded comment from SeamonkeyLoader
- 332. By Jason J. Herne
-
- Properly close connection to sqlite database files
- 331. By Jason J. Herne
-
- Rewrote existing get_credentials code to use CookieFinder when the user specifies a cookie file on the command line.
- 330. By Jason J. Herne
-
- Bug fix: Check that path exists in cookier_scanner routine before scanning it.
- Bug fix: Uncomment exception handling code (originally commented for testing purposes.)
- Cookie Finder/Loader now provides a method to detect if cookie files were loaded.
- All cookie loaders have been updated to support the above method.
- hugday now uses the above method to give a proper error message if no cookie files were found.
Preview Diff
1 | === modified file 'README' |
2 | --- README 2009-02-10 09:50:50 +0000 |
3 | +++ README 2010-04-13 16:46:31 +0000 |
4 | @@ -93,13 +93,26 @@ |
5 | $ hugday list --date 20090101 (for hugday at 2009-01-01) |
6 | |
7 | Before actually changing content of a wikipage the hugday tools needs |
8 | -to know the useres name and his wiki-id, 'hugday init' provides some |
9 | -options to get this information |
10 | - $ hugday init --user <USER> --cookie <PATH> (try to get the MOIN_ID |
11 | - out of the mozilla-like cookie file and use USER for entries in |
12 | - the 'Triager' column) |
13 | +to know the user's Launchpad ID and wiki-id, 'hugday init' provides some |
14 | +options to get this information: |
15 | + |
16 | +1) Let hugday automatically detect the cookie containing the wiki-id. |
17 | + |
18 | + $ hugday init --user <USER> |
19 | + |
20 | +2) Tell hugday which web brower you most recently used to log on to |
21 | + wiki.ubuntu.com |
22 | + |
23 | + $ hugday init --user <USER> --browser <firefox|seamonkey|chrome|epiphany|konqueror> |
24 | + |
25 | +3) Provide the path to a cookie file that contains a cookie for ".wiki.ubuntu.com" |
26 | + that has the name "MOIN_SESSION". |
27 | + |
28 | + $ hugday init --user <USER> --cookie <PATH> |
29 | + |
30 | +4) Provide the wiki-id directly. |
31 | + |
32 | $ hugday init --user <USER> --wiki-id <MOINID> |
33 | - $ hugday init (interactive mode, not implemented yet) |
34 | |
35 | To mark a bug as DONE run: |
36 | $ hugday close 123456 |
37 | |
38 | === modified file 'debian/changelog' |
39 | --- debian/changelog 2010-03-31 19:17:37 +0000 |
40 | +++ debian/changelog 2010-04-13 16:46:31 +0000 |
41 | @@ -1,3 +1,14 @@ |
42 | +ubuntu-qa-tools (0.1.4.4) lucid; urgency=low |
43 | + |
44 | + * Improvements to cookie processing in hugday tool. |
45 | + - Supported browsers: Firefox, Epiphany, Seamonkey, Google Chrome, Konqueror |
46 | + - Automatic cookie detection for all supported browsers. |
47 | + - user no longer has to close the browser to find cookies. |
48 | + - Cookie file format automatically detected when --cookie is given |
49 | + - User can specify a browser (--browser) and cookie is automatically found within that browser's cookie files. |
50 | + |
51 | + -- Jason J. Herne <hernejj@gmail.com> Thu, 8 Apr 2010 11:54:00 -0400 |
52 | + |
53 | ubuntu-qa-tools (0.1.4.3) lucid; urgency=low |
54 | |
55 | * Fix to work with launchpad's multi-version code. |
56 | |
57 | === modified file 'hugday-tools/hugday' |
58 | --- hugday-tools/hugday 2009-03-18 20:25:21 +0000 |
59 | +++ hugday-tools/hugday 2010-04-13 16:46:31 +0000 |
60 | @@ -19,13 +19,13 @@ |
61 | # |
62 | # ################################################################## |
63 | |
64 | -import sys |
65 | -import re |
66 | -import os |
67 | -import urllib |
68 | -import urllib2 |
69 | -import libxml2 |
70 | +import sys, os, shutil, time, re |
71 | +import urllib, urllib2, libxml2 |
72 | import cookielib |
73 | +import webbrowser |
74 | + |
75 | +from hugday_lib.cookie_lib.CookieFinder import CookieFinder |
76 | + |
77 | try: |
78 | import sqlite3 as sqlite |
79 | except ImportError: |
80 | @@ -55,11 +55,13 @@ |
81 | |
82 | RE_BUGS = "%s(%%s)%s" %(re.escape("https://bugs.launchpad.net/bugs/"), re.escape("|")) |
83 | |
84 | - |
85 | +###################################################### |
86 | +# Parses Command Line using Python's optparse module # |
87 | +###################################################### |
88 | class CmdOptions(OptionParser): |
89 | |
90 | USAGE = ( |
91 | - "\t%prog init --user <USERNAME> [--wiki-id <MOINID>] [--cookie <PATH>]\n" |
92 | + "\t%prog init --user <USERNAME> [--wiki-id <MOINID>] [--cookie <PATH>] [--browser <firefox|seamonkey|chrome|epiphany|konqueror>]\n" |
93 | "\t%prog current [--remember]\n" |
94 | "\t%prog close <Bugs> [--day <ID>] [--kde] [--user <TRIAGER>]\n" |
95 | "\t%prog list [--day <ID>] [--kde] [--filter <open|done>]\n" |
96 | @@ -80,13 +82,15 @@ |
97 | make_option("--wiki-id", action="store", type="string", dest="wiki_id", |
98 | help="the users MOIN-ID"), |
99 | make_option("--cookie", action="store", type="string", dest="cookie", |
100 | - help="path to users mozilla-like cookie file") |
101 | + help="path to users cookie file"), |
102 | + make_option("--browser", action="store", type="string", dest="browser", |
103 | + help="Web browser that will be searched for the wiki-id cookie") |
104 | ) |
105 | |
106 | TOOLS = { |
107 | "init": ( |
108 | ("user", ), |
109 | - ("wiki_id", "cookie"), |
110 | + ("wiki_id", "cookie", "browser"), |
111 | ), |
112 | "current": ( |
113 | tuple(), |
114 | @@ -121,7 +125,7 @@ |
115 | else: |
116 | self.error("Only one sub-tool allowed") |
117 | if tool == "close" and not options.bugs: |
118 | - self.error("at elast one bug needed") |
119 | + self.error("at least one bug needed") |
120 | given_options = set(i for i, k in self.defaults.iteritems() if not getattr(options, i) == k) |
121 | needed_options = set(CmdOptions.TOOLS[tool][0]) - given_options |
122 | if needed_options: |
123 | @@ -250,44 +254,93 @@ |
124 | title = b.xpathEval("td[2]")[0].content.strip() |
125 | triager = b.xpathEval("td[3]")[0].content.strip() or None |
126 | yield (nr, title, done, triager, section) |
127 | - |
128 | -def get_credentials(user, wiki_id=None, cookie=None): |
129 | + |
130 | +################################################################################ |
131 | +# Obtain the user's moin_id. This will allow us to authenticate to the wiki in # |
132 | +# order to make updates. The user may either provide the moin_id directly or # |
133 | +# the user provides their browser cookie file from which we can read the ID. # |
134 | +################################################################################ |
135 | +def get_credentials(user, wiki_id=None, cookie_file_path=None, browser_str=None): |
136 | + |
137 | + # User provided their moin_id string. Just save it in the config file. |
138 | if wiki_id: |
139 | config = update_config(user=user, moin_id=wiki_id) |
140 | - elif cookie: |
141 | - # parse cookie |
142 | - # this includes a workaround for new cookie format |
143 | - result = None |
144 | - try: |
145 | - cj = cookielib.MozillaCookieJar() |
146 | - cj.load(os.path.expanduser(cookie)) |
147 | - result = [i.value for i in cj if i.domain == "wiki.ubuntu.com" \ |
148 | - and i.name in ("MOIN_ID", "MOIN_SESSION")] |
149 | - except cookielib.LoadError: |
150 | - if not sqlite: |
151 | - raise TypeError(("Try to read cookiefile, cannot handle " |
152 | - "format of '%s'" %cookie)) |
153 | - try: |
154 | - con = sqlite.connect(cookie) |
155 | - con.row_factory = sqlite.Row |
156 | - |
157 | - cur = con.cursor() |
158 | - cur.execute(("select value from moz_cookies where " |
159 | - "(name=? or name=?) and host=?"), |
160 | - ("MOIN_SESSION", "MOIN_ID", "wiki.ubuntu.com",)) |
161 | - #TODO: add check for expired cookie |
162 | - result = [i["value"] for i in cur] |
163 | - except sqlite.Error, e: |
164 | - raise TypeError(("Error while trying to read cookie in " |
165 | - "sql format, cannot handle " |
166 | - "format of '%s'" %cookie)) |
167 | - if not result: |
168 | - raise ValueError("No cookie with name 'MOIN_ID' found in '%s'" %cookie) |
169 | - config = update_config(user=user, moin_id=result.pop()) |
170 | + |
171 | + # User provided their cookie file path. Attempt to read the MOIN_SESSION |
172 | + # cookie from the cookie file. |
173 | + elif cookie_file_path: |
174 | + |
175 | + cookie_file_path = os.path.expanduser(cookie_file_path) |
176 | + |
177 | + # Ensure cookie file exists |
178 | + if (not os.path.exists(cookie_file_path)): |
179 | + raise RuntimeError("The specified cookie file does not exist: '%s'" %cookie_file_path) |
180 | + |
181 | + # Load cookie file |
182 | + cookie_finder = CookieFinder() |
183 | + cookie_file_valid = cookie_finder.set_cookie_file(cookie_file_path) |
184 | + |
185 | + # Was cookie file valid? |
186 | + if (not cookie_file_valid): |
187 | + raise RuntimeError("The specified file is not a valid cookie file from a supported browser: '%s'. " %cookie_file_path) |
188 | + |
189 | + # Get desired cookie |
190 | + cookie = cookie_finder.get_newest_cookie(domain=".wiki.ubuntu.com", name="MOIN_SESSION", ignore_expired=True) |
191 | + |
192 | + # Did we find a matching cookie? |
193 | + if (not cookie): |
194 | + raise RuntimeError("Unable to find the MOIN_SESSION cookie in the given cookie file: '%s'. Please ensure you've logged in to wiki.ubuntu.com with this browser." %cookie_file_path ) |
195 | + |
196 | + # Found cookie! Update config file. |
197 | + config = update_config(user=user, moin_id=cookie['value']) |
198 | + print "Cookie detection succeeded." |
199 | + |
200 | + # User provided a string telling us which browser they wish to |
201 | + # search to find the wiki-id cookie. |
202 | + elif browser_str: |
203 | + |
204 | + # See if CookieFinder supports the user's browser |
205 | + cookie_finder = CookieFinder() |
206 | + browser_supported = cookie_finder.set_browser(browser_str) |
207 | + |
208 | + if (not browser_supported): |
209 | + raise RuntimeError("unsupported browser '%s'" % browser_str) |
210 | + |
211 | + # Get desired cookie. |
212 | + cookie = cookie_finder.get_newest_cookie(domain=".wiki.ubuntu.com", name="MOIN_SESSION", ignore_expired=True) |
213 | + |
214 | + # Did the cookie loader find valid files to read cookies from? |
215 | + if (not cookie_finder.has_cookie_files()): |
216 | + raise RuntimeError("No valid cookie files were found for the given browser.") |
217 | + |
218 | + # Did we find a matching cookie? |
219 | + if (not cookie): |
220 | + raise RuntimeError("Unable to find the MOIN_SESSION cookie in the given browser's cookie files: '%s'. Please ensure you've logged in to wiki.ubuntu.com with this browser." %browser_str) |
221 | + |
222 | + # Found cookie! Update config file. |
223 | + config = update_config(user=user, moin_id=cookie['value']) |
224 | + print "Cookie detection succeeded." |
225 | + |
226 | + # Only --user option was specified (--cookie & --wiki-id were omitted) |
227 | + # Do auto detection of cookie file. |
228 | else: |
229 | - # interactive mode or openid, TBD |
230 | - raise NotImplementedError |
231 | - |
232 | + |
233 | + # Try and automatically find the cookie we need |
234 | + cookie_finder = CookieFinder() |
235 | + cookie = cookie_finder.get_newest_cookie(domain=".wiki.ubuntu.com", name="MOIN_SESSION", ignore_expired=True) |
236 | + |
237 | + # Did the cookie loader find valid files to read cookies from? |
238 | + if (not cookie_finder.has_cookie_files()): |
239 | + raise RuntimeError("No valid cookie files were found. You do not seem to be using any supported browsers.") |
240 | + |
241 | + # Did we find a matching cookie? |
242 | + if (not cookie): |
243 | + raise RuntimeError("Unable to detect the MOIN_SESSION cookie. Please ensure you've logged in to wiki.ubuntu.com with a supported browser.") |
244 | + |
245 | + # Write the obtained moin_id to the user's configuration file |
246 | + print "Cookie detection succeeded." |
247 | + config = update_config(user=user, moin_id=cookie['value']) |
248 | + |
249 | def build_request(url, moin_id, proxy=None, data=None): |
250 | request = urllib2.Request(url, data) |
251 | #~ # moinmoin < 1.6 |
252 | @@ -379,18 +432,27 @@ |
253 | url = "%s/KDE" %url |
254 | return url |
255 | |
256 | + |
257 | +########################## |
258 | +# Main Program # |
259 | +# Execution starts here # |
260 | +########################## |
261 | def main(): |
262 | + |
263 | cmdoptions = CmdOptions() |
264 | tool, options = cmdoptions.parse_args() |
265 | + |
266 | if tool == "list-days": |
267 | print "list of all hugdays, latest first:" |
268 | for day in parse_all_hugdays(): |
269 | print "\t* https://wiki.ubuntu.com/UbuntuBugDay/%s" %day |
270 | + |
271 | elif tool == "current": |
272 | current = get_current() |
273 | print current |
274 | if options.remember: |
275 | update_config(current=current) |
276 | + |
277 | elif tool == "list": |
278 | url = get_url(options.day, options.kde) |
279 | i = None |
280 | @@ -399,15 +461,17 @@ |
281 | if i is not None: |
282 | print "="*50 |
283 | print "Total:", i + 1 |
284 | + |
285 | elif tool == "init": |
286 | - get_credentials(options.user, options.wiki_id, options.cookie) |
287 | + get_credentials(options.user, options.wiki_id, options.cookie, options.browser) |
288 | + |
289 | elif tool == "close": |
290 | url = get_url(options.day, options.kde) |
291 | close_bugs(options.bugs, url, options.user) |
292 | + |
293 | + # FIXME: Shouldn't this be impossible since we detect this in cmdoptions.parse_args()? |
294 | else: |
295 | raise RuntimeError("unknown tool '%s'" %tool) |
296 | - |
297 | - |
298 | |
299 | if __name__ == "__main__": |
300 | try: |
301 | |
302 | === added directory 'hugday_lib' |
303 | === added file 'hugday_lib/__init__.py' |
304 | === added directory 'hugday_lib/cookie_lib' |
305 | === added file 'hugday_lib/cookie_lib/ChromeCookieLoader.py' |
306 | --- hugday_lib/cookie_lib/ChromeCookieLoader.py 1970-01-01 00:00:00 +0000 |
307 | +++ hugday_lib/cookie_lib/ChromeCookieLoader.py 2010-04-13 16:46:31 +0000 |
308 | @@ -0,0 +1,63 @@ |
309 | +#!/usr/bin/python |
310 | + |
311 | +import os, shutil |
312 | + |
313 | +from CookieLoader import CookieLoader |
314 | + |
315 | +# This is a subclass of CookieLoader. Please see CookieLoader for more details. |
316 | +class ChromeCookieLoader(CookieLoader): |
317 | + |
318 | + # browser name |
319 | + browser_str = "chrome" |
320 | + |
321 | + # List of directories to search for cookie files. |
322 | + cookie_file_search_path = [ os.path.join( "~" , ".config" , "google-chrome" ) , |
323 | + os.path.join( "~" , ".config" , "chromium" ) |
324 | + ] |
325 | + |
326 | + # List of valid cookie file names. |
327 | + cookie_file_names = [ "Cookies" ] |
328 | + |
329 | + # attempts to load cookies from the given file. |
330 | + # If cookie_file_path points to a valid readable cookie file that can be |
331 | + # parsed then this function will return True. If it returns false then |
332 | + # the specified cookie file is not readable by this cookie finder. |
333 | + def load_cookies_from_file(self, cookie_file_path): |
334 | + |
335 | + # If the given cookie file does not exist, give up. |
336 | + if (not os.path.exists(cookie_file_path)): |
337 | + return False |
338 | + |
339 | + # If we attempt to use the cookie file while the browser is running |
340 | + # we will get errors. Only one process is allowed to use it at a time. |
341 | + # To get around this we can simply copy it to /tmp and then use it. |
342 | + tmp_cookie_file_path = os.path.join( "/" , "tmp" , os.path.basename(cookie_file_path) ) |
343 | + shutil.copyfile(cookie_file_path, tmp_cookie_file_path) |
344 | + |
345 | + # Chrome cookie files are sqlite databases |
346 | + cookies = self.load_sqlite_cookie_file(tmp_cookie_file_path, table_name="cookies", domain_key="host_key", name_key="name", value_key="value", expire_time_key="expires_utc") |
347 | + |
348 | + # If the load failed, then we've been given an invalid cookie file. |
349 | + if (not cookies): |
350 | + os.remove(tmp_cookie_file_path) |
351 | + return False |
352 | + |
353 | + # Fix timestamps in Google Chrome cookies!! |
354 | + # Chrome stores cookies with a 64-bit integer that represents the number of Microseconds that have passed since 1/1/1601. So it's not quite a |
355 | + # Unix timestamp. See top comments in this file for more info: http://src.chromium.org/svn/trunk/src/base/time_posix.cc |
356 | + # |
357 | + # The basic proceedure for converting a Chrome timestamp into a Unix timestap is this: unix_time = (chrome_time / 1000000) - 11644473600 |
358 | + for cookie in cookies: |
359 | + cookie['expire_time'] = (cookie['expire_time'] / 1000000) - 11644473600 |
360 | + |
361 | + # Loop over all cookies and add them to our cookies list |
362 | + for cookie in cookies: |
363 | + self.cookies.append(cookie) |
364 | + |
365 | + # Erase temporary cookie file. |
366 | + os.remove(tmp_cookie_file_path) |
367 | + |
368 | + # We were able to read cookies from this file. |
369 | + self.found_valid_cookie_file = True |
370 | + return True |
371 | + |
372 | |
373 | === added file 'hugday_lib/cookie_lib/CookieFinder.py' |
374 | --- hugday_lib/cookie_lib/CookieFinder.py 1970-01-01 00:00:00 +0000 |
375 | +++ hugday_lib/cookie_lib/CookieFinder.py 2010-04-13 16:46:31 +0000 |
376 | @@ -0,0 +1,149 @@ |
377 | +from FirefoxCookieLoader import FirefoxCookieLoader |
378 | +from EpiphanyCookieLoader import EpiphanyCookieLoader |
379 | +from SeamonkeyCookieLoader import SeamonkeyCookieLoader |
380 | +from ChromeCookieLoader import ChromeCookieLoader |
381 | +from KonquerorCookieLoader import KonquerorCookieLoader |
382 | + |
383 | +import time, os |
384 | +from datetime import datetime |
385 | + |
386 | +# Used to find/access cookies for many different web browsers. You can point |
387 | +# CookieFinder to a specific cookie file (set_cookie_file) or you can simply |
388 | +# perform a search (get_cookies/get_newest_cookie) and CookieFinder will |
389 | +# automatically scan every cookie file it can find for every browser it supports. |
390 | +class CookieFinder: |
391 | + |
392 | + # List of cookie loaders. Each one scans a different browser's cookie file(s). |
393 | + cookie_loaders = [] |
394 | + |
395 | + # Creates a new CookieFinder. |
396 | + def __init__(self): |
397 | + |
398 | + # Populate cookie loaders, one loader for each browser we support |
399 | + self.cookie_loaders.append( FirefoxCookieLoader() ) |
400 | + self.cookie_loaders.append( EpiphanyCookieLoader() ) |
401 | + self.cookie_loaders.append( SeamonkeyCookieLoader() ) |
402 | + self.cookie_loaders.append( ChromeCookieLoader() ) |
403 | + self.cookie_loaders.append( KonquerorCookieLoader() ) |
404 | + |
405 | + # Tells CookieFinder that you only wish to search the given browser for |
406 | + # cookies. All CookieLoaders will be search until one is found that matches |
407 | + # the given browser string. Once that loader is found all others will be |
408 | + # removed from our loaders list. All subsequent operations using this CookieFiner |
409 | + # will only invoke the one loader. |
410 | + # Returns True if a suitable CookieLoader was found. Returns False otherwise. |
411 | + def set_browser(self, browser_str): |
412 | + |
413 | + # Avoid case comparison problems |
414 | + browser_str = browser_str.lower() |
415 | + |
416 | + # Locate a cookie loader for the browser the user specified |
417 | + for cookie_loader in self.cookie_loaders: |
418 | + |
419 | + if ( cookie_loader.browser_str == browser_str): |
420 | + |
421 | + # We found a cookie loader that supports the user's browser. |
422 | + # Throw away the rest of the loaders. |
423 | + self.cookie_loaders = [cookie_loader] |
424 | + |
425 | + # Return true and the caller will be most pleased :) |
426 | + return True |
427 | + |
428 | + # We got to the end of the list and none of our cookie loaders match. |
429 | + return False |
430 | + |
431 | + # Tells CookieFinder that you only want to search the given file for cookies. |
432 | + # If cookie_file_path points to a valid readable cookie file that can be |
433 | + # parsed by CookieFinder then this function will return True. If it returns |
434 | + # false then the specified cookie file is not readable by CookieFinder. |
435 | + def set_cookie_file(self, cookie_file_path): |
436 | + |
437 | + # Ensure ~ is properly handled in the path |
438 | + cookie_file_path = os.path.expanduser(cookie_file_path) |
439 | + |
440 | + # Locate a cookie loader that can read this cookie file. |
441 | + for cookie_loader in self.cookie_loaders: |
442 | + |
443 | + if ( cookie_loader.load_cookies_from_file(cookie_file_path) ): |
444 | + |
445 | + # Since we found a cookie loader that can read this file there is |
446 | + # no need to keep the rest around. |
447 | + print "Using Cookie Loader " + str(cookie_loader.__module__) |
448 | + self.cookie_loaders = [cookie_loader] |
449 | + |
450 | + # We found a cookie loader that will read the caller's cookie |
451 | + # file. Return true and the caller will be most pleased :) |
452 | + return True |
453 | + |
454 | + # We got to the end of the list and none of our cookie loaders read the file. |
455 | + return False |
456 | + |
457 | + # Returns a list of cookies found by this CookieFinder that match the given criteria. |
458 | + # If caller does not specify any of the search criteria then all cookies will be |
459 | + # returned. An empty list is returned if no matching cookies were found. |
460 | + def get_cookies(self, domain=None, name=None, ignore_expired=False): |
461 | + |
462 | + cookies = [] |
463 | + |
464 | + for cookie_loader in self.cookie_loaders: |
465 | + |
466 | + # Ensure this cookie loader has its cookies loaded. |
467 | + found_cookies = cookie_loader.load_cookies() |
468 | + |
469 | + # If no cookies were found go try next loader |
470 | + if(not found_cookies): continue |
471 | + |
472 | + # Look for cookies matching the caller's search criteria. |
473 | + for cookie in cookie_loader.cookies: |
474 | + # If user specified a domain, skip this cookie if its domain does not match |
475 | + # The domain name is converted to lowercase to avoid comparison failue. |
476 | + # Any leading '.' character is also removed from domain name to prevent comparison failure. |
477 | + if domain and (not cookie['domain'].lower().lstrip(".") == domain.lower().lstrip(".")): |
478 | + continue |
479 | + |
480 | + # If user specified a name, skip this cookie if its name does not match |
481 | + if name and (not cookie['name'].lower() == name.lower()): |
482 | + continue |
483 | + |
484 | + # If user wants: skip this cookie if it has expired. |
485 | + if ignore_expired and (time.time() > cookie['expire_time']): |
486 | + continue |
487 | + |
488 | + # This cookie matches, add it to the list. |
489 | + #print ( " Cookie: name=%s value=%s expires=%s" % (cookie['name'], cookie['value'], datetime.fromtimestamp(cookie['expire_time'])) ) |
490 | + cookies.append(cookie) |
491 | + |
492 | + # Return cookie list |
493 | + return cookies |
494 | + |
495 | + # Returns the newest cookie found by this CookieFinder that match the given criteria. |
496 | + # The "newest" cookie is the cookie with the latest expiration date. This is really |
497 | + # only meaningful when comparing the same cookie but from different browser files. |
498 | + # It is useful when you just want ONE copy (the latest and greatest) of a specific |
499 | + # cookie but you're not sure which browser or cookie file has it. |
500 | + # None is returned if no matching cookie is found. |
501 | + def get_newest_cookie(self, domain=None, name=None, ignore_expired=False): |
502 | + |
503 | + # Get list of all matching cookies |
504 | + cookies = self.get_cookies(domain, name, ignore_expired) |
505 | + if (len(cookies) == 0): return None |
506 | + |
507 | + newest_cookie = cookies[0] |
508 | + |
509 | + # Look for the one with the largest expire time. |
510 | + for cookie in cookies: |
511 | + if ( cookie['expire_time'] > newest_cookie['expire_time'] ): |
512 | + newest_cookie = cookie |
513 | + |
514 | + return newest_cookie |
515 | + |
516 | + # Returns True if this CookieFinder was able to find/load at least one cookie file. |
517 | + # This is useful to distinguish between thw following cases: 1) No cookie files are |
518 | + # found to load cookies from. 2) No matching cookies were found inside the cookie file(s). |
519 | + def has_cookie_files(self): |
520 | + |
521 | + for cookie_loader in self.cookie_loaders: |
522 | + if (cookie_loader.found_valid_cookie_file): return True |
523 | + |
524 | + return False |
525 | + |
526 | |
527 | === added file 'hugday_lib/cookie_lib/CookieLoader.py' |
528 | --- hugday_lib/cookie_lib/CookieLoader.py 1970-01-01 00:00:00 +0000 |
529 | +++ hugday_lib/cookie_lib/CookieLoader.py 2010-04-13 16:46:31 +0000 |
530 | @@ -0,0 +1,209 @@ |
531 | +#!/usr/bin/python |
532 | + |
533 | +import os, cookielib |
534 | + |
535 | +try: import sqlite3 as sqlite |
536 | +except ImportError: |
537 | + try: from pysqlite2 import dbapi2 as sqlite |
538 | + except ImportError: sqlite = None |
539 | + |
540 | +# base CookieLoader class. This class is not mean to be used directly. Instead |
541 | +# child classes should be created to implement cookie loading for a specific |
542 | +# browser. |
543 | +# |
544 | +# Used to find/access cookies for some web browser. You can specify |
545 | +# a specific cookie file (load_cookies_from_file) or you can let this class |
546 | +# automatically detect cookie files and load cookies from them (load_cookies). |
547 | +class CookieLoader: |
548 | + |
549 | + # Child classes should provide this string. |
550 | + # all lower case String (no spaces) that represents the name of the webbrowser that |
551 | + # this CookieLoader loads cookies for. |
552 | + browser_str = None |
553 | + |
554 | + # List of cookies found. This is not valid until either load_cookies_from_file |
555 | + # has been called and has returned True or load_cookies has been called and |
556 | + # has returned true. |
557 | + # |
558 | + # cookie objects on this list are dictionaries and have the following properties: |
559 | + # 'domain' : string - Host setting the cookie |
560 | + # 'name' : string - Cookie name |
561 | + # 'value' : string - Cookie value |
562 | + # 'expire_time' : integer - Expire date/time (Unix Time) |
563 | + cookies = [] |
564 | + |
565 | + # Indicates that at least one valid cookie file was found during loading. |
566 | + found_valid_cookie_file = False |
567 | + |
568 | + # Child classes should provide this list. |
569 | + # List of directories to search for cookie files (searched recursively). |
570 | + cookie_file_search_path = [] |
571 | + |
572 | + # Child classes should provide this list. |
573 | + # List of valid cookie file names. Files with these names will be search for cookies |
574 | + # if they are found in the paths listed in the cookie_file_search_path. |
575 | + cookie_file_names = [] |
576 | + |
577 | + # Child classes should provide this method. |
578 | + # attempts to load cookies from the given file. |
579 | + # If cookie_file_path points to a valid readable cookie file that can be |
580 | + # parsed then this function will return True. If it returns false then |
581 | + # the specified cookie file is not readable by this cookie finder. |
582 | + def load_cookies_from_file(self, cookie_file_path): |
583 | + raise NotImplementedError("This method cannot be used directly. Subclass CookieFinder and override this method.") |
584 | + |
585 | + # Loads all cookies that are found in all cookie files found. |
586 | + # Cookie files are searched for in the paths found in |
587 | + # the cookie_file_search_paths list. |
588 | + # Returns True if at least one cookie file can be loaded, False otherwise. |
589 | + def load_cookies(self): |
590 | + |
591 | + # If there are already cookies loaded, do not reload them, just return. |
592 | + # This prevents reloading the cookie list on every search since load_cookies |
593 | + # is called before every search. |
594 | + if (self.cookies): |
595 | + return ( len(self.cookies) > 0 ) |
596 | + |
597 | + # Initialize cookies to an empty list |
598 | + self.cookies = [] |
599 | + |
600 | + found_cookies = False |
601 | + |
602 | + # Find all cookie files |
603 | + cookie_file_paths = self.find_cookie_files() |
604 | + |
605 | + # Try to load each cookie file individually. If we can load any one of them then |
606 | + # we can declare succcess. |
607 | + for cookie_file_path in cookie_file_paths: |
608 | + |
609 | + # If cookies are found, they are added to this objects cookies list. |
610 | + found_cookies = self.load_cookies_from_file(cookie_file_path) |
611 | + |
612 | + return found_cookies |
613 | + |
614 | + ###################################################### |
615 | + ################# Internal Functions ################# |
616 | + ###################################################### |
617 | + |
618 | + # Searches all predefined paths for cookie files. Returns a list of fully |
619 | + # qualified paths to cookie files found. |
620 | + def find_cookie_files(self): |
621 | + |
622 | + cookie_file_paths = [] |
623 | + |
624 | + # Search each directory for cookies |
625 | + for cur_dir in self.cookie_file_search_path: |
626 | + |
627 | + # Expand '~' in paths |
628 | + cur_dir = os.path.expanduser(cur_dir) |
629 | + |
630 | + cur_paths = self.cookie_file_scan(cur_dir, cookie_file_paths) |
631 | + |
632 | + return cookie_file_paths |
633 | + |
634 | + # Recursively scan for cookie files in the target directory and return a |
635 | + # list of the cookie files found. |
636 | + def cookie_file_scan(self, target_dir, cookie_file_list): |
637 | + |
638 | + # Ensure current directory exists |
639 | + if (not os.path.exists(target_dir)): return |
640 | + |
641 | + # Loop over all files/dirs in the current_dir |
642 | + for cur_file in os.listdir(target_dir): |
643 | + |
644 | + # Full path to current file |
645 | + cur_file_path = os.path.join(target_dir, cur_file) |
646 | + |
647 | + # If the current file is a folder, recursively process it |
648 | + if( os.path.isdir(cur_file_path)): |
649 | + |
650 | + # Scan it for cookie files |
651 | + self.cookie_file_scan(cur_file_path, cookie_file_list) |
652 | + |
653 | + # The current file is a file (not a directory), see if it is a cookie file. |
654 | + else: |
655 | + |
656 | + # If the current file matches any of the names in cookie_file_names, it is a cookie file. |
657 | + for name in self.cookie_file_names: |
658 | + if (cur_file.lower() == name.lower()): |
659 | + cookie_file_list.append(cur_file_path) |
660 | + |
661 | + #################################################################### |
662 | + ################# Generic Cookie Parsing Functions ################# |
663 | + #################################################################### |
664 | + # These parsing functions can be used by many different browsers. # |
665 | + # They are provided here for child classes to use. This avoid # |
666 | + # duplicating the code in each child class that can use them. # |
667 | + #################################################################### |
668 | + |
669 | + # Loads cookies from a sqlite cookie file and returns the list of cookies. |
670 | + # Returns list of cookies on success and None otherwise. |
671 | + # The table and key names default to the ones used by Mozilla based browsers as they are the most common. |
672 | + def load_sqlite_cookie_file(self, cookie_file_path, table_name="moz_cookies", domain_key="host", name_key="name", value_key="value", expire_time_key="expiry"): |
673 | + |
674 | + cookies = [] |
675 | + |
676 | + # "Connect" to cookie file :) |
677 | + try: |
678 | + con = sqlite.connect(cookie_file_path) |
679 | + con.row_factory = sqlite.Row |
680 | + |
681 | + # Perform SQL query to obtain all cookies |
682 | + cur = con.cursor().execute("select * from " + table_name) |
683 | + |
684 | + # If query fails, this is NOT a valid sqlite cookie file. |
685 | + except sqlite.Error, e: |
686 | + return None |
687 | + |
688 | + # cookie objects returned are dictionaries and have the following properties |
689 | + # 'id' - ??? |
690 | + # 'name' - Cookie name |
691 | + #'value' - Cookie value |
692 | + #'host' - Host setting the cookie |
693 | + #'path' - ??? |
694 | + #'expiry' - Expire date/time (Unix Time) |
695 | + #'lastAccessed' - last time cookie was used. |
696 | + #'isSecure' - ??? |
697 | + #'isHttpOnly' - ??? |
698 | + |
699 | + |
700 | + # Build and return list of cookies. |
701 | + for cookie in cur: |
702 | + new_cookie = {} |
703 | + new_cookie['domain'] = cookie[domain_key] |
704 | + new_cookie['name'] = cookie[name_key] |
705 | + new_cookie['value'] = cookie[value_key] |
706 | + new_cookie['expire_time'] = cookie[expire_time_key] |
707 | + cookies.append(new_cookie) |
708 | + |
709 | + # Close connection to database file |
710 | + con.close() |
711 | + |
712 | + return cookies |
713 | + |
714 | + # Loads cookies from a plain text cookie file and returns the list of cookies. |
715 | + # Returns list of cookies on success and None otherwise. |
716 | + def load_txt_cookie_file(self, cookie_file_path): |
717 | + |
718 | + cookies = [] |
719 | + |
720 | + # Load cookie file |
721 | + try: |
722 | + cookie_jar = cookielib.MozillaCookieJar() |
723 | + cookie_jar.load(cookie_file_path) |
724 | + |
725 | + # If load fails, this is NOT a valid plain text cookie file. |
726 | + except cookielib.LoadError: |
727 | + return None |
728 | + |
729 | + # Loop over all cookies and all them to our cookies list |
730 | + for cookie in cookie_jar: |
731 | + new_cookie = {} |
732 | + new_cookie['domain'] = cookie.domain |
733 | + new_cookie['name'] = cookie.name |
734 | + new_cookie['value'] = cookie.value |
735 | + new_cookie['expire_time'] = cookie.expires # int |
736 | + cookies.append(new_cookie) |
737 | + |
738 | + return cookies |
739 | + |
740 | |
741 | === added file 'hugday_lib/cookie_lib/EpiphanyCookieLoader.py' |
742 | --- hugday_lib/cookie_lib/EpiphanyCookieLoader.py 1970-01-01 00:00:00 +0000 |
743 | +++ hugday_lib/cookie_lib/EpiphanyCookieLoader.py 2010-04-13 16:46:31 +0000 |
744 | @@ -0,0 +1,53 @@ |
745 | +#!/usr/bin/python |
746 | + |
747 | +import os, shutil |
748 | + |
749 | +from CookieLoader import CookieLoader |
750 | + |
751 | +# This is a subclass of CookieLoader. Please see CookieLoader for more details. |
752 | +class EpiphanyCookieLoader(CookieLoader): |
753 | + |
754 | + # browser name |
755 | + browser_str = "epiphany" |
756 | + |
757 | + # List of directories to search for cookie files. |
758 | + cookie_file_search_path = [ os.path.join( "~" , ".gnome2" , "epiphany" ) ] |
759 | + |
760 | + # List of valid cookie file names. |
761 | + cookie_file_names = [ "cookies.sqlite" ] |
762 | + |
763 | + # attempts to load cookies from the given file. |
764 | + # If cookie_file_path points to a valid readable cookie file that can be |
765 | + # parsed then this function will return True. If it returns false then |
766 | + # the specified cookie file is not readable by this cookie finder. |
767 | + def load_cookies_from_file(self, cookie_file_path): |
768 | + |
769 | + # If the given cookie file does not exist, give up. |
770 | + if (not os.path.exists(cookie_file_path)): |
771 | + return False |
772 | + |
773 | + # If we attempt to use the cookie file while the browser is running |
774 | + # we will get errors. Only one process is allowed to use it at a time. |
775 | + # To get around this we can simply copy it to /tmp and then use it. |
776 | + tmp_cookie_file_path = os.path.join( "/" , "tmp" , os.path.basename(cookie_file_path) ) |
777 | + shutil.copyfile(cookie_file_path, tmp_cookie_file_path) |
778 | + |
779 | + # Epiphany cookie files are sqlite databases |
780 | + cookies = self.load_sqlite_cookie_file(tmp_cookie_file_path) |
781 | + |
782 | + # If the load failed, then we've been given an invalid cookie file. |
783 | + if (not cookies): |
784 | + os.remove(tmp_cookie_file_path) |
785 | + return False |
786 | + |
787 | + # Loop over all cookies and add them to our cookies list |
788 | + for cookie in cookies: |
789 | + self.cookies.append(cookie) |
790 | + |
791 | + # Erase temporary cookie file. |
792 | + os.remove(tmp_cookie_file_path) |
793 | + |
794 | + # We were able to read cookies from this file. |
795 | + self.found_valid_cookie_file = True |
796 | + return True |
797 | + |
798 | |
799 | === added file 'hugday_lib/cookie_lib/FirefoxCookieLoader.py' |
800 | --- hugday_lib/cookie_lib/FirefoxCookieLoader.py 1970-01-01 00:00:00 +0000 |
801 | +++ hugday_lib/cookie_lib/FirefoxCookieLoader.py 2010-04-13 16:46:31 +0000 |
802 | @@ -0,0 +1,60 @@ |
803 | +#!/usr/bin/python |
804 | + |
805 | +import os, shutil |
806 | + |
807 | +from CookieLoader import CookieLoader |
808 | + |
809 | +# This is a subclass of CookieLoader. Please see CookieLoader for more details. |
810 | +class FirefoxCookieLoader(CookieLoader): |
811 | + |
812 | + # browser name |
813 | + browser_str = "firefox" |
814 | + |
815 | + # List of directories to search for cookie files. |
816 | + cookie_file_search_path = [ os.path.join( "~" , ".mozilla" , "firefox" ) ] |
817 | + |
818 | + # List of valid cookie file names. |
819 | + cookie_file_names = [ "cookies.sqlite" , "cookies.txt" ] |
820 | + |
821 | + # attempts to load cookies from the given file. |
822 | + # If cookie_file_path points to a valid readable cookie file that can be |
823 | + # parsed then this function will return True. If it returns false then |
824 | + # the specified cookie file is not readable by this cookie finder. |
825 | + def load_cookies_from_file(self, cookie_file_path): |
826 | + |
827 | + # If the given cookie file does not exist, give up. |
828 | + if (not os.path.exists(cookie_file_path)): |
829 | + return False |
830 | + |
831 | + # If we attempt to use the cookie file while the browser is running |
832 | + # we will get errors. Only one process is allowed to use it at a time. |
833 | + # To get around this we can simply copy it to /tmp and then use it. |
834 | + tmp_cookie_file_path = os.path.join( "/" , "tmp" , os.path.basename(cookie_file_path) ) |
835 | + shutil.copyfile(cookie_file_path, tmp_cookie_file_path) |
836 | + |
837 | + # Firefox cookie files can be one of two formats: |
838 | + # old format - cookies.txt - Plain text cookie file |
839 | + # new format - cookies.sqlite - sqlite database file |
840 | + |
841 | + # 1st, try to read that file as if it is an sqlite cookie file. |
842 | + cookies = self.load_sqlite_cookie_file(tmp_cookie_file_path) |
843 | + |
844 | + # If that fails, try to read it as plain text |
845 | + if (not cookies): cookies = self.load_txt_cookie_file(tmp_cookie_file_path) |
846 | + |
847 | + # If they both failed, then we've been given an invalid cookie file. |
848 | + if (not cookies): |
849 | + os.remove(tmp_cookie_file_path) |
850 | + return False |
851 | + |
852 | + # Loop over all cookies and add them to our cookies list |
853 | + for cookie in cookies: |
854 | + self.cookies.append(cookie) |
855 | + |
856 | + # Erase temporary cookie file. |
857 | + os.remove(tmp_cookie_file_path) |
858 | + |
859 | + # We were able to read cookies from this file. |
860 | + self.found_valid_cookie_file = True |
861 | + return True |
862 | + |
863 | |
864 | === added file 'hugday_lib/cookie_lib/KonquerorCookieLoader.py' |
865 | --- hugday_lib/cookie_lib/KonquerorCookieLoader.py 1970-01-01 00:00:00 +0000 |
866 | +++ hugday_lib/cookie_lib/KonquerorCookieLoader.py 2010-04-13 16:46:31 +0000 |
867 | @@ -0,0 +1,111 @@ |
868 | + #!/usr/bin/python |
869 | + |
870 | +import os, shutil |
871 | + |
872 | +from CookieLoader import CookieLoader |
873 | + |
874 | +# This is a subclass of CookieLoader. Please see CookieLoader for more details. |
875 | +class KonquerorCookieLoader(CookieLoader): |
876 | + |
877 | + # browser name |
878 | + browser_str = "konqueror" |
879 | + |
880 | + # List of directories to search for cookie files. |
881 | + cookie_file_search_path = [ os.path.join( "~" , ".kde" , "share" , "apps" , "kcookiejar") ] |
882 | + |
883 | + # List of valid cookie file names. |
884 | + cookie_file_names = [ "cookies" ] |
885 | + |
886 | + # attempts to load cookies from the given file. |
887 | + # If cookie_file_path points to a valid readable cookie file that can be |
888 | + # parsed then this function will return True. If it returns false then |
889 | + # the specified cookie file is not readable by this cookie finder. |
890 | + def load_cookies_from_file(self, cookie_file_path): |
891 | + |
892 | + # If the given cookie file does not exist, give up. |
893 | + if (not os.path.exists(cookie_file_path)): |
894 | + return False |
895 | + |
896 | + # Konqueror cookie files are in plain text format |
897 | + cookies = self.load_konqueror_cookie_file(cookie_file_path) |
898 | + |
899 | + # If the load failed, then we've been given an invalid cookie file. |
900 | + if (not cookies): |
901 | + print "not cookies" |
902 | + return False |
903 | + |
904 | + # Loop over all cookies and add them to our cookies list |
905 | + for cookie in cookies: |
906 | + self.cookies.append(cookie) |
907 | + |
908 | + # We were able to read cookies from this file. |
909 | + return True |
910 | + |
911 | + |
912 | + |
913 | + # Loads cookies from a Konqueror plain text cookie file and returns the list of cookies. |
914 | + # Returns list of cookies on success and None otherwise. |
915 | + def load_konqueror_cookie_file(self, cookie_file_path): |
916 | + |
917 | + cookies = [] |
918 | + |
919 | + # Here is an example Konquerir cookie file: |
920 | + # # KDE Cookie File v2 |
921 | + # # |
922 | + # # Host Domain Path Exp.date Prot Name Sec Value |
923 | + # [.ubuntu.com] |
924 | + # wiki.ubuntu.com ".wiki.ubuntu.com" "/" 1286259931 0 __utmz 4 230736537.1270491931.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none) |
925 | + # wiki.ubuntu.com ".wiki.ubuntu.com" "/" 1333564241 0 __utma 4 230736537.1618083871.1270491931.1270492211.1270492242.3 |
926 | + # wiki.ubuntu.com ".wiki.ubuntu.com" "/" 1270494047 0 __utmb 4 230736537 |
927 | + # wiki.ubuntu.com ".wiki.ubuntu.com" "/" 1333564247 0 __utmv 4 230736537.UbuntuWiki |
928 | + # wiki.ubuntu.com "" "/" 1585852247 0 MOIN_SESSION 4 249_4_7z1eln24s28t8ifido5k3cnwc_ |
929 | + # [.launchpad.net] |
930 | + # login.launchpad.net "" "/" 1271701555 0 sessionid 5 9ebf071969502599c1d1f959d210c892 |
931 | + # [.slashdot.org] |
932 | + # slashdot.org ".slashdot.org" "/" 1286260261 0 __utmz 4 9273847.1270492261.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none) |
933 | + # slashdot.org ".slashdot.org" "/" 1333564271 0 __utma 4 9273847.870340038.1270492261.1270492261.1270492261.1 |
934 | + # slashdot.org ".slashdot.org" "/" 1270494071 0 __utmb 4 9273847.1.10.1270492261 |
935 | + # |
936 | + |
937 | + # Open cookie file |
938 | + cookie_file = open(cookie_file_path, "r") |
939 | + |
940 | + # Ensure this is a Konqueror cookie file |
941 | + first_line = cookie_file.readline() |
942 | + if ( first_line.find("KDE Cookie File") == -1 ): |
943 | + print "Not cookie file!" |
944 | + return False |
945 | + |
946 | + # Parse file line by line looking for cookies... |
947 | + for line in cookie_file: |
948 | + |
949 | + # Throw away comments, host headers |
950 | + if ( line.startswith("#") ): continue |
951 | + if ( line.startswith("[") ): continue |
952 | + |
953 | + # Split string on whitespace (Ignore lines that do not contain enough tokens) |
954 | + tokens = line.split() |
955 | + if ( len(tokens) < 8): continue |
956 | + |
957 | + # Construct cookie |
958 | + new_cookie = {} |
959 | + new_cookie['domain'] = tokens[0] |
960 | + new_cookie['name'] = tokens[5] |
961 | + new_cookie['value'] = tokens[7] |
962 | + new_cookie['expire_time'] = tokens[3] |
963 | + |
964 | + # Sanity check data & convert expire time to an integer |
965 | + if (new_cookie['expire_time'].isdigit()): |
966 | + new_cookie['expire_time'] = int(new_cookie['expire_time']) |
967 | + else: continue |
968 | + |
969 | + # We have a good cookie :) |
970 | + cookies.append(new_cookie) |
971 | + |
972 | + # Close file. |
973 | + cookie_file.close() |
974 | + |
975 | + # Found cookie files :) |
976 | + self.found_valid_cookie_file = True |
977 | + return cookies |
978 | + |
979 | |
980 | === added file 'hugday_lib/cookie_lib/SeamonkeyCookieLoader.py' |
981 | --- hugday_lib/cookie_lib/SeamonkeyCookieLoader.py 1970-01-01 00:00:00 +0000 |
982 | +++ hugday_lib/cookie_lib/SeamonkeyCookieLoader.py 2010-04-13 16:46:31 +0000 |
983 | @@ -0,0 +1,43 @@ |
984 | +#!/usr/bin/python |
985 | + |
986 | +import os, shutil |
987 | + |
988 | +from CookieLoader import CookieLoader |
989 | + |
990 | +# This is a subclass of CookieLoader. Please see CookieLoader for more details. |
991 | +class SeamonkeyCookieLoader(CookieLoader): |
992 | + |
993 | + # browser name |
994 | + browser_str = "seamonkey" |
995 | + |
996 | + # List of directories to search for cookie files. |
997 | + cookie_file_search_path = [ os.path.join( "~" , ".mozilla" , "default" ) ] |
998 | + |
999 | + # List of valid cookie file names. |
1000 | + cookie_file_names = [ "cookies.txt" ] |
1001 | + |
1002 | + # attempts to load cookies from the given file. |
1003 | + # If cookie_file_path points to a valid readable cookie file that can be |
1004 | + # parsed then this function will return True. If it returns false then |
1005 | + # the specified cookie file is not readable by this cookie finder. |
1006 | + def load_cookies_from_file(self, cookie_file_path): |
1007 | + |
1008 | + # If the given cookie file does not exist, give up. |
1009 | + if (not os.path.exists(cookie_file_path)): |
1010 | + return False |
1011 | + |
1012 | + # Epiphany cookie files are sqlite databases |
1013 | + cookies = self.load_txt_cookie_file(cookie_file_path) |
1014 | + |
1015 | + # If the load failed, then we've been given an invalid cookie file. |
1016 | + if (not cookies): |
1017 | + return False |
1018 | + |
1019 | + # Loop over all cookies and add them to our cookies list |
1020 | + for cookie in cookies: |
1021 | + self.cookies.append(cookie) |
1022 | + |
1023 | + # We were able to read cookies from this file. |
1024 | + self.found_valid_cookie_file = True |
1025 | + return True |
1026 | + |
1027 | |
1028 | === added file 'hugday_lib/cookie_lib/__init__.py' |
1029 | === modified file 'setup.py' |
1030 | --- setup.py 2010-03-31 16:37:16 +0000 |
1031 | +++ setup.py 2010-04-13 16:46:31 +0000 |
1032 | @@ -28,5 +28,7 @@ |
1033 | 'dl-ubuntu-test-iso/iso-ripper', |
1034 | 'hugday-tools/hugday', |
1035 | ], |
1036 | - packages=[], |
1037 | + packages=['hugday_lib', |
1038 | + 'hugday_lib/cookie_lib' |
1039 | + ] |
1040 | ) |
This sounds really exciting! You have followed the proper process for requesting such a merge but unfortunately I don't think we can get this into Lucid since we are past feature freeze. I'm requesting that the original author look at your changes too.