mpw 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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