phprpc 3.0.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.
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