pastehub 0.2.6 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # clientsync.rb - PasteHub's client sync library
3
3
  #
4
- # Copyright (c) 2009-2011 Kiyoka Nishiyama <kiyoka@sumibi.org>
4
+ # Copyright (c) 2009-2014 Kiyoka Nishiyama <kiyoka@sumibi.org>
5
5
  #
6
6
  # Redistribution and use in source and binary forms, with or without
7
7
  # modification, are permitted provided that the following conditions
@@ -35,253 +35,215 @@ require 'net/http'
35
35
  require 'uri'
36
36
  require 'open-uri'
37
37
  require 'fileutils'
38
+ require 'json'
38
39
 
39
40
  module PasteHub
40
41
 
41
- class ClientSync
42
+ class Status
43
+ TIMER_INIT = 60 # second
42
44
 
43
- def initialize( alive_entries, localdb_path, polling_interval )
44
- @alive_entries = alive_entries
45
- @localdb_path = localdb_path
46
- @polling_interval = polling_interval
45
+ def initialize( )
46
+ @comes = 0
47
+ @online = false
48
+ @errorFlag = false
49
+ @timer = TIMER_INIT
50
+ @curJsonStr = ""
51
+ end
52
+
53
+ def inc( )
54
+ @comes += 1
55
+ @timer = TIMER_INIT
56
+ end
57
+
58
+ def reset( )
59
+ @comes = 0
60
+ @timer = TIMER_INIT
61
+ end
62
+
63
+ def setOnline( arg )
64
+ @online = arg
47
65
  end
48
66
 
49
-
50
- def syncDb( auth, password )
51
- notifyFlag = false
52
-
53
- STDERR.puts "synchronizing..."
54
- client = PasteHub::Client.new( auth, password )
55
- util = PasteHub::Util.new
56
- # open local db
57
- localdb = PasteHub::LocalDB.new( @localdb_path )
58
- localdb.open( auth.username, true )
59
-
60
- masterList = localdb.getServerList()
61
- #pp [ "masterList.size", masterList.size ]
62
-
63
- # calc difference between master and local.
64
- localList = localdb.getList()
65
- #pp [ "localList.size(1)", localList.size ]
66
-
67
- # pickup first ALIVE_ENTRIES from localList
68
- #_half0 = util.takeList( localList, @alive_entries )
69
- _half1 = util.dropList( masterList, @alive_entries )
70
- localList = localList + _half1
71
- #pp [ "localList.size(2)", localList.size ]
72
-
73
- downList = util.diffList( masterList, localList )
74
- #pp [ "downList.size", downList.size ]
75
-
76
- upList = util.diffList( localList, masterList )
77
- #pp [ "upList.size", upList.size ]
78
-
79
- # push first element to clipboard.
80
- if 0 < downList.size
81
- key = downList.first
82
- value = client.getValue( key )
83
- if @prevData == value
84
- #p [ @prevData , value ]
85
- STDERR.puts "Info: did not push to OS's clipboard because prevData == donwloaded-firstEntry."
86
- else
87
- STDERR.printf( "Info: push to OS's clipboard (size=%d).\n", value.size )
88
- PasteHub::AbstractClipboard.push( value.dup )
89
- notifyFlag = true
90
- @prevData = value
91
- end
67
+ def tick( delta )
68
+ if @timer <= 0
69
+ @comes = 0
70
+ else
71
+ @timer -= delta
92
72
  end
73
+ end
93
74
 
94
- # donwload
95
- downList.each {|key|
96
- value = client.getValue( key )
97
- localdb_w = PasteHub::LocalDB.new( @localdb_path )
98
- localdb_w.open( auth.username )
99
- localdb_w.insertValue( key.dup, value.dup )
100
- localdb_w.close
101
- sleep( 0.2 )
102
- }
103
- # upload
104
- upList.each {|key|
105
- value = localdb.getValue( key )
106
- client.putValue( key.dup, value.dup )
107
- }
75
+ def update( )
76
+ prev = @curJsonStr
77
+ h = Hash.new
78
+ h[ 'online' ] = @online
79
+ h[ 'error' ] = @errorFlag
80
+ h[ 'comes' ] = @comes
81
+ @curJsonStr = JSON.dump( h )
82
+ return [ @curJsonStr, prev ]
83
+ end
108
84
 
