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.
- data/.document +5 -0
- data/.gitignore +6 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/bin/okayu +6 -0
- data/lib/app.rb +25 -0
- data/lib/app_dir.rb +29 -0
- data/lib/client.rb +210 -0
- data/lib/controllers/listeners_controller.rb +18 -0
- data/lib/controllers/live_controller.rb +82 -0
- data/lib/controllers/visitors_controller.rb +19 -0
- data/lib/controllers.rb +3 -0
- data/lib/cookie_thief.rb +172 -0
- data/lib/db/migrate/000_create_lives.rb +14 -0
- data/lib/db/migrate/001_create_comments.rb +22 -0
- data/lib/db/migrate/002_create_visitors.rb +15 -0
- data/lib/db/migrate/003_create_listeners.rb +15 -0
- data/lib/db/migrate/004_create_communities.rb +13 -0
- data/lib/db/migrate/005_create_listeners_lives.rb +12 -0
- data/lib/db/migrate/006_create_lives_visitors.rb +13 -0
- data/lib/factory.rb +16 -0
- data/lib/models/comment.rb +22 -0
- data/lib/models/community.rb +3 -0
- data/lib/models/listener.rb +5 -0
- data/lib/models/live.rb +6 -0
- data/lib/models/visitor.rb +5 -0
- data/lib/models.rb +23 -0
- data/lib/okayu.rb +10 -0
- data/lib/player_status.rb +16 -0
- data/lib/sample_msg +2 -0
- data/lib/thread_info.rb +32 -0
- data/lib/user_info.rb +49 -0
- data/lib/views/app_frame.rb +89 -0
- data/lib/views/color_dialog.rb +56 -0
- data/lib/views/colors.rb +168 -0
- data/lib/views/live_panel.rb +335 -0
- data/lib/views/login_dialog.rb +80 -0
- data/lib/views/main_panel.rb +50 -0
- data/lib/views.rb +11 -0
- data/logged_in_page +893 -0
- data/login_error_page +83 -0
- data/okayu.gemspec +126 -0
- data/tcp_log +0 -0
- data/test/client_test.rb +61 -0
- data/test/cookie_thief_test.rb +36 -0
- data/test/ie_cookie.txt +63 -0
- data/test/okayu_test.rb +8 -0
- data/test/player_status_test.rb +61 -0
- data/test/raw_player_status +2 -0
- data/test/raw_player_status_community_only +3 -0
- data/test/raw_player_status_notlogged_in +2 -0
- data/test/test_helper.rb +11 -0
- data/test/thread_info_test.rb +11 -0
- metadata +193 -0
data/.document
ADDED
data/.gitignore
ADDED
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
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
|
+
|
data/lib/controllers.rb
ADDED
data/lib/cookie_thief.rb
ADDED
@@ -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
|