pastehub 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
data/lib/pastehub.rb ADDED
@@ -0,0 +1,43 @@
1
+ #
2
+ # pastehub.rb - PasteHub's top-level library file
3
+ #
4
+ #
5
+ # Copyright (c) 2012 Kiyoka Nishiyama <kiyoka@sumibi.org>
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ #
14
+ # 2. Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in the
16
+ # documentation and/or other materials provided with the distribution.
17
+ #
18
+ # 3. Neither the name of the authors nor the names of its contributors
19
+ # may be used to endorse or promote products derived from this
20
+ # software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
+ #
34
+ #
35
+ require 'pastehub/config'
36
+ require 'pastehub/util'
37
+ require 'pastehub/crypt'
38
+ require 'pastehub/auth'
39
+ require 'pastehub/localdb'
40
+ require 'pastehub/store'
41
+ require 'pastehub/client'
42
+ require 'pastehub/clientsync'
43
+ require 'pastehub/clipboard'
@@ -0,0 +1,171 @@
1
+ #
2
+ # auth.rb - PasteHub's authentication library file
3
+ #
4
+ # Copyright (c) 2009-2011 Kiyoka Nishiyama <kiyoka@sumibi.org>
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ #
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # 3. Neither the name of the authors nor the names of its contributors
18
+ # may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
+ #
33
+ #
34
+ require "openssl"
35
+ require "base64"
36
+ require 'pp'
37
+
38
+ module PasteHub
39
+ MANDATORY_KEYS = [
40
+ 'x-pastehub-username',
41
+ 'x-pastehub-date',
42
+ 'x-pastehub-version'
43
+ ]
44
+
45
+
46
+ class Auth
47
+ def initialize()
48
+ @elements = Hash.new
49
+ end
50
+
51
+ def addElement( key, value )
52
+ @elements[key.downcase] = value
53
+ end
54
+
55
+ def calcSignature( secretKey )
56
+ a = Set.new( @elements.keys )
57
+ b = Set.new( MANDATORY_KEYS )
58
+ if a.superset?( b )
59
+ message = MANDATORY_KEYS.sort.map {|k|
60
+ k + ':' + @elements[k]
61
+ }.join( ',' ).downcase
62
+
63
+ h = OpenSSL::HMAC::digest(OpenSSL::Digest::SHA256.new, secretKey, message)
64
+ Base64.encode64(h).chomp
65
+ else
66
+ # not enough keys
67
+ :NotEnoughKeys
68
+ end
69
+ end
70
+
71
+ def getAuthHash( )
72
+ @elements
73
+ end
74
+
75
+ def username()
76
+ @elements['x-pastehub-username']
77
+ end
78
+
79
+ def curTime()
80
+ @elements['x-pastehub-date']
81
+ end
82
+ end
83
+
84
+ class AuthForClient
85
+ def initialize( username, secretKey )
86
+ util = Util.new
87
+ @token = Auth.new( )
88
+ @token.addElement( 'x-pastehub-username', username )
89
+ @token.addElement( 'x-pastehub-date', util.key_seconds( util.currentTime( )).to_s )
90
+ @token.addElement( 'x-pastehub-version', '2012-06-16' )
91
+ @secretKey = secretKey
92
+ end
93
+
94
+ def getAuthHash( )
95
+ sign = @token.calcSignature( @secretKey )
96
+ #pp ["sign", sign ]
97
+ if sign.is_a? String
98
+ @token.addElement( 'Authorization', sign )
99
+ @token.getAuthHash()
100
+ else
101
+ raise RuntimeError, "Error: can't create AuthForClient instance."
102
+ end
103
+ end
104
+
105
+ # for Testing with RSpec.
106
+ def _addElement( key, value )
107
+ @token.addElement( key, value )
108
+ end
109
+
110
+ def username()
111
+ @token.username
112
+ end
113
+ end
114
+
115
+ class AuthForServer
116
+ def initialize( users )
117
+ # db connecter of Users table
118
+ @users = users
119
+ end
120
+
121
+ def expired?( clientTime, serverTime )
122
+ (60*5) < ( clientTime.to_i - serverTime.to_i ).abs
123
+ end
124
+
125
+ # return:
126
+ # [ true/false, "username", :reason ]
127
+ def invoke( hash, serverTime )
128
+ auth = Auth.new
129
+ client_sign = ""
130
+ hash.each { |k,v|
131
+ k = k.downcase
132
+ #pp [ "invoke", k, v ]
133
+ if MANDATORY_KEYS.include?( k )
134
+ auth.addElement( k, v )
135
+ end
136
+ if 'authorization' == k
137
+ client_sign = v
138
+ end
139
+ }
140
+
141
+ #pp [ "auth.getAuthHash()", auth.getAuthHash() ]
142
+ #pp [ "client_sign", client_sign ]
143
+ #pp [ "auth.username", auth.username ]
144
+
145
+ if not auth.username
146
+ # fail... username was unspecified
147
+ [ false, 'UNSPECIFIED', :unspecified_user ]
148
+ else
149
+ if @users.getSecretKey(auth.username)
150
+ server_sign = auth.calcSignature( @users.getSecretKey(auth.username) )
151
+ #pp [ "server_sign", server_sign ]
152
+ if server_sign == client_sign
153
+
154
+ if expired?( auth.curTime, serverTime )
155
+ # authentication success
156
+ [ false, auth.username, :expired_client_request ]
157
+ else
158
+ [ true, auth.username, :ok ]
159
+ end
160
+ else
161
+ # fail...
162
+ [ false, auth.username, :illegal_signature ]
163
+ end
164
+ else
165
+ # unknown user
166
+ [ false, auth.username, :unknown_user ]
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,453 @@
1
+ #
2
+ # client.rb - PasteHub's client main library
3
+ #
4
+ # Copyright (c) 2009-2011 Kiyoka Nishiyama <kiyoka@sumibi.org>
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ #
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # 3. Neither the name of the authors nor the names of its contributors
18
+ # may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
+ #
33
+ #
34
+ require 'net/http'
35
+ require 'net/https'
36
+ require 'uri'
37
+ require 'open-uri'
38
+ require 'fileutils'
39
+
40
+ module PasteHub
41
+
42
+ def self.signfilePath
43
+ signfile = PasteHub::Config.instance.localDbPath + "authinfo.txt"
44
+ end
45
+
46
+ def self.loadUsername
47
+ signfile = self.signfilePath
48
+
49
+ # load authenticate information
50
+ if not File.exist?( signfile )
51
+ return nil
52
+ else
53
+ begin
54
+ open( signfile ) {|f|
55
+ # first line is email (is username)
56
+ username = f.readline.chomp
57
+ if username.match( /^[a-z]+/ )
58
+ return username
59
+ end
60
+ }
61
+ rescue Exception => e
62
+ STDERR.puts( "Error: can't load #{signfile}: #{e.to_s}" )
63
+ raise e
64
+ end
65
+ return nil
66
+ end
67
+ end
68
+
69
+ # setup user's directory
70
+ # result: true ... success / false ... fail
71
+ def self.setupDirectory
72
+ localdb_path = PasteHub::Config.instance.localDbPath
73
+ # Create directory
74
+ if not File.exist?( localdb_path )
75
+ if 0 == Dir.mkdir( localdb_path, 0700 )
76
+ STDERR.puts( "Info: created directory #{localdb_path}" )
77
+ else
78
+ STDERR.puts( "Error: can't create directory #{localdb_path}" )
79
+ return false
80
+ end
81
+ end
82
+ return true
83
+ end
84
+
85
+ def self.signIn
86
+ signfile = self.signfilePath
87
+ util = Util.new
88
+
89
+ # authenticate information
90
+ if not File.exist?( signfile )
91
+ 3.times { |n|
92
+ util.say( "Please input your account information" )
93
+ username = util.inputText(" email: ")
94
+ secretKey = util.inputText(" secret-key: " )
95
+ auth = AuthForClient.new( username, secretKey )
96
+ client = Client.new( auth )
97
+ if client.authTest()
98
+ # save authinfo with gpg
99
+ begin
100
+ password = util.inputPasswordTwice(
101
+ "Please input password for crypted file",
102
+ " password : ",
103
+ " password(for verify): " )
104
+ if password
105
+ crypt = PasteHub::Crypt.new( password )
106
+ open( signfile, "w" ) {|f|
107
+ f.puts( username )
108
+ f.puts( crypt.en( secretKey ))
109
+ }
110
+ # auth OK
111
+ return [ username, secretKey, password ]
112
+ end
113
+ STDERR.puts( "Error: password setting failed..." )
114
+ rescue Exception => e
115
+ STDERR.puts( "Error: can't save #{signfile}: #{e.to_s}" )
116
+ end
117
+ else
118
+ STDERR.puts( "your email or secret key is not registerd..." )
119
+ end
120
+ }
121
+ else
122
+ begin
123
+ open( signfile ) {|f|
124
+ util.say( "Please input password for crypted file" )
125
+ username = f.readline.chomp
126
+ signStr = f.read
127
+ 3.times { |n|
128
+ password = util.inputPassword(" crypt password: ")
129
+ crypt = PasteHub::Crypt.new( password )
130
+ secretKey= crypt.de( signStr )
131
+ if secretKey
132
+ auth = AuthForClient.new( username, secretKey )
133
+ client = Client.new( auth )
134
+ if client.authTest()
135
+ return [ username, secretKey, password ]
136
+ else
137
+ STDERR.puts( "Error: your secretKey may be old." )
138
+ end
139
+ end
140
+ STDERR.puts( "Error: missing password." )
141
+ }
142
+ }
143
+ rescue Exception => e
144
+ STDERR.puts( "Error: can't load #{signfile}: #{e.to_s}" )
145
+ raise e
146
+ end
147
+ end
148
+ return [ nil, nil, nil ]
149
+ end
150
+
151
+ def self.savePid( pid )
152
+ pidFile = PasteHub::Config.instance.localDbPath + "pid"
153
+ open( pidFile, "w" ) {|f|
154
+ f.puts pid
155
+ }
156
+ end
157
+
158
+ def self.loadPid
159
+ pidFile = PasteHub::Config.instance.localDbPath + "pid"
160
+ pid = 0
161
+ begin
162
+ pid = open( pidFile ) {|f|
163
+ f.readline.chomp.to_i
164
+ }
165
+ rescue Exception => e
166
+ STDERR.puts( "Warning: can't load #{pidFile}: #{e.to_s}" )
167
+ pid = 0
168
+ end
169
+ return pid
170
+ end
171
+
172
+
173
+ class ClientBase
174
+
175
+ # return: Net::HTTPResponse
176
+ def httpGet( uriStr, headerHash, errorMessage )
177
+ uri = URI.parse( uriStr )
178
+ begin
179
+ https = Net::HTTP.new(uri.host, uri.port)
180
+ if ( Net::HTTP.https_default_port == uri.port ) or ( "https" == uri.scheme )
181
+ https.use_ssl = true
182
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
183
+ end
184
+ https.start {|http|
185
+ resp = http.get(uri.request_uri, headerHash)
186
+ if "200" == resp.code
187
+ return resp
188
+ end
189
+ }
190
+ rescue Exception => e
191
+ STDERR.puts errorMessage + ":" + e.to_s
192
+ end
193
+ return nil
194
+ end
195
+
196
+ # return: Net::HTTPResponse
197
+ def httpPost( uriStr, bodyStr, headerHash, errorMessage )
198
+ uri = URI.parse( uriStr )
199
+ begin
200
+ https = Net::HTTP.new(uri.host, uri.port)
201
+ if ( Net::HTTP.https_default_port == uri.port ) or ( "https" == uri.scheme )
202
+ https.use_ssl = true
203
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
204
+ end
205
+ https.start {|http|
206
+ resp = http.post(uri.request_uri, bodyStr, headerHash)
207
+ if "200" == resp.code
208
+ return resp
209
+ end
210
+ }
211
+ rescue Exception => e
212
+ STDERR.puts errorMessage + ":" + e.to_s
213
+ end
214
+ return nil
215
+ end
216
+
217
+ # return: [ Net::HTTP, Net::HTTP::Get ]
218
+ def httpGetRequest( uriStr, headerHash, errorMessage )
219
+ uri = URI.parse( uriStr )
220
+ begin
221
+ https = Net::HTTP.new(uri.host, uri.port)
222
+ if ( Net::HTTP.https_default_port == uri.port ) or ( "https" == uri.scheme )
223
+ https.use_ssl = true
224
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
225
+ end
226
+ return [ https, Net::HTTP::Get.new(uri.request_uri, headerHash) ]
227
+ rescue Exception => e
228
+ STDERR.puts errorMessage + ":" + e.to_s
229
+ end
230
+ return nil
231
+ end
232
+
233
+ end
234
+
235
+
236
+ class Client < ClientBase
237
+ def initialize( auth, password = nil )
238
+ @auth = auth
239
+ ins = PasteHub::Config.instance
240
+ @localdb_path = ins.localDbPath
241
+ @server_api_url = ins.targetApiURL
242
+ @server_notifier_url = ins.targetNotifierURL
243
+ @list_items = ins.listItems
244
+ @syncTrigger = []
245
+ @crypt = if password
246
+ Crypt.new( password )
247
+ else
248
+ nil
249
+ end
250
+ end
251
+
252
+ def authTest
253
+ httpGet( "#{@server_api_url}authTest", @auth.getAuthHash(), "Error: can't connect to server for auth test" )
254
+ end
255
+
256
+
257
+ def getList( limit = nil )
258
+ h = {"content-type" => "plain/text"}
259
+ if limit
260
+ h[ 'X-Pastehub-Limit' ] = limit.to_i
261
+ end
262
+
263
+ resp = httpGet( "#{@server_api_url}getList", @auth.getAuthHash().merge( h ), "Error: fails 'getList' from server." )
264
+ str = resp.read_body()
265
+ masterList = str.split( /\n/ )
266
+ STDERR.puts "Info: masterList lines = #{masterList.size} #{str.size}Bytes"
267
+ masterList = masterList.select { |x|
268
+ okSize = "1340542369=2012-06-24.12:52:49=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".size
269
+ if okSize != x.size
270
+ STDERR.puts "Info: masterList(NG): " + x
271
+ false
272
+ else
273
+ x
274
+ end
275
+ }
276
+ masterList
277
+ end
278
+
279
+ def getValue( key )
280
+ raise RuntimeError, "Error: no encrypt password." unless @crypt
281
+
282
+ resp = httpPost( "#{@server_api_url}getValue", key, @auth.getAuthHash().merge( {"content-type" => "plain/text"} ), "Error: fails 'getValue' from server." )
283
+ ret = @crypt.de( resp.read_body() )
284
+ unless ret
285
+ STDERR.puts( "Warning: encrypt password is wrong. getValue missing..." )
286
+ ret = "error..."
287
+ end
288
+ ret
289
+ end
290
+
291
+ def putValue( key, data )
292
+ ret = ""
293
+ if not key
294
+ key = "_"
295
+ end
296
+ raise RuntimeError, "Error: no encrypt password." unless @crypt
297
+ _data = @crypt.en( data )
298
+ unless _data
299
+ STDERR.puts( "Warning: encrypt password is wrong. putValue missing..." )
300
+ ret = nil
301
+ else
302
+ resp = httpPost( "#{@server_api_url}putValue", _data,
303
+ @auth.getAuthHash().merge(
304
+ {
305
+ "content-type" => "plain/text",
306
+ "x-pastehub-key" => key }),
307
+ "Error: fails 'putValue' from server." )
308
+ if resp
309
+ ret = resp.read_body()
310
+ end
311
+ end
312
+ ret
313
+ end
314
+
315
+ def wait_notify( auth )
316
+ begin
317
+ pair = httpGetRequest("#{@server_notifier_url}", auth.getAuthHash(), "Error: fails 'notifier' api on server." )
318
+ https = pair[0]
319
+ getRequest = pair[1]
320
+ https.start() { |http|
321
+ http.request(getRequest) do |response|
322
+ raise 'Response is not chuncked' unless response.chunked?
323
+ response.read_body do |chunk|
324
+ serverValue = chunk.chomp
325
+ if serverHasNew?( serverValue )
326
+ puts "Info: server has new data: #{serverValue}"
327
+ return chunk.chomp.clone()
328
+ else
329
+ puts "Info: server is stable: #{serverValue}"
330
+ end
331
+ if localHasNew?( )
332
+ puts "Info: local has new data"
333
+ return :local
334
+ end
335
+ end
336
+ if "200" != response.code
337
+ STDERR.puts "Error: request error result=[#{response.code}]"
338
+ return :retry
339
+ end
340
+ end
341
+ }
342
+ rescue EOFError => e
343
+ STDERR.puts "Error: disconnected by server."
344
+ return :retry
345
+ rescue Errno::ECONNREFUSED => e
346
+ STDERR.puts "Error: can't connect to server(ConnectionRefused)."
347
+ return :retry
348
+ rescue SocketError => e
349
+ STDERR.puts "Error: can't connect to server(SocketError)."
350
+ return :retry
351
+ rescue Errno::ETIMEDOUT => e
352
+ STDERR.puts "Error: can't connect to server(Timeout1)."
353
+ return :retry
354
+ rescue Timeout::Error => e
355
+ # ONLINE and notifier has no INFO.
356
+ return :timeout
357
+ end
358
+ end
359
+
360
+ def serverHasNew?( serverValue )
361
+ # open local db
362
+ localdb = PasteHub::LocalDB.new( @localdb_path )
363
+ localdb.open( @auth.username )
364
+ localValue = localdb.getValue( PasteHub::SERVER_DATE_KEY )
365
+ ret = (localValue != serverValue)
366
+ if ret
367
+ localdb.insertValue( PasteHub::SERVER_DATE_KEY, serverValue )
368
+ end
369
+ localdb.close()
370
+ return ret
371
+ end
372
+
373
+ def localHasNew?( )
374
+ localdb = PasteHub::LocalDB.new( @localdb_path )
375
+ localdb.open( @auth.username )
376
+ localValue = localdb.getValue( PasteHub::LOCAL_DATE_KEY )
377
+ serverValue = localdb.getValue( PasteHub::SERVER_DATE_KEY )
378
+ ret = if localValue and serverValue
379
+ (localValue > serverValue)
380
+ else
381
+ false
382
+ end
383
+ if ret
384
+ localdb.insertValue( PasteHub::LOCAL_DATE_KEY, serverValue )
385
+ end
386
+ localdb.close()
387
+ return ret
388
+ end
389
+
390
+ def setOnlineState( online )
391
+ # open local db
392
+ if online
393
+ if online?() == false
394
+ @syncTrigger.unshift( true )
395
+ end
396
+ end
397
+ localdb = PasteHub::LocalDB.new( @localdb_path )
398
+ localdb.open( @auth.username )
399
+ localdb.insertValue( PasteHub::ONLINE_STATE_KEY, online ? "1" : "0" )
400
+ localdb.close()
401
+ end
402
+
403
+ def online?( )
404
+ # open local db
405
+ localdb = PasteHub::LocalDB.new( @localdb_path )
406
+ localdb.open( @auth.username, true )
407
+ # printf( "[%s]", localdb.getValue( PasteHub::ONLINE_STATE_KEY ))
408
+ ret = ("1" == localdb.getValue( PasteHub::ONLINE_STATE_KEY ))
409
+ localdb.close()
410
+ ret
411
+ end
412
+
413
+ def getTrigger()
414
+ if 0 < @syncTrigger.size
415
+ return @syncTrigger.pop
416
+ end
417
+ return false
418
+ end
419
+
420
+ def localSaveValue( argKey = nil, data )
421
+ # open local db
422
+ util = PasteHub::Util.new( )
423
+ if argKey
424
+ key = argKey
425
+ else
426
+ key = util.currentTime( ) + "=" + util.digest( data )
427
+ end
428
+
429
+ localdb = PasteHub::LocalDB.new( @localdb_path )
430
+ localdb.open( @auth.username )
431
+ list = localdb.getList( )
432
+ if util.key_digest( list[0] ) == util.digest( data )
433
+ # duplicate
434
+ localdb.close()
435
+ list[0]
436
+ else
437
+ localdb.insertValue( key, data.dup )
438
+ localdb.insertValue( PasteHub::LOCAL_DATE_KEY, util.currentTime( ) )
439
+ localdb.close()
440
+ key
441
+ end
442
+ end
443
+
444
+ def setServerFlags( keys )
445
+ localdb = PasteHub::LocalDB.new( @localdb_path )
446
+ localdb.open( @auth.username )
447
+ keys.each { |key|
448
+ localdb.setServerFlag( key )
449
+ }
450
+ localdb.close()
451
+ end
452
+ end
453
+ end