109
- STDERR.puts "Info: download #{downList.size} records."
110
- downList.each { |x|
111
- #STDERR.puts " key=#{x}"
112
- }
113
- STDERR.puts "Info: upload #{upList.size} records."
114
- upList.each { |x|
115
- #STDERR.puts " key=#{x}"
116
- }
117
- localdb.close()
85
+ def icon( )
86
+ result = if @errorFlag
87
+ "error"
88
+ elsif not @online
89
+ "offline"
90
+ else
91
+ case @comes
92
+ when 0
93
+ "online"
94
+ when 1
95
+ "one"
96
+ when 2
97
+ "two"
98
+ when 3
99
+ "three"
100
+ else
101
+ "threeplus"
102
+ end
103
+ end
104
+ return result
105
+ end
106
+ end
118
107
 
119
- if 0 < downList.size or 0 < upList.size
120
- STDERR.puts "Info: send signal to Emacs."
121
- system( "killall -SIGUSR1 Emacs emacs" )
122
- end
108
+ class ClientSync
109
+ ICONSOCKET = "/tmp/pastehub_icon"
123
110
 
124
- return notifyFlag
111
+ def initialize( hostname, polling_interval )
112
+ @hostname = hostname
113
+ @polling_interval = polling_interval
114
+ @status = Status.new()
115
+ @last_modify_time = Time.now()
125
116
  end
126
117
 
118
+ def addNoitfyCallback( receiveNotifyFunc )
119
+ @receiveNotifyFunc = receiveNotifyFunc
120
+ end
121
+
122
+ def notifyToReceive( message )
123
+ @receiveNotifyFunc.call( message ) if @receiveNotifyFunc
124
+ @status.inc( )
125
+ end
127
126
 
128
- def gcLocalDb( auth )
129
- STDERR.puts "GCing..."
130
-
131
- # open local db
132
- localdb_w = PasteHub::LocalDB.new( @localdb_path )
133
- localdb_w.open( auth.username )
134
- localList = localdb_w.getList()
135
- deleteList = localList[@alive_entries..(localList.size)]
136
- if deleteList and 0 < deleteList.size
137
- STDERR.puts "Info: delete #{deleteList.size} records."
138
- deleteList.each { |key|
139
- localdb_w.deleteValue( key.dup )
140
- }
141
- end
142
- localdb_w.close
127
+ # host's sync data file excludes local hostname
128
+ def get_other_hostfiles()
129
+ config = PasteHub::Config.instance
130
+ arr = Dir.glob( config.localSyncPath + "*.dat" )
131
+ retArr = arr.select { |path|
132
+ not path.match( "/" + @hostname + ".dat$" )
133
+ }
134
+ retArr
143
135
  end
144
136
 
137
+ # return: pathname of sync data of other host
138
+ def exist_sync_data?()
139
+ # check directory changes
140
+ config = PasteHub::Config.instance
141
+
142
+ paths = get_other_hostfiles()
143
+ result = paths.select { |path|
144
+ fs = File::Stat.new( path )
145
+ fs.mtime
145
146
 
146
- def fetchServerList( latestKey, auth )
147
- client = PasteHub::Client.new( auth )
148
- if latestKey.is_a? String
149
- STDERR.puts "Info: fetch one entry"
150
- list = [ latestKey.clone() ]
147
+ #printf( "last_modify_time: %s \n", @last_modify_time )
148
+ #printf( "mtime of [%s]: %s \n", path, fs.mtime )
149
+ 1 == (fs.mtime <=> @last_modify_time)
150
+ }
151
+ if 0 < result.size
152
+ result[0]
151
153
  else
152
- STDERR.puts "Info: fetch ALL entries"
153
- list = client.getList()
154
+ nil
154
155
  end
155
- client.setServerFlags( list )
156
156
  end
157
157
 
158
- def addNoitfyCallback( countUpNotifyFunc, connectNotifyFunc, disconnectNotifyFunc )
159
- @countUpNotifyFunc = countUpNotifyFunc
160
- @connectNotifyFunc = connectNotifyFunc
161
- @disconnectNotifyFunc = disconnectNotifyFunc
158
+ def path_to_hostname( path )
159
+ File.basename( path, ".dat" )
162
160
  end
