aboutsummaryrefslogtreecommitdiffstats
path: root/core/service_keeper.rb
blob: cdf18faf3955971bf07580638a79607717e34bc2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# -*- coding: utf-8 -*-

miquire :core, 'environment'

require 'fileutils'
require 'openssl'
require 'securerandom'

class Service
  module SaveData
    ACCOUNT_FILE = File.join(Environment::SETTINGDIR, 'core', 'token').freeze
    ACCOUNT_TMP = (ACCOUNT_FILE + ".write").freeze

    extend SaveData
    @@service_lock = Monitor.new

    def key
      UserConfig[:account_crypt_key] ||= SecureRandom.hex end

    # 全てのアカウント情報をオブジェクトとして返す
    # ==== Return
    # account_id => {token: ,secret:, ...}
    def accounts
      @account_data ||= @@service_lock.synchronize do
        if FileTest.exist? ACCOUNT_FILE
          File.open(ACCOUNT_FILE, 'rb'.freeze) do |file|
            YAML.load(decrypt(file.read)) end
        else
          # 旧データの引き継ぎ
          result = UserConfig[:accounts]
          if result.is_a? Hash
            # 0.3開発版のデータがある
            account_write result.inject({}){ |hash, item|
              key, value = item
              hash[key] = value.merge(provider: :twitter, slug: key)
              hash }
          elsif UserConfig[:twitter_token] and UserConfig[:twitter_secret]
            # 0.2.x以前のアカウント情報
            account_write({ default: {
                              provider: :twitter,
                              slug: :default,
                              token: UserConfig[:twitter_token],
                              secret: UserConfig[:twitter_secret],
                              user: UserConfig[:verify_credentials] } })
          else
            {} end end end end

    # アカウント情報を返す
    # ==== Args
    # [name] アカウントのキー(Symbol)
    # ==== Return
    # アカウント情報(Hash)
    def account_data(name)
      accounts[name.to_sym] or raise ArgumentError, 'account data `#{name}\' does not exists.' end

    # 新しいアカウントの情報を登録する
    # ==== Args
    # [name] アカウントのキー(Symbol)
    # [options] アカウント情報(Hash)
    # ==== Exceptions
    # ArgumentError name のサービスが既に存在している場合、optionsの情報が足りない場合
    # ==== Return
    # Service
    def account_register(name, options)
      name = name.to_sym
      @@service_lock.synchronize do
        raise ArgumentError, "account #{name} already exists." if accounts.has_key? name
        @account_data = account_write accounts.merge name => {
          provider: (options[:provider] or raise ArgumentError, 'options requires key `provider\'.'),
          slug: (options[:slug] or raise ArgumentError, 'options requires key `slug\'.'),
          token: (options[:token] or raise ArgumentError, 'options requires key `token\'.'),
          secret: (options[:secret] or raise ArgumentError, 'options requires key `secret\'.'),
          user: (options[:user] or raise ArgumentError, 'options requires key `user\'.') } end
      self end

    # アカウント情報を更新する
    # ==== Args
    # [name] アカウントのキー(Symbol)
    # [options] アカウント情報(Hash)
    # ==== Exceptions
    # ArgumentError name のサービスが存在しない場合
    # ==== Return
    # Service
    def account_modify(name, options)
      name = name.to_sym
      @@service_lock.synchronize do
        raise ArgumentError, "account #{name} is not exists." unless accounts.has_key? name
        account_data = accounts[name].merge(options)
        Plugin.call(:image_file_cache_cache, account_data[:user][:profile_image_url])
        @account_data = account_write(accounts.merge(name => account_data)) end
      self end

    # 垢消しの時間だ
    # ==== Args
    # [name]
    # ==== Return
    # Service
    def account_destroy(name)
      name = name.to_sym
      @@service_lock.synchronize do
        to_delete = accounts.dup
        to_delete.delete(name)
        @account_data = account_write(to_delete) end
      self end

    # アカウント情報をファイルに保存する
    def account_write(account_data = @account_data)
      FileUtils.mkdir_p File.dirname(ACCOUNT_FILE)
      File.open(ACCOUNT_TMP, 'wb'.freeze) do |file|
        file << encrypt(YAML.dump(account_data)) end
      FileUtils.mv(ACCOUNT_TMP, ACCOUNT_FILE)
      account_data end

    def encrypt(str)
      cipher = OpenSSL::Cipher.new('bf-ecb').encrypt
      cipher.key = key
      cipher.update(str) << cipher.final end

    def decrypt(binary_data)
      cipher = OpenSSL::Cipher.new('bf-ecb').decrypt
      cipher.key = key
      str = cipher.update(binary_data) << cipher.final
      str.force_encoding(Encoding::UTF_8)
      str end
  end
end