rubywmq 1.0.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/ext/wmq_message.c CHANGED
@@ -1,682 +1,671 @@
1
- /* --------------------------------------------------------------------------
2
- * Copyright 2006 J. Reid Morrison. Dimension Solutions, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- * --------------------------------------------------------------------------*/
16
-
17
- #include "wmq.h"
18
- #include "decode_rfh.h"
19
-
20
- /* --------------------------------------------------
21
- * Initialize Ruby ID's for Message Class
22
- *
23
- * This function is called when the library is loaded
24
- * by ruby
25
- * --------------------------------------------------*/
26
-
27
- static ID ID_data;
28
- static ID ID_message;
29
- static ID ID_descriptor;
30
- static ID ID_headers;
31
- static ID ID_data_set;
32
- static ID ID_size;
33
- static ID ID_name_value;
34
- static ID ID_xml;
35
- static ID ID_gsub;
36
- static ID ID_header_type;
37
-
38
- void Message_id_init()
39
- {
40
- ID_data = rb_intern("data");
41
- ID_size = rb_intern("size");
42
- ID_data_set = rb_intern("data=");
43
- ID_descriptor = rb_intern("descriptor");
44
- ID_headers = rb_intern("headers");
45
- ID_message = rb_intern("message");
46
- ID_name_value = rb_intern("name_value");
47
- ID_xml = rb_intern("xml");
48
- ID_gsub = rb_intern("gsub");
49
- ID_header_type = rb_intern("header_type");
50
- }
51
-
52
- void Message_build(PMQBYTE* pq_pp_buffer, PMQLONG pq_p_buffer_size, MQLONG trace_level,
53
- VALUE parms, PPMQVOID pp_buffer, PMQLONG p_total_length, PMQMD pmqmd)
54
- {
55
- VALUE data;
56
- VALUE descriptor;
57
- VALUE headers;
58
- VALUE message;
59
-
60
- /* :data is an optional parameter, that if supplied overrides message.data */
61
- data = rb_hash_aref(parms, ID2SYM(ID_data));
62
- if(!NIL_P(data))
63
- {
64
- Check_Type(data, T_STRING);
65
- *p_total_length = RSTRING(data)->len;
66
- *pp_buffer = RSTRING(data)->ptr;
67
- }
68
-
69
- /* :message is optional */
70
- message = rb_hash_aref(parms, ID2SYM(ID_message));
71
- if(!NIL_P(message))
72
- {
73
- if (NIL_P(data)) /* Obtain data from message.data */
74
- {
75
- data = rb_funcall(message, ID_data, 0);
76
- }
77
-
78
- descriptor = rb_funcall(message, ID_descriptor, 0);
79
- Check_Type(descriptor, T_HASH);
80
- Message_to_mqmd(descriptor, pmqmd);
81
-
82
- headers = rb_funcall(message, ID_headers, 0);
83
- Check_Type(headers, T_ARRAY);
84
-
85
- Check_Type(data, T_STRING);
86
-
87
- /* Headers present? */
88
- if ( !NIL_P(headers) &&
89
- (NUM2LONG(rb_funcall(headers, ID_size, 0))>0) )
90
- {
91
- MQLONG data_offset = 0;
92
- struct Message_build_header_arg arg;
93
- VALUE next_header;
94
- VALUE first_header;
95
- VALUE header_type;
96
- VALUE ind_val;
97
- size_t index;
98
-
99
- if(trace_level>2)
100
- printf ("WMQ::Queue#put %ld Header(s) supplied\n", NUM2LONG(rb_funcall(headers, ID_size, 0)));
101
-
102
- /* First sanity check: Do we even have enough space for the data being written and a small header */
103
- if(RSTRING(data)->len + 128 >= *pq_p_buffer_size)
104
- {
105
- MQLONG new_size = RSTRING(data)->len + 512; /* Add space for data and a header */
106
- if(trace_level>2)
107
- printf ("WMQ::Queue#reallocate Resizing buffer from %ld to %ld bytes\n", *pq_p_buffer_size, (long)new_size);
108
-
109
- *pq_p_buffer_size = new_size;
110
- free(*pq_pp_buffer);
111
- *pq_pp_buffer = ALLOC_N(unsigned char, new_size);
112
- }
113
-
114
- arg.pp_buffer = pq_pp_buffer;
115
- arg.p_buffer_size = pq_p_buffer_size;
116
- arg.data_length = RSTRING(data)->len;
117
- arg.p_data_offset = &data_offset;
118
- arg.trace_level = trace_level;
119
- arg.next_header_id = 0;
120
- arg.data_format = pmqmd->Format;
121
-
122
- if(trace_level>2)
123
- printf ("WMQ::Queue#put Building %ld headers.\n", RARRAY(headers)->len);
124
-
125
- for(index = 0; index < RARRAY(headers)->len; index++)
126
- {
127
- /*
128
- * Look at the next Header so that this header can set it's format
129
- * to that required for the next header.
130
- */
131
- ind_val = LONG2FIX(index+1);
132
- next_header = rb_ary_aref(1, &ind_val, headers);
133
-
134
- if(NIL_P(next_header))
135
- {
136
- arg.next_header_id = 0;
137
- }
138
- else
139
- {
140
- header_type = rb_hash_aref(next_header, ID2SYM(ID_header_type));
141
- if (!NIL_P(header_type))
142
- {
143
- arg.next_header_id = rb_to_id(header_type);
144
- }
145
- }
146
-
147
- ind_val = LONG2FIX(index);
148
- Message_build_header(rb_ary_aref(1, &ind_val, headers), &arg);
149
- }
150
-
151
- /* Obtain Format of first header and copy in MQMD.Format */
152
- ind_val = LONG2FIX(0);
153
- first_header = rb_ary_aref(1, &ind_val, headers);
154
- header_type = rb_hash_aref(first_header, ID2SYM(ID_header_type));
155
- if (!NIL_P(header_type))
156
- {
157
- Message_build_set_format(rb_to_id(header_type), pmqmd->Format);
158
- }
159
-
160
- if(trace_level>2)
161
- printf ("WMQ::Queue#put done building headers. Offset is now %ld\n", *arg.p_data_offset);
162
-
163
- memcpy((*pq_pp_buffer) + data_offset, RSTRING(data)->ptr, RSTRING(data)->len);
164
- *p_total_length = data_offset + RSTRING(data)->len;
165
- *pp_buffer = *pq_pp_buffer;
166
- }
167
- else
168
- {
169
- *p_total_length = RSTRING(data)->len;
170
- *pp_buffer = RSTRING(data)->ptr;
171
- }
172
- }
173
- /* If :message is not supplied, then :data must be supplied in the parameter list */
174
- else if(NIL_P(data))
175
- {
176
- rb_raise(rb_eArgError, "At least one of :message or :data is required.");
177
- }
178
- return;
179
- }
180
-
181
- /*
182
- * Extract MQMD from descriptor hash
183
- */
184
- void Message_build_mqmd(VALUE self, PMQMD pmqmd)
185
- {
186
- Message_to_mqmd(rb_funcall(self, ID_descriptor, 0), pmqmd);
187
- }
188
-
189
- /*
190
- * call-seq:
191
- * new(...)
192
- *
193
- * Optional Named Parameters (as a single hash):
194
- * * :data
195
- * * Data to be written, or was read from the queue
196
- * * :descriptor
197
- * * Desciptor
198
- *
199
- * Example:
200
- * message = WMQ::Message.new
201
- *
202
- * Example:
203
- * message = WMQ::Message.new(:data=>'Hello World',
204
- * :descriptor=> {
205
- * :format => WMQ::MQFMT_STRING
206
- * })
207
- */
208
- VALUE Message_initialize(int argc, VALUE *argv, VALUE self)
209
- {
210
- VALUE parms = Qnil;
211
-
212
- /* Extract optional parameter */
213
- rb_scan_args(argc, argv, "01", &parms);
214
-
215
- if (NIL_P(parms))
216
- {
217
- rb_iv_set(self, "@data", Qnil);
218
- rb_iv_set(self, "@headers", rb_ary_new());
219
- rb_iv_set(self, "@descriptor", rb_hash_new());
220
- }
221
- else
222
- {
223
- VALUE val;
224
- Check_Type(parms, T_HASH);
225
-
226
- rb_iv_set(self, "@data", rb_hash_aref(parms, ID2SYM(ID_data)));
227
-
228
- val = rb_hash_aref(parms, ID2SYM(ID_headers));
229
- if (NIL_P(val))
230
- {
231
- rb_iv_set(self, "@headers", rb_ary_new());
232
- }
233
- else
234
- {
235
- rb_iv_set(self, "@headers", val);
236
- }
237
-
238
- val = rb_hash_aref(parms, ID2SYM(ID_descriptor));
239
- if (NIL_P(val))
240
- {
241
- rb_iv_set(self, "@headers", rb_hash_new());
242
- }
243
- else
244
- {
245
- rb_iv_set(self, "@descriptor", val);
246
- }
247
- }
248
-
249
- return Qnil;
250
- }
251
-
252
- /*
253
- * Clear out the message data and headers
254
- *
255
- * Note:
256
- * * The descriptor is not affected in any way
257
- */
258
- VALUE Message_clear(VALUE self)
259
- {
260
- rb_iv_set(self, "@data", Qnil);
261
- rb_iv_set(self, "@headers", rb_ary_new());
262
-
263
- return self;
264
- }
265
-
266
- /*
267
- * Automatically grow size of buffer to meet required size
268
- * Existing data upto offset is preserved by copying to the new buffer
269
- *
270
- * additional_size: Size of any additional data to be written to this buffer
271
- * EXCLUDING current offset and size of data to be written
272
- *
273
- * Returns pointer to new buffer incremented by offset within buffer
274
- */
275
- PMQBYTE Message_autogrow_data_buffer(struct Message_build_header_arg* parg, MQLONG additional_size)
276
- {
277
- MQLONG size = *(parg->p_data_offset) + parg->data_length + additional_size;
278
- /* Is buffer large enough for headers */
279
- if(size >= *(parg->p_buffer_size))
280
- {
281
- PMQBYTE old_buffer = *(parg->pp_buffer);
282
- size += 512; /* Additional space for subsequent headers */
283
-
284
- if(parg->trace_level>2)
285
- printf ("WMQ::Message Reallocating buffer from %ld to %ld\n", *(parg->p_buffer_size), (long)size);
286
-
287
- *(parg->p_buffer_size) = size;
288
- *(parg->pp_buffer) = ALLOC_N(char, size);
289
- memcpy(*(parg->pp_buffer), old_buffer, *(parg->p_data_offset));
290
- free(old_buffer);
291
- }
292
- return *(parg->pp_buffer) + *(parg->p_data_offset);
293
- }
294
-
295
- /*
296
- * Concatenate the passed name or value element to the existing string
297
- */
298
- static void Message_name_value_concat(VALUE string, VALUE element)
299
- {
300
- VALUE str = StringValue(element);
301
- if (RSTRING(str)->len == 0) /* Empty String: "" */
302
- {
303
- rb_str_concat(string, rb_str_new2("\"\""));
304
- }
305
- else
306
- {
307
- void* contains_spaces = memchr(RSTRING(str)->ptr,' ',RSTRING(str)->len);
308
- void* contains_dbl_quotes = memchr(RSTRING(str)->ptr,'"',RSTRING(str)->len);
309
-
310
- if(contains_spaces == NULL && contains_dbl_quotes == NULL)
311
- {
312
- rb_str_concat(string, str);
313
- }
314
- else
315
- {
316
- VALUE quote = rb_str_new2("\"");
317
- rb_str_concat(string, quote);
318
- if(contains_dbl_quotes)
319
- {
320
- rb_str_concat(string, rb_funcall(str, ID_gsub, 2, quote, rb_str_new2("\"\"")));
321
- }
322
- else
323
- {
324
- rb_str_concat(string, str);
325
- }
326
- rb_str_concat(string, quote);
327
- }
328
- }
329
- }
330
-
331
- struct Message_build_rf_header_each_value_arg {
332
- VALUE string;
333
- VALUE key;
334
- VALUE space;
335
- };
336
-
337
- static VALUE Message_build_rf_header_each_value(VALUE value, struct Message_build_rf_header_each_value_arg* parg)
338
- {
339
- Message_name_value_concat(parg->string, parg->key);
340
- rb_str_concat(parg->string, parg->space);
341
- Message_name_value_concat(parg->string, value);
342
- rb_str_concat(parg->string, parg->space);
343
-
344
- return Qnil;
345
- }
346
-
347
- #if RUBY_VERSION_CODE > 183
348
- static int Message_build_rf_header_each (VALUE key, VALUE value, VALUE string)
349
- {
350
- #else
351
- static int Message_build_rf_header_each (VALUE array, VALUE string)
352
- {
353
- VALUE key = rb_ary_shift(array);
354
- VALUE value = rb_ary_shift(array);
355
- #endif
356
- VALUE space = rb_str_new2(" ");
357
- /* If Value is an Array, need to repeat name for each value */
358
- if (TYPE(value) == T_ARRAY)
359
- {
360
- struct Message_build_rf_header_each_value_arg arg;
361
- arg.key = key;
362
- arg.string = string;
363
- arg.space = space;
364
- rb_iterate (rb_each, value, Message_build_rf_header_each_value, (VALUE)&arg);
365
- }
366
- else
367
- {
368
- Message_name_value_concat(string, key);
369
- rb_str_concat(string, space);
370
- Message_name_value_concat(string, value);
371
- rb_str_concat(string, space);
372
- }
373
- return 0;
374
- }
375
-
376
- void Message_build_rf_header (VALUE hash, struct Message_build_header_arg* parg)
377
- {
378
- PMQBYTE p_data = *(parg->pp_buffer) + *(parg->p_data_offset);
379
-
380
- static MQRFH MQRFH_DEF = {MQRFH_DEFAULT};
381
- MQLONG name_value_len = 0;
382
- PMQCHAR p_name_value = 0;
383
- VALUE name_value = rb_hash_aref(hash, ID2SYM(ID_name_value));
384
-
385
- MQRFH_DEF.CodedCharSetId = MQCCSI_INHERIT;
386
-
387
- if(parg->trace_level>2)
388
- printf ("WMQ::Message#build_rf_header Found rf_header\n");
389
-
390
- if (!NIL_P(name_value))
391
- {
392
- if (TYPE(name_value) == T_HASH)
393
- {
394
- VALUE name_value_str = rb_str_buf_new(512); /* Allocate 512 char buffer, will grow as needed */
395
- #if RUBY_VERSION_CODE > 183
396
- rb_hash_foreach(name_value, Message_build_rf_header_each, name_value_str);
397
- #else
398
- rb_iterate (rb_each, name_value, Message_build_rf_header_each, name_value_str);
399
- #endif
400
- name_value = name_value_str;
401
- }
402
- else if(TYPE(name_value) != T_STRING)
403
- {
404
- rb_raise(rb_eArgError, ":name_value supplied in rf_header to WMQ::Message#headers must be either a String or a Hash");
405
- }
406
-
407
- name_value_len = RSTRING(name_value)->len;
408
- if (name_value_len % 4) /* Not on 4 byte boundary ? */
409
- {
410
- rb_str_concat(name_value, rb_str_new(" ", 4 - (name_value_len % 4)));
411
- name_value_len = RSTRING(name_value)->len;
412
- }
413
- p_name_value = RSTRING(name_value)->ptr;
414
- }
415
-
416
- p_data = Message_autogrow_data_buffer(parg, sizeof(MQRFH)+name_value_len);
417
-
418
- memcpy(p_data, &MQRFH_DEF, sizeof(MQRFH));
419
- Message_to_mqrfh(hash, (PMQRFH)p_data);
420
- ((PMQRFH)p_data)->StrucLength = sizeof(MQRFH) + name_value_len;
421
- if(parg->next_header_id)
422
- {
423
- Message_build_set_format(parg->next_header_id, ((PMQRFH)p_data)->Format);
424
- }
425
- else
426
- {
427
- strncpy(((PMQRFH)p_data)->Format, parg->data_format, MQ_FORMAT_LENGTH);
428
- }
429
-
430
- *(parg->p_data_offset) += sizeof(MQRFH);
431
- p_data += sizeof(MQRFH);
432
-
433
- if(name_value_len)
434
- {
435
- memcpy(p_data, p_name_value, name_value_len);
436
- *(parg->p_data_offset) += name_value_len;
437
- }
438
-
439
- if(parg->trace_level>3)
440
- printf ("WMQ::Message#build_rf_header Sizeof namevalue string:%ld\n", (long)name_value_len);
441
-
442
- if(parg->trace_level>2)
443
- printf ("WMQ::Message#build_rf_header data offset:%ld\n", *(parg->p_data_offset));
444
- }
445
-
446
- static void Message_deblock_rf_header_each_pair(const char *p_name, const char *p_value, void* p_name_value_hash)
447
- {
448
- VALUE name_value_hash = (VALUE)p_name_value_hash;
449
- VALUE key = rb_str_new2(p_name);
450
- VALUE value = rb_str_new2(p_value);
451
-
452
- /*
453
- * If multiple values arrive for the same name (key) need to put values in an array
454
- */
455
- VALUE existing = rb_hash_aref(name_value_hash, key);
456
- if(NIL_P(existing))
457
- {
458
- rb_hash_aset(name_value_hash, key, value);
459
- }
460
- else
461
- {
462
- if(TYPE(existing) == T_ARRAY) /* Add to existing Array */
463
- {
464
- rb_ary_push(existing, value);
465
- }
466
- else /* Convert existing entry into an array */
467
- {
468
- VALUE array = rb_ary_new();
469
- rb_ary_push(array, existing);
470
- rb_ary_push(array, value);
471
- rb_hash_aset(name_value_hash, key, array);
472
- }
473
- }
474
- }
475
-
476
- /*
477
- * Deblock Any custom data following RF Header
478
- * The RF Header has already been deblocked into hash
479
- * p_data points to the beginning of the RF Header structure
480
- *
481
- * msg_len is the length of the remainder of the message from this header onwards
482
- * It includes the length of any data that may follow
483
- *
484
- * Returns the length of RF Header plus the size of any custom data following it
485
- */
486
- MQLONG Message_deblock_rf_header (VALUE hash, PMQBYTE p_data, MQLONG data_len)
487
- {
488
- MQLONG size = ((PMQRFH)p_data)->StrucLength;
489
- VALUE name_value_hash = rb_hash_new();
490
-
491
- rfh_toktype_t toktype;
492
-
493
- if(size < 0 || size > data_len) /* Poison Message */
494
- {
495
- printf("WMQ::Message_deblock_rf_header StrucLength supplied in MQRFH exceeds total message length\n");
496
- return 0;
497
- }
498
-
499
- toktype = rfh_decode_name_val_str(p_data + sizeof(MQRFH),
500
- size - sizeof(MQRFH),
501
- Message_deblock_rf_header_each_pair,
502
- (void*)name_value_hash);
503
-
504
- if (toktype != TT_END)
505
- {
506
- printf("Could not parse rfh name value string, reason %s\n",rfh_toktype_to_s(toktype));
507
- }
508
-
509
- rb_hash_aset(hash, ID2SYM(ID_name_value), name_value_hash);
510
-
511
- return size;
512
- }
513
-
514
- /*
515
- * RFH2 Header can contain multiple XML-like strings
516
- * Message consists of:
517
- * MQRFH2
518
- * xml-string1-length (MQLONG)
519
- * xml-string1 (Padded with spaces to match 4 byte boundary)
520
- * xml-string2-length (MQLONG)
521
- * xml-string2 (Padded with spaces to match 4 byte boundary)
522
- * ....
523
- */
524
- MQLONG Message_deblock_rf_header_2 (VALUE hash, PMQBYTE p_buffer, MQLONG data_len)
525
- {
526
- MQLONG size = ((PMQRFH2)p_buffer)->StrucLength;
527
- PMQBYTE p_data = p_buffer + sizeof(MQRFH2);
528
- PMQBYTE p_end = p_buffer + size; /* Points to byte after last character */
529
- MQLONG xml_len = 0;
530
- VALUE xml_ary = rb_ary_new();
531
-
532
- PMQBYTE pChar;
533
- size_t length;
534
- size_t i;
535
-
536
- if(size < 0 || size > data_len) /* Poison Message */
537
- {
538
- printf("WMQ::Message_deblock_rf_header_2 StrucLength supplied in MQRFH exceeds total message length\n");
539
- return 0;
540
- }
541
-
542
- rb_hash_aset(hash, ID2SYM(ID_xml), xml_ary);
543
-
544
- while(p_data < p_end)
545
- {
546
- xml_len = *(PMQLONG)p_data;
547
- p_data += sizeof(MQLONG);
548
-
549
- if (xml_len < 0 || p_data+xml_len > p_end)
550
- {
551
- printf("WMQ::Message#deblock_rf_header_2 Poison Message received, stopping further processing\n");
552
- p_data = p_end;
553
- }
554
- else
555
- {
556
- /*
557
- * Strip trailing spaces added as pad characters during put
558
- */
559
- length = 0;
560
- pChar = p_data + xml_len - 1;
561
- for (i = xml_len; i > 0; i--)
562
- {
563
- if (*pChar != ' ')
564
- {
565
- length = i;
566
- break;
567
- }
568
- pChar--;
569
- }
570
- rb_ary_push(xml_ary, rb_str_new(p_data, length));
571
- p_data += xml_len;
572
- }
573
- }
574
-
575
- return size;
576
- }
577
-
578
- static VALUE Message_build_rf_header_2_each(VALUE element, struct Message_build_header_arg* parg)
579
- {
580
- VALUE str = StringValue(element);
581
- MQLONG length = RSTRING(str)->len;
582
-
583
- if (length % 4) /* Not on 4 byte boundary ? */
584
- {
585
- static const char * blanks = " ";
586
- MQLONG pad = 4 - (length % 4);
587
- MQLONG len = length + pad;
588
- PMQBYTE p_data = Message_autogrow_data_buffer(parg, sizeof(len) + length + pad);
589
-
590
- memcpy(p_data, (void*) &len, sizeof(len)); /* Start with MQLONG length indicator */
591
- p_data += sizeof(len);
592
- memcpy(p_data, RSTRING(str)->ptr, length);
593
- p_data += length;
594
- memcpy(p_data, blanks, pad);
595
- p_data += pad;
596
- *(parg->p_data_offset) += sizeof(len) + length + pad;
597
- }
598
- else
599
- {
600
- PMQBYTE p_data = Message_autogrow_data_buffer(parg, sizeof(length) + length);
601
-
602
- memcpy(p_data, (void*) &length, sizeof(length)); /* Start with MQLONG length indicator */
603
- p_data += sizeof(length);
604
- memcpy(p_data, RSTRING(str)->ptr, length);
605
- p_data += length;
606
- *(parg->p_data_offset) += sizeof(length) + length;
607
- }
608
-
609
- return Qnil;
610
- }
611
-
612
- /*
613
- * RFH2 Header can contain multiple XML-like strings
614
- * Message consists of:
615
- * MQRFH2
616
- * xml-string1-length (MQLONG)
617
- * xml-string1 (Padded with spaces to match 4 byte boundary)
618
- * xml-string2-length (MQLONG)
619
- * xml-string2 (Padded with spaces to match 4 byte boundary)
620
- * ....
621
- *
622
- * The input data is either a String or an Array of Strings
623
- * E.g.
624
- *
625
- * {
626
- * :header_type =>:rf_header_2,
627
- * :xml => '<hello>to the world</hello>'
628
- * }
629
- * OR
630
- * {
631
- * :header_type =>:rf_header_2,
632
- * :xml => ['<hello>to the world</hello>', '<another>xml like string</another>'],
633
- * }
634
- *
635
- */
636
- void Message_build_rf_header_2(VALUE hash, struct Message_build_header_arg* parg)
637
- {
638
- static MQRFH2 MQRFH2_DEF = {MQRFH2_DEFAULT};
639
- MQLONG rfh2_offset = *(parg->p_data_offset);
640
- PMQBYTE p_data;
641
- VALUE xml = rb_hash_aref(hash, ID2SYM(ID_xml));
642
-
643
- if(parg->trace_level>2)
644
- printf ("WMQ::Message#build_rf_header_2 Found rf_header_2\n");
645
-
646
- /* Add MQRFH2 Header, ensure that their is enough space */
647
- p_data = Message_autogrow_data_buffer(parg, sizeof(MQRFH2));
648
- memcpy(p_data, &MQRFH2_DEF, sizeof(MQRFH2));
649
- Message_to_mqrfh2(hash, (PMQRFH2)p_data);
650
- if(parg->next_header_id)
651
- {
652
- Message_build_set_format(parg->next_header_id, ((PMQRFH2)p_data)->Format);
653
- }
654
- else
655
- {
656
- memcpy(((PMQRFH2)p_data)->Format, parg->data_format, MQ_FORMAT_LENGTH);
657
- }
658
- *(parg->p_data_offset) += sizeof(MQRFH2);
659
-
660
- if (!NIL_P(xml))
661
- {
662
- if(TYPE(xml) == T_ARRAY)
663
- {
664
- rb_iterate (rb_each, xml, Message_build_rf_header_2_each, (VALUE)parg);
665
- }
666
- else if(TYPE(xml) != T_STRING)
667
- {
668
- Message_build_rf_header_2_each(xml, parg);
669
- }
670
- else
671
- {
672
- rb_raise(rb_eArgError, ":name_value supplied in rf_header_2 to WMQ::Message#headers must be a String or an Array");
673
- }
674
- }
675
-
676
- /* Set total length in MQRFH2 */
677
- ((PMQRFH)(*(parg->pp_buffer) + rfh2_offset))->StrucLength = *(parg->p_data_offset) - rfh2_offset;
678
-
679
- if(parg->trace_level>2)
680
- printf ("WMQ::Message#build_rf_header_2 data offset:%ld\n", *(parg->p_data_offset));
681
- }
682
-
1
+ /* --------------------------------------------------------------------------
2
+ * Copyright 2006 J. Reid Morrison. Dimension Solutions, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ * --------------------------------------------------------------------------*/
16
+
17
+ #include "wmq.h"
18
+ #include "decode_rfh.h"
19
+
20
+ /* --------------------------------------------------
21
+ * Initialize Ruby ID's for Message Class
22
+ *
23
+ * This function is called when the library is loaded
24
+ * by ruby
25
+ * --------------------------------------------------*/
26
+
27
+ static ID ID_data;
28
+ static ID ID_message;
29
+ static ID ID_descriptor;
30
+ static ID ID_headers;
31
+ static ID ID_data_set;
32
+ static ID ID_size;
33
+ static ID ID_name_value;
34
+ static ID ID_xml;
35
+ static ID ID_gsub;
36
+ static ID ID_header_type;
37
+
38
+ void Message_id_init()
39
+ {
40
+ ID_data = rb_intern("data");
41
+ ID_size = rb_intern("size");
42
+ ID_data_set = rb_intern("data=");
43
+ ID_descriptor = rb_intern("descriptor");
44
+ ID_headers = rb_intern("headers");
45
+ ID_message = rb_intern("message");
46
+ ID_name_value = rb_intern("name_value");
47
+ ID_xml = rb_intern("xml");
48
+ ID_gsub = rb_intern("gsub");
49
+ ID_header_type = rb_intern("header_type");
50
+ }
51
+
52
+ void Message_build(PMQBYTE* pq_pp_buffer, PMQLONG pq_p_buffer_size, MQLONG trace_level,
53
+ VALUE parms, PPMQVOID pp_buffer, PMQLONG p_total_length, PMQMD pmqmd)
54
+ {
55
+ VALUE data;
56
+ VALUE descriptor;
57
+ VALUE headers;
58
+ VALUE message;
59
+
60
+ /* :data is an optional parameter, that if supplied overrides message.data */
61
+ data = rb_hash_aref(parms, ID2SYM(ID_data));
62
+ if(!NIL_P(data))
63
+ {
64
+ Check_Type(data, T_STRING);
65
+ *p_total_length = RSTRING_LEN(data);
66
+ *pp_buffer = RSTRING_PTR(data);
67
+ }
68
+
69
+ /* :message is optional */
70
+ message = rb_hash_aref(parms, ID2SYM(ID_message));
71
+ if(!NIL_P(message))
72
+ {
73
+ if (NIL_P(data)) /* Obtain data from message.data */
74
+ {
75
+ data = rb_funcall(message, ID_data, 0);
76
+ }
77
+
78
+ descriptor = rb_funcall(message, ID_descriptor, 0);
79
+ Check_Type(descriptor, T_HASH);
80
+ Message_to_mqmd(descriptor, pmqmd);
81
+
82
+ headers = rb_funcall(message, ID_headers, 0);
83
+ Check_Type(headers, T_ARRAY);
84
+
85
+ Check_Type(data, T_STRING);
86
+
87
+ /* Headers present? */
88
+ if ( !NIL_P(headers) &&
89
+ (NUM2LONG(rb_funcall(headers, ID_size, 0))>0) )
90
+ {
91
+ MQLONG data_offset = 0;
92
+ struct Message_build_header_arg arg;
93
+ VALUE next_header;
94
+ VALUE first_header;
95
+ VALUE header_type;
96
+ VALUE ind_val;
97
+ size_t index;
98
+
99
+ if(trace_level>2)
100
+ printf ("WMQ::Queue#put %ld Header(s) supplied\n", NUM2LONG(rb_funcall(headers, ID_size, 0)));
101
+
102
+ /* First sanity check: Do we even have enough space for the data being written and a small header */
103
+ if(RSTRING_LEN(data) + 128 >= *pq_p_buffer_size)
104
+ {
105
+ MQLONG new_size = RSTRING_LEN(data) + 512; /* Add space for data and a header */
106
+ if(trace_level>2)
107
+ printf ("WMQ::Queue#reallocate Resizing buffer from %ld to %ld bytes\n", *pq_p_buffer_size, (long)new_size);
108
+
109
+ *pq_p_buffer_size = new_size;
110
+ free(*pq_pp_buffer);
111
+ *pq_pp_buffer = ALLOC_N(unsigned char, new_size);
112
+ }
113
+
114
+ arg.pp_buffer = pq_pp_buffer;
115
+ arg.p_buffer_size = pq_p_buffer_size;
116
+ arg.data_length = RSTRING_LEN(data);
117
+ arg.p_data_offset = &data_offset;
118
+ arg.trace_level = trace_level;
119
+ arg.next_header_id = 0;
120
+ arg.data_format = pmqmd->Format;
121
+
122
+ if(trace_level>2)
123
+ printf ("WMQ::Queue#put Building %ld headers.\n", RARRAY_LEN(headers));
124
+
125
+ for(index = 0; index < RARRAY_LEN(headers); index++)
126
+ {
127
+ /*
128
+ * Look at the next Header so that this header can set it's format
129
+ * to that required for the next header.
130
+ */
131
+ ind_val = LONG2FIX(index+1);
132
+ next_header = rb_ary_aref(1, &ind_val, headers);
133
+
134
+ if(NIL_P(next_header))
135
+ {
136
+ arg.next_header_id = 0;
137
+ }
138
+ else
139
+ {
140
+ header_type = rb_hash_aref(next_header, ID2SYM(ID_header_type));
141
+ if (!NIL_P(header_type))
142
+ {
143
+ arg.next_header_id = rb_to_id(header_type);
144
+ }
145
+ }
146
+
147
+ ind_val = LONG2FIX(index);
148
+ Message_build_header(rb_ary_aref(1, &ind_val, headers), &arg);
149
+ }
150
+
151
+ /* Obtain Format of first header and copy in MQMD.Format */
152
+ ind_val = LONG2FIX(0);
153
+ first_header = rb_ary_aref(1, &ind_val, headers);
154
+ header_type = rb_hash_aref(first_header, ID2SYM(ID_header_type));
155
+ if (!NIL_P(header_type))
156
+ {
157
+ Message_build_set_format(rb_to_id(header_type), pmqmd->Format);
158
+ }
159
+
160
+ if(trace_level>2)
161
+ printf ("WMQ::Queue#put done building headers. Offset is now %ld\n", *arg.p_data_offset);
162
+
163
+ memcpy((*pq_pp_buffer) + data_offset, RSTRING_PTR(data), RSTRING_LEN(data));
164
+ *p_total_length = data_offset + RSTRING_LEN(data);
165
+ *pp_buffer = *pq_pp_buffer;
166
+ }
167
+ else
168
+ {
169
+ *p_total_length = RSTRING_LEN(data);
170
+ *pp_buffer = RSTRING_PTR(data);
171
+ }
172
+ }
173
+ /* If :message is not supplied, then :data must be supplied in the parameter list */
174
+ else if(NIL_P(data))
175
+ {
176
+ rb_raise(rb_eArgError, "At least one of :message or :data is required.");
177
+ }
178
+ return;
179
+ }
180
+
181
+ /*
182
+ * Extract MQMD from descriptor hash
183
+ */
184
+ void Message_build_mqmd(VALUE self, PMQMD pmqmd)
185
+ {
186
+ Message_to_mqmd(rb_funcall(self, ID_descriptor, 0), pmqmd);
187
+ }
188
+
189
+ /*
190
+ * call-seq:
191
+ * new(...)
192
+ *
193
+ * Optional Named Parameters (as a single hash):
194
+ * * :data
195
+ * * Data to be written, or was read from the queue
196
+ * * :descriptor
197
+ * * Desciptor
198
+ *
199
+ * Example:
200
+ * message = WMQ::Message.new
201
+ *
202
+ * Example:
203
+ * message = WMQ::Message.new(:data=>'Hello World',
204
+ * :descriptor=> {
205
+ * :format => WMQ::MQFMT_STRING
206
+ * })
207
+ */
208
+ VALUE Message_initialize(int argc, VALUE *argv, VALUE self)
209
+ {
210
+ VALUE parms = Qnil;
211
+
212
+ /* Extract optional parameter */
213
+ rb_scan_args(argc, argv, "01", &parms);
214
+
215
+ if (NIL_P(parms))
216
+ {
217
+ rb_iv_set(self, "@data", Qnil);
218
+ rb_iv_set(self, "@headers", rb_ary_new());
219
+ rb_iv_set(self, "@descriptor", rb_hash_new());
220
+ }
221
+ else
222
+ {
223
+ VALUE val;
224
+ Check_Type(parms, T_HASH);
225
+
226
+ rb_iv_set(self, "@data", rb_hash_aref(parms, ID2SYM(ID_data)));
227
+
228
+ val = rb_hash_aref(parms, ID2SYM(ID_headers));
229
+ if (NIL_P(val))
230
+ {
231
+ rb_iv_set(self, "@headers", rb_ary_new());
232
+ }
233
+ else
234
+ {
235
+ rb_iv_set(self, "@headers", val);
236
+ }
237
+
238
+ val = rb_hash_aref(parms, ID2SYM(ID_descriptor));
239
+ if (NIL_P(val))
240
+ {
241
+ rb_iv_set(self, "@headers", rb_hash_new());
242
+ }
243
+ else
244
+ {
245
+ rb_iv_set(self, "@descriptor", val);
246
+ }
247
+ }
248
+
249
+ return Qnil;
250
+ }
251
+
252
+ /*
253
+ * Clear out the message data and headers
254
+ *
255
+ * Note:
256
+ * * The descriptor is not affected in any way
257
+ */
258
+ VALUE Message_clear(VALUE self)
259
+ {
260
+ rb_iv_set(self, "@data", Qnil);
261
+ rb_iv_set(self, "@headers", rb_ary_new());
262
+
263
+ return self;
264
+ }
265
+
266
+ /*
267
+ * Automatically grow size of buffer to meet required size
268
+ * Existing data upto offset is preserved by copying to the new buffer
269
+ *
270
+ * additional_size: Size of any additional data to be written to this buffer
271
+ * EXCLUDING current offset and size of data to be written
272
+ *
273
+ * Returns pointer to new buffer incremented by offset within buffer
274
+ */
275
+ PMQBYTE Message_autogrow_data_buffer(struct Message_build_header_arg* parg, MQLONG additional_size)
276
+ {
277
+ MQLONG size = *(parg->p_data_offset) + parg->data_length + additional_size;
278
+ /* Is buffer large enough for headers */
279
+ if(size >= *(parg->p_buffer_size))
280
+ {
281
+ PMQBYTE old_buffer = *(parg->pp_buffer);
282
+ size += 512; /* Additional space for subsequent headers */
283
+
284
+ if(parg->trace_level>2)
285
+ printf ("WMQ::Message Reallocating buffer from %ld to %ld\n", *(parg->p_buffer_size), (long)size);
286
+
287
+ *(parg->p_buffer_size) = size;
288
+ *(parg->pp_buffer) = ALLOC_N(char, size);
289
+ memcpy(*(parg->pp_buffer), old_buffer, *(parg->p_data_offset));
290
+ free(old_buffer);
291
+ }
292
+ return *(parg->pp_buffer) + *(parg->p_data_offset);
293
+ }
294
+
295
+ /*
296
+ * Concatenate the passed name or value element to the existing string
297
+ */
298
+ static void Message_name_value_concat(VALUE string, VALUE element)
299
+ {
300
+ VALUE str = StringValue(element);
301
+ if (RSTRING_LEN(str) == 0) /* Empty String: "" */
302
+ {
303
+ rb_str_concat(string, rb_str_new2("\"\""));
304
+ }
305
+ else
306
+ {
307
+ void* contains_spaces = memchr(RSTRING_PTR(str),' ',RSTRING_LEN(str));
308
+ void* contains_dbl_quotes = memchr(RSTRING_PTR(str),'"',RSTRING_LEN(str));
309
+
310
+ if(contains_spaces == NULL && contains_dbl_quotes == NULL)
311
+ {
312
+ rb_str_concat(string, str);
313
+ }
314
+ else
315
+ {
316
+ VALUE quote = rb_str_new2("\"");
317
+ rb_str_concat(string, quote);
318
+ if(contains_dbl_quotes)
319
+ {
320
+ rb_str_concat(string, rb_funcall(str, ID_gsub, 2, quote, rb_str_new2("\"\"")));
321
+ }
322
+ else
323
+ {
324
+ rb_str_concat(string, str);
325
+ }
326
+ rb_str_concat(string, quote);
327
+ }
328
+ }
329
+ }
330
+
331
+ struct Message_build_rf_header_each_value_arg {
332
+ VALUE string;
333
+ VALUE key;
334
+ VALUE space;
335
+ };
336
+
337
+ static VALUE Message_build_rf_header_each_value(VALUE value, struct Message_build_rf_header_each_value_arg* parg)
338
+ {
339
+ Message_name_value_concat(parg->string, parg->key);
340
+ rb_str_concat(parg->string, parg->space);
341
+ Message_name_value_concat(parg->string, value);
342
+ rb_str_concat(parg->string, parg->space);
343
+
344
+ return Qnil;
345
+ }
346
+
347
+ static int Message_build_rf_header_each (VALUE key, VALUE value, VALUE string)
348
+ {
349
+ VALUE space = rb_str_new2(" ");
350
+ /* If Value is an Array, need to repeat name for each value */
351
+ if (TYPE(value) == T_ARRAY)
352
+ {
353
+ struct Message_build_rf_header_each_value_arg arg;
354
+ arg.key = key;
355
+ arg.string = string;
356
+ arg.space = space;
357
+ rb_iterate (rb_each, value, Message_build_rf_header_each_value, (VALUE)&arg);
358
+ }
359
+ else
360
+ {
361
+ Message_name_value_concat(string, key);
362
+ rb_str_concat(string, space);
363
+ Message_name_value_concat(string, value);
364
+ rb_str_concat(string, space);
365
+ }
366
+ return 0;
367
+ }
368
+
369
+ void Message_build_rf_header (VALUE hash, struct Message_build_header_arg* parg)
370
+ {
371
+ PMQBYTE p_data = *(parg->pp_buffer) + *(parg->p_data_offset);
372
+
373
+ static MQRFH MQRFH_DEF = {MQRFH_DEFAULT};
374
+ MQLONG name_value_len = 0;
375
+ PMQCHAR p_name_value = 0;
376
+ VALUE name_value = rb_hash_aref(hash, ID2SYM(ID_name_value));
377
+
378
+ MQRFH_DEF.CodedCharSetId = MQCCSI_INHERIT;
379
+
380
+ if(parg->trace_level>2)
381
+ printf ("WMQ::Message#build_rf_header Found rf_header\n");
382
+
383
+ if (!NIL_P(name_value))
384
+ {
385
+ if (TYPE(name_value) == T_HASH)
386
+ {
387
+ VALUE name_value_str = rb_str_buf_new(512); /* Allocate 512 char buffer, will grow as needed */
388
+ rb_hash_foreach(name_value, Message_build_rf_header_each, name_value_str);
389
+ name_value = name_value_str;
390
+ }
391
+ else if(TYPE(name_value) != T_STRING)
392
+ {
393
+ rb_raise(rb_eArgError, ":name_value supplied in rf_header to WMQ::Message#headers must be either a String or a Hash");
394
+ }
395
+
396
+ name_value_len = RSTRING_LEN(name_value);
397
+ if (name_value_len % 4) /* Not on 4 byte boundary ? */
398
+ {
399
+ rb_str_concat(name_value, rb_str_new(" ", 4 - (name_value_len % 4)));
400
+ name_value_len = RSTRING_LEN(name_value);
401
+ }
402
+ p_name_value = RSTRING_PTR(name_value);
403
+ }
404
+
405
+ p_data = Message_autogrow_data_buffer(parg, sizeof(MQRFH)+name_value_len);
406
+
407
+ memcpy(p_data, &MQRFH_DEF, sizeof(MQRFH));
408
+ Message_to_mqrfh(hash, (PMQRFH)p_data);
409
+ ((PMQRFH)p_data)->StrucLength = sizeof(MQRFH) + name_value_len;
410
+ if(parg->next_header_id)
411
+ {
412
+ Message_build_set_format(parg->next_header_id, ((PMQRFH)p_data)->Format);
413
+ }
414
+ else
415
+ {
416
+ strncpy(((PMQRFH)p_data)->Format, parg->data_format, MQ_FORMAT_LENGTH);
417
+ }
418
+
419
+ *(parg->p_data_offset) += sizeof(MQRFH);
420
+ p_data += sizeof(MQRFH);
421
+
422
+ if(name_value_len)
423
+ {
424
+ memcpy(p_data, p_name_value, name_value_len);
425
+ *(parg->p_data_offset) += name_value_len;
426
+ }
427
+
428
+ if(parg->trace_level>3)
429
+ printf ("WMQ::Message#build_rf_header Sizeof namevalue string:%ld\n", (long)name_value_len);
430
+
431
+ if(parg->trace_level>2)
432
+ printf ("WMQ::Message#build_rf_header data offset:%ld\n", *(parg->p_data_offset));
433
+ }
434
+
435
+ static void Message_deblock_rf_header_each_pair(const char *p_name, const char *p_value, void* p_name_value_hash)
436
+ {
437
+ VALUE name_value_hash = (VALUE)p_name_value_hash;
438
+ VALUE key = rb_str_new2(p_name);
439
+ VALUE value = rb_str_new2(p_value);
440
+
441
+ /*
442
+ * If multiple values arrive for the same name (key) need to put values in an array
443
+ */
444
+ VALUE existing = rb_hash_aref(name_value_hash, key);
445
+ if(NIL_P(existing))
446
+ {
447
+ rb_hash_aset(name_value_hash, key, value);
448
+ }
449
+ else
450
+ {
451
+ if(TYPE(existing) == T_ARRAY) /* Add to existing Array */
452
+ {
453
+ rb_ary_push(existing, value);
454
+ }
455
+ else /* Convert existing entry into an array */
456
+ {
457
+ VALUE array = rb_ary_new();
458
+ rb_ary_push(array, existing);
459
+ rb_ary_push(array, value);
460
+ rb_hash_aset(name_value_hash, key, array);
461
+ }
462
+ }
463
+ }
464
+
465
+ /*
466
+ * Deblock Any custom data following RF Header
467
+ * The RF Header has already been deblocked into hash
468
+ * p_data points to the beginning of the RF Header structure
469
+ *
470
+ * msg_len is the length of the remainder of the message from this header onwards
471
+ * It includes the length of any data that may follow
472
+ *
473
+ * Returns the length of RF Header plus the size of any custom data following it
474
+ */
475
+ MQLONG Message_deblock_rf_header (VALUE hash, PMQBYTE p_data, MQLONG data_len)
476
+ {
477
+ MQLONG size = ((PMQRFH)p_data)->StrucLength;
478
+ VALUE name_value_hash = rb_hash_new();
479
+
480
+ rfh_toktype_t toktype;
481
+
482
+ if(size < 0 || size > data_len) /* Poison Message */
483
+ {
484
+ printf("WMQ::Message_deblock_rf_header StrucLength supplied in MQRFH exceeds total message length\n");
485
+ return 0;
486
+ }
487
+
488
+ toktype = rfh_decode_name_val_str(p_data + sizeof(MQRFH),
489
+ size - sizeof(MQRFH),
490
+ Message_deblock_rf_header_each_pair,
491
+ (void*)name_value_hash);
492
+
493
+ if (toktype != TT_END)
494
+ {
495
+ printf("Could not parse rfh name value string, reason %s\n",rfh_toktype_to_s(toktype));
496
+ }
497
+
498
+ rb_hash_aset(hash, ID2SYM(ID_name_value), name_value_hash);
499
+
500
+ return size;
501
+ }
502
+
503
+ /*
504
+ * RFH2 Header can contain multiple XML-like strings
505
+ * Message consists of:
506
+ * MQRFH2
507
+ * xml-string1-length (MQLONG)
508
+ * xml-string1 (Padded with spaces to match 4 byte boundary)
509
+ * xml-string2-length (MQLONG)
510
+ * xml-string2 (Padded with spaces to match 4 byte boundary)
511
+ * ....
512
+ */
513
+ MQLONG Message_deblock_rf_header_2 (VALUE hash, PMQBYTE p_buffer, MQLONG data_len)
514
+ {
515
+ MQLONG size = ((PMQRFH2)p_buffer)->StrucLength;
516
+ PMQBYTE p_data = p_buffer + sizeof(MQRFH2);
517
+ PMQBYTE p_end = p_buffer + size; /* Points to byte after last character */
518
+ MQLONG xml_len = 0;
519
+ VALUE xml_ary = rb_ary_new();
520
+
521
+ PMQBYTE pChar;
522
+ size_t length;
523
+ size_t i;
524
+
525
+ if(size < 0 || size > data_len) /* Poison Message */
526
+ {
527
+ printf("WMQ::Message_deblock_rf_header_2 StrucLength supplied in MQRFH exceeds total message length\n");
528
+ return 0;
529
+ }
530
+
531
+ rb_hash_aset(hash, ID2SYM(ID_xml), xml_ary);
532
+
533
+ while(p_data < p_end)
534
+ {
535
+ xml_len = *(PMQLONG)p_data;
536
+ p_data += sizeof(MQLONG);
537
+
538
+ if (xml_len < 0 || p_data+xml_len > p_end)
539
+ {
540
+ printf("WMQ::Message#deblock_rf_header_2 Poison Message received, stopping further processing\n");
541
+ p_data = p_end;
542
+ }
543
+ else
544
+ {
545
+ /*
546
+ * Strip trailing spaces added as pad characters during put
547
+ */
548
+ length = 0;
549
+ pChar = p_data + xml_len - 1;
550
+ for (i = xml_len; i > 0; i--)
551
+ {
552
+ if (*pChar != ' ')
553
+ {
554
+ length = i;
555
+ break;
556
+ }
557
+ pChar--;
558
+ }
559
+ rb_ary_push(xml_ary, rb_str_new(p_data, length));
560
+ p_data += xml_len;
561
+ }
562
+ }
563
+
564
+ return size;
565
+ }
566
+
567
+ static VALUE Message_build_rf_header_2_each(VALUE element, struct Message_build_header_arg* parg)
568
+ {
569
+ VALUE str = StringValue(element);
570
+ MQLONG length = RSTRING_LEN(str);
571
+
572
+ if (length % 4) /* Not on 4 byte boundary ? */
573
+ {
574
+ static const char * blanks = " ";
575
+ MQLONG pad = 4 - (length % 4);
576
+ MQLONG len = length + pad;
577
+ PMQBYTE p_data = Message_autogrow_data_buffer(parg, sizeof(len) + length + pad);
578
+
579
+ memcpy(p_data, (void*) &len, sizeof(len)); /* Start with MQLONG length indicator */
580
+ p_data += sizeof(len);
581
+ memcpy(p_data, RSTRING_PTR(str), length);
582
+ p_data += length;
583
+ memcpy(p_data, blanks, pad);
584
+ p_data += pad;
585
+ *(parg->p_data_offset) += sizeof(len) + length + pad;
586
+ }
587
+ else
588
+ {
589
+ PMQBYTE p_data = Message_autogrow_data_buffer(parg, sizeof(length) + length);
590
+
591
+ memcpy(p_data, (void*) &length, sizeof(length)); /* Start with MQLONG length indicator */
592
+ p_data += sizeof(length);
593
+ memcpy(p_data, RSTRING_PTR(str), length);
594
+ p_data += length;
595
+ *(parg->p_data_offset) += sizeof(length) + length;
596
+ }
597
+
598
+ return Qnil;
599
+ }
600
+
601
+ /*
602
+ * RFH2 Header can contain multiple XML-like strings
603
+ * Message consists of:
604
+ * MQRFH2
605
+ * xml-string1-length (MQLONG)
606
+ * xml-string1 (Padded with spaces to match 4 byte boundary)
607
+ * xml-string2-length (MQLONG)
608
+ * xml-string2 (Padded with spaces to match 4 byte boundary)
609
+ * ....
610
+ *
611
+ * The input data is either a String or an Array of Strings
612
+ * E.g.
613
+ *
614
+ * {
615
+ * :header_type =>:rf_header_2,
616
+ * :xml => '<hello>to the world</hello>'
617
+ * }
618
+ * OR
619
+ * {
620
+ * :header_type =>:rf_header_2,
621
+ * :xml => ['<hello>to the world</hello>', '<another>xml like string</another>'],
622
+ * }
623
+ *
624
+ */
625
+ void Message_build_rf_header_2(VALUE hash, struct Message_build_header_arg* parg)
626
+ {
627
+ static MQRFH2 MQRFH2_DEF = {MQRFH2_DEFAULT};
628
+ MQLONG rfh2_offset = *(parg->p_data_offset);
629
+ PMQBYTE p_data;
630
+ VALUE xml = rb_hash_aref(hash, ID2SYM(ID_xml));
631
+
632
+ if(parg->trace_level>2)
633
+ printf ("WMQ::Message#build_rf_header_2 Found rf_header_2\n");
634
+
635
+ /* Add MQRFH2 Header, ensure that their is enough space */
636
+ p_data = Message_autogrow_data_buffer(parg, sizeof(MQRFH2));
637
+ memcpy(p_data, &MQRFH2_DEF, sizeof(MQRFH2));
638
+ Message_to_mqrfh2(hash, (PMQRFH2)p_data);
639
+ if(parg->next_header_id)
640
+ {
641
+ Message_build_set_format(parg->next_header_id, ((PMQRFH2)p_data)->Format);
642
+ }
643
+ else
644
+ {
645
+ memcpy(((PMQRFH2)p_data)->Format, parg->data_format, MQ_FORMAT_LENGTH);
646
+ }
647
+ *(parg->p_data_offset) += sizeof(MQRFH2);
648
+
649
+ if (!NIL_P(xml))
650
+ {
651
+ if(TYPE(xml) == T_ARRAY)
652
+ {
653
+ rb_iterate (rb_each, xml, Message_build_rf_header_2_each, (VALUE)parg);
654
+ }
655
+ else if(TYPE(xml) != T_STRING)
656
+ {
657
+ Message_build_rf_header_2_each(xml, parg);
658
+ }
659
+ else
660
+ {
661
+ rb_raise(rb_eArgError, ":name_value supplied in rf_header_2 to WMQ::Message#headers must be a String or an Array");
662
+ }
663
+ }
664
+
665
+ /* Set total length in MQRFH2 */
666
+ ((PMQRFH)(*(parg->pp_buffer) + rfh2_offset))->StrucLength = *(parg->p_data_offset) - rfh2_offset;
667
+
668
+ if(parg->trace_level>2)
669
+ printf ("WMQ::Message#build_rf_header_2 data offset:%ld\n", *(parg->p_data_offset));
670
+ }
671
+