163
161
 
164
- def notifyCountUp()
165
- @countUpNotifyFunc.call() if @countUpNotifyFunc
162
+ def get_sync_entry_body( path )
163
+ hostname = path_to_hostname( path )
164
+ entry = Entry.new( hostname )
165
+ if entry.can_load?()
166
+ entry.load()[1]
167
+ else
168
+ nil
169
+ end
166
170
  end
167
171
 
168
- def notifyConnect()
169
- @connectNotifyFunc.call() if @connectNotifyFunc
172
+ def get_latest_entry( )
173
+ # check directory changes
174
+ config = PasteHub::Config.instance
175
+
176
+ paths = get_other_hostfiles()
177
+ result = paths.map { |path|
178
+ hostname = path_to_hostname( path )
179
+ entry = Entry.new( hostname )
180
+ if entry.can_load?()
181
+ ret = entry.load()
182
+ [ ret[0]['create_unixtime'].to_i, ret[0], ret[1] ]
183
+ else
184
+ [ 0, nil, nil ]
185
+ end
186
+ }
187
+ if 0 == result.size()
188
+ nil
189
+ else
190
+ result = result.sort {|a,b| a[0] <=> b[0]}.reverse
191
+ [
192
+ result[0][1], result[0][2]
193
+ ]
194
+ end
170
195
  end
171
196
 
172
- def notifyDisconnect()
173
- @disconnectNotifyFunc.call() if @disconnectNotifyFunc
197
+ def touch( )
198
+ @last_modify_time = Time.now()
174
199
  end
175
200
 
201
+ def sync_main()
202
+ config = PasteHub::Config.instance
176
203
 
177
- def syncMain( username, secretKey, password )
178
- freeCounter = 0
179
-
180
- auth = PasteHub::AuthForClient.new( username, secretKey )
181
- client = PasteHub::Client.new( auth )
182
- client.setOnlineState( false )
183
- client.setOnlineState( true ) # create Trigger token ___-___
184
- client.setOnlineState( false )
185
- result = :start
204
+ STDERR.puts "Info: sync_main thread start"
205
+ free_counter = 0
186
206
 
187
207
  while true
188
- begin
189
- freeCounter += 1
190
-
191
- if client.getTrigger() or (0 == (freeCounter % 60))
192
- STDERR.puts "Info: force sync."
193
- result = :sync
194
- gcLocalDb( auth )
195
- else
196
- auth = PasteHub::AuthForClient.new( username, secretKey )
197
- result = client.wait_notify( auth )
198
- end
199
-
200
- case result
201
- when :timeout
202
- STDERR.puts "waiting..."
203
- client.setOnlineState( true )
204
- notifyConnect()
205
- when :retry
206
- STDERR.puts "retrying...."
207
- client.setOnlineState( false )
208
- notifyDisconnect()
209
- sleep 60
208
+ free_counter += 1
209
+
210
+ path = exist_sync_data?()
211
+ if path
212
+ body = get_sync_entry_body( path )
213
+ if body
214
+ STDERR.printf( "Info: push to OS's clipboard ([%s...] size=%d).\n", body[0...4], body.size )
215
+ PasteHub::AbstractClipboard.push( body.dup )
216
+ touch()
210
217
  else
211
- if not client.online?()
212
- notifyConnect()
213
- end
214
- client.setOnlineState( true )
215
- fetchServerList( result, auth )
216
- if syncDb( auth, password )
217
- notifyCountUp()
218
- end
218
+ STDERR.printf( "X" ) if config.getVerbose( )
219
219
  end
220
- rescue Errno::EAGAIN => e
221
- STDERR.puts "retrying... DB is locked"
222
- notifyDisconnect()
223
- sleep 2
224
- rescue Errno::ECONNREFUSED => e
225
- STDERR.puts "retrying... pastehub server is down(1)"
226
- notifyDisconnect()
227
- sleep 60
228
- rescue Errno::ETIMEDOUT => e
229
- STDERR.puts "retrying... network is offline(1)"
230
- notifyDisconnect()
231
- sleep 60
232
- rescue SocketError => e
233
- STDERR.puts "retrying... network is offline(2)"
234
- notifyDisconnect()
235
- sleep 60
236
- rescue Timeout::Error => e
237
- # ONLINE, but server is not helthy
238
- notifyDisconnect()
239
- STDERR.puts "retrying... pastehub server is down(2)"
240
- sleep 60
220
+ else
221
+ STDERR.printf( "x" ) if config.getVerbose( )
241
222
  end
