copper-cti 2.2.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5cdd69afbbde6d1d1b50191f409c57627960fe85190d4f86ec5c1f94c334e0fa
4
+ data.tar.gz: 76969132610a3b8cedc70ffd088d75792dbaf90e74031432c4f1914a96c21065
5
+ SHA512:
6
+ metadata.gz: 0cc2ea939f18e9a8e9d281551b138e955845b13cb70fee2f90deadf375b9b411a3dfb186983a6d69bd91303405b54f597a1f9be4d574a18cd5f0b35cac675bf2
7
+ data.tar.gz: 212f9c76afe757de47d15c9efdc307354dd0e638d57385e8aa44300ac06297512ee2461c2e6012ca6899b6f6cbff883b7a53fd612d9e46fa4809d71cb4d17172
@@ -0,0 +1,36 @@
1
+ require File.expand_path('StreamBuilder', File.dirname(__FILE__))
2
+
3
+ module CTI
4
+ module Builder
5
+ =begin rdoc
6
+ Version:: $Id: FileBuilder.rb 902 2013-04-23 05:07:04Z miyabe $
7
+
8
+ ファイルに対して結果を構築するオブジェクトです。
9
+ =end
10
+ class FileBuilder < StreamBuilder
11
+ =begin rdoc
12
+ 結果構築オブジェクトを作成します。
13
+
14
+ file:: 結果ファイル
15
+ =end
16
+ def initialize(file)
17
+ super(nil)
18
+ @file = file
19
+ end
20
+
21
+ def serial_write(data)
22
+ @out = File.open(@file, 'wb') unless @out
23
+ @out.write(data)
24
+ end
25
+
26
+ def finish
27
+ unless @out
28
+ @out = File.open(@file, 'wb')
29
+ super
30
+ end
31
+ @out.close
32
+ @out = nil
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ module CTI
2
+ module Builder
3
+ =begin rdoc
4
+ このオブジェクトは結果を構築しません。
5
+ =end
6
+ class NullBuilder
7
+ def add_block
8
+ end
9
+
10
+ def insert_block_before (anchor_id)
11
+ end
12
+
13
+ def write (id, data)
14
+ end
15
+
16
+ def close_block (id)
17
+ end
18
+
19
+ def serial_write (data)
20
+ end
21
+
22
+ def finish
23
+ end
24
+
25
+ def dispose
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,212 @@
1
+ require 'tempfile'
2
+
3
+ module CTI
4
+ =begin rdoc
5
+ このモジュールのクラスは、1つの出力結果を構築するためのものです。
6
+ =end
7
+ module Builder
8
+ class Fragment
9
+ def initialize(id)
10
+ @id = id
11
+ @prev = nil
12
+ @nxt = nil
13
+ @length = 0
14
+ @buffer = ''
15
+ end
16
+
17
+ def id
18
+ @id
19
+ end
20
+
21
+ def prev=(prev)
22
+ @prev = prev
23
+ end
24
+
25
+ def prev
26
+ @prev
27
+ end
28
+
29
+ def nxt=(nxt)
30
+ @nxt = nxt
31
+ end
32
+
33
+ def nxt
34
+ @nxt
35
+ end
36
+
37
+ =begin
38
+ フラグメントにデータを書き込みます。
39
+
40
+ tempFile:: 一時ファイル
41
+ onMemory:: メモリ上に置かれたデータの合計サイズ
42
+ segment:: セグメント番号シーケンス
43
+ bytes:: データ
44
+
45
+ 戻り値:: 書き込んだバイト数
46
+ =end
47
+
48
+ def write(tempFile, onMemory, segment, bytes)
49
+ len = bytes.bytesize
50
+ if (!@segments &&
51
+ @length + len <= CTIP2::FRG_MEM_SIZE &&
52
+ onMemory + len <= CTIP2::ON_MEMORY)
53
+ @buffer += bytes
54
+ onMemory += len
55
+ else
56
+ if @buffer
57
+ segment, wlen = raf_write(tempFile, segment, @buffer)
58
+ onMemory -= wlen
59
+ @buffer = nil
60
+ end
61
+ segment, len = raf_write(tempFile, segment, bytes)
62
+ end
63
+ @length += len
64
+ return onMemory, segment, len
65
+ end
66
+
67
+ =begin
68
+ フラグメントの内容を吐き出して、フラグメントを破棄します。
69
+
70
+ tempFile:: 一時ファイル
71
+ out:: 出力先ストリーム( IO )
72
+ =end
73
+
74
+ def flush(tempFile, out)
75
+ unless @segments
76
+ out.write(@buffer)
77
+ @buffer = nil
78
+ else
79
+ segcount = @segments.size
80
+ for seg in @segments
81
+ tempFile.pos = seg * CTIP2::SEGMENT_SIZE
82
+ buff = tempFile.read(seg == @segments.last ? @segLen : CTIP2::SEGMENT_SIZE)
83
+ out.write(buff)
84
+ end
85
+ end
86
+ end
87
+
88
+ private
89
+ =begin
90
+ 一時ファイルに書き込みます。
91
+
92
+ tempFile:: 一時ファイル
93
+ segment:: セグメント番号シーケンス
94
+ bytes:: データ
95
+
96
+ 戻り値:: 書き込んだバイト数
97
+ =end
98
+
99
+ def raf_write(tempFile, segment, bytes)
100
+ unless @segments
101
+ @segments = [segment]
102
+ segment += 1
103
+ @segLen = 0
104
+ end
105
+ written = 0
106
+ while (len = bytes.bytesize) > 0
107
+ if @segLen == CTIP2::SEGMENT_SIZE
108
+ @segments << segment
109
+ segment += 1
110
+ @segLen = 0
111
+ end
112
+ seg = @segments.last
113
+ wlen = [len, CTIP2::SEGMENT_SIZE - @segLen].min
114
+ wpos = seg * CTIP2::SEGMENT_SIZE + @segLen
115
+ tempFile.pos = wpos
116
+ data = bytes.slice!(0, wlen)
117
+ tempFile.write(data)
118
+ @segLen += wlen
119
+ written += wlen
120
+ end
121
+ return segment, written
122
+ end
123
+ end
124
+ =begin rdoc
125
+ Version:: $Id: StreamBuilder.rb 902 2013-04-23 05:07:04Z miyabe $
126
+
127
+ ストリームに対して結果を構築するオブジェクトです。
128
+ =end
129
+
130
+ class StreamBuilder
131
+ =begin rdoc
132
+ 結果構築オブジェクトを作成します。
133
+
134
+ out:: 出力先ストリーム
135
+ &finish:: 結果を実際に出力する前に呼び出されるブロック。引数として結果のバイト数が渡されます
136
+
137
+ 例:
138
+ SingleResult.new(StreamBuilder.new($stdout) do |length|
139
+ print "Content-Length: #{length}\r\n\r\n"
140
+ end) do |opts|
141
+ print "Content-Type: #{opts['mime_type']}\r\n"
142
+ end
143
+ =end
144
+ def initialize(out, &finish)
145
+ @tempFile = Tempfile.open(['cti', '.tmp'])
146
+ @tempFile.binmode
147
+ @out = out
148
+ @finish = finish
149
+ @frgs = []
150
+ @first = nil
151
+ @last = nil
152
+ @onMemory = 0
153
+ @length = 0
154
+ @segment = 0
155
+ end
156
+
157
+ def add_block
158
+ id = @frgs.size
159
+ frg = Fragment.new(id)
160
+ @frgs[id] = frg
161
+ unless @first
162
+ @first = frg
163
+ else
164
+ @last.nxt = frg
165
+ frg.prev = @last
166
+ end
167
+ @last = frg
168
+ end
169
+
170
+ def insert_block_before(anchor_id)
171
+ id = @frgs.size
172
+ anchor = @frgs[anchor_id]
173
+ frg = Fragment.new(id)
174
+ @frgs[id] = frg
175
+ frg.prev = anchor.prev
176
+ frg.nxt = anchor
177
+ anchor.prev.nxt = frg
178
+ anchor.prev = frg
179
+ @first = frg if @first.id == anchor.id
180
+ end
181
+
182
+ def write(id, data)
183
+ frg = @frgs[id]
184
+ @onMemory, @segment, len = frg.write(@tempFile, @onMemory, @segment, data)
185
+ @length += len
186
+ end
187
+
188
+ def close_block(id)
189
+ end
190
+
191
+ def serial_write(data)
192
+ @out.write(data)
193
+ end
194
+
195
+ def finish
196
+ begin
197
+ @finish.call(@length) if @finish
198
+ frg = @first
199
+ while frg
200
+ frg.flush(@tempFile, @out)
201
+ frg = frg.nxt
202
+ end
203
+ ensure
204
+ @tempFile.close
205
+ end
206
+ end
207
+
208
+ def dispose
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,396 @@
1
+ require File.expand_path('Utils', File.dirname(__FILE__))
2
+
3
+ module CTI
4
+ =begin rdoc
5
+ Version:: $Id: CTIP2.rb 902 2013-04-23 05:07:04Z miyabe $
6
+
7
+ 通信プロトコルに関連する各操作です。
8
+
9
+ このモジュールに所属するメソッドは、 IO に対するものです。
10
+ =end
11
+ module CTIP2
12
+ REQ_PROPERTY = 0x01
13
+ REQ_START_MAIN = 0x02
14
+ REQ_SERVER_MAIN = 0x03
15
+ REQ_CLIENT_RESOURCE = 0x04
16
+ REQ_CONTINUOUS = 0x05
17
+ REQ_DATA = 0x11
18
+ REQ_START_RESOURCE = 0x21
19
+ REQ_MISSING_RESOURCE = 0x22
20
+ REQ_EOF = 0x31
21
+ REQ_ABORT = 0x32
22
+ REQ_JOIN = 0x33
23
+ REQ_RESET = 0x41
24
+ REQ_CLOSE = 0x42
25
+ REQ_SERVER_INFO = 0x51
26
+
27
+ RES_START_DATA = 0x01
28
+ RES_BLOCK_DATA = 0x11
29
+ RES_ADD_BLOCK = 0x12
30
+ RES_INSERT_BLOCK = 0x13
31
+ RES_MESSAGE = 0x14
32
+ RES_MAIN_LENGTH = 0x15
33
+ RES_MAIN_READ = 0x16
34
+ RES_DATA = 0x17
35
+ RES_CLOSE_BLOCK = 0x18
36
+ RES_RESOURCE_REQUEST = 0x21
37
+ RES_EOF = 0x31
38
+ RES_ABORT = 0x32
39
+ RES_NEXT = 0x33
40
+
41
+ =begin rdoc
42
+ パケットの送信に使うバッファのサイズです。
43
+ =end
44
+ CTI_BUFFER_SIZE = 1024
45
+
46
+ =begin rdoc
47
+ メモリ上のフラグメントの最大サイズです。
48
+
49
+ フラグメントがこの大きさを超えるとディスクに書き込みます。
50
+ =end
51
+ FRG_MEM_SIZE = 256
52
+
53
+ =begin rdoc
54
+ メモリ上に置かれるデータの最大サイズです。
55
+
56
+ メモリ上のデータがこのサイズを超えると、
57
+ FRG_MEM_SIZEとは無関係にディスクに書き込まれます。
58
+ =end
59
+ ON_MEMORY = 1024 * 1024
60
+
61
+ =begin rdoc
62
+ 一時ファイルのセグメントサイズです。
63
+ =end
64
+ SEGMENT_SIZE = 8192
65
+
66
+ =begin rdoc
67
+ セッションを開始します。
68
+
69
+ encoding:: 通信に用いるエンコーディング
70
+ =end
71
+ def cti_connect(encoding)
72
+ self.write("CTIP/2.0 #{encoding}\n")
73
+ end
74
+
75
+ =begin rdoc
76
+ サーバー情報を要求します。
77
+
78
+ uri:: URI
79
+ =end
80
+
81
+ def req_server_info(uri)
82
+ payload = 1 + 2 + uri.bytesize
83
+ self.write_int(payload)
84
+ self.write_byte(CTIP2::REQ_SERVER_INFO)
85
+ self.write_bytes(uri)
86
+ self.flush
87
+ end
88
+
89
+ =begin rdoc
90
+ サーバーからクライアントのリソースを要求するモードを切り替えます。
91
+
92
+ mode:: 0=off, 1=on
93
+ =end
94
+
95
+ def req_client_resource(mode)
96
+ payload = 2
97
+ self.write_int(payload)
98
+ self.write_byte(CTIP2::REQ_CLIENT_RESOURCE)
99
+ self.write_byte(mode)
100
+ self.flush
101
+ end
102
+
103
+ =begin rdoc
104
+ 複数の結果を結合するモードを切り替えます。
105
+
106
+ mode:: 0=off, 1=on
107
+ =end
108
+
109
+ def req_continuous(mode)
110
+ payload = 2
111
+ self.write_int(payload)
112
+ self.write_byte(CTIP2::REQ_CONTINUOUS)
113
+ self.write_byte(mode)
114
+ self.flush
115
+ end
116
+
117
+ =begin rdoc
118
+ リソースの不存在を通知します。
119
+
120
+ uri:: URI
121
+ =end
122
+
123
+ def req_missing_resource(uri)
124
+ payload = 1 + 2 + uri.bytesize
125
+ self.write_int(payload)
126
+ self.write_byte(CTIP2::REQ_MISSING_RESOURCE)
127
+ self.write_bytes(uri)
128
+ self.flush
129
+ end
130
+
131
+ =begin rdoc
132
+ 状態のリセットを要求します。
133
+ =end
134
+
135
+ def req_reset
136
+ payload = 1
137
+ self.write_int(payload)
138
+ self.write_byte(CTIP2::REQ_RESET)
139
+ self.flush
140
+ end
141
+
142
+ =begin rdoc
143
+ 変換処理の中断を要求します。
144
+
145
+ mode:: 0=生成済みのデータを出力して中断, 1=即時中断
146
+ =end
147
+
148
+ def req_abort(mode)
149
+ payload = 2
150
+ self.write_int(payload)
151
+ self.write_byte(CTIP2::REQ_ABORT)
152
+ self.write_byte(mode)
153
+ self.flush
154
+ end
155
+
156
+ =begin rdoc
157
+ 変換結果を結合します。
158
+ =end
159
+
160
+ def req_join
161
+ payload = 1
162
+ self.write_int(payload)
163
+ self.write_byte(CTIP2::REQ_JOIN)
164
+ self.flush
165
+ end
166
+
167
+ =begin rdoc
168
+ 終了を通知します。
169
+ =end
170
+
171
+ def req_eof
172
+ payload = 1
173
+ self.write_int(payload)
174
+ self.write_byte(CTIP2::REQ_EOF)
175
+ self.flush
176
+ end
177
+
178
+ =begin rdoc
179
+ プロパティを送ります。
180
+
181
+ name:: 名前
182
+ value:: 値
183
+ =end
184
+
185
+ def req_property(name, value)
186
+ payload = name.bytesize + value.bytesize + 5
187
+ self.write_int(payload)
188
+ self.write_byte(CTIP2::REQ_PROPERTY)
189
+ self.write_bytes(name)
190
+ self.write_bytes(value)
191
+ self.flush
192
+ end
193
+
194
+ =begin rdoc
195
+ サーバー側データの変換を要求します。
196
+
197
+ uri:: URI
198
+ =end
199
+
200
+ def req_server_main(uri)
201
+ payload = uri.bytesize + 3
202
+ self.write_int(payload)
203
+ self.write_byte(CTIP2::REQ_SERVER_MAIN)
204
+ self.write_bytes(uri)
205
+ self.flush
206
+ end
207
+
208
+ =begin rdoc
209
+ リソースの開始を通知します。
210
+
211
+ uri:: URI
212
+ mime_type:: MIME型
213
+ encoding:: エンコーディング
214
+ length:: 長さ
215
+ =end
216
+
217
+ def req_resource(uri, mime_type = 'text/css', encoding = '', length = -1)
218
+ payload = uri.bytesize + mime_type.bytesize + encoding.bytesize + 7 + 8
219
+ self.write_int(payload)
220
+ self.write_byte(CTIP2::REQ_START_RESOURCE)
221
+ self.write_bytes(uri)
222
+ self.write_bytes(mime_type)
223
+ self.write_bytes(encoding)
224
+ self.write_long(length)
225
+ self.flush
226
+ end
227
+
228
+ =begin rdoc
229
+ 本体の開始を通知します。
230
+
231
+ uri:: URI
232
+ mime_type:: MIME型
233
+ encoding:: エンコーディング
234
+ length:: 長さ
235
+ =end
236
+
237
+ def req_start_main(uri, mime_type = 'text/html', encoding = '', length = -1)
238
+ payload = uri.bytesize + mime_type.bytesize + encoding.bytesize + 7 + 8
239
+ self.write_int(payload)
240
+ self.write_byte(CTIP2::REQ_START_MAIN)
241
+ self.write_bytes(uri)
242
+ self.write_bytes(mime_type)
243
+ self.write_bytes(encoding)
244
+ self.write_long(length)
245
+ self.flush
246
+ end
247
+
248
+ =begin rdoc
249
+ データを送ります。
250
+
251
+ b:: データ
252
+ =end
253
+
254
+ def req_write(b)
255
+ payload = b.bytesize + 1
256
+ self.write_int(payload)
257
+ self.write_byte(CTIP2::REQ_DATA)
258
+ self.write(b)
259
+ self.flush
260
+ end
261
+
262
+ =begin rdoc
263
+ 通信を終了します。
264
+ =end
265
+
266
+ def req_close
267
+ payload = 1
268
+ self.write_int(payload)
269
+ self.write_byte(CTIP2::REQ_CLOSE)
270
+ self.flush
271
+ end
272
+
273
+ =begin rdoc
274
+ 次のレスポンスを取得します。
275
+
276
+ 結果ハッシュには次のデータが含まれます。
277
+
278
+ - 'type' レスポンスタイプ
279
+ - 'anchorId' 挿入する場所の直後のフラグメントID
280
+ - 'level' エラーレベル
281
+ - 'error' エラーメッセージ
282
+ - 'id' 断片ID
283
+ - 'progress' 処理済バイト数
284
+ - 'bytes' データのバイト列
285
+
286
+ 戻り値:: レスポンス
287
+ =end
288
+
289
+ def res_next
290
+ payload = self.read_int
291
+ type = self.read_byte
292
+
293
+ case type
294
+ when CTIP2::RES_ADD_BLOCK, CTIP2::RES_EOF, CTIP2::RES_NEXT
295
+ return {
296
+ 'type' => type,
297
+ }
298
+
299
+ when CTIP2::RES_START_DATA
300
+ uri = self.read_bytes
301
+ mime_type = self.read_bytes
302
+ encoding = self.read_bytes
303
+ length = self.read_long
304
+ return {
305
+ 'type' => type,
306
+ 'uri' => uri,
307
+ 'mime_type' => mime_type,
308
+ 'encoding' => encoding,
309
+ 'length' => length
310
+ }
311
+
312
+ when CTIP2::RES_MAIN_LENGTH, CTIP2::RES_MAIN_READ
313
+ length = self.read_long
314
+ return {
315
+ 'type' => type,
316
+ 'length' => length
317
+ }
318
+
319
+ when CTIP2::RES_INSERT_BLOCK, CTIP2::RES_CLOSE_BLOCK
320
+ block_id = self.read_int
321
+ return {
322
+ 'type' => type,
323
+ 'block_id' => block_id
324
+ }
325
+
326
+ when CTIP2::RES_MESSAGE
327
+ code = self.read_short
328
+ payload -= 1 + 2
329
+ message = self.read_bytes
330
+ payload -= 2 + message.bytesize
331
+ args = []
332
+ while payload > 0
333
+ arg = self.read_bytes
334
+ payload -= 2 + arg.bytesize
335
+ args << arg
336
+ end
337
+ return {
338
+ 'type' => type,
339
+ 'code' => code,
340
+ 'message' => message,
341
+ 'args' => args
342
+ }
343
+
344
+ when CTIP2::RES_BLOCK_DATA
345
+ length = payload - 5
346
+ block_id = self.read_int
347
+ bytes = self.read(length)
348
+ return {
349
+ 'type' => type,
350
+ 'block_id' => block_id,
351
+ 'bytes' => bytes,
352
+ 'length' => length
353
+ }
354
+
355
+ when CTIP2::RES_DATA
356
+ length = payload - 1
357
+ bytes = self.read(length)
358
+ return {
359
+ 'type' => type,
360
+ 'bytes' => bytes,
361
+ 'length' => length
362
+ }
363
+
364
+ when CTIP2::RES_RESOURCE_REQUEST
365
+ uri = self.read_bytes
366
+ return {
367
+ 'type' => type,
368
+ 'uri' => uri
369
+ }
370
+
371
+ when CTIP2::RES_ABORT
372
+ mode = self.read_byte
373
+ code = self.read_short
374
+ payload -= 1 + 1 + 2
375
+ message = self.read_bytes
376
+ payload -= 2 + message.bytesize
377
+ args = []
378
+ while payload > 0
379
+ arg = self.read_bytes
380
+ payload -= 2 + arg.bytesize
381
+ args << arg
382
+ end
383
+ return {
384
+ 'type' => type,
385
+ 'mode' => mode,
386
+ 'code' => code,
387
+ 'message' => message,
388
+ 'args' => args
389
+ }
390
+
391
+ else
392
+ raise "Bad response type:#{type}"
393
+ end
394
+ end
395
+ end
396
+ end