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.
- checksums.yaml +6 -14
- data/README.md +11 -16
- data/Rakefile +32 -79
- data/VERSION.yml +2 -2
- data/bin/PastehubSync +75 -49
- data/{lib/pastehub/store.rb → bin/pastehubGet} +41 -43
- data/bin/pastehubPost +10 -17
- data/lib/pastehub.rb +2 -5
- data/lib/pastehub/client.rb +18 -380
- data/lib/pastehub/clientsync.rb +161 -199
- data/lib/pastehub/clipboard.rb +1 -1
- data/lib/pastehub/config.rb +25 -96
- data/lib/pastehub/syncentry.rb +122 -0
- metadata +64 -71
- data/bin/pastehubDump +0 -114
- data/lib/pastehub/auth.rb +0 -171
- data/lib/pastehub/crypt.rb +0 -70
- data/lib/pastehub/localdb.rb +0 -187
- data/lib/pastehub/log.rb +0 -95
- data/lib/pastehub/macosx.rb +0 -66
- data/lib/pastehub/masterdb.rb +0 -213
- data/server/masterdb.rb +0 -141
- data/server/notifier.rb +0 -89
data/lib/pastehub/clientsync.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#
|
2
2
|
# clientsync.rb - PasteHub's client sync library
|
3
3
|
#
|
4
|
-
# Copyright (c) 2009-
|
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
|
42
|
+
class Status
|
43
|
+
TIMER_INIT = 60 # second
|
42
44
|
|
43
|
-
def initialize(
|
44
|
-
@
|
45
|
-
@
|
46
|
-
@
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
120
|
-
|
121
|
-
system( "killall -SIGUSR1 Emacs emacs" )
|
122
|
-
end
|
108
|
+
class ClientSync
|
109
|
+
ICONSOCKET = "/tmp/pastehub_icon"
|
123
110
|
|
124
|
-
|
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
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
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
|
-
|
153
|
-
list = client.getList()
|
154
|
+
nil
|
154
155
|
end
|
155
|
-
client.setServerFlags( list )
|
156
156
|
end
|
157
157
|
|
158
|
-
def
|
159
|
-
|
160
|
-
@connectNotifyFunc = connectNotifyFunc
|
161
|
-
@disconnectNotifyFunc = disconnectNotifyFunc
|
158
|
+
def path_to_hostname( path )
|
159
|
+
File.basename( path, ".dat" )
|
162
160
|
end
|
163
161
|
|
164
|
-
def
|
165
|
-
|
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
|
169
|
-
|
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
|
173
|
-
@
|
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
|
-
|
178
|
-
|
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
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
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
|
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
|
-
|
221
|
-
STDERR.
|
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?(
|
235
|
+
data = PasteHub::AbstractClipboard.hasNew?( )
|
252
236
|
if data
|
253
237
|
if @prevData != data
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
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
|
data/lib/pastehub/clipboard.rb
CHANGED
data/lib/pastehub/config.rb
CHANGED
@@ -43,107 +43,39 @@ module PasteHub
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def initialize( )
|
46
|
-
self.setupServer( {} )
|
47
46
|
self.setupClient( {} )
|
48
|
-
@
|
47
|
+
@verbose_flag = false
|
48
|
+
@notifyMessageMax = 80
|
49
49
|
end
|
50
50
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
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
|
-
|
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
|
-
|
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( "
|
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( { :
|
155
|
-
:
|
156
|
-
|
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 :
|
163
|
-
attr_reader :targetApiURL, :targetNotifierURL, :localDbPath, :listItems
|
164
|
-
attr_accessor :awsWarn
|
93
|
+
attr_reader :localDbPath, :localSyncPath, :notifyMessageMax
|
165
94
|
end
|
166
95
|
end
|