cookie_extractor 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/cookie_extractor +16 -4
- data/cookie_extractor.gemspec +4 -4
- data/lib/cookie_extractor/browser_detector.rb +31 -0
- data/lib/cookie_extractor/chrome_cookie_extractor.rb +29 -0
- data/lib/cookie_extractor/common.rb +19 -0
- data/lib/cookie_extractor/firefox_cookie_extractor.rb +5 -19
- data/lib/cookie_extractor/version.rb +1 -1
- data/lib/cookie_extractor.rb +3 -0
- data/spec/browser_detector_spec.rb +43 -0
- data/spec/chrome_cookie_extractor_spec.rb +126 -0
- data/spec/firefox_cookie_extractor_spec.rb +21 -1
- metadata +45 -65
data/bin/cookie_extractor
CHANGED
@@ -2,10 +2,22 @@
|
|
2
2
|
|
3
3
|
require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "cookie_extractor"))
|
4
4
|
|
5
|
-
# TODO:
|
5
|
+
# TODO: Locate cookie dbs automatically
|
6
6
|
filename = ARGV.first
|
7
|
-
|
8
|
-
puts
|
7
|
+
unless filename
|
8
|
+
puts "Usage: (Firefox) cookie_extractor /path/to/cookies.sqlite"
|
9
|
+
puts " (Chrome) cookie_extractor /path/to/Cookies"
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
if File.exists?(filename)
|
14
|
+
begin
|
15
|
+
extractor = CookieExtractor::BrowserDetector.new_extractor(filename)
|
16
|
+
puts extractor.extract.join("\n")
|
17
|
+
rescue SQLite3::NotADatabaseException,
|
18
|
+
CookieExtractor::BrowserNotDetectedException
|
19
|
+
puts "Error: File '#{filename}' is not a Firefox or Chrome cookie database"
|
20
|
+
end
|
9
21
|
else
|
10
|
-
puts "
|
22
|
+
puts "Error: File '#{filename}' does not exist"
|
11
23
|
end
|
data/cookie_extractor.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["Jeff Dallien"]
|
9
9
|
s.email = ["jeff@dallien.net"]
|
10
10
|
s.homepage = "http://github.com/jdallien/cookie_extractor"
|
11
|
-
s.summary = %q{Create cookies.txt from Firefox cookies}
|
12
|
-
s.description = %q{Extract cookies from Firefox sqlite databases into a wget-compatible cookies.txt file.}
|
11
|
+
s.summary = %q{Create cookies.txt from Firefox or Chrome/Chromium cookies}
|
12
|
+
s.description = %q{Extract cookies from Firefox, Chrome or Chromium sqlite databases into a wget-compatible cookies.txt file.}
|
13
13
|
|
14
14
|
s.rubyforge_project = "cookie_extractor"
|
15
15
|
|
@@ -18,6 +18,6 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
|
-
s.add_development_dependency "rspec"
|
22
|
-
s.add_runtime_dependency "sqlite3-ruby"
|
21
|
+
s.add_development_dependency "rspec", "~> 2.8"
|
22
|
+
s.add_runtime_dependency "sqlite3-ruby", "~> 1.3"
|
23
23
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module CookieExtractor
|
2
|
+
class BrowserNotDetectedException < Exception; end
|
3
|
+
|
4
|
+
class BrowserDetector
|
5
|
+
|
6
|
+
def self.new_extractor(db_filename)
|
7
|
+
browser = detect_browser(db_filename)
|
8
|
+
if browser
|
9
|
+
CookieExtractor.const_get("#{browser}CookieExtractor").new(db_filename)
|
10
|
+
else
|
11
|
+
raise BrowserNotDetectedException, "Could not detect browser type."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.detect_browser(db_filename)
|
16
|
+
db = SQLite3::Database.new(db_filename)
|
17
|
+
browser =
|
18
|
+
if has_table?(db, 'moz_cookies')
|
19
|
+
'Firefox'
|
20
|
+
elsif has_table?(db, 'cookies')
|
21
|
+
'Chrome'
|
22
|
+
end
|
23
|
+
db.close
|
24
|
+
browser
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.has_table?(db, table_name)
|
28
|
+
db.table_info(table_name).size > 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'sqlite3'
|
2
|
+
|
3
|
+
module CookieExtractor
|
4
|
+
class ChromeCookieExtractor
|
5
|
+
include Common
|
6
|
+
|
7
|
+
def initialize(cookie_file)
|
8
|
+
@cookie_file = cookie_file
|
9
|
+
end
|
10
|
+
|
11
|
+
def extract
|
12
|
+
db = SQLite3::Database.new @cookie_file
|
13
|
+
db.results_as_hash = true
|
14
|
+
result = []
|
15
|
+
db.execute("SELECT * FROM cookies") do |row|
|
16
|
+
result << [ row['host_key'],
|
17
|
+
true_false_word(is_domain_wide(row['host_key'])),
|
18
|
+
row['path'],
|
19
|
+
true_false_word(row['secure']),
|
20
|
+
row['expires_utc'],
|
21
|
+
row['name'],
|
22
|
+
row['value']
|
23
|
+
].join("\t")
|
24
|
+
end
|
25
|
+
db.close
|
26
|
+
result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CookieExtractor
|
2
|
+
module Common
|
3
|
+
private
|
4
|
+
|
5
|
+
def is_domain_wide(hostname)
|
6
|
+
hostname[0..0] == "."
|
7
|
+
end
|
8
|
+
|
9
|
+
def true_false_word(value)
|
10
|
+
if value == "1" || value == 1 || value == true
|
11
|
+
"TRUE"
|
12
|
+
elsif value == "0" || value == 0 || value == false
|
13
|
+
"FALSE"
|
14
|
+
else
|
15
|
+
raise "Invalid value passed to true_false_word: #{value.inspect}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -2,6 +2,7 @@ require 'sqlite3'
|
|
2
2
|
|
3
3
|
module CookieExtractor
|
4
4
|
class FirefoxCookieExtractor
|
5
|
+
include Common
|
5
6
|
|
6
7
|
def initialize(cookie_file)
|
7
8
|
@cookie_file = cookie_file
|
@@ -10,9 +11,9 @@ module CookieExtractor
|
|
10
11
|
def extract
|
11
12
|
db = SQLite3::Database.new @cookie_file
|
12
13
|
db.results_as_hash = true
|
13
|
-
|
14
|
+
result = []
|
14
15
|
db.execute("SELECT * FROM moz_cookies") do |row|
|
15
|
-
|
16
|
+
result << [ row['host'],
|
16
17
|
true_false_word(is_domain_wide(row['host'])),
|
17
18
|
row['path'],
|
18
19
|
true_false_word(row['isSecure']),
|
@@ -21,23 +22,8 @@ module CookieExtractor
|
|
21
22
|
row['value']
|
22
23
|
].join("\t")
|
23
24
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def is_domain_wide(hostname)
|
30
|
-
hostname[0..0] == "."
|
31
|
-
end
|
32
|
-
|
33
|
-
def true_false_word(value)
|
34
|
-
if value == "1" || value == 1 || value == true
|
35
|
-
"TRUE"
|
36
|
-
elsif value == "0" || value == 0 || value == false
|
37
|
-
"FALSE"
|
38
|
-
else
|
39
|
-
raise "Invalid value passed to true_false_word: #{value.inspect}"
|
40
|
-
end
|
25
|
+
db.close
|
26
|
+
result
|
41
27
|
end
|
42
28
|
end
|
43
29
|
end
|
data/lib/cookie_extractor.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe CookieExtractor::BrowserDetector, "determining the correct extractor to use" do
|
4
|
+
before :each do
|
5
|
+
@fake_cookie_db = double("cookie database", :close => true)
|
6
|
+
SQLite3::Database.should_receive(:new).
|
7
|
+
with('filename').
|
8
|
+
and_return(@fake_cookie_db)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "given a sqlite database with a 'moz_cookies' table" do
|
12
|
+
before :each do
|
13
|
+
@fake_cookie_db.should_receive(:table_info).
|
14
|
+
with("moz_cookies").
|
15
|
+
and_return(
|
16
|
+
{ 'name' => 'some_field',
|
17
|
+
'type' => "some_type" })
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return a firefox extractor instance" do
|
21
|
+
extractor = CookieExtractor::BrowserDetector.new_extractor('filename')
|
22
|
+
extractor.instance_of?(CookieExtractor::FirefoxCookieExtractor).should be_true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "given a sqlite database with a 'cookies' table" do
|
27
|
+
before :each do
|
28
|
+
@fake_cookie_db.should_receive(:table_info).
|
29
|
+
with("moz_cookies").
|
30
|
+
and_return([])
|
31
|
+
@fake_cookie_db.should_receive(:table_info).
|
32
|
+
with("cookies").
|
33
|
+
and_return(
|
34
|
+
[{ 'name' => 'some_field',
|
35
|
+
'type' => "some_type" }])
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return a chrome extractor instance" do
|
39
|
+
extractor = CookieExtractor::BrowserDetector.new_extractor('filename')
|
40
|
+
extractor.instance_of?(CookieExtractor::ChromeCookieExtractor).should be_true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe CookieExtractor::ChromeCookieExtractor do
|
4
|
+
before :each do
|
5
|
+
@fake_cookie_db = double("cookie database",
|
6
|
+
:results_as_hash= => true,
|
7
|
+
:close => true)
|
8
|
+
SQLite3::Database.should_receive(:new).
|
9
|
+
with('filename').
|
10
|
+
and_return(@fake_cookie_db)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "opening and closing a sqlite db" do
|
14
|
+
before :each do
|
15
|
+
@fake_cookie_db.should_receive(:execute).and_yield(
|
16
|
+
{ 'host_key' => '.dallien.net',
|
17
|
+
'path' => '/',
|
18
|
+
'secure' => '0',
|
19
|
+
'expires_utc' => '1234567890',
|
20
|
+
'name' => 'NAME',
|
21
|
+
'value' => 'VALUE'})
|
22
|
+
@extractor = CookieExtractor::ChromeCookieExtractor.new('filename')
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should close the db when finished" do
|
26
|
+
@fake_cookie_db.should_receive(:close)
|
27
|
+
@extractor.extract
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "with a cookie that has a host starting with a dot" do
|
32
|
+
before :each do
|
33
|
+
@fake_cookie_db.should_receive(:execute).and_yield(
|
34
|
+
{ 'host_key' => '.dallien.net',
|
35
|
+
'path' => '/',
|
36
|
+
'secure' => '0',
|
37
|
+
'expires_utc' => '1234567890',
|
38
|
+
'name' => 'NAME',
|
39
|
+
'value' => 'VALUE'})
|
40
|
+
@extractor = CookieExtractor::ChromeCookieExtractor.new('filename')
|
41
|
+
@result = @extractor.extract
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should return one cookie string" do
|
45
|
+
@result.size.should == 1
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should put TRUE in the domain wide field" do
|
49
|
+
cookie_string = @result.first
|
50
|
+
cookie_string.split("\t")[1].should == "TRUE"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should build the correct cookie string" do
|
54
|
+
cookie_string = @result.first
|
55
|
+
cookie_string.should ==
|
56
|
+
".dallien.net\tTRUE\t/\tFALSE\t1234567890\tNAME\tVALUE"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "with a cookie that has a host not starting with a dot" do
|
61
|
+
before :each do
|
62
|
+
@fake_cookie_db.should_receive(:execute).and_yield(
|
63
|
+
{ 'host_key' => 'jeff.dallien.net',
|
64
|
+
'path' => '/path',
|
65
|
+
'secure' => '1',
|
66
|
+
'expires_utc' => '1234567890',
|
67
|
+
'name' => 'NAME',
|
68
|
+
'value' => 'VALUE'})
|
69
|
+
@extractor = CookieExtractor::ChromeCookieExtractor.new('filename')
|
70
|
+
@result = @extractor.extract
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should return one cookie string" do
|
74
|
+
@result.size.should == 1
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should put FALSE in the domain wide field" do
|
78
|
+
cookie_string = @result.first
|
79
|
+
cookie_string.split("\t")[1].should == "FALSE"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should build the correct cookie string" do
|
83
|
+
cookie_string = @result.first
|
84
|
+
cookie_string.should ==
|
85
|
+
"jeff.dallien.net\tFALSE\t/path\tTRUE\t1234567890\tNAME\tVALUE"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "with a cookie that is not marked as secure" do
|
90
|
+
before :each do
|
91
|
+
@fake_cookie_db.should_receive(:execute).and_yield(
|
92
|
+
{ 'host_key' => '.dallien.net',
|
93
|
+
'path' => '/',
|
94
|
+
'secure' => '0',
|
95
|
+
'expires_utc' => '1234567890',
|
96
|
+
'name' => 'NAME',
|
97
|
+
'value' => 'VALUE'})
|
98
|
+
@extractor = CookieExtractor::ChromeCookieExtractor.new('filename')
|
99
|
+
@result = @extractor.extract
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should put FALSE in the secure field" do
|
103
|
+
cookie_string = @result.first
|
104
|
+
cookie_string.split("\t")[3].should == "FALSE"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "with a cookie that is marked as secure" do
|
109
|
+
before :each do
|
110
|
+
@fake_cookie_db.should_receive(:execute).and_yield(
|
111
|
+
{ 'host_key' => '.dallien.net',
|
112
|
+
'path' => '/',
|
113
|
+
'secure' => '1',
|
114
|
+
'expires_utc' => '1234567890',
|
115
|
+
'name' => 'NAME',
|
116
|
+
'value' => 'VALUE'})
|
117
|
+
@extractor = CookieExtractor::ChromeCookieExtractor.new('filename')
|
118
|
+
@result = @extractor.extract
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should put TRUE in the secure field" do
|
122
|
+
cookie_string = @result.first
|
123
|
+
cookie_string.split("\t")[3].should == "TRUE"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -2,12 +2,32 @@ require File.join(File.dirname(__FILE__), "spec_helper")
|
|
2
2
|
|
3
3
|
describe CookieExtractor::FirefoxCookieExtractor do
|
4
4
|
before :each do
|
5
|
-
@fake_cookie_db = double("cookie database",
|
5
|
+
@fake_cookie_db = double("cookie database",
|
6
|
+
:results_as_hash= => true,
|
7
|
+
:close => true)
|
6
8
|
SQLite3::Database.should_receive(:new).
|
7
9
|
with('filename').
|
8
10
|
and_return(@fake_cookie_db)
|
9
11
|
end
|
10
12
|
|
13
|
+
describe "opening and closing a sqlite db" do
|
14
|
+
before :each do
|
15
|
+
@fake_cookie_db.should_receive(:execute).and_yield(
|
16
|
+
{'host' => '.dallien.net',
|
17
|
+
'path' => '/',
|
18
|
+
'isSecure' => '0',
|
19
|
+
'expiry' => '1234567890',
|
20
|
+
'name' => 'NAME',
|
21
|
+
'value' => 'VALUE'})
|
22
|
+
@extractor = CookieExtractor::FirefoxCookieExtractor.new('filename')
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should close the db when finished" do
|
26
|
+
@fake_cookie_db.should_receive(:close)
|
27
|
+
@extractor.extract
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
11
31
|
describe "with a cookie that has a host starting with a dot" do
|
12
32
|
before :each do
|
13
33
|
@fake_cookie_db.should_receive(:execute).and_yield(
|
metadata
CHANGED
@@ -1,61 +1,47 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: cookie_extractor
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 0.0.1
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Jeff Dallien
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-02-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
22
15
|
name: rspec
|
23
|
-
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &17748000 !ruby/object:Gem::Requirement
|
25
17
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.8'
|
33
22
|
type: :development
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: sqlite3-ruby
|
37
23
|
prerelease: false
|
38
|
-
|
24
|
+
version_requirements: *17748000
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: sqlite3-ruby
|
27
|
+
requirement: &17747360 !ruby/object:Gem::Requirement
|
39
28
|
none: false
|
40
|
-
requirements:
|
41
|
-
- -
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
segments:
|
45
|
-
- 0
|
46
|
-
version: "0"
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.3'
|
47
33
|
type: :runtime
|
48
|
-
|
49
|
-
|
50
|
-
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *17747360
|
36
|
+
description: Extract cookies from Firefox, Chrome or Chromium sqlite databases into
|
37
|
+
a wget-compatible cookies.txt file.
|
38
|
+
email:
|
51
39
|
- jeff@dallien.net
|
52
|
-
executables:
|
40
|
+
executables:
|
53
41
|
- cookie_extractor
|
54
42
|
extensions: []
|
55
|
-
|
56
43
|
extra_rdoc_files: []
|
57
|
-
|
58
|
-
files:
|
44
|
+
files:
|
59
45
|
- .gitignore
|
60
46
|
- .rvmrc
|
61
47
|
- Gemfile
|
@@ -65,44 +51,38 @@ files:
|
|
65
51
|
- bin/cookie_extractor
|
66
52
|
- cookie_extractor.gemspec
|
67
53
|
- lib/cookie_extractor.rb
|
54
|
+
- lib/cookie_extractor/browser_detector.rb
|
55
|
+
- lib/cookie_extractor/chrome_cookie_extractor.rb
|
56
|
+
- lib/cookie_extractor/common.rb
|
68
57
|
- lib/cookie_extractor/firefox_cookie_extractor.rb
|
69
58
|
- lib/cookie_extractor/version.rb
|
59
|
+
- spec/browser_detector_spec.rb
|
60
|
+
- spec/chrome_cookie_extractor_spec.rb
|
70
61
|
- spec/cookie_extractor_spec.rb
|
71
62
|
- spec/firefox_cookie_extractor_spec.rb
|
72
63
|
- spec/spec_helper.rb
|
73
|
-
has_rdoc: true
|
74
64
|
homepage: http://github.com/jdallien/cookie_extractor
|
75
65
|
licenses: []
|
76
|
-
|
77
66
|
post_install_message:
|
78
67
|
rdoc_options: []
|
79
|
-
|
80
|
-
require_paths:
|
68
|
+
require_paths:
|
81
69
|
- lib
|
82
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
71
|
none: false
|
84
|
-
requirements:
|
85
|
-
- -
|
86
|
-
- !ruby/object:Gem::Version
|
87
|
-
|
88
|
-
|
89
|
-
- 0
|
90
|
-
version: "0"
|
91
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
77
|
none: false
|
93
|
-
requirements:
|
94
|
-
- -
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
|
97
|
-
segments:
|
98
|
-
- 0
|
99
|
-
version: "0"
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
100
82
|
requirements: []
|
101
|
-
|
102
83
|
rubyforge_project: cookie_extractor
|
103
|
-
rubygems_version: 1.
|
84
|
+
rubygems_version: 1.8.6
|
104
85
|
signing_key:
|
105
86
|
specification_version: 3
|
106
|
-
summary: Create cookies.txt from Firefox cookies
|
87
|
+
summary: Create cookies.txt from Firefox or Chrome/Chromium cookies
|
107
88
|
test_files: []
|
108
|
-
|