my_bitcasa 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/example/delete.rb +17 -0
- data/example/directory.rb +15 -0
- data/example/download_photo_legacy_thumbs.rb +27 -0
- data/example/download_photo_thumbs.rb +27 -0
- data/example/download_photos.rb +27 -0
- data/example/list.rb +15 -0
- data/example/login.rb +15 -0
- data/example/mkdir.rb +15 -0
- data/example/recursive_directory.rb +27 -0
- data/example/rename.rb +19 -0
- data/example/setting.yml.example +2 -0
- data/example/share.rb +19 -0
- data/example/upload.rb +18 -0
- data/example/zip_download_photos.rb +26 -0
- data/lib/my_bitcasa/api_error.rb +6 -0
- data/lib/my_bitcasa/authorization_error.rb +6 -0
- data/lib/my_bitcasa/bitcasa_base.rb +11 -0
- data/lib/my_bitcasa/bitcasa_drive.rb +15 -0
- data/lib/my_bitcasa/bitcasa_file.rb +61 -0
- data/lib/my_bitcasa/bitcasa_folder.rb +46 -0
- data/lib/my_bitcasa/bitcasa_item.rb +38 -0
- data/lib/my_bitcasa/bitcasa_share.rb +13 -0
- data/lib/my_bitcasa/connection.rb +102 -0
- data/lib/my_bitcasa/connection_error.rb +6 -0
- data/lib/my_bitcasa/connection_finalizer.rb +12 -0
- data/lib/my_bitcasa/connection_pool.rb +26 -0
- data/lib/my_bitcasa/data_accessor.rb +31 -0
- data/lib/my_bitcasa/delete.rb +26 -0
- data/lib/my_bitcasa/directory.rb +59 -0
- data/lib/my_bitcasa/download.rb +105 -0
- data/lib/my_bitcasa/downloadable.rb +60 -0
- data/lib/my_bitcasa/error.rb +4 -0
- data/lib/my_bitcasa/legacy_thumbnail.rb +33 -0
- data/lib/my_bitcasa/list.rb +71 -0
- data/lib/my_bitcasa/login_engine/phantomjs.rb +29 -0
- data/lib/my_bitcasa/login_engine/phantomjs_login.js +58 -0
- data/lib/my_bitcasa/login_engine/pure.rb +47 -0
- data/lib/my_bitcasa/login_engine/selenium.rb +87 -0
- data/lib/my_bitcasa/login_engine.rb +15 -0
- data/lib/my_bitcasa/mkdir.rb +26 -0
- data/lib/my_bitcasa/profile.rb +39 -0
- data/lib/my_bitcasa/rename.rb +24 -0
- data/lib/my_bitcasa/response_format_error.rb +6 -0
- data/lib/my_bitcasa/response_middleware.rb +39 -0
- data/lib/my_bitcasa/share.rb +30 -0
- data/lib/my_bitcasa/thumbnail.rb +33 -0
- data/lib/my_bitcasa/upload.rb +42 -0
- data/lib/my_bitcasa/version.rb +3 -0
- data/lib/my_bitcasa/zip_download.rb +21 -0
- data/lib/my_bitcasa.rb +38 -0
- data/my_bitcasa.gemspec +28 -0
- metadata +198 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'my_bitcasa/connection_finalizer'
|
2
|
+
require 'my_bitcasa/response_format_error'
|
3
|
+
require 'my_bitcasa/connection_error'
|
4
|
+
require 'my_bitcasa/authorization_error'
|
5
|
+
require 'faraday'
|
6
|
+
require 'faraday_middleware'
|
7
|
+
require 'my_bitcasa/response_middleware'
|
8
|
+
require 'active_support/core_ext'
|
9
|
+
require 'uri'
|
10
|
+
|
11
|
+
module MyBitcasa
|
12
|
+
class Connection < Faraday::Connection
|
13
|
+
attr_writer :login_engine
|
14
|
+
attr_accessor :cookie
|
15
|
+
|
16
|
+
def initialize(user: nil, password: nil, multipart: false)
|
17
|
+
super(:url => 'https://my.bitcasa.com') do |conn|
|
18
|
+
conn.use FaradayMiddleware::FollowRedirects
|
19
|
+
if multipart
|
20
|
+
conn.request :multipart
|
21
|
+
else
|
22
|
+
conn.request :url_encoded
|
23
|
+
end
|
24
|
+
#conn.response :logger
|
25
|
+
conn.response :my_bitcasa
|
26
|
+
conn.adapter Faraday.default_adapter
|
27
|
+
end
|
28
|
+
@headers[:user_agent] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:10.0.2) Gecko/20100101 Firefox/10.0.2"
|
29
|
+
|
30
|
+
yield self if block_given?
|
31
|
+
|
32
|
+
login(user, password) if user && password
|
33
|
+
ensure
|
34
|
+
#ObjectSpace.define_finalizer(self) { logout! }
|
35
|
+
end
|
36
|
+
|
37
|
+
def login_engine
|
38
|
+
@login_engine ||= LoginEngine.autodetect.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def login(user, password)
|
42
|
+
login_engine.login(user, password)
|
43
|
+
@cookie = login_engine.cookie
|
44
|
+
end
|
45
|
+
|
46
|
+
def loggedin?
|
47
|
+
!!@cookie
|
48
|
+
end
|
49
|
+
|
50
|
+
def logout!
|
51
|
+
if loggedin?
|
52
|
+
self.get("/logout")
|
53
|
+
@cookie = nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
[:get, :post, :put, :delete, :head, :patch].each do |method|
|
58
|
+
class_eval %{
|
59
|
+
def #{method}_with_session(*args, &block)
|
60
|
+
res = #{method}_without_session(*args, &block)
|
61
|
+
_after_request(:#{method}, res)
|
62
|
+
res
|
63
|
+
end
|
64
|
+
alias_method_chain :#{method}, :session
|
65
|
+
|
66
|
+
def #{method}_with_loggedin(*args, &block)
|
67
|
+
_before_request(:#{method}, *args)
|
68
|
+
res = #{method}_without_loggedin(*args) {|req|
|
69
|
+
req.headers["Cookie"] = @cookie if @cookie
|
70
|
+
block.call(req) if block
|
71
|
+
}
|
72
|
+
res
|
73
|
+
end
|
74
|
+
alias_method_chain :#{method}, :loggedin
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def multipart
|
79
|
+
@multipart ||= self.class.new(multipart: true)
|
80
|
+
@multipart.cookie = self.cookie
|
81
|
+
@multipart
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def _before_request(method, *args)
|
86
|
+
raise AuthorizationError, "login required" unless loggedin?
|
87
|
+
end
|
88
|
+
|
89
|
+
def _after_request(method, res)
|
90
|
+
@cookie = res.headers["set-cookie"] || @cookie
|
91
|
+
if @cookie
|
92
|
+
@cookie.sub!(/; Domain=(\.?my)?.bitcasa.com; Path=\//, "")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class << self
|
97
|
+
def uri_encode(path)
|
98
|
+
URI.encode(path).gsub("[", "%5B").gsub("]", "%5D")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'my_bitcasa/connection'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
|
4
|
+
module MyBitcasa
|
5
|
+
module ConnectionPool
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
attr_writer :connection
|
9
|
+
|
10
|
+
def connection
|
11
|
+
@connection || self.class.connection
|
12
|
+
end
|
13
|
+
|
14
|
+
def multipart_connection
|
15
|
+
connection.multipart
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
mattr_accessor :connection
|
20
|
+
|
21
|
+
def establish_connection(*args)
|
22
|
+
@@connection = Connection.new(*args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module MyBitcasa
|
2
|
+
module DataAccessor
|
3
|
+
def data_value_reader(key)
|
4
|
+
class_eval %{
|
5
|
+
def #{key}
|
6
|
+
@data["#{key}"]
|
7
|
+
end
|
8
|
+
}
|
9
|
+
end
|
10
|
+
private :data_value_reader
|
11
|
+
|
12
|
+
def data_bool_reader(key)
|
13
|
+
class_eval %{
|
14
|
+
def #{key}?
|
15
|
+
!!@data["#{key}"]
|
16
|
+
end
|
17
|
+
}
|
18
|
+
end
|
19
|
+
private :data_bool_reader
|
20
|
+
|
21
|
+
def data_reader(key)
|
22
|
+
key, question = key.to_s.split("?", -1)
|
23
|
+
if question
|
24
|
+
data_bool_reader(key)
|
25
|
+
else
|
26
|
+
data_value_reader(key)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
private :data_reader
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'my_bitcasa/connection_pool'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module MyBitcasa
|
5
|
+
class Delete
|
6
|
+
include ConnectionPool
|
7
|
+
|
8
|
+
def initialize(*paths)
|
9
|
+
@paths = paths.flatten
|
10
|
+
end
|
11
|
+
|
12
|
+
def delete
|
13
|
+
res = connection.post do |req|
|
14
|
+
req.url "/delete"
|
15
|
+
req.body = {
|
16
|
+
selection: JSON.generate({
|
17
|
+
paths: @paths,
|
18
|
+
albums: {},
|
19
|
+
artists: [],
|
20
|
+
photo_albums: [],
|
21
|
+
}),
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'my_bitcasa/connection_pool'
|
2
|
+
require 'my_bitcasa/bitcasa_item'
|
3
|
+
|
4
|
+
module MyBitcasa
|
5
|
+
class Directory
|
6
|
+
include ConnectionPool
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_accessor :path
|
10
|
+
attr_accessor :top
|
11
|
+
attr_accessor :bottom
|
12
|
+
attr_accessor :sort_column
|
13
|
+
attr_accessor :sort_ascending
|
14
|
+
attr_accessor :show_incomplete
|
15
|
+
attr_accessor :seamless
|
16
|
+
|
17
|
+
def initialize(path, top: 0, bottom: 500, sort_column: :name, sort_ascending: true, show_incomplete: true, seamless: true)
|
18
|
+
@path = path.sub(/^\/?/, "/")
|
19
|
+
@top = top
|
20
|
+
@bottom = bottom
|
21
|
+
@sort_column = sort_column
|
22
|
+
@sort_ascending = sort_ascending
|
23
|
+
@show_incomplete = show_incomplete
|
24
|
+
@seamless = seamless
|
25
|
+
end
|
26
|
+
|
27
|
+
def each
|
28
|
+
top = @top
|
29
|
+
|
30
|
+
begin
|
31
|
+
res = connection.get {|req|
|
32
|
+
req.url Connection.uri_encode("/directory#{@path}")
|
33
|
+
req.params = {
|
34
|
+
top: top,
|
35
|
+
bottom: @bottom,
|
36
|
+
sort_column: @sort_column,
|
37
|
+
sort_ascending: @sort_ascending,
|
38
|
+
"show-incomplete" => @show_incomplete,
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
sentinel = res.body["sentinel"]
|
43
|
+
length = res.body["length"]
|
44
|
+
top = res.body["range"]["top"]
|
45
|
+
bottom = res.body["range"]["bottom"]
|
46
|
+
name = res.body["name"]
|
47
|
+
items = res.body["items"]
|
48
|
+
|
49
|
+
items.each do |item|
|
50
|
+
yield BitcasaItem.create(item)
|
51
|
+
end
|
52
|
+
|
53
|
+
if length<=bottom
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end while @seamless
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'my_bitcasa/connection_pool'
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
4
|
+
require 'cgi'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'tempfile'
|
7
|
+
|
8
|
+
module MyBitcasa
|
9
|
+
class Download
|
10
|
+
include ConnectionPool
|
11
|
+
|
12
|
+
def initialize(path, params, basename)
|
13
|
+
@path = path
|
14
|
+
@params = params
|
15
|
+
@basename = basename
|
16
|
+
@req_class = Net::HTTP::Get
|
17
|
+
end
|
18
|
+
|
19
|
+
def stream(&block)
|
20
|
+
# path
|
21
|
+
query = @params.map{|k,v|
|
22
|
+
"#{k}=#{CGI.escape(v.to_s)}"
|
23
|
+
}.join("&")
|
24
|
+
|
25
|
+
# headers
|
26
|
+
headers = connection.headers.dup
|
27
|
+
headers["Cookie"] = connection.cookie
|
28
|
+
|
29
|
+
# body
|
30
|
+
body = nil
|
31
|
+
if @body
|
32
|
+
body = @body.map{|k,v|
|
33
|
+
"#{k}=#{CGI.escape(v.to_s)}"
|
34
|
+
}.join("&")
|
35
|
+
end
|
36
|
+
|
37
|
+
# http
|
38
|
+
http = Net::HTTP.new(connection.url_prefix.host, connection.url_prefix.port)
|
39
|
+
http.use_ssl = connection.url_prefix.scheme=="https"
|
40
|
+
|
41
|
+
# request
|
42
|
+
req = @req_class.new(@path+"?"+query, headers)
|
43
|
+
http.request(req, body) do |res|
|
44
|
+
case res
|
45
|
+
when Net::HTTPSuccess
|
46
|
+
# 200 OK
|
47
|
+
when Net::HTTPUnauthorized
|
48
|
+
# 401 Auhorization error
|
49
|
+
raise AuthorizationError, "login required"
|
50
|
+
else
|
51
|
+
# other status
|
52
|
+
raise ConnectionError, "response status code: #{res.code}"
|
53
|
+
end
|
54
|
+
res.read_body(&block)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def save(dest_path, use_tempfile=true)
|
59
|
+
# normalize dest path
|
60
|
+
if File.directory?(dest_path)
|
61
|
+
dest_path = dest_path.sub(/\/?$/, "/") + @basename
|
62
|
+
end
|
63
|
+
|
64
|
+
# check dest dir
|
65
|
+
dest_dir = File.dirname(dest_path)
|
66
|
+
unless File.directory?(dest_dir)
|
67
|
+
raise Errno::ENOENT, "No such directory - #{dest_dir}"
|
68
|
+
end
|
69
|
+
unless File.writable?(dest_dir)
|
70
|
+
raise Errno::EACCES, "Permission denied - #{dest_dir}"
|
71
|
+
end
|
72
|
+
|
73
|
+
# download
|
74
|
+
if use_tempfile
|
75
|
+
# use tempfile
|
76
|
+
temp_path = nil
|
77
|
+
begin
|
78
|
+
Tempfile.open("bitcasa_tempfile_") {|f|
|
79
|
+
temp_path = f.path
|
80
|
+
self.stream {|x|
|
81
|
+
f.write(x)
|
82
|
+
}
|
83
|
+
}
|
84
|
+
FileUtils.mv(temp_path, dest_path)
|
85
|
+
ensure
|
86
|
+
if temp_path && File.file?(temp_path)
|
87
|
+
File.unlink(temp_path) rescue nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
else
|
91
|
+
# direct write
|
92
|
+
open(dest_path, "w") {|f|
|
93
|
+
self.stream {|x|
|
94
|
+
f.write(x)
|
95
|
+
}
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
# chmod
|
100
|
+
File.chmod(0644, dest_path)
|
101
|
+
|
102
|
+
dest_path
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'my_bitcasa/download'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
require 'cgi'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
module MyBitcasa
|
8
|
+
module Downloadable
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
def stream(&block)
|
12
|
+
# path
|
13
|
+
download = Download.new(_download_path, _download_params)
|
14
|
+
download.stream(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def save(dest_path, use_tempfile=true)
|
18
|
+
download = Download.new(_download_path, _download_params, _download_basename)
|
19
|
+
download.save(dest_path, use_tempfile)
|
20
|
+
end
|
21
|
+
|
22
|
+
# downloadable info
|
23
|
+
|
24
|
+
def _download_path
|
25
|
+
path_proc = self.class.downloadable_path_proc
|
26
|
+
instance_eval &path_proc
|
27
|
+
end
|
28
|
+
private :_download_path
|
29
|
+
|
30
|
+
def _download_params
|
31
|
+
params_proc = self.class.downloadable_params_proc
|
32
|
+
instance_eval ¶ms_proc
|
33
|
+
end
|
34
|
+
private :_download_params
|
35
|
+
|
36
|
+
def _download_basename
|
37
|
+
basename_proc = self.class.downloadable_basename_proc
|
38
|
+
instance_eval &basename_proc
|
39
|
+
end
|
40
|
+
private :_download_basename
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
attr_reader :downloadable_path_proc
|
44
|
+
attr_reader :downloadable_params_proc
|
45
|
+
attr_reader :downloadable_basename_proc
|
46
|
+
|
47
|
+
def downloadable_path(&block)
|
48
|
+
@downloadable_path_proc = block
|
49
|
+
end
|
50
|
+
|
51
|
+
def downloadable_params(&block)
|
52
|
+
@downloadable_params_proc = block
|
53
|
+
end
|
54
|
+
|
55
|
+
def downloadable_basename(&block)
|
56
|
+
@downloadable_basename_proc = block
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'my_bitcasa/connection_pool'
|
2
|
+
require 'my_bitcasa/downloadable'
|
3
|
+
|
4
|
+
module MyBitcasa
|
5
|
+
class LegacyThumbnail
|
6
|
+
include ConnectionPool
|
7
|
+
include Downloadable
|
8
|
+
|
9
|
+
THUMB_SIZE = {
|
10
|
+
small: "35x35c",
|
11
|
+
medium: "150x150c",
|
12
|
+
large: "260x260c",
|
13
|
+
preview: "1024x768s",
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
downloadable_path {
|
17
|
+
"/file/#{@file.id}/thumbnail/#{@specific_size}.png"
|
18
|
+
}
|
19
|
+
downloadable_params {{
|
20
|
+
size: @file.size,
|
21
|
+
mime: @file.mime,
|
22
|
+
}}
|
23
|
+
downloadable_basename {
|
24
|
+
name = File.basename(@file.name, "."+@file.extension)
|
25
|
+
"#{name}_#{@specific_size}.png"
|
26
|
+
}
|
27
|
+
|
28
|
+
def initialize(file, size=:small)
|
29
|
+
@file = file
|
30
|
+
@specific_size = THUMB_SIZE[size] || size
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'my_bitcasa/connection_pool'
|
2
|
+
require 'my_bitcasa/bitcasa_item'
|
3
|
+
|
4
|
+
module MyBitcasa
|
5
|
+
class List
|
6
|
+
include ConnectionPool
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
EVERYTHING = "everything".freeze
|
10
|
+
PHOTOS = "photos".freeze
|
11
|
+
MUSIC = "music".freeze
|
12
|
+
MUSIC_ALBUMS = "music/albums".freeze
|
13
|
+
MUSIC_ARTISTS = "music/artists".freeze
|
14
|
+
VIDEOS = "videos".freeze
|
15
|
+
DOCUMENTS = "documents".freeze
|
16
|
+
|
17
|
+
TYPES = Set[EVERYTHING, PHOTOS, MUSIC, MUSIC_ALBUMS, MUSIC_ARTISTS, VIDEOS, DOCUMENTS].freeze
|
18
|
+
|
19
|
+
attr_accessor :type
|
20
|
+
attr_accessor :search
|
21
|
+
attr_accessor :top
|
22
|
+
attr_accessor :bottom
|
23
|
+
attr_accessor :sort_column
|
24
|
+
attr_accessor :sort_ascending
|
25
|
+
attr_accessor :seamless
|
26
|
+
|
27
|
+
def initialize(type: EVERYTHING, search: nil, top: 0, bottom: 500, sort_column: :name, sort_ascending: true, seamless: true)
|
28
|
+
raise "type error: #{type}" unless TYPES.include?(type)
|
29
|
+
|
30
|
+
@type = type
|
31
|
+
@search = search
|
32
|
+
@top = top
|
33
|
+
@bottom = bottom
|
34
|
+
@sort_column = sort_column
|
35
|
+
@sort_ascending = sort_ascending
|
36
|
+
@seamless = seamless
|
37
|
+
end
|
38
|
+
|
39
|
+
def each
|
40
|
+
top = @top
|
41
|
+
|
42
|
+
begin
|
43
|
+
res = connection.get {|req|
|
44
|
+
req.url "/list/#{@type}"
|
45
|
+
req.params = {
|
46
|
+
search: @search,
|
47
|
+
top: top,
|
48
|
+
bottom: @bottom,
|
49
|
+
sort_column: @sort_column,
|
50
|
+
sort_ascending: @sort_ascending,
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
sentinel = res.body["sentinel"]
|
55
|
+
length = res.body["length"]
|
56
|
+
top = res.body["range"]["top"]
|
57
|
+
bottom = res.body["range"]["bottom"]
|
58
|
+
name = res.body["name"]
|
59
|
+
items = res.body["items"]
|
60
|
+
|
61
|
+
items.each do |item|
|
62
|
+
yield BitcasaItem.create(item)
|
63
|
+
end
|
64
|
+
|
65
|
+
if length<=bottom
|
66
|
+
break
|
67
|
+
end
|
68
|
+
end while @seamless
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'my_bitcasa/authorization_error'
|
2
|
+
|
3
|
+
module MyBitcasa
|
4
|
+
module LoginEngine
|
5
|
+
class Phantomjs
|
6
|
+
attr_reader :cookie
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
require 'phantomjs'
|
10
|
+
end
|
11
|
+
|
12
|
+
def login(user, password)
|
13
|
+
js_path = ::File.expand_path("phantomjs_login.js", ::File.dirname(__FILE__))
|
14
|
+
cookie = ::Phantomjs.run(js_path, user, password)
|
15
|
+
cookie = cookie.to_s.strip
|
16
|
+
if cookie.length==0
|
17
|
+
raise AuthorizationError, "login failure"
|
18
|
+
end
|
19
|
+
@cookie = cookie
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def available?
|
24
|
+
new && true rescue false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
var page = require('webpage').create();
|
2
|
+
var args = require('system').args;
|
3
|
+
|
4
|
+
var user = args[1]==void 0 ? "" : args[1];
|
5
|
+
var password = args[2]==void 0 ? "" : args[2];
|
6
|
+
|
7
|
+
page.onInitialized = function() {
|
8
|
+
page.evaluate(function() {
|
9
|
+
document.addEventListener('DOMContentLoaded', function() {
|
10
|
+
window.callPhantom('DOMContentLoaded');
|
11
|
+
}, false);
|
12
|
+
});
|
13
|
+
};
|
14
|
+
|
15
|
+
var funcs = function(funcs) {
|
16
|
+
this.funcs = funcs;
|
17
|
+
this.init();
|
18
|
+
};
|
19
|
+
funcs.prototype = {
|
20
|
+
init: function() {
|
21
|
+
var self = this;
|
22
|
+
page.onCallback = function(data){
|
23
|
+
if (data === 'DOMContentLoaded') self.next();
|
24
|
+
}
|
25
|
+
},
|
26
|
+
next: function() {
|
27
|
+
var func = this.funcs.shift();
|
28
|
+
if (func !== undefined) {
|
29
|
+
func();
|
30
|
+
} else {
|
31
|
+
page.onCallback = function(){};
|
32
|
+
}
|
33
|
+
}
|
34
|
+
};
|
35
|
+
|
36
|
+
new funcs([
|
37
|
+
function() {
|
38
|
+
page.open('https://my.bitcasa.com/login');
|
39
|
+
},
|
40
|
+
function() {
|
41
|
+
page.evaluate(function(user, password) {
|
42
|
+
document.getElementById('user').value = user;
|
43
|
+
document.getElementById('password').value = password;
|
44
|
+
document.querySelector('form').submit();
|
45
|
+
}, user, password);
|
46
|
+
},
|
47
|
+
function() {
|
48
|
+
var cookie = page.evaluate(function() {
|
49
|
+
if (window.location.pathname.indexOf("/login")==0) {
|
50
|
+
return null;
|
51
|
+
} else {
|
52
|
+
return document.cookie;
|
53
|
+
}
|
54
|
+
});
|
55
|
+
console.log(cookie);
|
56
|
+
phantom.exit();
|
57
|
+
}
|
58
|
+
]).next();
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'my_bitcasa/response_format_error'
|
2
|
+
|
3
|
+
module MyBitcasa
|
4
|
+
module LoginEngine
|
5
|
+
# MyBitcasa::LoginEngine::Pure is not work.
|
6
|
+
class Pure
|
7
|
+
include ConnectionPool
|
8
|
+
|
9
|
+
attr_reader :cookie
|
10
|
+
|
11
|
+
def initialize(connection=nil)
|
12
|
+
@connection = connection
|
13
|
+
end
|
14
|
+
|
15
|
+
def login(user, password)
|
16
|
+
# login form
|
17
|
+
res = @conn.get_without_loggedin("/login")
|
18
|
+
|
19
|
+
csrf_tag = res.body.match(/<input [^<>]*name="csrf_token"[^<>]*>/){|m| m[0]}
|
20
|
+
raise ResponseFormatError, "csrf_token tag is not found" unless csrf_tag
|
21
|
+
|
22
|
+
csrf_token = csrf_tag.match(/value="([^"]+)"/){|m| m[1]}
|
23
|
+
raise ResponseFormatError, "csrf_token is not found" unless csrf_token
|
24
|
+
|
25
|
+
# login post
|
26
|
+
res = @conn.post_without_loggedin("/login", {
|
27
|
+
user: user,
|
28
|
+
password: password,
|
29
|
+
csrf_token: csrf_token,
|
30
|
+
redirect: "/",
|
31
|
+
})
|
32
|
+
|
33
|
+
if res.env[:url].path.start_with?("/login")
|
34
|
+
raise AuthorizationError, "login failure"
|
35
|
+
end
|
36
|
+
|
37
|
+
@cookie = res.headers["set-cookie"]
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def available?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|