diff --git a/CHANGES.md b/CHANGES.md deleted file mode 100644 index 0900169..0000000 --- a/CHANGES.md +++ /dev/null @@ -1,16 +0,0 @@ -CHANGES: cookie_extractor -------------------------- - -### v0.2.0 2013-07-02 - -- Browser guessing code contributed by Ben Eills (github.com/beneills): - - Added --guess flag to automatically choose most recently used cookie file - - Added --browser flag to specify browser by name - -### v0.1.0 2012-02-23 - -- Updated to include Chrome/Chromium support. - -### v0.0.1 2012-02-20 - -- Initial version. Supports extracting Firefox cookies. diff --git a/README.md b/README.md index 2ba69f5..fff51ee 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,15 @@ cookie_extractor ---------------- -Extract cookies from Firefox, Chrome or Chromium sqlite cookie stores into a wget-compatible cookies.txt file. +Extract cookies from Firefox sqlite cookie store (eventually Chrome also) into a wget-compatible cookies.txt file. ### Install ### - gem install cookie_extractor +gem install cookie_extractor ### Usage ### - cookie_extractor /path/to/firefox/cookies.sqlite > cookies.txt - - cookie_extractor --guess # Guess which browser to use and open corresponding file - - cookie_extractor --browser chrome|chromium|firefox # Open corresponding DB - - -Typical locations for the cookies file on Linux are: - - * Firefox: *~/.mozilla/firefox/(profile directory)/cookies.sqlite* - * Chrome: *~/.config/google-chrome/Default/Cookies* - * Chromium: *~/.config/chromium/Default/Cookies* +cookie_extractor /path/to/firefox/cookies.sqlite > cookies.txt ### License ### diff --git a/bin/cookie_extractor b/bin/cookie_extractor index 95a25d6..16b36a4 100755 --- a/bin/cookie_extractor +++ b/bin/cookie_extractor @@ -2,46 +2,10 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "cookie_extractor")) - -def usage msg=nil - puts msg if msg - puts "Usage: cookie_extractor /path/to/cookies.sqlite Open a DB file" - puts " cookie_extractor --guess Guess which browser to use and open corresponding file" - puts " cookie_extractor --browser chrome|chromium|firefox Open DB corresponding to a particular browser" - exit +# TODO: detect firefox or chrome input file and/or locate it automatically +filename = ARGV.first +if filename + puts CookieExtractor::FirefoxCookieExtractor.new(filename).extract.join("\n") +else + puts "Usage: cookie_extractor /path/to/cookies.sqlite" end - - -begin - extractor = - case ARGV.first - when '--guess', '-g' - begin - CookieExtractor::BrowserDetector.guess() - rescue CookieExtractor::NoCookieFileFoundException - abort "Error: we couldn't find any supported broswer's cookies" - end - when '--browser', '-b' - browser = ARGV[1] - usage "Error: Please supply a browser name" unless browser - begin - CookieExtractor::BrowserDetector.browser_extractor(browser) - rescue CookieExtractor::InvalidBrowserNameException - abort "Error: '#{browser}' is not a valid browser name" - rescue CookieExtractor::NoCookieFileFoundException - abort "Error: Could not locate cookie file for browser #{browser}" - end - when nil - usage - else - filename = ARGV.first - abort "Error: #{filename} does not exist" unless File.exists?(filename) - CookieExtractor::BrowserDetector.new_extractor(filename) - end - puts extractor.extract.join("\n") -rescue SQLite3::NotADatabaseException, - CookieExtractor::BrowserNotDetectedException - abort "Error: File '#{filename}' is not a Firefox or Chrome cookie database" -end - - diff --git a/cookie_extractor.gemspec b/cookie_extractor.gemspec index 54c7227..a53359a 100644 --- a/cookie_extractor.gemspec +++ b/cookie_extractor.gemspec @@ -8,8 +8,8 @@ Gem::Specification.new do |s| s.authors = ["Jeff Dallien"] s.email = ["jeff@dallien.net"] s.homepage = "http://github.com/jdallien/cookie_extractor" - s.summary = %q{Create cookies.txt from Firefox or Chrome/Chromium cookies} - s.description = %q{Extract cookies from Firefox, Chrome or Chromium sqlite databases into a wget-compatible cookies.txt file.} + s.summary = %q{Create cookies.txt from Firefox cookies} + s.description = %q{Extract cookies from Firefox sqlite databases into a wget-compatible cookies.txt file.} s.rubyforge_project = "cookie_extractor" @@ -18,6 +18,6 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] - s.add_development_dependency "rspec", "~> 2.8" - s.add_runtime_dependency "sqlite3-ruby", "~> 1.3" + s.add_development_dependency "rspec" + s.add_runtime_dependency "sqlite3-ruby" end diff --git a/lib/cookie_extractor.rb b/lib/cookie_extractor.rb index 99a1e71..20f68f4 100644 --- a/lib/cookie_extractor.rb +++ b/lib/cookie_extractor.rb @@ -1,11 +1,5 @@ -$:.unshift(File.dirname(__FILE__)) unless - $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__))) - require "cookie_extractor/version" -require "cookie_extractor/common" require "cookie_extractor/firefox_cookie_extractor" -require "cookie_extractor/chrome_cookie_extractor" -require "cookie_extractor/browser_detector" module CookieExtractor end diff --git a/lib/cookie_extractor/browser_detector.rb b/lib/cookie_extractor/browser_detector.rb deleted file mode 100644 index 8700344..0000000 --- a/lib/cookie_extractor/browser_detector.rb +++ /dev/null @@ -1,78 +0,0 @@ -module CookieExtractor - class BrowserNotDetectedException < Exception; end - class InvalidBrowserNameException < Exception; end - class NoCookieFileFoundException < Exception; end - - class BrowserDetector - COOKIE_LOCATIONS = { - "chrome" => "~/.config/google-chrome/Default/Cookies", - "chromium" => "~/.config/chromium/Default/Cookies", - "firefox" => "~/.mozilla/firefox/*.default/cookies.sqlite" - } - - # Returns the extractor of the most recently used browser's cookies - # or raise NoCookieFileFoundException if there are no cookies - def self.guess - most_recently_used_detected_browsers.each { |browser, path| - begin - extractor = self.browser_extractor(browser) - rescue BrowserNotDetectedException, NoCookieFileFoundException - # better try the next one... - else - return extractor - end - } - # If we make it here, we've failed... - raise NoCookieFileFoundException, "Couldn't find any browser's cookies" - end - - # Open a browser's cookie file using intelligent guesswork - def self.browser_extractor(browser) - raise InvalidBrowserNameException, "Browser must be one of: #{self.supported_browsers.join(', ')}" unless self.supported_browsers.include?(browser) - paths = Dir.glob(File.expand_path(COOKIE_LOCATIONS[browser])) - if paths.length < 1 or not File.exists?(paths.first) - raise NoCookieFileFoundException, "File #{paths.first} does not exist!" - end - self.new_extractor(paths.first) - end - - def self.new_extractor(db_filename) - browser = detect_browser(db_filename) - if browser - CookieExtractor.const_get("#{browser}CookieExtractor").new(db_filename) - else - raise BrowserNotDetectedException, "Could not detect browser type." - end - end - - def self.supported_browsers - COOKIE_LOCATIONS.keys - end - - def self.detect_browser(db_filename) - db = SQLite3::Database.new(db_filename) - browser = - if has_table?(db, 'moz_cookies') - 'Firefox' - elsif has_table?(db, 'cookies') - 'Chrome' - end - db.close - browser - end - - def self.has_table?(db, table_name) - db.table_info(table_name).size > 0 - end - - def self.most_recently_used_detected_browsers - COOKIE_LOCATIONS.select { |browser, path| - Dir.glob(File.expand_path(path)).any? - }.sort_by { |browser, path| - File.mtime(Dir.glob(File.expand_path(path)).first) - }.reverse - end - - private_class_method :most_recently_used_detected_browsers - end -end diff --git a/lib/cookie_extractor/chrome_cookie_extractor.rb b/lib/cookie_extractor/chrome_cookie_extractor.rb deleted file mode 100644 index 6e11004..0000000 --- a/lib/cookie_extractor/chrome_cookie_extractor.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'sqlite3' - -module CookieExtractor - class ChromeCookieExtractor - include Common - - def initialize(cookie_file) - @cookie_file = cookie_file - end - - def extract - db = SQLite3::Database.new @cookie_file - db.results_as_hash = true - result = [] - db.execute("SELECT * FROM cookies") do |row| - result << [ row['host_key'], - true_false_word(is_domain_wide(row['host_key'])), - row['path'], - true_false_word(row['secure']), - row['expires_utc'], - row['name'], - row['value'] - ].join("\t") - end - db.close - result - end - end -end diff --git a/lib/cookie_extractor/common.rb b/lib/cookie_extractor/common.rb deleted file mode 100644 index a1af08f..0000000 --- a/lib/cookie_extractor/common.rb +++ /dev/null @@ -1,19 +0,0 @@ -module CookieExtractor - module Common - private - - def is_domain_wide(hostname) - hostname[0..0] == "." - end - - def true_false_word(value) - if value == "1" || value == 1 || value == true - "TRUE" - elsif value == "0" || value == 0 || value == false - "FALSE" - else - raise "Invalid value passed to true_false_word: #{value.inspect}" - end - end - end -end diff --git a/lib/cookie_extractor/firefox_cookie_extractor.rb b/lib/cookie_extractor/firefox_cookie_extractor.rb index 68d1252..5072464 100644 --- a/lib/cookie_extractor/firefox_cookie_extractor.rb +++ b/lib/cookie_extractor/firefox_cookie_extractor.rb @@ -2,7 +2,6 @@ require 'sqlite3' module CookieExtractor class FirefoxCookieExtractor - include Common def initialize(cookie_file) @cookie_file = cookie_file @@ -11,9 +10,9 @@ module CookieExtractor def extract db = SQLite3::Database.new @cookie_file db.results_as_hash = true - result = [] + @result = [] db.execute("SELECT * FROM moz_cookies") do |row| - result << [ row['host'], + @result << [ row['host'], true_false_word(is_domain_wide(row['host'])), row['path'], true_false_word(row['isSecure']), @@ -22,8 +21,23 @@ module CookieExtractor row['value'] ].join("\t") end - db.close - result + @result + end + + private + + def is_domain_wide(hostname) + hostname[0..0] == "." + end + + def true_false_word(value) + if value == "1" || value == 1 || value == true + "TRUE" + elsif value == "0" || value == 0 || value == false + "FALSE" + else + raise "Invalid value passed to true_false_word: #{value.inspect}" + end end end end diff --git a/lib/cookie_extractor/version.rb b/lib/cookie_extractor/version.rb index 424c034..56fb8d9 100644 --- a/lib/cookie_extractor/version.rb +++ b/lib/cookie_extractor/version.rb @@ -1,3 +1,3 @@ module CookieExtractor - VERSION = "0.2.0" + VERSION = "0.0.1" end diff --git a/spec/browser_detector_spec.rb b/spec/browser_detector_spec.rb deleted file mode 100644 index 788a371..0000000 --- a/spec/browser_detector_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -require File.join(File.dirname(__FILE__), "spec_helper") - -describe CookieExtractor::BrowserDetector, "determining the correct extractor to use" do - before :each do - @fake_cookie_db = double("cookie database", :close => true) - SQLite3::Database.should_receive(:new). - with('filename'). - and_return(@fake_cookie_db) - end - - describe "given a sqlite database with a 'moz_cookies' table" do - before :each do - @fake_cookie_db.should_receive(:table_info). - with("moz_cookies"). - and_return( - { 'name' => 'some_field', - 'type' => "some_type" }) - end - - it "should return a firefox extractor instance" do - extractor = CookieExtractor::BrowserDetector.new_extractor('filename') - extractor.instance_of?(CookieExtractor::FirefoxCookieExtractor).should be_true - end - end - - describe "given a sqlite database with a 'cookies' table" do - before :each do - @fake_cookie_db.should_receive(:table_info). - with("moz_cookies"). - and_return([]) - @fake_cookie_db.should_receive(:table_info). - with("cookies"). - and_return( - [{ 'name' => 'some_field', - 'type' => "some_type" }]) - end - - it "should return a chrome extractor instance" do - extractor = CookieExtractor::BrowserDetector.new_extractor('filename') - extractor.instance_of?(CookieExtractor::ChromeCookieExtractor).should be_true - end - end -end - -describe CookieExtractor::BrowserDetector, "guessing the location of the cookie file" do - describe "when no cookie files are found in the standard locations" do - before :each do - Dir.stub!(:glob).and_return([]) - end - - it "should raise NoCookieFileFoundException" do - lambda { CookieExtractor::BrowserDetector.guess }. - should raise_error(CookieExtractor::NoCookieFileFoundException) - end - end - - describe "when multiple cookie files are found in the standard locations" do - before :each do - cookie_locations = CookieExtractor::BrowserDetector::COOKIE_LOCATIONS - Dir.stub!(:glob).and_return([cookie_locations['chrome']], - [], - [cookie_locations['firefox']]) - end - - describe "and chrome was the most recently used" do - before :each do - File.should_receive(:mtime).twice.and_return( - Time.parse("July 2 2013 00:00:00"), - Time.parse("July 1 2013 00:00:00")) - end - - it "should build a ChromeCookieExtractor" do - CookieExtractor::BrowserDetector. - should_receive(:browser_extractor). - once.with("chrome") - CookieExtractor::BrowserDetector.guess - end - end - end -end diff --git a/spec/chrome_cookie_extractor_spec.rb b/spec/chrome_cookie_extractor_spec.rb deleted file mode 100644 index d9a13a4..0000000 --- a/spec/chrome_cookie_extractor_spec.rb +++ /dev/null @@ -1,126 +0,0 @@ -require File.join(File.dirname(__FILE__), "spec_helper") - -describe CookieExtractor::ChromeCookieExtractor do - before :each do - @fake_cookie_db = double("cookie database", - :results_as_hash= => true, - :close => true) - SQLite3::Database.should_receive(:new). - with('filename'). - and_return(@fake_cookie_db) - end - - describe "opening and closing a sqlite db" do - before :each do - @fake_cookie_db.should_receive(:execute).and_yield( - { 'host_key' => '.dallien.net', - 'path' => '/', - 'secure' => '0', - 'expires_utc' => '1234567890', - 'name' => 'NAME', - 'value' => 'VALUE'}) - @extractor = CookieExtractor::ChromeCookieExtractor.new('filename') - end - - it "should close the db when finished" do - @fake_cookie_db.should_receive(:close) - @extractor.extract - end - end - - describe "with a cookie that has a host starting with a dot" do - before :each do - @fake_cookie_db.should_receive(:execute).and_yield( - { 'host_key' => '.dallien.net', - 'path' => '/', - 'secure' => '0', - 'expires_utc' => '1234567890', - 'name' => 'NAME', - 'value' => 'VALUE'}) - @extractor = CookieExtractor::ChromeCookieExtractor.new('filename') - @result = @extractor.extract - end - - it "should return one cookie string" do - @result.size.should == 1 - end - - it "should put TRUE in the domain wide field" do - cookie_string = @result.first - cookie_string.split("\t")[1].should == "TRUE" - end - - it "should build the correct cookie string" do - cookie_string = @result.first - cookie_string.should == - ".dallien.net\tTRUE\t/\tFALSE\t1234567890\tNAME\tVALUE" - end - end - - describe "with a cookie that has a host not starting with a dot" do - before :each do - @fake_cookie_db.should_receive(:execute).and_yield( - { 'host_key' => 'jeff.dallien.net', - 'path' => '/path', - 'secure' => '1', - 'expires_utc' => '1234567890', - 'name' => 'NAME', - 'value' => 'VALUE'}) - @extractor = CookieExtractor::ChromeCookieExtractor.new('filename') - @result = @extractor.extract - end - - it "should return one cookie string" do - @result.size.should == 1 - end - - it "should put FALSE in the domain wide field" do - cookie_string = @result.first - cookie_string.split("\t")[1].should == "FALSE" - end - - it "should build the correct cookie string" do - cookie_string = @result.first - cookie_string.should == - "jeff.dallien.net\tFALSE\t/path\tTRUE\t1234567890\tNAME\tVALUE" - end - end - - describe "with a cookie that is not marked as secure" do - before :each do - @fake_cookie_db.should_receive(:execute).and_yield( - { 'host_key' => '.dallien.net', - 'path' => '/', - 'secure' => '0', - 'expires_utc' => '1234567890', - 'name' => 'NAME', - 'value' => 'VALUE'}) - @extractor = CookieExtractor::ChromeCookieExtractor.new('filename') - @result = @extractor.extract - end - - it "should put FALSE in the secure field" do - cookie_string = @result.first - cookie_string.split("\t")[3].should == "FALSE" - end - end - - describe "with a cookie that is marked as secure" do - before :each do - @fake_cookie_db.should_receive(:execute).and_yield( - { 'host_key' => '.dallien.net', - 'path' => '/', - 'secure' => '1', - 'expires_utc' => '1234567890', - 'name' => 'NAME', - 'value' => 'VALUE'}) - @extractor = CookieExtractor::ChromeCookieExtractor.new('filename') - @result = @extractor.extract - end - - it "should put TRUE in the secure field" do - cookie_string = @result.first - cookie_string.split("\t")[3].should == "TRUE" - end - end -end diff --git a/spec/firefox_cookie_extractor_spec.rb b/spec/firefox_cookie_extractor_spec.rb index 8fd4ae5..fb86adf 100644 --- a/spec/firefox_cookie_extractor_spec.rb +++ b/spec/firefox_cookie_extractor_spec.rb @@ -2,32 +2,12 @@ require File.join(File.dirname(__FILE__), "spec_helper") describe CookieExtractor::FirefoxCookieExtractor do before :each do - @fake_cookie_db = double("cookie database", - :results_as_hash= => true, - :close => true) + @fake_cookie_db = double("cookie database", :results_as_hash= => true) SQLite3::Database.should_receive(:new). with('filename'). and_return(@fake_cookie_db) end - describe "opening and closing a sqlite db" do - before :each do - @fake_cookie_db.should_receive(:execute).and_yield( - {'host' => '.dallien.net', - 'path' => '/', - 'isSecure' => '0', - 'expiry' => '1234567890', - 'name' => 'NAME', - 'value' => 'VALUE'}) - @extractor = CookieExtractor::FirefoxCookieExtractor.new('filename') - end - - it "should close the db when finished" do - @fake_cookie_db.should_receive(:close) - @extractor.extract - end - end - describe "with a cookie that has a host starting with a dot" do before :each do @fake_cookie_db.should_receive(:execute).and_yield(