phprpc 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/phprpc.rb ADDED
@@ -0,0 +1,65 @@
1
+ ############################################################
2
+ # #
3
+ # The implementation of PHPRPC Protocol 3.0 #
4
+ # #
5
+ # phprpc.rb #
6
+ # #
7
+ # Release 3.0.0 #
8
+ # Copyright (c) 2005-2008 by Team-PHPRPC #
9
+ # #
10
+ # WebSite: http://www.phprpc.org/ #
11
+ # http://www.phprpc.net/ #
12
+ # http://www.phprpc.com/ #
13
+ # http://sourceforge.net/projects/php-rpc/ #
14
+ # #
15
+ # Authors: Ma Bingyao <andot@ujn.edu.cn> #
16
+ # #
17
+ # This file may be distributed and/or modified under the #
18
+ # terms of the GNU Lesser General Public License (LGPL) #
19
+ # version 3.0 as published by the Free Software Foundation #
20
+ # and appearing in the included file LICENSE. #
21
+ # #
22
+ ############################################################
23
+ #
24
+ # PHPRPC library.
25
+ #
26
+ # Copyright (C) 2005-2008 Ma Bingyao <andot@ujn.edu.cn>
27
+ # Version: 3.0
28
+ # LastModified: Sep 13, 2008
29
+ # This library is free. You can redistribute it and/or modify it.
30
+
31
+ $: << File.expand_path(File.dirname(__FILE__))
32
+
33
+ module Crypt
34
+ autoload :XXTEA, 'crypt/xxtea'
35
+ end
36
+
37
+ module PHP
38
+ autoload :Formator, 'php/formator'
39
+ end
40
+
41
+ module PHPRPC
42
+
43
+ VERSION = [3,0]
44
+
45
+ def self.version
46
+ VERSION.join(".")
47
+ end
48
+
49
+ def self.release
50
+ "1.0"
51
+ end
52
+
53
+ autoload :Client, 'phprpc/client'
54
+ autoload :Server, 'phprpc/server'
55
+ autoload :BaseServer, 'phprpc/base_server'
56
+ autoload :CGIServer, 'phprpc/cgi_server'
57
+ autoload :MongrelServer, 'phprpc/mongrel_server'
58
+ autoload :ThinServer, 'phprpc/thin_server'
59
+ autoload :FCGIServer, 'phprpc/fcgi_server'
60
+ autoload :SCGIServer, 'phprpc/scgi_server'
61
+ autoload :LSAPIServer, 'phprpc/lsapi_server'
62
+ autoload :EbbServer, 'phprpc/ebb_server'
63
+ autoload :WEBrickServer, 'phprpc/webrick_server'
64
+ autoload :FakeServer, 'phprpc/fake_server'
65
+ end
@@ -0,0 +1,392 @@
1
+ ############################################################
2
+ # #
3
+ # The implementation of PHPRPC Protocol 3.0 #
4
+ # #
5
+ # base_server.rb #
6
+ # #
7
+ # Release 3.0.0 #
8
+ # Copyright (c) 2005-2008 by Team-PHPRPC #
9
+ # #
10
+ # WebSite: http://www.phprpc.org/ #
11
+ # http://www.phprpc.net/ #
12
+ # http://www.phprpc.com/ #
13
+ # http://sourceforge.net/projects/php-rpc/ #
14
+ # #
15
+ # Authors: Ma Bingyao <andot@ujn.edu.cn> #
16
+ # #
17
+ # This file may be distributed and/or modified under the #
18
+ # terms of the GNU Lesser General Public License (LGPL) #
19
+ # version 3.0 as published by the Free Software Foundation #
20
+ # and appearing in the included file LICENSE. #
21
+ # #
22
+ ############################################################
23
+ #
24
+ # PHPRPC BaseServer library.
25
+ #
26
+ # Copyright (C) 2005-2008 Ma Bingyao <andot@ujn.edu.cn>
27
+ # Version: 3.0
28
+ # LastModified: Sep 12, 2008
29
+ # This library is free. You can redistribute it and/or modify it.
30
+
31
+ require "digest/md5"
32
+ require "php/formator"
33
+ require "crypt/xxtea"
34
+ require "powmod"
35
+ require "cgi"
36
+ require "cgi/session"
37
+ require 'optparse'
38
+
39
+ module PHPRPC
40
+
41
+ # String for carriage return
42
+ CR = "\015"
43
+
44
+ # String for linefeed
45
+ LF = "\012"
46
+
47
+ # Standard internet newline sequence
48
+ EOL = CR + LF
49
+
50
+ class BaseServer
51
+
52
+ attr_accessor :charset, :debug
53
+
54
+ def initialize()
55
+ @methods = {}
56
+ @charset = 'utf-8'
57
+ @debug = $DEBUG
58
+ end
59
+
60
+ def add(methodname, obj = nil, aliasname = nil, &block)
61
+ raise TypeError, "methodname must be a string or a string list." if methodname.nil?
62
+ aliasname = methodname if aliasname.nil?
63
+ raise TypeError, "aliasname's type must match with methodname's type" if aliasname.class != methodname.class
64
+ if methodname.kind_of?(String) then
65
+ methodname = [methodname]
66
+ aliasname = [aliasname]
67
+ end
68
+ raise RangeError, "aliasname's size must equal methodname's size" if methodname.size != aliasname.size
69
+ obj = Object if obj.nil?
70
+ methodname.each_with_index { |name, index|
71
+ if block_given? then
72
+ @methods[aliasname[index].downcase] = block
73
+ elsif obj.respond_to?(name, true) then
74
+ @methods[aliasname[index].downcase] = [name, obj]
75
+ end
76
+ }
77
+ end
78
+
79
+ def call(env)
80
+ [200, *call!(env, PHPRPC::Request.new(env))]
81
+ end
82
+
83
+ def call!(env, request)
84
+ body = ''
85
+ callback = ''
86
+ encode = true
87
+ session = (env['rack.session'].is_a?(Hash) ? env['rack.session'] : CGI::Session.new(request))
88
+ begin
89
+ params = request.params
90
+ callback = get_base64('phprpc_callback', params)
91
+ encode = get_boolean('phprpc_encode', params)
92
+ encrypt = get_encrypt(params)
93
+ cid = "phprpc_#{(params.key?('phprpc_id') ? params['phprpc_id'][0] : '0')}"
94
+ if params.key?('phprpc_func') then
95
+ func = params['phprpc_func'][0].downcase
96
+ if @methods.key?(func) then
97
+ hash = get_session(session, cid)
98
+ if hash.key?('key') then
99
+ key = hash['key']
100
+ elsif encrypt > 0 then
101
+ encrypt = 0
102
+ raise "Can't find the key for decryption."
103
+ end
104
+ ref = get_boolean('phprpc_ref', params)
105
+ args = get_args(params, key, encrypt)
106
+ result = encode_string(encrypt_string(PHP::Formator.serialize(invoke(func, args)), key, 2, encrypt), encode)
107
+ body << 'phprpc_result="' << result << '";' << EOL
108
+ if ref then
109
+ args = encode_string(encrypt_string(PHP::Formator.serialize(args), key, 1, encrypt), encode)
110
+ body << 'phprpc_args="' << args << '";' << EOL
111
+ end
112
+ else
113
+ raise "Can't find this function #{func}()."
114
+ end
115
+ write_error(body, 0, '', callback, encode)
116
+ elsif (encrypt != false) and (encrypt != 0) then
117
+ hash = get_session(session, cid)
118
+ keylen = get_keylength(params, hash)
119
+ key_exchange(body, env, request, hash, callback, encode, encrypt, keylen)
120
+ set_session(session, cid, hash)
121
+ else
122
+ write_functions(body, callback, encode)
123
+ end
124
+ rescue Exception => e
125
+ body = ''
126
+ if @debug then
127
+ write_error(body, 1, e.backtrace.unshift(e.message).join(EOL), callback, encode)
128
+ else
129
+ write_error(body, 1, e.message, callback, encode)
130
+ end
131
+ ensure
132
+ session.close if session.respond_to?(:close)
133
+ return [header(request, body), body]
134
+ end
135
+ end
136
+
137
+ private
138
+
139
+ def invoke(methodname, args)
140
+ if @methods.key?(methodname) then
141
+ m = @methods[methodname]
142
+ m.kind_of?(Array) ? m[1].send(m[0], *args) : m.call(*args)
143
+ end
144
+ end
145
+
146
+ def add_js_slashes(str, flag)
147
+ range = flag ? [0..31, 34, 39, 92, 127..255] : [0..31, 34, 39, 92, 127]
148
+ result = ''
149
+ str.each_byte { |c|
150
+ result << case c
151
+ when *range then
152
+ '\x' << c.to_s(16).rjust(2, '0')
153
+ else
154
+ c.chr
155
+ end
156
+ }
157
+ result
158
+ end
159
+
160
+ def encode_string(str, encode = true, flag = true)
161
+ return str if str == ''
162
+ encode ? [str].pack('m').delete!("\n") : add_js_slashes(str, flag)
163
+ end
164
+
165
+ def encrypt_string(str, key, level, encrypt)
166
+ (encrypt >= level) ? Crypt::XXTEA.encrypt(str, key) : str
167
+ end
168
+
169
+ def decrypt_string(str, key, level, encrypt)
170
+ (encrypt >= level) ? Crypt::XXTEA.decrypt(str, key) : str
171
+ end
172
+
173
+ def header(request, body)
174
+ h = {
175
+ 'X-Powered-By' => 'PHPRPC Server/3.0',
176
+ 'Expires' => CGI::rfc1123_date(Time.now),
177
+ 'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0',
178
+ 'Content-Type' => "text/plain; charset=#{@charset}",
179
+ 'Content-Length' => body.length.to_s,
180
+ }
181
+ output_cookies = request.instance_variable_get(:@output_cookies)
182
+ if not output_cookies.nil? then
183
+ h["Set-Cookie"] = output_cookies[0].to_s
184
+ request.instance_variable_set(:@output_cookies, nil)
185
+ end
186
+ return h
187
+ end
188
+
189
+ def write_url(body, env, request, encode)
190
+ output_hidden = request.instance_variable_get(:@output_hidden)
191
+ output_hidden = { env['rack.session.options'][:id] => env['rack.session.options'][:key] } if env['rack.session.options'].is_a?(Hash)
192
+ if (output_hidden) then
193
+ scheme = env["rack.url_scheme"]
194
+ scheme = (["yes", "on", "1"].include?(env["HTTPS"]) ? 'https': 'http') if scheme.nil? or scheme.empty?
195
+ host = (env["HTTP_HOST"] || env["SERVER_NAME"]).gsub(/:\d+\z/, '')
196
+ port = env['SERVER_PORT']
197
+ path = env['SCRIPT_NAME']
198
+ path = env['PATH_INFO'] if path.nil? or path.empty?
199
+ path = env['REQUEST_PATH'] if path.nil? or path.empty?
200
+ url = "#{scheme}://#{host}#{((port == '80') ? '' : ':' + port)}#{path}"
201
+ url << '?'
202
+ output_hidden.each { |key, value|
203
+ url << "#{key}=#{CGI::escape(value)}&"
204
+ }
205
+ params = request.params
206
+ if (params.size > 0) then
207
+ params.each { |key, values|
208
+ values.each { |value|
209
+ url << "#{key}=#{CGI::escape(value)}&"
210
+ } if not key.index(/^phprpc_/i)
211
+ }
212
+ end
213
+ url[-1] = ''
214
+ body << 'phprpc_url="' << encode_string(url, encode) << '";' << EOL
215
+ end
216
+ end
217
+
218
+ def write_functions(body, callback, encode)
219
+ body << 'phprpc_functions="' << encode_string(PHP::Formator.serialize(@methods.keys), encode) << '";' << EOL
220
+ body << callback
221
+ end
222
+
223
+ def write_error(body, errno, errstr, callback, encode)
224
+ body << 'phprpc_errno="' << errno.to_s << '";' << EOL
225
+ body << 'phprpc_errstr="' << encode_string(errstr, encode, false) << '";' << EOL
226
+ body << 'phprpc_output="";' << EOL
227
+ body << callback
228
+ end
229
+
230
+ def get_boolean(name, params)
231
+ (params.key?(name) ? (params[name][0].downcase != "false") : true)
232
+ end
233
+
234
+ def get_base64(name, params)
235
+ (params.key?(name) ? params[name][0].unpack('m')[0] : '')
236
+ end
237
+
238
+ def get_encrypt(params)
239
+ if params.key?('phprpc_encrypt') then
240
+ encrypt = params['phprpc_encrypt'][0].downcase
241
+ case encrypt
242
+ when "true" then true
243
+ when "false" then false
244
+ else encrypt.to_i
245
+ end
246
+ else
247
+ 0
248
+ end
249
+ end
250
+
251
+ def get_args(params, key, encrypt)
252
+ args = []
253
+ if params.key?('phprpc_args') then
254
+ arguments = PHP::Formator.unserialize(decrypt_string(params['phprpc_args'][0].unpack('m')[0], key, 1, encrypt))
255
+ arguments.size.times { |i|
256
+ args[i] = arguments[i]
257
+ }
258
+ end
259
+ return args
260
+ end
261
+
262
+ def set_session(session, cid, hash)
263
+ session[cid] = PHP::Formator.serialize(hash)
264
+ end
265
+
266
+ def get_session(session, cid)
267
+ str = session[cid]
268
+ if str then
269
+ PHP::Formator.unserialize(str)
270
+ else
271
+ {}
272
+ end
273
+ end
274
+
275
+ def get_keylength(params, hash)
276
+ (params.key?('phprpc_keylen') ? params['phprpc_keylen'][0].to_i : ((hash.key?('keylen')) ? hash['keylen'] : 128))
277
+ end
278
+
279
+ def key_exchange(body, env, request, hash, callback, encode, encrypt, keylen)
280
+ if (encrypt == true) then
281
+ keylen, encrypt = DHParams.get(keylen)
282
+ x = rand(1 << (keylen - 1)) or (1 << (keylen - 2))
283
+ hash['x'] = x.to_s
284
+ hash['p'] = encrypt['p']
285
+ hash['keylen'] = keylen
286
+ encrypt['y'] = Math.powmod(encrypt['g'].to_i, x, encrypt['p'].to_i).to_s
287
+ body << 'phprpc_encrypt="' << encode_string(PHP::Formator.serialize(encrypt), encode) << '";' << EOL
288
+ body << 'phprpc_keylen="' << keylen.to_s << '";' << EOL if keylen != 128
289
+ write_url(body, env, request, encode)
290
+ else
291
+ y = encrypt
292
+ x = hash['x'].to_i
293
+ p = hash['p'].to_i
294
+ key = Math.powmod(y, x, p)
295
+ hash['key'] = ((keylen == 128) ? [key.to_s(16).rjust(32, '0')].pack('H*') : Digest::MD5.digest(key.to_s))
296
+ end
297
+ body << callback
298
+ end
299
+
300
+ end # class BaseServer
301
+
302
+ class Request
303
+
304
+ attr_accessor :cookies
305
+ attr :params
306
+
307
+ def initialize(env)
308
+ @params = env["QUERY_STRING"] ? CGI::parse(env["QUERY_STRING"].to_s) : {}
309
+ if (env['REQUEST_METHOD'] == 'POST' and
310
+ (env['CONTENT_TYPE'].nil? or
311
+ env['CONTENT_TYPE'].split(/\s*[;,]\s*/, 2)[0].downcase ==
312
+ 'application/x-www-form-urlencoded')) then
313
+ # fix ebb bug, the read method of ebb can't return all bytes from the I/O stream
314
+ if input_body = env['rack.input'].read then
315
+ while chunk = env['rack.input'].read(512) do
316
+ input_body << chunk
317
+ end
318
+ else
319
+ input_body = ''
320
+ end
321
+ @params.update(CGI::parse(input_body))
322
+ end
323
+ @cookies = if env["HTTP_COOKIE"] then CGI::Cookie::parse(env["HTTP_COOKIE"].to_s) else {} end
324
+ @output_cookies = nil
325
+ @output_hidden = nil
326
+ end
327
+
328
+ def [](key)
329
+ params = @params[key]
330
+ return '' unless params
331
+ value = params[0]
332
+ if value then value else "" end
333
+ end
334
+
335
+ def []=(key, val)
336
+ @params[key] = val
337
+ end
338
+
339
+ def keys
340
+ @params.keys
341
+ end
342
+
343
+ def has_key?(key)
344
+ @params.has_key?(key)
345
+ end
346
+
347
+ alias key? has_key?
348
+
349
+ alias include? has_key?
350
+
351
+ end # class Request
352
+
353
+
354
+ class DHParams
355
+
356
+ class << self
357
+
358
+ def get(length)
359
+ length = get_nearest(length)
360
+ dhparams = @dhparams_gen[length]
361
+ [length, dhparams[rand(dhparams.size)]]
362
+ end
363
+
364
+ private
365
+
366
+ def init
367
+ @lengths = [96, 128, 160, 192, 256, 512, 768, 1024, 1536, 2048, 3072, 4096]
368
+ @dhparams_gen = {}
369
+ @lengths.each { |length|
370
+ @dhparams_gen[length] = PHP::Formator.unserialize(IO.read("dhparams/#{length}.dhp"))
371
+ }
372
+ end
373
+
374
+ def get_nearest(n)
375
+ init if @lengths.nil?
376
+ j = 0
377
+ m = (@lengths[0] - n).abs
378
+ 1.upto(@lengths.size - 1) { |i|
379
+ t = (@lengths[i] - n).abs
380
+ if (m > t) then
381
+ m = t
382
+ j = i
383
+ end
384
+ }
385
+ return @lengths[j]
386
+ end
387
+
388
+ end # class self
389
+
390
+ end # class DHParams
391
+
392
+ end # module PHPRPC