pastehub 0.2.6

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/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