Coverage Report

Created: 2025-06-13 07:22

/src/libnk2/libnk2/libnk2_mapi_value.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * MAPI value functions
3
 *
4
 * Copyright (C) 2009-2024, Joachim Metz <joachim.metz@gmail.com>
5
 *
6
 * Refer to AUTHORS for acknowledgements.
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
#include <common.h>
23
#include <types.h>
24
25
#include "libnk2_libcerror.h"
26
#include "libnk2_libuna.h"
27
#include "libnk2_mapi.h"
28
#include "libnk2_mapi_value.h"
29
30
/* Retrieves the value data size
31
 * Returns 1 if successful or -1 on error
32
 */
33
int libnk2_mapi_value_get_data_size(
34
     uint32_t value_type,
35
     size_t *value_data_size,
36
     libcerror_error_t **error )
37
589k
{
38
589k
  static char *function = "libnk2_mapi_value_get_data_size";
39
40
589k
  if( value_data_size == NULL )
41
0
  {
42
0
    libcerror_error_set(
43
0
     error,
44
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
45
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
46
0
     "%s: invalid value data size.",
47
0
     function );
48
49
0
    return( -1 );
50
0
  }
51
  /* TODO add other value types to the item entry
52
   */
53
589k
  switch( value_type )
54
589k
  {
55
671
    case LIBNK2_VALUE_TYPE_BOOLEAN:
56
918
    case LIBNK2_VALUE_TYPE_INTEGER_16BIT_SIGNED:
57
918
      *value_data_size = 2;
58
918
      break;
59
60
242
    case LIBNK2_VALUE_TYPE_INTEGER_32BIT_SIGNED:
61
152k
    case LIBNK2_VALUE_TYPE_FLOAT_32BIT:
62
259k
    case LIBNK2_VALUE_TYPE_ERROR:
63
259k
      *value_data_size = 4;
64
259k
      break;
65
66
375
    case LIBNK2_VALUE_TYPE_INTEGER_64BIT_SIGNED:
67
1.31k
    case LIBNK2_VALUE_TYPE_DOUBLE_64BIT:
68
36.6k
    case LIBNK2_VALUE_TYPE_CURRENCY:
69
319k
    case LIBNK2_VALUE_TYPE_FLOATINGTIME:
70
327k
    case LIBNK2_VALUE_TYPE_FILETIME:
71
327k
      *value_data_size = 8;
72
327k
      break;
73
74
765
    case LIBNK2_VALUE_TYPE_STRING_ASCII:
75
1.39k
    case LIBNK2_VALUE_TYPE_STRING_UNICODE:
76
1.79k
    case LIBNK2_VALUE_TYPE_BINARY_DATA:
77
1.79k
      *value_data_size = 0;
78
1.79k
      break;
79
80
40
    default:
81
40
      libcerror_error_set(
82
40
       error,
83
40
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
84
40
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
85
40
       "%s: unsupported value type: 0x%04" PRIx32 ".",
86
40
       function,
87
40
       value_type );
88
89
40
      return( -1 );
90
589k
  }
91
589k
  return( 1 );
92
589k
}
93
94
/* Determines if there are zero bytes in a string, trailing zero bytes not included
95
 * Returns 1 if the data contains zero bytes, 0 if not or -1 on error
96
 */
97
int libnk2_mapi_value_data_contains_zero_bytes(
98
     const uint8_t *data,
99
     size_t data_size,
100
     libcerror_error_t **error )
101
0
{
102
0
  static char *function   = "libnk2_mapi_value_data_contains_zero_bytes";
103
0
  size_t data_offset      = 0;
104
0
  uint8_t zero_byte_found = 0;
105
106
0
  if( data == NULL )
107
0
  {
108
0
    libcerror_error_set(
109
0
     error,
110
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
111
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
112
0
     "%s: invalid data.",
113
0
     function );
114
115
0
    return( -1 );
116
0
  }
117
0
  if( data_size > (size_t) SSIZE_MAX )
118
0
  {
119
0
    libcerror_error_set(
120
0
     error,
121
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
122
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
123
0
     "%s: invalid data size value exceeds maximum.",
124
0
     function );
125
126
0
    return( -1 );
127
0
  }
128
0
  for( data_offset = 0;
129
0
       data_offset < data_size;
130
0
       data_offset++ )
131
0
  {
132
0
    if( zero_byte_found == 0 )
133
0
    {
134
0
      if( data[ data_offset ] == 0 )
135
0
      {
136
0
        zero_byte_found = 1;
137
0
      }
138
0
    }
139
0
    else
140
0
    {
141
0
      if( data[ data_offset ] != 0 )
142
0
      {
143
0
        return( 1 );
144
0
      }
145
0
    }
146
0
  }
147
0
  return( 0 );
148
0
}
149
150
/* Retrieves the size of the MAPI value formatted as an UTF-8 string
151
 * The returned size includes the end of string character
152
 * Returns 1 if successful or -1 on error
153
 */
