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.
@@ -0,0 +1,287 @@
1
+ #
2
+ # clientsync.rb - PasteHub's client sync 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 'uri'
36
+ require 'open-uri'
37
+ require 'fileutils'
38
+
39
+ module PasteHub
40
+
41
+ class ClientSync
42
+
43
+ def initialize( alive_entries, localdb_path, polling_interval )
44
+ @alive_entries = alive_entries
45
+ @localdb_path = localdb_path
46
+ @polling_interval = polling_interval
47
+ end
48
+
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
92
+ end
93
+
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
+ }
108
+
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()
118
+
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
123
+
124
+ return notifyFlag
125
+ end
126
+
127
+
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
143
+ end
144
+
145
+
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() ]
151
+ else
152
+ STDERR.puts "Info: fetch ALL entries"
153
+ list = client.getList()
154
+ end
155
+ client.setServerFlags( list )
156
+ end
157
+
158
+ def addNoitfyCallback( countUpNotifyFunc, connectNotifyFunc, disconnectNotifyFunc )
159
+ @countUpNotifyFunc = countUpNotifyFunc
160
+ @connectNotifyFunc = connectNotifyFunc
161
+ @disconnectNotifyFunc = disconnectNotifyFunc
162
+ end
163
+
164
+ def notifyCountUp()
165
+ @countUpNotifyFunc.call() if @countUpNotifyFunc
166
+ end
167
+
168
+ def notifyConnect()
169
+ @connectNotifyFunc.call() if @connectNotifyFunc
170
+ end
171
+
172
+ def notifyDisconnect()
173
+ @disconnectNotifyFunc.call() if @disconnectNotifyFunc
174
+ end
175
+
176
+
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
186
+
187
+ 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
210
+ 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
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
241
+ end
242
+ end
243
+ end
244
+
245
+
246
+ def clipboardCheck( username, secretKey, password )
247
+ STDERR.puts "Info: clipboardCheck thread start"
248
+ @prevData = ""
249
+ while true
250
+ sleep @polling_interval
251
+ data = PasteHub::AbstractClipboard.hasNew?( username )
252
+ if data
253
+ 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
270
+ @prevData = data
271
+ end
272
+ end
273
+ end
274
+ 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
+ end
287
+ end
@@ -0,0 +1,56 @@
1
+ #
2
+ # clipboard.rb - PasteHub's platform independent clipboard library.
3
+ #
4
+ # Copyright (c) 2013-2013 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 'clipboard'
35
+
36
+ module PasteHub
37
+
38
+ class AbstractClipboard
39
+
40
+ def self.push( data )
41
+ Encoding.default_external = "UTF-8"
42
+ Clipboard.copy( data.force_encoding("UTF-8") )
43
+ nil
44
+ end
45
+
46
+ def self.hasNew?( username )
47
+ Encoding.default_external = "UTF-8"
48
+ str = Clipboard.paste( )
49
+ str = str.force_encoding("UTF-8")
50
+ if 0 == str.size
51
+ return nil
52
+ end
53
+ return str
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,166 @@
1
+ #
2
+ # config.rb - PasteHub's config file loader and config object.
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 'singleton'
35
+ require 'json'
36
+
37
+ module PasteHub
38
+ class Config
39
+ include Singleton
40
+
41
+ def self.instance
42
+ @@instance ||= new
43
+ end
44
+
45
+ def initialize( )
46
+ self.setupServer( {} )
47
+ self.setupClient( {} )
48
+ @listItems = 10
49
+ end
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
107
+ else
108
+ uri + "/"
109
+ end
110
+ end
111
+
112
+ 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
123
+ @localDbPath = if hash[ :localDbPath ]
124
+ hash[ :localDbPath ]
125
+ else
126
+ File.expand_path( "~/.pastehub/" ) + "/"
127
+ 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
+ end
148
+
149
+ def loadClient()
150
+ name = File.expand_path( "~/.pastehub.conf" )
151
+ if File.exist?( name )
152
+ open( name ) { |f|
153
+ json = JSON.parse( f.read )
154
+ self.setupClient( { :targetApiURL => json[ 'targetApiURL' ],
155
+ :targetNotifierURL => json[ 'targetNotifierURL' ],
156
+ :localDbPath => json[ 'localDbPath' ] } )
157
+
158
+ }
159
+ end
160
+ end
161
+
162
+ attr_reader :aws, :dynamoEp, :dynamoAccessKey, :dynamoSecretKey, :memcacheEp, :keyCacheTime, :domain, :keystore, :keystorePassword
163
+ attr_reader :targetApiURL, :targetNotifierURL, :localDbPath, :listItems
164
+ attr_accessor :awsWarn
165
+ end
166
+ end