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/AUTHORS +13 -0
- data/LICENSE +165 -0
- data/README +46 -0
- data/examples/client.rb +15 -0
- data/examples/server.rb +17 -0
- data/lib/crypt/xxtea.rb +112 -0
- data/lib/dhparams/1024.dhp +1 -0
- data/lib/dhparams/128.dhp +1 -0
- data/lib/dhparams/1536.dhp +1 -0
- data/lib/dhparams/160.dhp +1 -0
- data/lib/dhparams/192.dhp +1 -0
- data/lib/dhparams/2048.dhp +1 -0
- data/lib/dhparams/256.dhp +1 -0
- data/lib/dhparams/3072.dhp +1 -0
- data/lib/dhparams/4096.dhp +1 -0
- data/lib/dhparams/512.dhp +1 -0
- data/lib/dhparams/768.dhp +1 -0
- data/lib/dhparams/96.dhp +1 -0
- data/lib/php/formator.rb +454 -0
- data/lib/phprpc.rb +65 -0
- data/lib/phprpc/base_server.rb +392 -0
- data/lib/phprpc/cgi_server.rb +46 -0
- data/lib/phprpc/client.rb +268 -0
- data/lib/phprpc/ebb_server.rb +89 -0
- data/lib/phprpc/fake_server.rb +50 -0
- data/lib/phprpc/fcgi_server.rb +125 -0
- data/lib/phprpc/lsapi_server.rb +101 -0
- data/lib/phprpc/mongrel_server.rb +168 -0
- data/lib/phprpc/scgi_server.rb +134 -0
- data/lib/phprpc/server.rb +55 -0
- data/lib/phprpc/thin_server.rb +161 -0
- data/lib/phprpc/webrick_server.rb +139 -0
- data/lib/powmod.rb +39 -0
- metadata +91 -0
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
|