mpw 2.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.
data/lib/mpw/server.rb ADDED
@@ -0,0 +1,343 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module MPW
4
+
5
+ require 'socket'
6
+ require 'json'
7
+ require 'highline/import'
8
+ require 'digest'
9
+ require 'logger'
10
+
11
+
12
+ class Server
13
+
14
+ attr_accessor :error_msg
15
+
16
+ # Constructor
17
+ def initialize
18
+ YAML::ENGINE.yamler='syck'
19
+ end
20
+
21
+ # Start the server
22
+ def start
23
+ server = TCPServer.open(@host, @port)
24
+ @log.info("The server is started on #{@host}:#{@port}")
25
+
26
+ loop do
27
+ Thread.start(server.accept) do |client|
28
+ @log.info("#{client.peeraddr[3]} is connected")
29
+
30
+ while true do
31
+ msg = get_client_msg(client)
32
+
33
+ next if not msg
34
+
35
+ if msg['gpg_key'].nil? or msg['gpg_key'].empty? or msg['password'].nil? or msg['password'].empty?
36
+ @log.warning("#{client.peeraddr[3]} is disconnected because no password or no gpg_key")
37
+ close_connection(client)
38
+ next
39
+ end
40
+
41
+ case msg['action']
42
+ when 'get'
43
+ @log.debug("#{client.peeraddr[3]} GET gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
44
+ client.puts get_file(msg)
45
+ when 'update'
46
+ @log.debug("#{client.peeraddr[3]} UPDATE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
47
+ client.puts update_file(msg)
48
+ when 'delete'
49
+ @log.debug("#{client.peeraddr[3]} DELETE gpg_key=#{msg['gpg_key']} suffix=#{msg['suffix']}")
50
+ client.puts delete_file(msg)
51
+ else
52
+ @log.warning("#{client.peeraddr[3]} is disconnected because unkwnow command")
53
+ send_msg = {action: 'unknown',
54
+ gpg_key: msg['gpg_key'],
55
+ error: 'server.error.client.unknown'
56
+ }
57
+ client.puts send_msg
58
+ close_connection(client)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ rescue Exception => e
65
+ puts "Impossible to start the server: #{e}"
66
+ @log.error("Impossible to start the server: #{e}")
67
+ exit 2
68
+ end
69
+
70
+ # Get a gpg file
71
+ # @args: msg -> message puts by the client
72
+ # @rtrn: json message
73
+ def get_file(msg)
74
+ gpg_key = msg['gpg_key'].sub('@', '_')
75
+
76
+ if msg['suffix'].nil? or msg['suffix'].empty?
77
+ file_gpg = "#{@data_dir}/#{gpg_key}.yml"
78
+ else
79
+ file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
80
+ end
81
+
82
+ if File.exist?(file_gpg)
83
+ gpg_data = YAML::load_file(file_gpg)
84
+ salt = gpg_data['gpg']['salt']
85
+ hash = gpg_data['gpg']['hash']
86
+ data = gpg_data['gpg']['data']
87
+
88
+ if is_authorized?(msg['password'], salt, hash)
89
+ send_msg = {action: 'get',
90
+ gpg_key: msg['gpg_key'],
91
+ data: data,
92
+ error: nil
93
+ }
94
+ else
95
+ send_msg = {action: 'get',
96
+ gpg_key: msg['gpg_key'],
97
+ error: 'server.error.client.no_authorized'
98
+ }
99
+ end
100
+ else
101
+ send_msg = {action: 'get',
102
+ gpg_key: msg['gpg_key'],
103
+ data: '',
104
+ error: nil
105
+ }
106
+ end
107
+
108
+ return send_msg.to_json
109
+ end
110
+
111
+ # Update a file
112
+ # @args: msg -> message puts by the client
113
+ # @rtrn: json message
114
+ def update_file(msg)
115
+ gpg_key = msg['gpg_key'].sub('@', '_')
116
+ data = msg['data']
117
+
118
+ if data.nil? or data.empty?
119
+ send_msg = {action: 'update',
120
+ gpg_key: msg['gpg_key'],
121
+ error: 'server.error.client.no_data'
122
+ }
123
+
124
+ return send_msg.to_json
125
+ end
126
+
127
+ if msg['suffix'].nil? or msg['suffix'].empty?
128
+ file_gpg = "#{@data_dir}/#{gpg_key}.yml"
129
+ else
130
+ file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
131
+ end
132
+
133
+ if File.exist?(file_gpg)
134
+ gpg_data = YAML::load_file(file_gpg)
135
+ salt = gpg_data['gpg']['salt']
136
+ hash = gpg_data['gpg']['hash']
137
+
138
+ else
139
+ salt = generate_salt
140
+ hash = Digest::SHA256.hexdigest(salt + msg['password'])
141
+ end
142
+
143
+ if is_authorized?(msg['password'], salt, hash)
144
+ begin
145
+ config = {'gpg' => {'salt' => salt,
146
+ 'hash' => hash,
147
+ 'data' => data
148
+ }
149
+ }
150
+
151
+ File.open(file_gpg, 'w+') do |file|
152
+ file << config.to_yaml
153
+ end
154
+
155
+ send_msg = {action: 'update',
156
+ gpg_key: msg['gpg_key'],
157
+ error: nil
158
+ }
159
+ rescue Exception => e
160
+ send_msg = {action: 'update',
161
+ gpg_key: msg['gpg_key'],
162
+ error: 'server.error.client.unknown'
163
+ }
164
+ end
165
+ else
166
+ send_msg = {action: 'update',
167
+ gpg_key: msg['gpg_key'],
168
+ error: 'server.error.client.no_authorized'
169
+ }
170
+ end
171
+
172
+ return send_msg.to_json
173
+ end
174
+
175
+ # Remove a gpg file
176
+ # @args: msg -> message puts by the client
177
+ # @rtrn: json message
178
+ def delete_file(msg)
179
+ gpg_key = msg['gpg_key'].sub('@', '_')
180
+
181
+ if msg['suffix'].nil? or msg['suffix'].empty?
182
+ file_gpg = "#{@data_dir}/#{gpg_key}.yml"
183
+ else
184
+ file_gpg = "#{@data_dir}/#{gpg_key}-#{msg['suffix']}.yml"
185
+ end
186
+
187
+ if not File.exist?(file_gpg)
188
+ send_msg = {:action => 'delete',
189
+ :gpg_key => msg['gpg_key'],
190
+ :error => nil
191
+ }
192
+
193
+ return send_msg.to_json
194
+ end
195
+
196
+ gpg_data = YAML::load_file(file_gpg)
197
+ salt = gpg_data['gpg']['salt']
198
+ hash = gpg_data['gpg']['hash']
199
+
200
+ if is_authorized?(msg['password'], salt, hash)
201
+ begin
202
+ File.unlink(file_gpg)
203
+
204
+ send_msg = {action: 'delete',
205
+ gpg_key: msg['gpg_key'],
206
+ error: nil
207
+ }
208
+ rescue Exception => e
209
+ send_msg = {action: 'delete',
210
+ gpg_key: msg['gpg_key'],
211
+ error: 'server.error.client.unknown'
212
+ }
213
+ end
214
+ else
215
+ send_msg = {action: 'delete',
216
+ gpg_key: msg['gpg_key'],
217
+ error: 'server.error.client.no_authorized'
218
+ }
219
+ end
220
+
221
+ return send_msg.to_json
222
+ end
223
+
224
+ # Check is the hash equal the password with the salt
225
+ # @args: password -> the user password
226
+ # salt -> the salt
227
+ # hash -> the hash of the password with the salt
228
+ # @rtrn: true is is good, else false
229
+ def is_authorized?(password, salt, hash)
230
+ if hash == Digest::SHA256.hexdigest(salt + password)
231
+ return true
232
+ else
233
+ return false
234
+ end
235
+ end
236
+
237
+ # Get message to client
238
+ # @args: client -> client connection
239
+ # @rtrn: array of the json string, or false if isn't json message
240
+ def get_client_msg(client)
241
+ msg = client.gets
242
+ return JSON.parse(msg)
243
+ rescue
244
+ closeConnection(client)
245
+ return false
246
+ end
247
+
248
+ # Close the client connection
249
+ # @args: client -> client connection
250
+ def close_connection(client)
251
+ client.puts "Closing the connection. Bye!"
252
+ client.close
253
+ end
254
+
255
+ # Check the config file
256
+ # @args: file_config -> the configuration file
257
+ # @rtrn: true if the config file is correct
258
+ def checkconfig(file_config)
259
+ config = YAML::load_file(file_config)
260
+ @host = config['config']['host']
261
+ @port = config['config']['port'].to_i
262
+ @data_dir = config['config']['data_dir']
263
+ @log_file = config['config']['log_file']
264
+ @timeout = config['config']['timeout'].to_i
265
+
266
+ if @host.empty? or @port <= 0 or @data_dir.empty?
267
+ puts I18n.t('checkconfig.fail')
268
+ puts I18n.t('checkconfig.empty')
269
+ return false
270
+ end
271
+
272
+ if not Dir.exist?(@data_dir)
273
+ puts I18n.t('checkconfig.fail')
274
+ puts I18n.t('checkconfig.datadir')
275
+ return false
276
+ end
277
+
278
+ if @log_file.nil? or @log_file.empty?
279
+ puts I18n.t('checkconfig.fail')
280
+ puts I18n.t('checkconfig.log_file_empty')
281
+ return false
282
+ else
283
+ begin
284
+ @log = Logger.new(@log_file)
285
+ rescue
286
+ puts I18n.t('checkconfig.fail')
287
+ puts I18n.t('checkconfig.log_file_create')
288
+ return false
289
+ end
290
+ end
291
+
292
+ return true
293
+ rescue Exception => e
294
+ puts "#{I18n.t('checkconfig.fail')}\n#{e}"
295
+ return false
296
+ end
297
+
298
+ # Create a new config file
299
+ # @args: file_config -> the configuration file
300
+ # @rtrn: true if le config file is create
301
+ def setup(file_config)
302
+ puts I18n.t('form.setup.title')
303
+ puts '--------------------'
304
+ host = ask(I18n.t('form.setup.host')).to_s
305
+ port = ask(I18n.t('form.setup.port')).to_s
306
+ data_dir = ask(I18n.t('form.setup.data_dir')).to_s
307
+ log_file = ask(I18n.t('form.setup.log_file')).to_s
308
+ timeout = ask(I18n.t('form.setup.timeout')).to_s
309
+
310
+ config = {'config' => {'host' => host,
311
+ 'port' => port,
312
+ 'data_dir' => data_dir,
313
+ 'log_file' => log_file,
314
+ 'timeout' => timeout
315
+ }
316
+ }
317
+
318
+ File.open(file_config, 'w') do |file|
319
+ file << config.to_yaml
320
+ end
321
+
322
+ return true
323
+ rescue Exception => e
324
+ puts "#{I18n.t('form.setup.not_valid')}\n#{e}"
325
+ return false
326
+ end
327
+
328
+ # Generate a random salt
329
+ # @args: length -> the length salt
330
+ # @rtrn: a random string
331
+ def generate_salt(length=4)
332
+ if length.to_i <= 0 or length.to_i > 16
333
+ length = 4
334
+ else
335
+ length = length.to_i
336
+ end
337
+
338
+ return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
339
+ end
340
+
341
+ end
342
+
343
+ end
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/ruby
2
+ # author: nishiki
3
+ # mail: nishiki@yaegashi.fr
4
+
5
+ require 'rubygems'
6
+ require 'i18n'
7
+ require 'net/ftp'
8
+
9
+ module MPW
10
+ class FTP
11
+
12
+ attr_accessor :error_msg
13
+ attr_accessor :enable
14
+
15
+ # Constructor
16
+ # @args: host -> the server host
17
+ # port -> ther connection port
18
+ # gpg_key -> the gpg key
19
+ # password -> the remote password
20
+ # suffix -> the suffix file
21
+ def initialize(host, user, password, path, port=nil)
22
+ @error_msg = nil
23
+ @enable = false
24
+
25
+ @host = host
26
+ @user = user
27
+ @password = password
28
+ @path = path
29
+ @port = port.instance_of?(Integer) ? 21 : port
30
+ end
31
+
32
+ # Connect to server
33
+ # @rtrn: false if the connection fail
34
+ def connect
35
+ Net::FTP.open(@host) do |ftp|
36
+ ftp.login(@user, @password)
37
+ @enable = true
38
+ end
39
+ rescue Exception => e
40
+ @error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
41
+ @enable = false
42
+ else
43
+ return @enable
44
+ end
45
+
46
+ # Get data on server
47
+ # @args: gpg_password -> the gpg password
48
+ # @rtrn: nil if nothing data or error
49
+ def get(file_tmp)
50
+ return false if not @enable
51
+
52
+ Net::FTP.open(@host) do |ftp|
53
+ ftp.login(@user, @password)
54
+ ftp.gettextfile(@path, file_tmp)
55
+ end
56
+
57
+ return true
58
+ rescue Exception => e
59
+ @error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
60
+ return false
61
+ end
62
+
63
+ # Update the remote data
64
+ # @args: data -> the data to send on server
65
+ # @rtrn: false if there is a problem
66
+ def update(file_gpg)
67
+ return true if not @enable
68
+
69
+ Net::FTP.open(@host) do |ftp|
70
+ ftp.login(@user, @password)
71
+ ftp.puttextfile(file_gpg, @path)
72
+ end
73
+
74
+ return true
75
+ rescue Exception => e
76
+ @error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
77
+ return false
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/ruby
2
+ # author: nishiki
3
+
4
+ require 'rubygems'
5
+ require 'i18n'
6
+ require 'socket'
7
+ require 'json'
8
+ require 'timeout'
9
+
10
+ module MPW
11
+ class SyncMPW
12
+
13
+ attr_accessor :error_msg
14
+ attr_accessor :enable
15
+
16
+ # Constructor
17
+ # @args: host -> the server host
18
+ # port -> ther connection port
19
+ # gpg_key -> the gpg key
20
+ # password -> the remote password
21
+ # suffix -> the suffix file
22
+ def initialize(host, user, password, suffix, port=nil)
23
+ @error_msg = nil
24
+ @enable = false
25
+
26
+ @host = host
27
+ @port = !port.instance_of?(Integer) ? 2201 : port
28
+ @gpg_key = user
29
+ @password = password
30
+ @suffix = suffix
31
+ end
32
+
33
+ # Connect to server
34
+ # @rtrn: false if the connection fail
35
+ def connect
36
+ Timeout.timeout(10) do
37
+ begin
38
+ TCPSocket.open(@host, @port) do
39
+ @enable = true
40
+ end
41
+ rescue Errno::ENETUNREACH
42
+ retry
43
+ end
44
+ end
45
+ rescue Timeout::Error
46
+ @error_msg = "#{I18n.t('error.timeout')}\n#{e}"
47
+ @enable = false
48
+ rescue Exception => e
49
+ @error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
50
+ @enable = false
51
+ else
52
+ return @enable
53
+ end
54
+
55
+ # Get data on server
56
+ # @args: gpg_password -> the gpg password
57
+ # @rtrn: nil if nothing data or error
58
+ def get(file_tmp)
59
+ return false if not @enable
60
+
61
+ msg = nil
62
+ TCPSocket.open(@host, @port) do |socket|
63
+ send_msg = {action: 'get',
64
+ gpg_key: @gpg_key,
65
+ password: @password,
66
+ suffix: @suffix
67
+ }
68
+
69
+ socket.puts send_msg.to_json
70
+ msg = JSON.parse(socket.gets)
71
+ end
72
+
73
+ if not defined?(msg['error'])
74
+ @error_msg = I18n.t('error.sync.communication')
75
+ return false
76
+ elsif not msg['error'].nil?
77
+ @error_msg = I18n.t(msg['error'])
78
+ return false
79
+ end
80
+
81
+ File.open(file_tmp, 'w') do |file|
82
+ file << msg['data']
83
+ end
84
+
85
+ return true
86
+ rescue Exception => e
87
+ @error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
88
+ return false
89
+ end
90
+
91
+ # Update the remote data
92
+ # @args: data -> the data to send on server
93
+ # @rtrn: false if there is a problem
94
+ def update(file_gpg)
95
+ return true if not @enable
96
+
97
+ data = File.open(file_gpg, 'r').read
98
+
99
+ msg = nil
100
+ TCPSocket.open(@host, @port) do |socket|
101
+ send_msg = {action: 'update',
102
+ gpg_key: @gpg_key,
103
+ password: @password,
104
+ suffix: @suffix,
105
+ data: data
106
+ }
107
+
108
+ socket.puts send_msg.to_json
109
+ msg = JSON.parse(socket.gets)
110
+ end
111
+
112
+ if not defined?(msg['error'])
113
+ @error_msg = I18n.t('error.sync.communication')
114
+ return false
115
+ elsif msg['error'].nil?
116
+ return true
117
+ else
118
+ @error_msg = I18n.t(msg['error'])
119
+ return false
120
+ end
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/ruby
2
+ # author: nishiki
3
+ # mail: nishiki@yaegashi.fr
4
+
5
+ require 'rubygems'
6
+ require 'i18n'
7
+ require 'net/ssh'
8
+ require 'net/sftp'
9
+
10
+ module MPW
11
+ class SyncSSH
12
+
13
+ attr_accessor :error_msg
14
+ attr_accessor :enable
15
+
16
+ # Constructor
17
+ # @args: host -> the server host
18
+ # port -> ther connection port
19
+ # gpg_key -> the gpg key
20
+ # password -> the remote password
21
+ def initialize(host, user, password, path, port=nil)
22
+ @error_msg = nil
23
+ @enable = false
24
+
25
+ @host = host
26
+ @user = user
27
+ @password = password
28
+ @path = path
29
+ @port = port.instance_of?(Integer) ? 22 : port
30
+ end
31
+
32
+ # Connect to server
33
+ # @rtrn: false if the connection fail
34
+ def connect
35
+ Net::SSH.start(@host, @user, password: @password, port: @port) do |ssh|
36
+ @enable = true
37
+ end
38
+ rescue Exception => e
39
+ @error_msg = "#{I18n.t('error.sync.connection')}\n#{e}"
40
+ @enable = false
41
+ else
42
+ return @enable
43
+ end
44
+
45
+ # Get data on server
46
+ # @args: gpg_password -> the gpg password
47
+ # @rtrn: nil if nothing data or error
48
+ def get(file_tmp)
49
+ return false if not @enable
50
+
51
+ Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
52
+ sftp.lstat(@path) do |response|
53
+ sftp.download!(@path, file_tmp) if response.ok?
54
+ end
55
+ end
56
+
57
+ return true
58
+ rescue Exception => e
59
+ @error_msg = "#{I18n.t('error.sync.download')}\n#{e}"
60
+ return false
61
+ end
62
+
63
+ # Update the remote data
64
+ # @args: file_gpg -> the data to send on server
65
+ # @rtrn: false if there is a problem
66
+ def update(file_gpg)
67
+ return true if not @enable
68
+
69
+ Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
70
+ sftp.upload!(file_gpg, @path)
71
+ end
72
+
73
+ return true
74
+ rescue Exception => e
75
+ @error_msg = "#{I18n.t('error.sync.upload')}\n#{e}"
76
+ return false
77
+ end
78
+
79
+ end
80
+ end