154
int libnk2_mapi_value_get_data_as_utf8_string_size(
155
     uint32_t value_type,
156
     const uint8_t *value_data,
157
     size_t value_data_size,
158
     int ascii_codepage,
159
     size_t *utf8_string_size,
160
     libcerror_error_t **error )
161
0
{
162
0
  static char *function   = "libnk2_mapi_value_get_data_as_utf8_string_size";
163
0
  uint8_t is_ascii_string = 0;
164
0
  int result              = 0;
165
166
0
  if( utf8_string_size == NULL )
167
0
  {
168
0
    libcerror_error_set(
169
0
     error,
170
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
171
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
172
0
     "%s: invalid UTF-8 string size.",
173
0
     function );
174
175
0
    return( -1 );
176
0
  }
177
0
  if( ( value_data == NULL )
178
0
   || ( value_data_size == 0 ) )
179
0
  {
180
0
    *utf8_string_size = 0;
181
182
0
    return( 1 );
183
0
  }
184
0
  if( value_type == LIBNK2_VALUE_TYPE_STRING_ASCII )
185
0
  {
186
0
    is_ascii_string = 1;
187
0
  }
188
  /* Codepage 1200 represents Unicode
189
   * If the codepage is 1200 find out if the string is encoded in UTF-8 or UTF-16 little-endian
190
   */
191
0
  if( ( is_ascii_string != 0 )
192
0
   && ( ascii_codepage == 1200 ) )
193
0
  {
194
0
    result = libnk2_mapi_value_data_contains_zero_bytes(
195
0
        value_data,
196
0
        value_data_size,
197
0
        error );
198
199
0
    if( result == -1 )
200
0
    {
201
0
      libcerror_error_set(
202
0
       error,
203
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
204
0
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
205
0
       "%s: unable to determine if value data contains zero bytes.",
206
0
       function );
207
208
0
      return( -1 );
209
0
    }
210
0
    else if( result != 0 )
211
0
    {
212
0
      is_ascii_string = 0;
213
0
    }
214
0
  }
215
  /* String is in UTF-16 little-endian
216
   */
217
0
  if( is_ascii_string == 0 )
218
0
  {
219
0
    result = libuna_utf8_string_size_from_utf16_stream(
220
0
              value_data,
221
0
              value_data_size,
222
0
              LIBUNA_ENDIAN_LITTLE,
223
0
              utf8_string_size,
224
0
              error );
225
0
  }
226
  /* Codepage 65000 represents UTF-7
227
   */
228
0
  else if( ascii_codepage == 65000 )
229
0
  {
230
0
    result = libuna_utf8_string_size_from_utf7_stream(
231
0
        value_data,
232
0
        value_data_size,
233
0
        utf8_string_size,
234
0
        error );
235
0
  }
236
  /* Codepage 1200 or 65001 represents UTF-8
237
   */
238
0
  else if( ( ascii_codepage == 1200 )
239
0
        || ( ascii_codepage == 65001 ) )
240
0
  {
241
0
    result = libuna_utf8_string_size_from_utf8_stream(
242
0
        value_data,
243
0
        value_data_size,
244
0
        utf8_string_size,
245
0
        error );
246
0
  }
247
0
  else
248
0
  {
249
    /* TODO currently libuna uses the same numeric values for the codepages as NK2
250
     * add a mapping function if this implementation changes
251
     */
252
0
    result = libuna_utf8_string_size_from_byte_stream(
253
0
        value_data,
254
0
        value_data_size,
255
0
        ascii_codepage,
256
0
        utf8_string_size,
257
0
        error );
258
0
  }
259
0
  if( result != 1 )
260
0
  {
261
0
    libcerror_error_set(
262
0
     error,
263
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
264
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
265
0
     "%s: unable to determine size of value data as UTF-8 string.",
266
0
     function );
267
268
0
    return( -1 );
269
0
  }
270
0
  return( 1 );
271
0
}
272
273
/* Retrieves the MAPI value formatted as an UTF-8 string value
274
 * The size should include the end of string character
275
 * Returns 1 if successful or -1 on error
276
 */
