kentaroi-okayu 0.0.1

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.
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