kentaroi-okayu 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.document +5 -0
  2. data/.gitignore +6 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +18 -0
  5. data/Rakefile +63 -0
  6. data/VERSION +1 -0
  7. data/bin/okayu +6 -0
  8. data/lib/app.rb +25 -0
  9. data/lib/app_dir.rb +29 -0
  10. data/lib/client.rb +210 -0
  11. data/lib/controllers/listeners_controller.rb +18 -0
  12. data/lib/controllers/live_controller.rb +82 -0
  13. data/lib/controllers/visitors_controller.rb +19 -0
  14. data/lib/controllers.rb +3 -0
  15. data/lib/cookie_thief.rb +172 -0
  16. data/lib/db/migrate/000_create_lives.rb +14 -0
  17. data/lib/db/migrate/001_create_comments.rb +22 -0
  18. data/lib/db/migrate/002_create_visitors.rb +15 -0
  19. data/lib/db/migrate/003_create_listeners.rb +15 -0
  20. data/lib/db/migrate/004_create_communities.rb +13 -0
  21. data/lib/db/migrate/005_create_listeners_lives.rb +12 -0
  22. data/lib/db/migrate/006_create_lives_visitors.rb +13 -0
  23. data/lib/factory.rb +16 -0
  24. data/lib/models/comment.rb +22 -0
  25. data/lib/models/community.rb +3 -0
  26. data/lib/models/listener.rb +5 -0
  27. data/lib/models/live.rb +6 -0
  28. data/lib/models/visitor.rb +5 -0
  29. data/lib/models.rb +23 -0
  30. data/lib/okayu.rb +10 -0
  31. data/lib/player_status.rb +16 -0
  32. data/lib/sample_msg +2 -0
  33. data/lib/thread_info.rb +32 -0
  34. data/lib/user_info.rb +49 -0
  35. data/lib/views/app_frame.rb +89 -0
  36. data/lib/views/color_dialog.rb +56 -0
  37. data/lib/views/colors.rb +168 -0
  38. data/lib/views/live_panel.rb +335 -0
  39. data/lib/views/login_dialog.rb +80 -0
  40. data/lib/views/main_panel.rb +50 -0
  41. data/lib/views.rb +11 -0
  42. data/logged_in_page +893 -0
  43. data/login_error_page +83 -0
  44. data/okayu.gemspec +126 -0
  45. data/tcp_log +0 -0
  46. data/test/client_test.rb +61 -0
  47. data/test/cookie_thief_test.rb +36 -0
  48. data/test/ie_cookie.txt +63 -0
  49. data/test/okayu_test.rb +8 -0
  50. data/test/player_status_test.rb +61 -0
  51. data/test/raw_player_status +2 -0
  52. data/test/raw_player_status_community_only +3 -0
  53. data/test/raw_player_status_notlogged_in +2 -0
  54. data/test/test_helper.rb +11 -0
  55. data/test/thread_info_test.rb +11 -0
  56. metadata +193 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ test/id_pass.txt
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Kentaro Imai
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ = okayu
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but
13
+ bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2009 Kentaro Imai. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,63 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "okayu"
8
+ gem.summary = %Q{A comment viewer for http://live.nicovideo.jp/}
9
+ gem.description = %Q{A comment viewer for http://live.nicovideo.jp/}
10
+ gem.email = "kentaroi@gmail.com"
11
+ gem.homepage = "http://github.com/kentaroi/okayu"
12
+ gem.authors = ["Kentaro Imai"]
13
+ gem.add_dependency "mechanize"
14
+ gem.add_dependency "nokogiri"
15
+ gem.add_dependency "facter"
16
+ gem.add_dependency "sqlite3-ruby"
17
+ gem.add_dependency "activerecord", ">=2.2.0"
18
+ gem.add_dependency "wxruby"
19
+ gem.add_development_dependency "thoughtbot-shoulda"
20
+ gem.add_development_dependency "redgreen"
21
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
22
+ end
23
+ rescue LoadError
24
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
25
+ end
26
+
27
+ require 'rake/testtask'
28
+ Rake::TestTask.new(:test) do |test|
29
+ test.libs << 'lib' << 'test'
30
+ test.pattern = 'test/**/*_test.rb'
31
+ test.verbose = true
32
+ end
33
+
34
+ begin
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/*_test.rb'
39
+ test.verbose = true
40
+ end
41
+ rescue LoadError
42
+ task :rcov do
43
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
44
+ end
45
+ end
46
+
47
+ task :test => :check_dependencies
48
+
49
+ task :default => :test
50
+
51
+ require 'rake/rdoctask'
52
+ Rake::RDocTask.new do |rdoc|
53
+ if File.exist?('VERSION')
54
+ version = File.read('VERSION')
55
+ else
56
+ version = ""
57
+ end
58
+
59
+ rdoc.rdoc_dir = 'rdoc'
60
+ rdoc.title = "okayu #{version}"
61
+ rdoc.rdoc_files.include('README*')
62
+ rdoc.rdoc_files.include('lib/**/*.rb')
63
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/okayu ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'okayu.rb'
4
+
5
+ okayu = Okayu::App.new
6
+ okayu.main_loop
data/lib/app.rb ADDED
@@ -0,0 +1,25 @@
1
+ module Okayu
2
+ class App < Wx::App
3
+ def initialize
4
+ @teardowned = false
5
+ super()
6
+ end
7
+
8
+
9
+ def on_init
10
+ UserInfo.load
11
+ Okayu::connect_db
12
+
13
+ frame = AppFrame.new("おかゆ")
14
+ frame.show
15
+ end
16
+
17
+ def on_exit
18
+ unless @teardowned
19
+ UserInfo.save
20
+ @teardowned = true
21
+ puts "teardown!!"
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/app_dir.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'facter'
3
+
4
+ module Okayu
5
+ class AppDir
6
+ if Facter.value(:kernel) == 'Linux'
7
+ if File.exist?(@@app_dir = ENV['HOME'] + '/.okayu')
8
+ else
9
+ Dir.mkdir(@@app_dir)
10
+ end
11
+ elsif Facter.value(:kernel) == 'windows'
12
+ if Facter.value(:operatingsystemrelease).slice(/^\d*/).to_i < 6 # Before Vista
13
+ if File.exist?(@@app_dir = ENV['HOME'] + '/Local Settings/Application Data/okayu')
14
+ else
15
+ Dir.mkdir(@@app_dir)
16
+ end
17
+ else # From Vista
18
+ if File.exist?(@@app_dir = ENV['HOME'] + '/AppData/Local/okayu')
19
+ else
20
+ Dir.mkdir(@@app_dir)
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.to_s
26
+ @@app_dir
27
+ end
28
+ end
29
+ end
data/lib/client.rb ADDED
@@ -0,0 +1,210 @@
1
+ require 'singleton'
2
+ require 'rubygems'
3
+ require 'mechanize'
4
+ require 'thread'
5
+ require 'player_status.rb'
6
+ require 'thread_info.rb'
7
+ require 'ruby-debug'
8
+
9
+ class WWW::Mechanize::CookieJar
10
+ def load_cookie cookie
11
+ @jar = cookie
12
+ end
13
+ end
14
+
15
+
16
+ module Okayu
17
+
18
+ class Channel
19
+ attr_accessor :id, :player_status, :thread, :queue
20
+ def initialize
21
+ @queue = Queue.new
22
+ end
23
+ end
24
+
25
+ class Client
26
+ TOP_URL = 'http://www.nicovideo.jp/'
27
+ LOGIN_URL = 'https://secure.nicovideo.jp/secure/login?site=niconico'
28
+ PLAYER_STATUS_URL = 'http://live.nicovideo.jp/api/getplayerstatus?v='
29
+ @@channels = []
30
+ @@connected = false
31
+
32
+ class << self
33
+ def channels
34
+ @@channels
35
+ end
36
+ def connected?
37
+ @@connected
38
+ end
39
+
40
+ def agent
41
+ @@agent
42
+ end
43
+
44
+ def connect auth={}
45
+ @@agent = WWW::Mechanize.new
46
+ begin
47
+ if auth[:mail] && auth[:password]
48
+ @@login_page = @@agent.post(LOGIN_URL, 'mail' => auth[:mail], 'password' => auth[:password])
49
+ elsif auth[:cookie]
50
+ @@agent.cookie_jar.load_cookiestxt(auth[:cookie])
51
+ @@login_page = @@agent.get(TOP_URL)
52
+ end
53
+
54
+ if @@login_page
55
+ @@connected = true unless @@login_page.forms.first.name == 'login'
56
+ else
57
+ @@connected = false
58
+ end
59
+ rescue
60
+ @@connected = false
61
+ end
62
+ end
63
+
64
+ def live_id_from_url url
65
+ if /lv\d+/ =~ url
66
+ $&
67
+ else
68
+ nil
69
+ end
70
+ end
71
+
72
+ def open live_id, res_from=-200
73
+ if live_id
74
+ channel = Channel.new
75
+ channel.id = live_id
76
+ channel.player_status = get_player_status(live_id)
77
+ if channel.player_status.status
78
+ @@channels << channel
79
+ channel.thread = Thread.new(@@channels.index(channel), channel.player_status, res_from) do |index, ps, res_from|
80
+ begin
81
+ s = TCPSocket.open(ps.addr, ps.port)
82
+ s.print live_query_msg(ps.thread, res_from)
83
+ thread_info = ThreadInfo.new s.gets("\0")
84
+ while true
85
+ @@channels[index].queue.enq s.gets("\0").slice(0...-1)
86
+ end
87
+ ensure
88
+ s.close
89
+ end
90
+ end
91
+ self.new channel
92
+ end
93
+ end
94
+ end
95
+
96
+ def send_comment live_id, comment, mail
97
+ if live_id
98
+ player_status = get_player_status(live_id)
99
+ if player_status.status
100
+ s = TCPSocket.open(player_status.addr, player_status.port)
101
+ s.print live_query_msg(player_status.thread, -0)
102
+ thread_info = ThreadInfo.new s.gets("\0")
103
+ postkey = get_postkey(thread_info)
104
+ s.print(send_comment_msg(comment, mail, player_status, thread_info, postkey))
105
+ s.close
106
+ end
107
+ end
108
+ end
109
+
110
+ def get_player_status live_id
111
+ PlayerStatus.new(@@agent.get(PLAYER_STATUS_URL + live_id).body)
112
+ end
113
+
114
+ def live_query_msg thread, res_from
115
+ %Q[<thread thread="#{thread}" res_from="#{res_from}" version="20061206" />\0]
116
+ end
117
+
118
+ def postkey_url thread, last_res
119
+ block_no = last_res.to_i / 100
120
+ %Q[http://live.nicovideo.jp/api/getpostkey?thread=#{thread}&block_no=#{block_no}]
121
+ end
122
+
123
+ def get_postkey thread_info
124
+ postkey_page = @@agent.get(postkey_url(thread_info.thread, thread_info.last_res))
125
+ if /postkey=(.+)/ =~ postkey_page.body
126
+ $1
127
+ else
128
+ nil
129
+ end
130
+ end
131
+
132
+ def send_comment_msg comment, mail, player_status, thread_info, postkey
133
+ vpos = ((Time.new - Time.at(player_status.open_time.to_i)) * 100).to_i
134
+ %Q[<chat thread="#{thread_info.thread}" ticket="#{thread_info.ticket}" vpos="#{vpos}" postkey="#{postkey}" mail="#{mail}" user_id="#{player_status.user_id}" premium="#{player_status.is_premium}">#{comment}</chat>\0]
135
+ end
136
+
137
+ def close channel
138
+ index = @@channels.index(channel)
139
+ @@channels[index].thread.kill
140
+ @@channels[index] = nil
141
+ end
142
+
143
+ def close_all
144
+ @@channels.each do |channel|
145
+ channel.thread.kill
146
+ end
147
+ end
148
+ end
149
+
150
+ # instance methods
151
+ #
152
+ attr_reader :channel
153
+ def initialize channel
154
+ @channel = channel
155
+ end
156
+
157
+ def close
158
+ self.class.close @channel
159
+ end
160
+
161
+ def comments
162
+ comments = []
163
+ until @channel.queue.empty?
164
+ comments << @channel.queue.deq
165
+ end
166
+ comments.map{|comment| comment_hash_from_xml(comment)}
167
+ end
168
+
169
+ def comment_hash_from_xml xml
170
+ hash = Hash.new
171
+ reader = Nokogiri::XML::Reader(xml)
172
+ reader.read
173
+ if reader.name == 'chat'
174
+ hash[:commented_at] = Time.at(reader.attribute('date').to_i)
175
+ hash[:mail] = reader.attribute('mail') || ''
176
+ hash[:number] = reader.attribute('no')
177
+ hash[:user_id] = reader.attribute('user_id')
178
+ case reader.attribute('premium')
179
+ when 1
180
+ hash[:premium] = true
181
+ hash[:owner] = false
182
+ hash[:command] = false
183
+ when 2
184
+ hash[:premium] = true
185
+ hash[:owner] = true
186
+ hash[:command] = true
187
+ when 3
188
+ hash[:premium] = true
189
+ hash[:owner] = true
190
+ hash[:command] = false
191
+ else
192
+ hash[:premium] = false
193
+ hash[:owner] = false
194
+ hash[:command] = false
195
+ end
196
+ reader.read
197
+ hash[:message] = reader.value
198
+ end
199
+ hash
200
+ end
201
+
202
+ def community
203
+ @channel.player_status.default_community
204
+ end
205
+
206
+ def base_time
207
+ Time.at(@channel.player_status.base_time.to_i)
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,18 @@
1
+ module Okayu
2
+ class ListenersController
3
+ def self.set_color_index params
4
+ begin
5
+ listener = Listener.find params[:id]
6
+ listener.color_index = params[:color_index]
7
+ listener.save!
8
+ rescue
9
+ return false
10
+ end
11
+ true
12
+ end
13
+
14
+ def self.unset_color_index params
15
+ self.set_color_index params
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,82 @@
1
+ module Okayu
2
+ class LiveController
3
+ def initialize live_id
4
+ @live_id = live_id
5
+ if @client = Client.open(live_id)
6
+ @live_now = true
7
+ if @live = Live.find(:first, :conditions => {:live_id => @live_id})
8
+ @community = @live.community
9
+ else
10
+ @live = Live.new
11
+ @live.live_id = @live_id
12
+ @live.base_time = @client.base_time
13
+ unless @community = Community.find(:first, :conditions => {:community_id => @client.community})
14
+ @community = Community.new
15
+ @community.community_id = @client.community
16
+ @community.save
17
+ end
18
+ @live.community = @community
19
+ @live.save
20
+ end
21
+ else
22
+ @live_now = false
23
+ if @live = Live.find(:first, :conditions => {:live_id => @live_id})
24
+ @community = @live.community
25
+ else
26
+ @community = nil
27
+ end
28
+ end
29
+ end
30
+
31
+ def valid?
32
+ !!@live
33
+ end
34
+
35
+ def live_now?
36
+ @live_now
37
+ end
38
+
39
+
40
+ def base_time
41
+ @live.base_time
42
+ end
43
+
44
+ def comments
45
+ @live.comments
46
+ end
47
+
48
+ def new_comments
49
+ if @live_now
50
+ @client.comments.map{|comment_hash|
51
+ if Comment.find :first, :conditions => {:live_id => @live_id, :number => comment_hash[:number]}
52
+ nil
53
+ else
54
+ comment = Comment.new(comment_hash)
55
+ comment.community = @community
56
+ comment.live = @live
57
+ if comment_hash[:user_id]
58
+ if visitor = Visitor.find(:first, :conditions => {:user_id => comment_hash[:user_id]})
59
+ comment.visitor = visitor
60
+ if visitor.listener
61
+ comment.listener = visitor.listener
62
+ end
63
+ else
64
+ comment.create_visitor(:user_id => comment_hash[:user_id])
65
+ end
66
+ end
67
+ comment.save
68
+ comment
69
+ end
70
+ }.compact
71
+ else
72
+ []
73
+ end
74
+ end
75
+
76
+ def close
77
+ if @live_now && @client
78
+ @client.close
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,19 @@
1
+ module Okayu
2
+ class VisitorsController
3
+ def self.set_color_index params
4
+ begin
5
+ visitor = Visitor.find params[:id]
6
+ visitor.color_index = params[:color_index]
7
+ visitor.save!
8
+ rescue
9
+ return false
10
+ end
11
+ true
12
+ end
13
+
14
+ def self.unset_color_index params
15
+ self.set_color_index params
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,3 @@
1
+ require 'controllers/live_controller.rb'
2
+ require 'controllers/listeners_controller.rb'
3
+ require 'controllers/visitors_controller.rb'
@@ -0,0 +1,172 @@
1
+ require 'rubygems'
2
+ require 'sqlite3'
3
+
4
+ module Okayu
5
+ class CookieThief
6
+ class << self
7
+ def get args
8
+ cookiestxt_from_array(send_query(sql_from_hash(args)))
9
+ end
10
+
11
+ def send_query sql
12
+ db = SQLite3::Database.new(cookies_filename)
13
+ cookie_array = db.execute(sql).first
14
+ db.close
15
+ cookie_array
16
+ end
17
+
18
+ def cookiestxt_from_array cookie_array
19
+ if cookie_array
20
+ cookie_array[1] = 'TRUE'
21
+ cookie_array[3] = cookie_array[3] != '0' ? 'TRUE' : 'FALSE'
22
+ cookie_array.join("\t") + "\n"
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ class IECookieThief < Okayu::CookieThief
29
+ class << self
30
+ def get args
31
+ cookies = []
32
+ if args[:host]
33
+ cookie_filenames(args[:host]).map do |cookie_filename|
34
+ cookies = cookies_from_cookie_file(cookie_filename)
35
+ end
36
+ end
37
+
38
+ if args[:name]
39
+ cookies = select_by_name(cookies, args[:name])
40
+ end
41
+ cookies.join("\n")
42
+ end
43
+
44
+ def select_by_name cookies, name
45
+ cookies.select{|cookie|cookie.split("\t")[5] == name}
46
+ end
47
+
48
+ def cookies_from_cookie_file filename
49
+ cookies = []
50
+ File.open filename, 'r' do |f|
51
+ cookies = cookies_from_cookies_string(f.read)
52
+ end
53
+ cookies
54
+ end
55
+
56
+ def cookies_from_cookies_string str
57
+ str.split(/^\*\n/m).map{|s|cookie_from_cookie_string(s)}
58
+ end
59
+
60
+ def cookie_from_cookie_string str
61
+ a = str.split(/\n/m)
62
+ name = a[0]
63
+ value = a[1]
64
+ host = "." + a[2].sub(%r{/.*$}, '')
65
+ path = "/" + a[2].sub(%r{^.*/}, '')
66
+ expiry = ((10**-7) * (a[4].to_i * (2**32) + a[3].to_i) - 11644473600).to_i # 11644473600 is midnight January 1, 1970
67
+ "#{host}\tTRUE\t#{path}\tTRUE\t#{expiry}\t#{name}\t#{value}"
68
+ end
69
+
70
+ def cookie_filenames host
71
+ cookie_filename = []
72
+ case Facter.value(:kernel)
73
+ when 'windows'
74
+ cookie_filenames = Dir.glob("#{ENV['HOME']}/AppData/Roaming/Microsoft/Windows/Cookies/Low/*").select{|path| path =~ /#{host_main_str(host)}/}
75
+ end
76
+ cookie_filenames
77
+ end
78
+ def host_main_str host
79
+ host_main_str = host.sub(/^\./, '').sub(/\..*$/, '')
80
+ end
81
+ end
82
+ end
83
+
84
+ class FirefoxCookieThief < Okayu::CookieThief
85
+ class << self
86
+ def sql_from_hash args
87
+ sql = "select host, isHttpOnly, path, isSecure, expiry, name, value from moz_cookies where "
88
+ sql << args.map{|key, value|"#{key.to_s} = '#{value}'"}.join(' and ')
89
+ sql << ';'
90
+ end
91
+
92
+ def cookies_filename
93
+ cookies_filename = nil
94
+ case Facter.value(:kernel)
95
+ when 'Linux'
96
+ if dir = Dir.glob("#{ENV['HOME']}/.mozilla/firefox/*").select{|path| path =~ /.*\.default$/}.first
97
+ if File.exist?("#{dir}/cookies.sqlite")
98
+ cookies_filename = "#{dir}/cookies.sqlite"
99
+ end
100
+ end
101
+ when 'windows'
102
+ if Facter.value(:operatingsystemrelease).to_i < 6 # before Vista
103
+ unless dir = Dir.glob("#{ENV['HOME']}/Application Data/Mozilla/Firefox/Profiles/*").select{|path| path =~ /.*\.default$/}.first
104
+ dir = Dir.glob("#{ENV['HOME']}/../Administrator/Application Data/Mozilla/Firefox/Profiles/*").select{|path| path =~ /.*\.default$/}.first
105
+ end
106
+ if dir && File.exist?("#{dir}/cookies.sqlite")
107
+ cookies_filename = "#{dir}/cookies.sqlite"
108
+ end
109
+ else # from Vista
110
+ if dir = Dir.glob("#{ENV['HOME']}/AppData/Roaming/Mozilla/Firefox/Profiles/*").select{|path| path =~ /.*\.default$/}.first
111
+ if File.exist?("#{dir}/cookies.sqlite")
112
+ cookies_filename = "#{dir}/cookies.sqlite"
113
+ end
114
+ end
115
+ end
116
+ end
117
+ cookies_filename
118
+ end
119
+ end
120
+ end
121
+
122
+ class ChromeCookieThief < Okayu::CookieThief
123
+ class << self
124
+ def cookies_filename
125
+ cookies_filename = nil
126
+ case Facter.value(:kernel)
127
+ when 'windows'
128
+ if Facter.value(:operatingsystemrelease).to_i < 6 # before Vista
129
+ if File.exist?(filename = "#{ENV['HOME']}/LocalApplication Data/Google/Chrome/User Data/Default/Cookies")
130
+ cookies_filename = filename
131
+ elsif File.exist?(filename = "#{ENV['HOME']}/../Administrator/LocalApplication Data/Google/Chrome/User Data/Default/Cookies")
132
+ cookies_filename = filename
133
+ end
134
+ else # from Vista
135
+ if File.exist?(filename = "#{ENV['HOME']}/AppData/Local/Google/Chrome/User Data/Default/Cookies")
136
+ cookies_filename = filename
137
+ end
138
+ end
139
+ end
140
+ cookies_filename
141
+ end
142
+
143
+ def sql_from_hash args
144
+ args = chrome_from_firefox(args)
145
+ sql = "select host_key, httponly, path, secure, expires_utc, name, value from cookies where "
146
+ sql << args.map{|key, value|"#{key.to_s} = '#{value}'"}.join(' and ')
147
+ sql << ';'
148
+ end
149
+
150
+ def chrome_from_firefox args
151
+ chrome_args = {}
152
+ args.each do |key, value|
153
+ case key
154
+ when :host
155
+ chrome_args[:host_key] = value
156
+ when :isHttpOnly
157
+ chrome_args[:httponly] = value
158
+ when :isSecure
159
+ chrome_args[:secure] = value
160
+ when :expiry
161
+ chrome_args[:expires_utc] = value
162
+ else
163
+ chrome_args[key] = value
164
+ end
165
+ end
166
+ chrome_args
167
+ end
168
+
169
+
170
+ end
171
+ end
172
+ end