277
int libnk2_mapi_value_get_data_as_utf8_string(
278
     uint32_t value_type,
279
     const uint8_t *value_data,
280
     size_t value_data_size,
281
     int ascii_codepage,
282
     uint8_t *utf8_string,
283
     size_t utf8_string_size,
284
     libcerror_error_t **error )
285
0
{
286
0
  static char *function   = "libnk2_mapi_value_get_data_as_utf8_string";
287
0
  uint8_t is_ascii_string = 0;
288
0
  int result              = 0;
289
290
0
  if( utf8_string == NULL )
291
0
  {
292
0
    libcerror_error_set(
293
0
     error,
294
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
295
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
296
0
     "%s: invalid UTF-8 string.",
297
0
     function );
298
299
0
    return( -1 );
300
0
  }
301
0
  if( ( utf8_string_size == 0 )
302
0
   || ( utf8_string_size > (size_t) SSIZE_MAX ) )
303
0
  {
304
0
    libcerror_error_set(
305
0
     error,
306
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
307
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
308
0
     "%s: invalid UTF-8 string size value out of bounds.",
309
0
     function );
310
311
0
    return( -1 );
312
0
  }
313
0
  if( ( value_data == NULL )
314
0
   || ( value_data_size == 0 ) )
315
0
  {
316
0
    utf8_string[ 0 ] = 0;
317
318
0
    return( 1 );
319
0
  }
320
0
  if( value_type == LIBNK2_VALUE_TYPE_STRING_ASCII )
321
0
  {
322
0
    is_ascii_string = 1;
323
0
  }
324
  /* Codepage 1200 represents Unicode
325
   * If the codepage is 1200 find out if the string is encoded in UTF-8 or UTF-16 little-endian
326
   */
327
0
  if( ( is_ascii_string != 0 )
328
0
   && ( ascii_codepage == 1200 ) )
329
0
  {
330
0
    result = libnk2_mapi_value_data_contains_zero_bytes(
331
0
        value_data,
332
0
        value_data_size,
333
0
        error );
334
335
0
    if( result == -1 )
336
0
    {
337
0
      libcerror_error_set(
338
0
       error,
339
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
340
0
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
341
0
       "%s: unable to determine if value data contains zero bytes.",
342
0
       function );
343
344
0
      return( -1 );
345
0
    }
346
0
    else if( result != 0 )
347
0
    {
348
0
      is_ascii_string = 0;
349
0
    }
350
0
  }
351
  /* String is in UTF-16 little-endian
352
   */
353
0
  if( is_ascii_string == 0 )
354
0
  {
355
0
    result = libuna_utf8_string_copy_from_utf16_stream(
356
0
              utf8_string,
357
0
              utf8_string_size,
358
0
              value_data,
359
0
              value_data_size,
360
0
              LIBUNA_ENDIAN_LITTLE,
361
0
              error );
362
0
  }
363
  /* Codepage 65000 represents UTF-7
364
   */
365
0
  else if( ascii_codepage == 65000 )
366
0
  {
367
0
    result = libuna_utf8_string_copy_from_utf7_stream(
368
0
              utf8_string,
369
0
              utf8_string_size,
370
0
              value_data,
371
0
              value_data_size,
372
0
              error );
373
0
  }
374
  /* Codepage 1200 or 65001 represents UTF-8
375
   */
376
0
  else if( ( ascii_codepage == 1200 )
377
0
        || ( ascii_codepage == 65001 ) )
378
0
  {
379
0
    result = libuna_utf8_string_copy_from_utf8_stream(
380
0
              utf8_string,
381
0
              utf8_string_size,
382
0
              value_data,
383
0
              value_data_size,
384
0
              error );
385
0
  }
386
0
  else
387
0
  {
388
    /* TODO currently libuna uses the same numeric values for the codepages as NK2
389
     * add a mapping function if this implementation changes
390
     */
391
0
    result = libuna_utf8_string_copy_from_byte_stream(
392
0
              utf8_string,
393
0
              utf8_string_size,
394
0
              value_data,
395
0
              value_data_size,
396
0
              ascii_codepage,
397
0
              error );
398
0
  }
399
0
  if( result != 1 )
400
0
  {
401
0
    libcerror_error_set(
402
0
     error,
403
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
404
0
     LIBCERROR_RUNTIME_ERROR_COPY_FAILED,
405
0
     "%s: unable to copy value data to UTF-8 string.",
406
0
     function );
407
408
0
    return( -1 );
409
0
  }
410
0
  return( 1 );
411
0
}
412
413
/* Retrieves the size of the MAPI value formatted as an UTF-16 string
414
 * The returned size includes the end of string character
415
 * Returns 1 if successful or -1 on error
416
 */
