pastehub 0.2.6 → 0.4.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.
@@ -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