223
+ # interval time
224
+ sleep @polling_interval
242
225
  end
243
226
  end
244
227
 
228
+ def clipboard_check()
229
+ config = PasteHub::Config.instance
245
230
 
246
- def clipboardCheck( username, secretKey, password )
247
231
  STDERR.puts "Info: clipboardCheck thread start"
248
232
  @prevData = ""
249
233
  while true
250
234
  sleep @polling_interval
251
- data = PasteHub::AbstractClipboard.hasNew?( username )
235
+ data = PasteHub::AbstractClipboard.hasNew?( )
252
236
  if data
253
237
  if @prevData != data
254
- #p [ @prevData, data ]
255
- auth = PasteHub::AuthForClient.new( username, secretKey )
256
- client = PasteHub::Client.new( auth, password )
257
- if client.online?( )
258
- STDERR.puts( "Info: posted data from OS." )
259
- begin
260
- client.putValue( "_", data )
261
- rescue Errno::ECONNREFUSED => e
262
- # if postData was fail, save to local.
263
- client.setOnlineState( false )
264
- client.localSaveValue( data )
265
- sleep 60
266
- end
267
- else
268
- client.localSaveValue( data )
269
- end
238
+ entry = Entry.new( @hostname )
239
+ entry.save( data )
240
+ STDERR.printf( "Info: clipboard to File ([%s...] size=%d).\n", data[0...4], data.size )
241
+ notifyToReceive( data )
270
242
  @prevData = data
271
243
  end
272
244
  end
245
+ STDERR.printf( "." ) if config.getVerbose( )
273
246
  end
274
247
  end
275
-
276
-
277
- def syncNow( username, secretKey, password )
278
- STDERR.puts( "Info: caught signal." )
279
- store = PasteHub::LocalStore.new( username, true )
280
- pair = store.top()
281
- auth = PasteHub::AuthForClient.new( username, secretKey )
282
- client = PasteHub::Client.new( auth, password )
283
- client.putValue( pair[0], pair[1] )
284
- end
285
-
286
248
  end
287
249
  end
@@ -43,7 +43,7 @@ module PasteHub
43
43
  nil
44
44
  end
45
45
 
46
- def self.hasNew?( username )
46
+ def self.hasNew?( )
47
47
  Encoding.default_external = "UTF-8"
48
48
  str = Clipboard.paste( )
49
49
  str = str.force_encoding("UTF-8")
@@ -43,107 +43,39 @@ module PasteHub
43
43
  end
44
44
 
45
45
  def initialize( )
46
- self.setupServer( {} )
47
46
  self.setupClient( {} )
48
- @listItems = 10
47
+ @verbose_flag = false
48
+ @notifyMessageMax = 80
49
49
  end
50
50
 
51
- def setupServer( hash )
52
- @aws = if hash[ :aws ]
53
- hash[ :aws ]
54
- else
55
- false
56
- end
57
- @awsWarn = if hash[ :awsWarn ]
58
- hash[ :awsWarn ]
59
- else
60
- false
61
- end
62
- @dynamoEp = if hash[ :dynamoEp ]
63
- hash[ :dynamoEp ]
64
- else
65
- dynamoEp = 'dynamodb.ap-northeast-1.amazonaws.com' # Default DynamoDB's endpoint is Tokyo Region
66
- end
67
- @dynamoAccessKey = if hash[ :dynamoAccessKey ]
68
- hash[ :dynamoAccessKey ]
69
- else
70
- "xxxx"
71
- end
72
- @dynamoSecretKey = if hash[ :dynamoSecretKey ]
73
- hash[ :dynamoSecretKey ]
74
- else
75
- "xxxx"
76
- end
77
- @memcacheEp = if hash[ :memcacheEp ]
78
- hash[ :memcacheEp ]
79
- else
80
- "localhost:11211"
81
- end
82
- @keyCacheTime = if hash[ :keyCacheTime ]
83
- hash[ :keyCacheTime ]
84
- else
85
- 24 * 3600
86
- end
87
- @domain = if hash[ :domain ]
88
- hash[ :domain ]
89
- else
90
- "localhost"
91
- end
92
- @keystore = if hash[ :keystore ]
93
- hash[ :keystore ]
94
- else
95
- nil
96
- end
97
- @keystorePassword = if hash[ :keystorePassword ]
98
- hash[ :keystorePassword ]
99
- else
100
- "password"
101
- end
102
- end
103
-
104
- def appendSlash( uri )
105
- if uri.match( /\/$/ )
106
- uri
51
+ def getHomeDirectory( )
52
+ if ENV[ 'HOME' ]
53
+ return ENV[ 'HOME' ]
107
54
  else