417
int libnk2_mapi_value_get_data_as_utf16_string_size(
418
     uint32_t value_type,
419
     const uint8_t *value_data,
420
     size_t value_data_size,
421
     int ascii_codepage,
422
     size_t *utf16_string_size,
423
     libcerror_error_t **error )
424
0
{
425
0
  static char *function   = "libnk2_mapi_value_get_data_as_utf16_string_size";
426
0
  uint8_t is_ascii_string = 0;
427
0
  int result              = 0;
428
429
0
  if( utf16_string_size == NULL )
430
0
  {
431
0
    libcerror_error_set(
432
0
     error,
433
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
434
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
435
0
     "%s: invalid UTF-16 string size.",
436
0
     function );
437
438
0
    return( -1 );
439
0
  }
440
0
  if( ( value_data == NULL )
441
0
   || ( value_data_size == 0 ) )
442
0
  {
443
0
    *utf16_string_size = 0;
444
445
0
    return( 1 );
446
0
  }
447
0
  if( value_type == LIBNK2_VALUE_TYPE_STRING_ASCII )
448
0
  {
449
0
    is_ascii_string = 1;
450
0
  }
451
  /* Codepage 1200 represents Unicode
452
   * If the codepage is 1200 find out if the string is encoded in UTF-8 or UTF-16 little-endian
453
   */
454
0
  if( ( is_ascii_string != 0 )
455
0
   && ( ascii_codepage == 1200 ) )
456
0
  {
457
0
    result = libnk2_mapi_value_data_contains_zero_bytes(
458
0
        value_data,
459
0
        value_data_size,
460
0
        error );
461
462
0
    if( result == -1 )
463
0
    {
464
0
      libcerror_error_set(
465
0
       error,
466
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
467
0
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
468
0
       "%s: unable to determine if value data contains zero bytes.",
469
0
       function );
470
471
0
      return( -1 );
472
0
    }
473
0
    else if( result != 0 )
474
0
    {
475
0
      is_ascii_string = 0;
476
0
    }
477
0
  }
478
  /* String is in UTF-16 little-endian
479
   */
480
0
  if( is_ascii_string == 0 )
481
0
  {
482
0
    result = libuna_utf16_string_size_from_utf16_stream(
483
0
              value_data,
484
0
              value_data_size,
485
0
              LIBUNA_ENDIAN_LITTLE,
486
0
              utf16_string_size,
487
0
              error );
488
0
  }
489
  /* Codepage 65000 represents UTF-7
490
   */
491
0
  else if( ascii_codepage == 65000 )
492
0
  {
493
0
    result = libuna_utf16_string_size_from_utf7_stream(
494
0
        value_data,
495
0
        value_data_size,
496
0
        utf16_string_size,
497
0
        error );
498
0
  }
499
  /* Codepage 1200 or 65001 represents UTF-8
500
   */
501
0
  else if( ( ascii_codepage == 1200 )
502
0
        || ( ascii_codepage == 65001 ) )
503
0
  {
504
0
    result = libuna_utf16_string_size_from_utf8_stream(
505
0
        value_data,
506
0
        value_data_size,
507
0
        utf16_string_size,
508
0
        error );
509
0
  }
510
0
  else
511
0
  {
512
    /* TODO currently libuna uses the same numeric values for the codepages as NK2
513
     * add a mapping function if this implementation changes
514
     */
515
0
    result = libuna_utf16_string_size_from_byte_stream(
516
0
        value_data,
517
0
        value_data_size,
518
0
        ascii_codepage,
519
0
        utf16_string_size,
520
0
        error );
521
0
  }
522
0
  if( result != 1 )
523
0
  {
524
0
    libcerror_error_set(
525
0
     error,
526
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
527
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
528
0
     "%s: unable to determine size of value data as UTF-16 string.",
529
0
     function );
530
531
0
    return( -1 );
532
0
  }
533
0
  return( 1 );
534
0
}
535
536
/* Retrieves the MAPI value formatted as an UTF-16 string value
537
 * The size should include the end of string character
538
 * Returns 1 if successful or -1 on error
539
 */
