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,83 @@
1
+ #
2
+ # store.rb - PasteHub's storage 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
+
35
+ module PasteHub
36
+
37
+ class LocalStore
38
+ def initialize( username, reader = false )
39
+ @db = PasteHub::LocalDB.new( PasteHub::Config.instance.localDbPath )
40
+ @db.open( username, reader )
41
+ end
42
+
43
+ def getList
44
+ @db.getList()
45
+ end
46
+
47
+ def getServerList
48
+ @db.getServerList()
49
+ end
50
+
51
+ def top
52
+ lst = @db.getList( 1 )
53
+ if 0 < lst.size
54
+ [ lst.first, @db.getValue( lst.first ) ]
55
+ else
56
+ [ nil, nil ]
57
+ end
58
+ end
59
+
60
+ def getValue( key )
61
+ @db.getValue( key.dup )
62
+ end
63
+
64
+ def latest
65
+ [ @db.getValue( PasteHub::SERVER_DATE_KEY ).to_s,
66
+ @db.getValue( PasteHub::LOCAL_DATE_KEY ).to_s ]
67
+ end
68
+
69
+ def insertValue( key, value )
70
+ @db.insertValue( key, value )
71
+ end
72
+
73
+ def close
74
+ @db.close
75
+ end
76
+
77
+ def clear
78
+ @db.clear
79
+ end
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,153 @@
1
+ require 'digest'
2
+ require 'date'
3
+ require 'set'
4
+
5
+ module PasteHub
6
+ require 'rubygems'
7
+ begin
8
+ require 'highline'
9
+ USE_HIGHLINE = true
10
+ rescue LoadError
11
+ USE_HIGHLINE = false
12
+ end
13
+
14
+ class Util
15
+ def initialize()
16
+ end
17
+
18
+ # return message digest for str.
19
+ def digest( str )
20
+ Digest::SHA1.hexdigest( str )
21
+ end
22
+
23
+ # return the currentTime in Unixtime
24
+ def currentTime( )
25
+ dt = Time.new.gmtime.to_datetime()
26
+ currentDate = dt.strftime( "%s" ) + "=" + dt.strftime( "%F.%H:%M:%S" )
27
+ currentDate
28
+ end
29
+
30
+ def currentSeconds( )
31
+ self.key_seconds( self.currentTime() )
32
+ end
33
+
34
+ def _splitKey( key )
35
+ key.split( /[=]/ )
36
+ end
37
+
38
+ def key_seconds( key )
39
+ arr = _splitKey( key )
40
+ if 1 < arr.size
41
+ arr[0].to_i
42
+ else
43
+ nil
44
+ end
45
+ end
46
+
47
+ def key_timestamp( key )
48
+ arr = _splitKey( key )
49
+ if 1 < arr.size
50
+ arr[1]
51
+ else
52
+ nil
53
+ end
54
+ end
55
+
56
+ def key_digest( key )
57
+ arr = _splitKey( key )
58
+ if 2 < arr.size
59
+ arr[2]
60
+ else
61
+ nil
62
+ end
63
+ end
64
+
65
+ def diffList( list1, list2 )
66
+ set1 = Set.new
67
+ set2 = Set.new
68
+ list1.each { |e| set1.add(e) }
69
+ list2.each { |e| set2.add(e) }
70
+ set1.difference( set2 ).to_a
71
+ end
72
+
73
+ # Same as Gauche's take* function
74
+ def takeList( list1, num )
75
+ if ( num < 0 )
76
+ list1
77
+ elsif num <= list1.size
78
+ list1[ 0 ... num ]
79
+ else
80
+ list1
81
+ end
82
+ end
83
+
84
+ # Same as Gauche's drop* function
85
+ def dropList( list1, num )
86
+ if num < 0
87
+ list1
88
+ elsif num <= list1.size
89
+ list1[ num .. list1.size ]
90
+ else
91
+ []
92
+ end
93
+ end
94
+
95
+ def say( message )
96
+ if USE_HIGHLINE
97
+ HighLine.new.say( message ) {|q|
98
+ q.readline = true
99
+ }
100
+ else
101
+ puts "#{message}"
102
+ end
103
+ end
104
+
105
+ def inputText( label )
106
+ if USE_HIGHLINE
107
+ HighLine.new.ask( label ) {|q|
108
+ q.readline = true
109
+ }
110
+ else
111
+ print "#{label}"
112
+ return gets.chomp
113
+ end
114
+ end
115
+
116
+ def inputPassword( label )
117
+ if USE_HIGHLINE
118
+ HighLine.new.ask( label ) {|q|
119
+ q.readline = true
120
+ q.echo = '*'
121
+ }
122
+ else
123
+ print "#{label}"
124
+ return gets.chomp
125
+ end
126
+ end
127
+
128
+ # input utility
129
+ def inputPasswordTwice( message, firstLabel, secondLabel )
130
+ required_password_chars = 6
131
+
132
+ STDERR.puts( message )
133
+ 3.times { |n|
134
+ firstStr = nil
135
+ while not firstStr
136
+ firstStr = inputPassword(firstLabel)
137
+ if required_password_chars > firstStr.size()
138
+ STDERR.puts( "you must input #{required_password_chars} or more characters." )
139
+ firstStr = nil
140
+ elsif firstStr.match( /[ \t]/i )
141
+ STDERR.puts( "you must not use white space characters." )
142
+ firstStr = nil
143
+ end
144
+ end
145
+ secondStr = inputPassword(secondLabel)
146
+ if firstStr == secondStr
147
+ return firstStr
148
+ end
149
+ }
150
+ return nil
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,141 @@
1
+ require 'vertx'
2
+ require 'json/pure'
3
+ require 'cgi'
4
+ require 'date'
5
+ require 'memcache'
6
+
7
+ $LOAD_PATH.push( File.dirname(__FILE__) + "/../lib" )
8
+ require 'pastehub'
9
+ require 'pastehub/log'
10
+ PasteHub::Config.instance.loadServer
11
+
12
+ # display config info
13
+ ins = PasteHub::Config.instance
14
+ printf( "Use AWS: %s\n", ins.aws )
15
+ printf( "Domain: %s\n", ins.domain )
16
+ printf( "Dynamo Endpoint: %s\n", ins.dynamoEp )
17
+ printf( "Memcache Endpoint: %s\n", ins.memcacheEp )
18
+
19
+ # initialize master database
20
+ require 'pastehub/masterdb'
21
+
22
+
23
+ # setup user table for Fake DynamoDB
24
+ users = PasteHub::Users.new( )
25
+ if not ins.aws
26
+ open( "/var/pastehub/users.tsv", "r" ) {|f|
27
+ f.readlines.each { |line|
28
+ pair = line.chomp.split( /[\t ]+/ )
29
+ printf( "Added local user table: %s\n", pair[0] )
30
+ users.addUser( pair[0], pair[1] )
31
+ }
32
+ }
33
+ end
34
+
35
+
36
+ notifyHash = Memcache.new( :server => PasteHub::Config.instance.memcacheEp )
37
+
38
+ masterdb_server = Vertx::HttpServer.new
39
+ masterdb_server.request_handler do |req|
40
+
41
+ req.body_handler do |body|
42
+ util = PasteHub::Util.new
43
+ auth = PasteHub::AuthForServer.new( users )
44
+ ret = auth.invoke( req.headers, util.currentSeconds() )
45
+ username = ret[1]
46
+
47
+ log = PasteHub::Log.new( :api => req.path, :user => username )
48
+ if ret[0]
49
+ log.info( "connected" )
50
+ else
51
+ log.error( 'Auth failure:' + ret[2].to_s, { :reason => ret[2].to_s } )
52
+ req.response.status_code = 403
53
+ req.response.status_message = "Authorization failure."
54
+ req.response.end
55
+ return
56
+ end
57
+
58
+ entries = PasteHub::Entries.new( username )
59
+ users = PasteHub::Users.new( )
60
+
61
+ case req.path
62
+ when "/authTest"
63
+ req.response.end
64
+
65
+ when "/putValue"
66
+ data = body.to_s.dup
67
+ digest = util.digest( data )
68
+
69
+ # update db
70
+ key = req.headers[ 'X-Pastehub-Key' ].dup
71
+ puts "[#{username}]:putValue: key=[#{key}] "
72
+
73
+ # client have no specified key
74
+ if "_" == key
75
+ key = util.currentTime( ) + "=" + digest
76
+ end
77
+
78
+ # data duplicate check
79
+ insertFlag = true
80
+ prevKey = notifyHash.get( username )
81
+ if prevKey
82
+ if util.key_digest( prevKey ) == digest
83
+ log.info( "canceled because data is duplicate. ", { :key => key } )
84
+ insertFlag = false
85
+ key = prevKey
86
+ end
87
+ end
88
+
89
+ if insertFlag
90
+ Vertx.set_timer(1000) do
91
+ log.info( "START: delayed job." )
92
+ # update db
93
+ log.info( "insert", { :key => key } )
94
+ entries.insertValue( key, data )
95
+ users.touch( username )
96
+ # notify to client
97
+ notifyHash.set( username, key, PasteHub::Config.instance.keyCacheTime )
98
+
99
+ # remove Last entry
100
+ arr = entries.getList( )
101
+ if PasteHub::Config.instance.listItems < arr.size
102
+ entries.deleteValue( arr[arr.size-1] )
103
+ end
104
+ log.info( "END: delayed job", { :entries => arr.size } )
105
+ end
106
+ end
107
+ req.response.end( key )
108
+
109
+
110
+ when "/getList"
111
+ limit = req.headers[ 'X-Pastehub-Limit' ]
112
+ if limit
113
+ str = entries.getList( ).take( limit.to_i ).join( "\n" )
114
+ else
115
+ str = entries.getList( ).join( "\n" )
116
+ end
117
+ log.info( "getList", { :limit => limit, :entries => entries.getList( ).size } )
118
+ puts str
119
+ req.response.end( str )
120
+
121
+ when "/getValue"
122
+ k = body.to_s.chomp
123
+ if 0 < k.size
124
+ str = entries.getValue( k.dup )
125
+ else
126
+ str = ""
127
+ end
128
+ log.info( "getValue", { :key => k } )
129
+ puts "[#{username}]:getValue:" + k
130
+ req.response.end( str )
131
+
132
+ else
133
+ mes = "Error: Unknown API #{req.path}"
134
+ log.error( mes )
135
+ req.response.status_code = 400
136
+ req.response.status_message = mes
137
+ req.response.end
138
+
139
+ end
140
+ end
141
+ end.listen(8000)
@@ -0,0 +1,89 @@
1
+ require 'vertx'
2
+ require 'cgi'
3
+ require 'memcache'
4
+ require 'pp'
5
+ $LOAD_PATH.push( File.dirname(__FILE__) + "/../lib" )
6
+ require 'pastehub'
7
+ require 'pastehub/log'
8
+ PasteHub::Config.instance.loadServer
9
+
10
+ # display config info
11
+ ins = PasteHub::Config.instance
12
+ printf( "Use AWS: %s\n", ins.aws )
13
+ printf( "Domain: %s\n", ins.domain )
14
+ printf( "Dynamo Endpoint: %s\n", ins.dynamoEp )
15
+ printf( "Memcache Endpoint: %s\n", ins.memcacheEp )
16
+ printf( "ssl keystore file: %s\n", ins.keystore )
17
+
18
+ # initialize master database
19
+ require 'pastehub/masterdb'
20
+
21
+ # setup user table for Fake DynamoDB
22
+ users = PasteHub::Users.new( )
23
+ if not ins.aws
24
+ open( "/var/pastehub/users.tsv", "r" ) {|f|
25
+ f.readlines.each { |line|
26
+ pair = line.chomp.split( /[\t ]+/ )
27
+ printf( "Added local user table: %s\n", pair[0] )
28
+ users.addUser( pair[0], pair[1] )
29
+ }
30
+ }
31
+ end
32
+
33
+
34
+ INTERVAL = 0.5
35
+ POLLING_SEC = 60
36
+
37
+ notifyHash = Memcache.new( :server => PasteHub::Config.instance.memcacheEp )
38
+
39
+ def notify( res, str )
40
+ res.write_str( "#{str}\n" )
41
+ end
42
+
43
+ notifier = Vertx::HttpServer.new
44
+ if ins.keystore
45
+ notifier.ssl = true
46
+ notifier.key_store_path = ins.keystore
47
+ notifier.key_store_password = ins.keystorePassword
48
+ end
49
+
50
+ notifier.request_handler do |req|
51
+ util = PasteHub::Util.new
52
+ auth = PasteHub::AuthForServer.new( users )
53
+
54
+ ret = auth.invoke( req.headers, util.currentSeconds() )
55
+ username = ret[1]
56
+ # Now send back a response
57
+ req.response.chunked = true
58
+
59
+ log = PasteHub::Log.new( :api => 'notifier', :user => username )
60
+ if ret[0]
61
+ log.info( "connected" )
62
+ else
63
+ log.error( 'Auth failure:' + ret[2].to_s, { :reason => ret[2].to_s } )
64
+ req.response.status_code = 403
65
+ req.response.status_message = "Authorization failure."
66
+ req.response.end
67
+ return
68
+ end
69
+
70
+ got = nil
71
+ timer_count = 0
72
+
73
+ Vertx::set_periodic (1000 * INTERVAL) { |timer_id|
74
+ timer_count += INTERVAL
75
+ if notifyHash[ username ]
76
+ if got != notifyHash[ username ]
77
+ got = notifyHash[ username ]
78
+ log.info( 'notify', { :notify => got } )
79
+ notify( req.response, got )
80
+ end
81
+ end
82
+
83
+ if ( POLLING_SEC < timer_count )
84
+ log.info( 'timeout', { :notify => nil } )
85
+ req.response.end
86
+ Vertx::cancel_timer(timer_id)
87
+ end
88
+ }
89
+ end.listen(8001)