Bump patch version post tag.
[asdcplib.git] / src / KM_util.cpp
1 /*
2 Copyright (c) 2005-2015, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27   /*! \file    KM_util.cpp
28     \version $Id$
29     \brief   Utility functions
30   */
31
32 #include <KM_util.h>
33 #include <KM_prng.h>
34 #include <KM_memio.h>
35 #include <KM_fileio.h>
36 #include <KM_log.h>
37 #include <KM_mutex.h>
38 #include <ctype.h>
39 #include <list>
40 #include <map>
41 #include <string>
42
43 const char*
44 Kumu::Version()
45 {
46   return PACKAGE_VERSION;
47 }
48
49
50 //------------------------------------------------------------------------------------------
51
52 // Result_t Internals
53
54 struct map_entry_t
55 {
56   int rcode;
57   Kumu::Result_t* result;
58 };
59
60
61 // WIN32 does not init this in time for use with Result_t(...) below, so it is
62 // now a pointer that Result_t(...) fills in when it needs it.
63 static Kumu::Mutex* s_MapLock = 0;
64
65 static ui32_t s_MapSize = 0;
66 static const ui32_t MapMax = 2048;
67 static struct map_entry_t s_ResultMap[MapMax];
68
69
70 //
71 const Kumu::Result_t&
72 Kumu::Result_t::Find(int v)
73 {
74   if ( v == 0 )
75     return RESULT_OK;
76
77   assert(s_MapLock);
78   AutoMutex L(*s_MapLock);
79
80   for ( ui32_t i = 0; i < s_MapSize; ++i )
81     {
82       if ( s_ResultMap[i].rcode == v )
83         return *s_ResultMap[i].result;
84     }
85
86   return RESULT_UNKNOWN;
87 }
88
89 //
90 Kumu::Result_t
91 Kumu::Result_t::Delete(int v)
92 {
93   if ( v < -99 || v > 99 )
94     {
95       DefaultLogSink().Error("Cannot delete core result code: %ld\n", v);
96       return RESULT_FAIL;
97     }
98
99   assert(s_MapLock);
100   AutoMutex L(*s_MapLock);
101
102   for ( ui32_t i = 0; i < s_MapSize; ++i )
103     {
104       if ( s_ResultMap[i].rcode == v )
105         {
106           for ( ++i; i < s_MapSize; ++i )
107             s_ResultMap[i-1] = s_ResultMap[i];
108
109           --s_MapSize;
110           return RESULT_OK;
111         }
112     }
113
114   return RESULT_FALSE;
115 }
116
117 //
118 unsigned int
119 Kumu::Result_t::End()
120 {
121   return s_MapSize;
122 }
123
124 //
125 const Kumu::Result_t&
126 Kumu::Result_t::Get(unsigned int i)
127 {
128   return *s_ResultMap[i].result;
129 }
130
131 //
132 Kumu::Result_t::Result_t(int v, const std::string& s, const std::string& l) : value(v), symbol(s), label(l)
133 {
134   assert(!l.empty());
135   assert(!s.empty());
136
137   if ( v == 0 )
138     return;
139
140   // This may seem tricky, but it is certain that the static values declared in KM_error.h will
141   // be created (and thus this method will be called) before main(...) is called.  It is not
142   // until then that threads could be created, thus the mutex will exist before multi-threaded
143   // access could occur.
144   if ( s_MapLock == 0 )
145     s_MapLock = new Kumu::Mutex;
146
147   assert(s_MapLock);
148   AutoMutex L(*s_MapLock);
149
150   for ( ui32_t i = 0; i < s_MapSize; ++i )
151     {
152       if ( s_ResultMap[i].rcode == v )
153         return;
154     }
155
156   assert(s_MapSize+1 < MapMax);
157
158   s_ResultMap[s_MapSize].rcode = v;
159   s_ResultMap[s_MapSize].result = this;
160   ++s_MapSize;
161
162   return;
163 }
164
165
166 Kumu::Result_t::Result_t(const Result_t& rhs)
167 {
168   value = rhs.value;
169   symbol = rhs.symbol;
170   label = rhs.label;
171   message = rhs.message;
172 }
173
174 Kumu::Result_t::~Result_t() {}
175
176 //
177 const Kumu::Result_t&
178 Kumu::Result_t::operator=(const Result_t& rhs)
179 {
180   value = rhs.value;
181   symbol = rhs.symbol;
182   label = rhs.label;
183   message = rhs.message;
184   return *this;
185 }
186
187 //
188 const Kumu::Result_t
189 Kumu::Result_t::operator()(const std::string& message) const
190 {
191   Result_t result = *this;
192   result.message = message;
193   return result;
194 }
195
196 static int const MESSAGE_BUF_MAX = 2048;
197
198 //
199 const Kumu::Result_t
200 Kumu::Result_t::operator()(const int& line, const char* filename) const
201 {
202   assert(filename);
203   char buf[MESSAGE_BUF_MAX];
204   snprintf(buf, MESSAGE_BUF_MAX-1, "%s, line %d", filename, line);
205
206   Result_t result = *this;
207   result.message = buf;
208   return result;
209 }
210
211 //
212 const Kumu::Result_t
213 Kumu::Result_t::operator()(const std::string& message, const int& line, const char* filename) const
214 {
215   assert(filename);
216   char buf[MESSAGE_BUF_MAX];
217   snprintf(buf, MESSAGE_BUF_MAX-1, "%s, line %d", filename, line);
218
219   Result_t result = *this;
220   result.message = message + buf;
221   return result;
222 }
223
224
225 //------------------------------------------------------------------------------------------
226 // DTrace internals
227
228 static int s_DTraceSequence = 0;
229
230 Kumu::DTrace_t::DTrace_t(const char* Label, Kumu::Result_t* Watch, int Line, const char* File)
231   : m_Label(Label), m_Watch(Watch), m_Line(Line), m_File(File)
232 {
233   m_Sequence = s_DTraceSequence++;
234   DefaultLogSink().Debug("@enter %s[%d] (%s at %d)\n", m_Label, m_Sequence, m_File, m_Line);
235 }
236
237 Kumu::DTrace_t::~DTrace_t()
238 {
239   if ( m_Watch != 0  )
240     DefaultLogSink().Debug("@exit %s[%d]: %s\n", m_Label, m_Sequence, m_Watch->Label());
241   else
242     DefaultLogSink().Debug("@exit %s[%d]\n", m_Label, m_Sequence);
243 }
244
245 //------------------------------------------------------------------------------------------
246
247
248 const char  fill = '=';
249 const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
250
251 const byte_t decode_map[] =
252 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
253   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
254   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
255   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
256   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
257   0xff, 0xff, 0xff, 62,   0xff, 0xff, 0xff, 63,
258   52,   53,   54,   55,   56,   57,   58,   59,
259   60,   61,   0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
260   0xff, 0,    1,    2,    3,    4,    5,    6,
261   7,    8,    9,    10,   11,   12,   13,   14,
262   15,   16,   17,   18,   19,   20,   21,   22,
263   23,   24,   25,   0xff, 0xff, 0xff, 0xff, 0xff,
264   0xff, 26,   27,   28,   29,   30,   31,   32,
265   33,   34,   35,   36,   37,   38,   39,   40,
266   41,   42,   43,   44,   45,   46,   47,   48,
267   49,   50,   51,   0xff, 0xff, 0xff, 0xff, 0xff,
268   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
269   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
270   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
271   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
272   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
273   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
274   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
275   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
276   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
277   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
278   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
279   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
280   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
281   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
282   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
283   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
284 };
285
286
287 // Convert a binary string to NULL-terminated UTF-8 hexadecimal, returns the buffer
288 // if the binary buffer was large enough to hold the result. If the output buffer
289 // is too small or any of the pointer arguments are NULL, the subroutine will
290 // return 0.
291 //
292 const char*
293 Kumu::base64encode(const byte_t* buf, ui32_t buf_len, char* strbuf, ui32_t strbuf_len)
294 {
295   ui32_t out_char = 0;
296   ui32_t i, block_len, diff;
297
298   if ( buf == 0 || strbuf == 0 )
299     return 0;
300
301   if ( strbuf_len < base64_encode_length(buf_len) + 1 )
302     return 0;
303
304   block_len = buf_len;
305
306   while ( block_len % 3 )
307     block_len--;
308
309   for ( i = 0; i < block_len; i += 3 )
310     {
311       strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
312       strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
313       strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) | ( buf[2] >> 6 ) )];
314       strbuf[out_char++] = base64_chars[( buf[2] & 0x3f )];
315       buf += 3;
316     }
317
318   if ( i < buf_len )
319     {
320       diff = buf_len - i;
321       assert(diff > 0);
322       assert(diff < 3);
323       
324       strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
325
326       if ( diff == 1 )
327         {
328           strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) )];
329           strbuf[out_char++] = fill;
330         }
331       else if ( diff == 2 )
332         {
333           strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
334           strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) )];
335         }
336
337       strbuf[out_char++] = fill;
338     }
339
340   strbuf[out_char] = 0;
341   return strbuf;;
342 }
343
344
345
346
347 // Convert NULL-terminated UTF-8 Base64 string to binary, returns 0 if
348 // the binary buffer was large enough to hold the result. The output parameter
349 // 'char_count' will contain the length of the converted string. If the output
350 // buffer is too small or any of the pointer arguments are NULL, the subroutine
351 // will return -1 and set 'char_count' to the required buffer size. No data will
352 // be written to 'buf' if the subroutine fails.
353 //
354 i32_t
355 Kumu::base64decode(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count)
356 {
357   register byte_t c = 0, d = 0;
358   register ui32_t phase = 0, i = 0;
359
360   if ( str == 0 || buf == 0 || char_count == 0 )
361     return -1;
362
363   while ( *str != 0 && i < buf_len )
364     {
365       c = decode_map[(int)*str++];
366       if ( c == 0xff ) continue;
367       if ( c == 0xfe ) break;
368
369       switch ( phase++ )
370         {
371         case 0:
372           buf[i++] =  c << 2;
373           break;
374
375         case 1:
376           buf[i - 1] |= c >> 4;
377           d = c;
378           break;
379
380         case 2:
381           buf[i++] =  ( d << 4 ) | ( c >> 2 );
382           d = c;
383           break;
384
385         case 3:
386           buf[i++] =  ( d << 6 ) | c;
387           phase = 0;
388           break;
389         }
390     }
391
392   *char_count = i;
393   return 0;
394 }
395
396 //------------------------------------------------------------------------------------------
397
398 // convert utf-8 hext string to bin
399 i32_t
400 Kumu::hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* conv_size)
401 {
402   KM_TEST_NULL_L(str);
403   KM_TEST_NULL_L(buf);
404   KM_TEST_NULL_L(conv_size);
405
406   *conv_size = 0;
407
408   if ( str[0] == 0 ) // nothing to convert
409     return 0;
410
411   for ( int j = 0; str[j]; j++ )
412     {
413       if ( isxdigit(str[j]) )
414         (*conv_size)++;
415     }
416
417   if ( *conv_size & 0x01 ) (*conv_size)++;
418   *conv_size /= 2;
419
420   if ( *conv_size > buf_len )// maximum possible data size
421     return -1;
422
423   *conv_size = 0;
424
425   int phase = 0; // track high/low nybble
426
427   // for each character, fill in the high nybble then the low
428   for ( int i = 0; str[i]; i++ )
429     {
430       if ( ! isxdigit(str[i]) )
431         continue;
432
433       byte_t val = str[i] - ( isdigit(str[i]) ? 0x30 : ( isupper(str[i]) ? 0x37 : 0x57 ) );
434
435       if ( phase == 0 )
436         {
437           buf[*conv_size] = val << 4;
438           phase++;
439         }
440       else
441         {
442           buf[*conv_size] |= val;
443           phase = 0;
444           (*conv_size)++;
445         }
446     }
447
448   return 0;
449 }
450
451 #ifdef CONFIG_RANDOM_UUID
452
453 // convert a memory region to a NULL-terminated hexadecimal string
454 //
455 static const char*
456 bin2hex_rand(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
457 {
458   if ( bin_buf == 0
459        || str_buf == 0
460        || ((bin_len * 2) + 1) > str_len )
461     return 0;
462
463   char* p = str_buf;
464   Kumu::mem_ptr<byte_t> rand_buf = new byte_t[bin_len];
465   Kumu::FortunaRNG RNG;
466   RNG.FillRandom(rand_buf, bin_len);
467
468   for ( ui32_t i = 0; i < bin_len; i++ )
469     {
470       *p = (bin_buf[i] >> 4) & 0x0f;
471       *p += *p < 10 ? 0x30 : (( ((rand_buf[i] & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
472       p++;
473
474       *p = bin_buf[i] & 0x0f;
475       *p += *p < 10 ? 0x30 : (( (((rand_buf[i] >> 1) & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
476       p++;
477     }
478
479   *p = '\0';
480   return str_buf;
481 }
482 #endif
483
484 // convert a memory region to a NULL-terminated hexadecimal string
485 //
486 const char*
487 Kumu::bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
488 {
489   if ( bin_buf == 0
490        || str_buf == 0
491        || ((bin_len * 2) + 1) > str_len )
492     return 0;
493
494 #ifdef CONFIG_RANDOM_UUID
495   const char* use_random_uuid  = getenv("KM_USE_RANDOM_UUID");
496   if ( use_random_uuid != 0 && use_random_uuid[0] != 0 && use_random_uuid[0] != '0' )
497     return bin2hex_rand(bin_buf, bin_len, str_buf, str_len);
498 #endif
499
500   char* p = str_buf;
501
502   for ( ui32_t i = 0; i < bin_len; i++ )
503     {
504       *p = (bin_buf[i] >> 4) & 0x0f;
505       *p += *p < 10 ? 0x30 : 0x61 - 10;
506       p++;
507
508       *p = bin_buf[i] & 0x0f;
509       *p += *p < 10 ? 0x30 : 0x61 - 10;
510       p++;
511     }
512
513   *p = '\0';
514   return str_buf;
515 }
516
517
518 // spew a range of bin data as hex
519 void
520 Kumu::hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream)
521 {
522   if ( buf == 0 )
523     return;
524
525   if ( stream == 0 )
526     stream = stderr;
527
528   static ui32_t row_len = 16;
529   const byte_t* p = buf;
530   const byte_t* end_p = p + dump_len;
531
532   for ( ui32_t line = 0; p < end_p; line++ )
533     {
534       fprintf(stream, "  %06x: ", line);
535       ui32_t i;
536       const byte_t* pp;
537
538       for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
539         fprintf(stream, "%02x ", *pp);
540
541       while ( i++ < row_len )
542         fputs("   ", stream);
543
544       for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
545         fputc((isprint(*pp) ? *pp : '.'), stream);
546
547       fputc('\n', stream);
548       p += row_len;
549     }
550 }
551
552 //
553 const char*
554 Kumu::bin2UUIDhex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
555 {
556   ui32_t i, j, k;
557
558   if ( str_len < 34 || bin_len != UUID_Length )
559     return 0;
560
561   if ( bin2hex(bin_buf, bin_len, str_buf, str_len) == 0 )
562     return 0;
563
564   // shift the node id
565   for ( k = 19, i = 12; i > 0; i-- )
566     str_buf[k+i+4] = str_buf[k+i];
567
568   // shift the time (mid+hi+clk)
569   for ( k = 15, j = 3; k > 6; k -= 4, j-- )
570     {
571       for ( i = 4; i > 0; i-- )
572         str_buf[k+i+j] = str_buf[k+i];
573     }
574
575   // add in the hyphens and trainling null
576   for ( i = 8; i < 24; i += 5 )
577     str_buf[i] = '-';
578   
579   str_buf[36] = 0;
580   return str_buf;
581 }
582
583 //
584 void
585 Kumu::GenRandomValue(UUID& ID)
586 {
587   byte_t tmp_buf[UUID_Length];
588   GenRandomUUID(tmp_buf);
589   ID.Set(tmp_buf);
590 }
591
592 //
593 void
594 Kumu::GenRandomUUID(byte_t* buf)
595 {
596   FortunaRNG RNG;
597   RNG.FillRandom(buf, UUID_Length);
598   buf[6] &= 0x0f; // clear bits 4-7
599   buf[6] |= 0x40; // set UUID version
600   buf[8] &= 0x3f; // clear bits 6&7
601   buf[8] |= 0x80; // set bit 7
602 }
603
604 //
605 void
606 Kumu::GenRandomValue(SymmetricKey& Key)
607 {
608   byte_t tmp_buf[SymmetricKey_Length];
609   FortunaRNG RNG;
610   RNG.FillRandom(tmp_buf, SymmetricKey_Length);
611   Key.Set(tmp_buf);
612 }
613
614
615 //------------------------------------------------------------------------------------------
616 // read a ber value from the buffer and compare with test value.
617 // Advances buffer to first character after BER value.
618 //
619 bool
620 Kumu::read_test_BER(byte_t **buf, ui64_t test_value)
621 {
622   if ( buf == 0 )
623     return false;
624
625   if ( ( **buf & 0x80 ) == 0 )
626     return false;
627
628   ui64_t val = 0;
629   ui8_t ber_size = ( **buf & 0x0f ) + 1;
630
631   if ( ber_size > 9 )
632     return false;
633
634   for ( ui8_t i = 1; i < ber_size; i++ )
635     {
636       if ( (*buf)[i] > 0 )
637         val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 );
638     }
639
640   *buf += ber_size;
641   return ( val == test_value );
642 }
643
644
645 //
646 bool
647 Kumu::read_BER(const byte_t* buf, ui64_t* val)
648 {
649   ui8_t ber_size, i;
650   
651   if ( buf == 0 || val == 0 )
652     return false;
653
654   if ( ( *buf & 0x80 ) == 0 )
655     return false;
656
657   *val = 0;
658   ber_size = ( *buf & 0x0f ) + 1;
659
660   if ( ber_size > 9 )
661     return false;
662
663   for ( i = 1; i < ber_size; i++ )
664     {
665       if ( buf[i] > 0 )
666         *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 );
667     }
668
669   return true;
670 }
671
672
673 static const ui64_t ber_masks[9] =
674   { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00), 
675     ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000),
676     ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000),
677     ui64_C(0xffff000000000000), ui64_C(0xff00000000000000),
678     0
679   };
680
681 //
682 ui32_t
683 Kumu::get_BER_length_for_value(ui64_t val)
684 {
685   for ( ui32_t i = 0; i < 9; i++ )
686     {
687       if ( ( val & ber_masks[i] ) == 0 )
688         return i + 1;
689     }
690
691   ui64Printer tmp_i(val);
692   DefaultLogSink().Error("BER integer encoding not supported for large value %s\n", tmp_i.c_str());
693   return 0;
694 }
695
696 //
697 bool
698 Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
699 {
700   if ( buf == 0 )
701     return false;
702
703   if ( ber_len == 0 )
704     { // calculate default length
705       if ( val < 0x01000000L )
706         ber_len = 4;
707       else if ( val < ui64_C(0x0100000000000000) )
708         ber_len = 8;
709       else
710         ber_len = 9;
711     }
712   else
713     { // sanity check BER length
714       if ( ber_len > 9 )
715         {
716           DefaultLogSink().Error("BER integer length %u exceeds maximum size of 9\n", ber_len);
717           return false;
718         }
719       
720       if ( ( val & ber_masks[ber_len - 1] ) != 0 )
721         {
722           ui64Printer tmp_i(val);
723           DefaultLogSink().Error("BER integer length %u too small for value %s\n", ber_len, tmp_i.c_str());
724           return false;
725         }
726     }
727
728   buf[0] = 0x80 + ( ber_len - 1 );
729
730   for ( ui32_t i = ber_len - 1; i > 0; i-- )
731     {
732       buf[i] = (ui8_t)(val & 0xff);
733       val >>= 8;
734     }
735
736   return true;
737 }
738
739
740 //------------------------------------------------------------------------------------------
741
742 #ifndef KM_WIN32
743 #include <time.h>
744 #endif
745
746 //
747 Kumu::Timestamp::Timestamp() : m_TZOffsetMinutes(0) {
748   m_Timestamp.now();
749 }
750
751 Kumu::Timestamp::Timestamp(const Timestamp& rhs) {
752   m_Timestamp = rhs.m_Timestamp;
753   m_TZOffsetMinutes = rhs.m_TZOffsetMinutes;
754 }
755
756 Kumu::Timestamp::Timestamp(const char* datestr) : m_TZOffsetMinutes(0) {
757   DecodeString(datestr);
758 }
759
760 Kumu::Timestamp::~Timestamp() {
761 }
762
763 //
764 const Kumu::Timestamp&
765 Kumu::Timestamp::operator=(const Timestamp& rhs)
766 {
767   m_Timestamp = rhs.m_Timestamp;
768   m_TZOffsetMinutes = rhs.m_TZOffsetMinutes;
769   return *this;
770 }
771
772 bool Kumu::Timestamp::operator<(const Timestamp& rhs) const {
773   return m_Timestamp.x < rhs.m_Timestamp.x;
774 }
775
776 bool Kumu::Timestamp::operator>(const Timestamp& rhs) const {
777   return m_Timestamp.x > rhs.m_Timestamp.x;
778 }
779
780 bool Kumu::Timestamp::operator==(const Timestamp& rhs) const {
781   return m_Timestamp.x == rhs.m_Timestamp.x;
782 }
783
784 bool Kumu::Timestamp::operator!=(const Timestamp& rhs) const {
785   return m_Timestamp.x != rhs.m_Timestamp.x;
786 }
787
788 //
789 void
790 Kumu::Timestamp::GetComponents(ui16_t& Year, ui8_t&  Month, ui8_t&  Day,
791                                ui8_t&  Hour, ui8_t&  Minute, ui8_t&  Second) const
792 {
793   TAI::caltime ct;
794   ct = m_Timestamp;
795   Year = ct.date.year;
796   Month = ct.date.month;
797   Day = ct.date.day;
798   Hour = ct.hour;
799   Minute = ct.minute;
800   Second = ct.second;
801 }
802
803 //
804 void
805 Kumu::Timestamp::SetComponents(const ui16_t& Year, const ui8_t&  Month, const ui8_t&  Day,
806                                const ui8_t&  Hour, const ui8_t&  Minute, const ui8_t&  Second)
807 {
808   TAI::caltime ct;
809   ct.date.year = Year;
810   ct.date.month = Month;
811   ct.date.day = Day;
812   ct.hour = Hour;
813   ct.minute = Minute;
814   ct.second = Second;
815   ct.offset = 0;
816   m_Timestamp = ct;
817   m_TZOffsetMinutes = 0;
818 }
819
820 // returns false if the requested adjustment is out of range
821 bool
822 Kumu::Timestamp::SetTZOffsetMinutes(const i32_t& minutes)
823 {
824   static const i32_t tz_limit = 14 * 60 * 60;
825
826   if ( minutes < ( - tz_limit) || minutes > tz_limit )
827     return false;
828
829   m_TZOffsetMinutes = minutes;
830   return true;
831 }
832
833 // 
834 const char*
835 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
836 {
837   if ( buf_len < ( DateTimeLen + 1 ) )
838     return 0;
839
840   ui16_t year;
841   ui8_t month, day, hour, minute, second;
842   ui32_t ofst_hours = 0, ofst_minutes = 0;
843   char direction = '+';
844
845   if ( m_TZOffsetMinutes == 0 )
846     {
847       GetComponents(year, month, day, hour, minute, second);
848     }
849   else
850     {
851       // calculate local time
852       Kumu::Timestamp tmp_t(*this);
853       tmp_t.AddMinutes(m_TZOffsetMinutes);
854       tmp_t.GetComponents(year, month, day, hour, minute, second);
855
856       ofst_hours = Kumu::xabs(m_TZOffsetMinutes) / 60;
857       ofst_minutes = Kumu::xabs(m_TZOffsetMinutes) % 60;
858
859       if ( m_TZOffsetMinutes < 0 )
860         direction = '-';
861     }
862   
863   // 2004-05-01T13:20:00+00:00
864   snprintf(str_buf, buf_len,
865            "%04hu-%02hhu-%02hhuT%02hhu:%02hhu:%02hhu%c%02u:%02u",
866            year, month, day, hour, minute, second,
867            direction, ofst_hours, ofst_minutes);
868
869   return str_buf;
870 }
871
872 // ^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(?:([+-]\d{2}):(\d{2}))?)?$
873 bool
874 Kumu::Timestamp::DecodeString(const char* datestr)
875 {
876   if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
877        || datestr[4] != '-'
878        || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
879        || datestr[7] != '-'
880        || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
881     return false;
882
883   ui32_t char_count = 10;
884   TAI::caltime YMDhms;
885   YMDhms.hour = 0;
886   YMDhms.minute = 0;
887   YMDhms.second = 0;
888   YMDhms.offset = 0;
889   YMDhms.date.year = strtol(datestr, 0, 10);
890   YMDhms.date.month = strtol(datestr + 5, 0, 10);
891   YMDhms.date.day = strtol(datestr + 8, 0, 10);
892  
893   if ( datestr[10] == 'T' )
894     {
895       if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
896            || datestr[13] != ':'
897            || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
898         return false;
899
900       char_count += 6;
901       YMDhms.hour = strtol(datestr + 11, 0, 10);
902       YMDhms.minute = strtol(datestr + 14, 0, 10);
903
904       if ( datestr[16] == ':' )
905         {
906           if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
907             return false;
908
909           char_count += 3;
910           YMDhms.second = strtol(datestr + 17, 0, 10);
911         }
912
913       if ( datestr[19] == '.' )
914         {
915           if ( ! isdigit(datestr[20]) )
916             {
917               return false;
918             }
919
920           // we don't carry the ms value
921           while ( isdigit(datestr[20]) )
922             {
923               ++datestr;
924             }
925
926           ++datestr;
927         }
928
929       if ( datestr[19] == '-' || datestr[19] == '+' )
930         {
931           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
932                || datestr[22] != ':'
933                || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
934             return false;
935
936           char_count += 6;
937
938           ui32_t TZ_hh = strtol(datestr + 20, 0, 10);
939           ui32_t TZ_mm = strtol(datestr + 23, 0, 10);
940           if ((TZ_hh > 14) || (TZ_mm > 59) || ((TZ_hh == 14) && (TZ_mm > 0)))
941             return false;
942
943           i32_t TZ_offset = 60 * TZ_hh + TZ_mm;
944           if (datestr[19] == '-')
945             TZ_offset = -TZ_offset;
946           /* at this point, TZ_offset reflects the contents of the string */
947
948           /* a negative offset is behind UTC and so needs to increment to
949            * convert, while a positive offset must do the reverse */
950           YMDhms.offset = TZ_offset;
951         }
952       else if (datestr[19] == 'Z')
953         {
954           /* act as if the offset were +00:00 */
955           char_count++;
956         }
957     }
958
959   if ( datestr[char_count] != 0 )
960     {
961       Kumu::DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n",
962                                    datestr, char_count);
963       return false;
964     }
965   
966   m_Timestamp = YMDhms;
967   m_TZOffsetMinutes = YMDhms.offset;
968   return true;
969 }
970
971 //
972 bool
973 Kumu::Timestamp::HasValue() const
974 {
975   return true;
976 }
977
978 //
979 bool
980 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
981 {
982   ui16_t year;
983   ui8_t month, day, hour, minute, second, tick;
984
985   assert(Reader);
986   if ( ! Reader->ReadUi16BE(&year) ) return false;
987   if ( ! Reader->ReadUi8(&month) ) return false;
988   if ( ! Reader->ReadUi8(&day) ) return false;
989   if ( ! Reader->ReadUi8(&hour) ) return false;
990   if ( ! Reader->ReadUi8(&minute) ) return false;
991   if ( ! Reader->ReadUi8(&second) ) return false;
992   if ( ! Reader->ReadUi8(&tick) ) return false;
993   SetComponents(year, month, day, hour, minute, second);
994   return true;
995 }
996
997 //
998 bool
999 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
1000 {
1001   assert(Writer);
1002
1003   ui16_t year;
1004   ui8_t month, day, hour, minute, second, tick = 0;
1005   GetComponents(year, month, day, hour, minute, second);
1006
1007   if ( ! Writer->WriteUi16BE(year) ) return false;      
1008   if ( ! Writer->WriteUi8(month) ) return false;
1009   if ( ! Writer->WriteUi8(day) ) return false;
1010   if ( ! Writer->WriteUi8(hour) ) return false;
1011   if ( ! Writer->WriteUi8(minute) ) return false;
1012   if ( ! Writer->WriteUi8(second) ) return false;
1013   if ( ! Writer->WriteUi8(tick) ) return false;
1014   return true;
1015 }
1016
1017 //
1018 ui64_t
1019 Kumu::Timestamp::GetCTime() const
1020 {
1021   return m_Timestamp.x - ui64_C(4611686018427387914);
1022 }
1023
1024 //
1025 void
1026 Kumu::Timestamp::SetCTime(const ui64_t& ctime)
1027 {
1028   m_Timestamp.x = ctime + ui64_C(4611686018427387914);
1029 }
1030
1031
1032
1033
1034 //------------------------------------------------------------------------------------------
1035
1036 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
1037   : m_p(0), m_capacity(0), m_size(0)
1038 {
1039   m_p = Buf->Data();
1040   m_capacity = Buf->Capacity();
1041   assert(m_p); assert(m_capacity);
1042 }
1043
1044 bool
1045 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
1046 {
1047   if ( ( m_size + ber_len ) > m_capacity )
1048     return false;
1049
1050   if ( ! write_BER(m_p + m_size, i, ber_len) )
1051     return false;
1052
1053   m_size += ber_len;
1054   return true;
1055 }
1056
1057
1058 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
1059   : m_p(0), m_capacity(0), m_size(0)
1060 {
1061   m_p = Buf->RoData();
1062   m_capacity = Buf->Length();
1063   assert(m_p); assert(m_capacity);
1064 }
1065
1066 bool
1067 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
1068 {
1069   if ( i == 0 || ber_len == 0 ) return false;
1070
1071   if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
1072     return false;
1073
1074   if ( ( m_size + *ber_len ) > m_capacity )
1075     return false;
1076
1077   if ( ! read_BER(m_p + m_size, i) )
1078     return false;
1079
1080   m_size += *ber_len;
1081   return true;
1082 }
1083
1084 //------------------------------------------------------------------------------------------
1085
1086 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
1087
1088 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
1089 {
1090   Capacity(cap);
1091 }
1092
1093 Kumu::ByteString::~ByteString()
1094 {
1095   if ( m_Data != 0 )
1096     free(m_Data);
1097 }
1098
1099
1100 // copy the given data into the ByteString, set Length value.
1101 // Returns error if the ByteString is too small.
1102 Kumu::Result_t
1103 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
1104 {
1105   if ( m_Capacity < buf_len )
1106     return RESULT_ALLOC;
1107
1108   memcpy(m_Data, buf, buf_len);
1109   m_Length = buf_len;
1110   return RESULT_OK;
1111 }
1112
1113
1114 // copy the given data into the ByteString, set Length value.
1115 // Returns error if the ByteString is too small.
1116 Kumu::Result_t
1117 Kumu::ByteString::Set(const ByteString& Buf)
1118 {
1119   if ( m_Capacity < Buf.m_Capacity )
1120     return RESULT_ALLOC;
1121
1122   memcpy(m_Data, Buf.m_Data, Buf.m_Length);
1123   m_Length = Buf.m_Length;
1124   return RESULT_OK;
1125 }
1126
1127
1128 // Sets the size of the internally allocate buffer.
1129 Kumu::Result_t
1130 Kumu::ByteString::Capacity(ui32_t cap_size)
1131 {
1132   if ( m_Capacity >= cap_size )
1133     return RESULT_OK;
1134
1135   byte_t* tmp_data = 0;
1136   if ( m_Data != 0 )
1137     {
1138       if ( m_Length > 0 )
1139         tmp_data = m_Data;
1140       else
1141         free(m_Data);
1142     }
1143                 
1144   if ( ( m_Data = (byte_t*)malloc(cap_size) ) == 0 )
1145     return RESULT_ALLOC;
1146
1147   if ( tmp_data != 0 )
1148     {
1149       assert(m_Length > 0);
1150       memcpy(m_Data, tmp_data, m_Length);
1151       free(tmp_data);
1152     }
1153                 
1154   m_Capacity = cap_size;
1155   return RESULT_OK;
1156 }
1157
1158 //
1159 Kumu::Result_t
1160 Kumu::ByteString::Append(const ByteString& Buf)
1161 {
1162   Result_t result = RESULT_OK;
1163   ui32_t diff = m_Capacity - m_Length;
1164
1165   if ( diff < Buf.Length() )
1166     result = Capacity(m_Capacity + Buf.Length());
1167
1168   if ( KM_SUCCESS(result) )
1169     {
1170       memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1171       m_Length += Buf.Length();
1172     }
1173
1174   return result;
1175 }
1176
1177 //
1178 Kumu::Result_t
1179 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1180 {
1181   Result_t result = RESULT_OK;
1182   ui32_t diff = m_Capacity - m_Length;
1183
1184   if ( diff < buf_len )
1185     result = Capacity(m_Capacity + buf_len);
1186
1187   if ( KM_SUCCESS(result) )
1188     {
1189       memcpy(m_Data + m_Length, buf, buf_len);
1190       m_Length += buf_len;
1191     }
1192
1193   return result;
1194 }
1195
1196 //------------------------------------------------------------------------------------------
1197
1198 //
1199 const char*
1200 Kumu::km_strnstr(const char *s, const char *find, size_t slen)
1201 {
1202   char c, sc;
1203   size_t len;
1204
1205   if ( ( c = *find++ ) != '\0' )
1206     {
1207       len = strlen(find);
1208       do
1209         {
1210           do
1211             {
1212               if ( slen-- < 1 || ( sc = *s++ ) == '\0' )
1213                 return 0;
1214             }
1215           while ( sc != c );
1216
1217           if ( len > slen )
1218             return 0;
1219         }
1220       while ( strncmp(s, find, len) != 0 );
1221       --s;
1222     }
1223
1224   return s;
1225 }
1226
1227 //
1228 std::list<std::string>
1229 Kumu::km_token_split(const std::string& str, const std::string& separator)
1230 {
1231   std::list<std::string> components;
1232   const char* pstr = str.c_str();
1233   const char* r = strstr(pstr, separator.c_str());
1234
1235   while ( r != 0 )
1236     {
1237       assert(r >= pstr);
1238       std::string tmp_str;
1239       tmp_str.assign(pstr, r - pstr);
1240       components.push_back(tmp_str);
1241       pstr = r + separator.size();
1242       r = strstr(pstr, separator.c_str());
1243     }
1244       
1245   components.push_back(std::string(pstr));
1246   return components;
1247 }
1248
1249 //
1250 // end KM_util.cpp
1251 //