540
int libnk2_mapi_value_get_data_as_utf16_string(
541
     uint32_t value_type,
542
     const uint8_t *value_data,
543
     size_t value_data_size,
544
     int ascii_codepage,
545
     uint16_t *utf16_string,
546
     size_t utf16_string_size,
547
     libcerror_error_t **error )
548
0
{
549
0
  static char *function   = "libnk2_mapi_value_get_data_as_utf16_string";
550
0
  uint8_t is_ascii_string = 0;
551
0
  int result              = 0;
552
553
0
  if( utf16_string == NULL )
554
0
  {
555
0
    libcerror_error_set(
556
0
     error,
557
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
558
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
559
0
     "%s: invalid UTF-16 string.",
560
0
     function );
561
562
0
    return( -1 );
563
0
  }
564
0
  if( ( utf16_string_size == 0 )
565
0
   || ( utf16_string_size > (size_t) SSIZE_MAX ) )
566
0
  {
567
0
    libcerror_error_set(
568
0
     error,
569
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
570
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
571
0
     "%s: invalid UTF-16 string size value out of bounds.",
572
0
     function );
573
574
0
    return( -1 );
575
0
  }
576
0
  if( ( value_data == NULL )
577
0
   || ( value_data_size == 0 ) )
578
0
  {
579
0
    utf16_string[ 0 ] = 0;
580
581
0
    return( 1 );
582
0
  }
583
0
  if( value_type == LIBNK2_VALUE_TYPE_STRING_ASCII )
584
0
  {
585
0
    is_ascii_string = 1;
586
0
  }
587
  /* Codepage 1200 represents Unicode
588
   * If the codepage is 1200 find out if the string is encoded in UTF-8 or UTF-16 little-endian
589
   */
590
0
  if( ( is_ascii_string != 0 )
591
0
   && ( ascii_codepage == 1200 ) )
592
0
  {
593
0
    result = libnk2_mapi_value_data_contains_zero_bytes(
594
0
        value_data,
595
0
        value_data_size,
596
0
        error );
597
598
0
    if( result == -1 )
599
0
    {
600
0
      libcerror_error_set(
601
0
       error,
602
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
603
0
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
604
0
       "%s: unable to determine if value data contains zero bytes.",
605
0
       function );
606
607
0
      return( -1 );
608
0
    }
609
0
    else if( result != 0 )
610
0
    {
611
0
      is_ascii_string = 0;
612
0
    }
613
0
  }
614
  /* String is in UTF-16 little-endian
615
   */
616
0
  if( is_ascii_string == 0 )
617
0
  {
618
0
    result = libuna_utf16_string_copy_from_utf16_stream(
619
0
              utf16_string,
620
0
              utf16_string_size,
621
0
              value_data,
622
0
              value_data_size,
623
0
              LIBUNA_ENDIAN_LITTLE,
624
0
              error );
625
0
  }
626
  /* Codepage 65000 represents UTF-7
627
   */
628
0
  else if( ascii_codepage == 65000 )
629
0
  {
630
0
    result = libuna_utf16_string_copy_from_utf7_stream(
631
0
              utf16_string,
632
0
              utf16_string_size,
633
0
              value_data,
634
0
              value_data_size,
635
0
              error );
636
0
  }
637
  /* Codepage 1200 or 65001 represents UTF-8
638
   */
639
0
  else if( ( ascii_codepage == 1200 )
640
0
        || ( ascii_codepage == 65001 ) )
641
0
  {
642
0
    result = libuna_utf16_string_copy_from_utf8_stream(
643
0
              utf16_string,
644
0
              utf16_string_size,
645
0
              value_data,
646
0
              value_data_size,
647
0
              error );
648
0
  }
649
0
  else
650
0
  {
651
    /* TODO currently libuna uses the same numeric values for the codepages as NK2
652
     * add a mapping function if this implementation changes
653
     */
654
0
    result = libuna_utf16_string_copy_from_byte_stream(
655
0
              utf16_string,
656
0
              utf16_string_size,
657
0
              value_data,
658
0
              value_data_size,
659
0
              ascii_codepage,
660
0
              error );
661
0
  }
662
0
  if( result != 1 )
663
0
  {
664
0
    libcerror_error_set(
665
0
     error,
666
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
667
0
     LIBCERROR_RUNTIME_ERROR_COPY_FAILED,
668
0
     "%s: unable to copy value data to UTF-16 string.",
669
0
     function );
670
671
0
    return( -1 );
672
0
  }
673
0
  return( 1 );
674
0
}
675