108
- uri + "/"
55
+ return "~/"
109
56
  end
110
57
  end
111
58
 
59
+ def setVerbose( verbose_flag )
60
+ @verbose_flag = verbose_flag
61
+ end
62
+
63
+ def getVerbose( )
64
+ @verbose_flag
65
+ end
66
+
112
67
  def setupClient( hash )
113
- @targetApiURL = if hash[ :targetApiURL ]
114
- appendSlash( hash[ :targetApiURL ] )
115
- else
116
- "https://pastehub.net/api/"
117
- end
118
- @targetNotifierURL = if hash[ :targetNotifierURL ]
119
- appendSlash( hash[ :targetNotifierURL ] )
120
- else
121
- "https://pastehub.net:8001/"
122
- end
68
+
123
69
  @localDbPath = if hash[ :localDbPath ]
124
70
  hash[ :localDbPath ]
125
71
  else
126
- File.expand_path( "~/.pastehub/" ) + "/"
72
+ File.expand_path( sprintf( "%s/.pastehub/", getHomeDirectory() )) + "/"
73
+ end
74
+ @localSyncPath = if hash[ :localSyncPath ]
75
+ hash[ :localSyncPath ]
76
+ else
77
+ File.expand_path( sprintf( "%s/Dropbox/pastehub/", getHomeDirectory() )) + "/"
127
78
  end
128
- end
129
-
130
- def loadServer()
131
- name = "/etc/pastehub.conf"
132
- if File.exist?( name )
133
- open( name ) { |f|
134
- json = JSON.parse( f.read )
135
- self.setupServer( { :aws => json[ 'aws' ],
136
- :awsWarn => json[ 'awsWarn' ],
137
- :dynamoEp => json[ 'dynamoEp' ],
138
- :dynamoAccessKey => json[ 'dynamoAccessKey' ],
139
- :dynamoSecretKey => json[ 'dynamoSecretKey' ],
140
- :memcacheEp => json[ 'memcacheEp' ],
141
- :keyCacheTime => json[ 'keyCacheTime' ],
142
- :domain => json[ 'domain' ],
143
- :keystore => json[ 'keystore' ],
144
- :keystorePassword => json[ 'keystorePassword' ] } )
145
- }
146
- end
147
79
  end
148
80
 
149
81
  def loadClient()
@@ -151,16 +83,13 @@ module PasteHub
151
83
  if File.exist?( name )
152
84
  open( name ) { |f|
153
85
  json = JSON.parse( f.read )
154
- self.setupClient( { :targetApiURL => json[ 'targetApiURL' ],
155
- :targetNotifierURL => json[ 'targetNotifierURL' ],
156
- :localDbPath => json[ 'localDbPath' ] } )
157
-
86
+ self.setupClient( { :localDbPath => json[ 'localDbPath' ],
87
+ :localSyncPath => json[ 'localSyncPath' ],
88
+ } )
158
89
  }
159
90
  end
160
91
  end
161
92
 
162
- attr_reader :aws, :dynamoEp, :dynamoAccessKey, :dynamoSecretKey, :memcacheEp, :keyCacheTime, :domain, :keystore, :keystorePassword
163
- attr_reader :targetApiURL, :targetNotifierURL, :localDbPath, :listItems
164
- attr_accessor :awsWarn
93
+ attr_reader :localDbPath, :localSyncPath, :notifyMessageMax
165
94
  end
166
95
  end