Coverage Report

Created: 2025-06-13 07:22

/src/libevtx/libfwevt/libfwevt_date_time.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Date and time functions
3
 *
4
 * Copyright (C) 2011-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 "libfwevt_date_time.h"
26
#include "libfwevt_libcerror.h"
27
28
#if !defined( LIBFWEVT_ATTRIBUTE_FALLTHROUGH )
29
#if defined( __GNUC__ ) && __GNUC__ >= 7
30
#define LIBFWEVT_ATTRIBUTE_FALLTHROUGH  __attribute__ ((fallthrough))
31
#else
32
#define LIBFWEVT_ATTRIBUTE_FALLTHROUGH
33
#endif
34
#endif
35
36
/* Copies an ISO 8601 UTF-16 stream to a FILETIME value
37
 * Returns 1 if successful or -1 on error
38
 */
39
int libfwevt_filetime_copy_from_utf16_stream(
40
     uint64_t *filetime,
41
     const uint8_t *utf16_stream,
42
     size_t utf16_stream_size,
43
     libcerror_error_t **error )
44
0
{
45
0
  static char *function       = "libfwevt_filetime_copy_from_utf16_stream";
46
0
  size_t utf16_stream_offset  = 0;
47
0
  uint64_t safe_filetime      = 0;
48
0
  uint32_t fraction_of_second = 0;
49
0
  uint16_t current_year       = 0;
50
0
  uint16_t year               = 0;
51
0
  uint8_t day_of_month        = 0;
52
0
  uint8_t days_per_month      = 0;
53
0
  uint8_t hours               = 0;
54
0
  uint8_t minutes             = 0;
55
0
  uint8_t month               = 0;
56
0
  uint8_t is_leap_year        = 0;
57
0
  uint8_t seconds             = 0;
58
59
0
  if( filetime == NULL )
60
0
  {
61
0
    libcerror_error_set(
62
0
     error,
63
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
64
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
65
0
     "%s: invalid FILETIME.",
66
0
     function );
67
68
0
    return( -1 );
69
0
  }
70
0
  if( utf16_stream == NULL )
71
0
  {
72
0
    libcerror_error_set(
73
0
     error,
74
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
75
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
76
0
     "%s: invalid UTF-16 stream.",
77
0
     function );
78
79
0
    return( -1 );
80
0
  }
81
0
  if( ( utf16_stream_size < 62 )
82
0
   || ( utf16_stream_size > (size_t) SSIZE_MAX )
83
0
   || ( ( utf16_stream_size % 2 ) != 0 ) )
84
0
  {
85
0
    libcerror_error_set(
86
0
     error,
87
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
88
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
89
0
     "%s: UTF-16 stream size value out of bounds.",
90
0
     function );
91
92
0
    return( -1 );
93
0
  }
94
0
  if( ( utf16_stream[ 8 ] != (uint8_t) '-' )
95
0
   || ( utf16_stream[ 9 ] != 0 )
96
0
   || ( utf16_stream[ 14 ] != (uint8_t) '-' )
97
0
   || ( utf16_stream[ 15 ] != 0 )
98
0
   || ( utf16_stream[ 20 ] != (uint8_t) 'T' )
99
0
   || ( utf16_stream[ 21 ] != 0 )
100
0
   || ( utf16_stream[ 26 ] != (uint8_t) ':' )
101
0
   || ( utf16_stream[ 27 ] != 0 )
102
0
   || ( utf16_stream[ 32 ] != (uint8_t) ':' )
103
0
   || ( utf16_stream[ 33 ] != 0 )
104
0
   || ( utf16_stream[ 38 ] != (uint8_t) '.' )
105
0
   || ( utf16_stream[ 39 ] != 0 )
106
0
   || ( utf16_stream[ 58 ] != (uint8_t) 'Z' )
107
0
   || ( utf16_stream[ 59 ] != 0 )
108
0
   || ( utf16_stream[ 60 ] != 0 )
109
0
   || ( utf16_stream[ 61 ] != 0 ) )
110
0
  {
111
0
    libcerror_error_set(
112
0
     error,
113
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
114
0
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
115
0
     "%s: unsupported date time string.",
116
0
     function );
117
118
0
    return( -1 );
119
0
  }
120
0
  for( utf16_stream_offset = 0;
121
0
       utf16_stream_offset < 8;
122
0
       utf16_stream_offset += 2 )
123
0
  {
124
0
    if( ( utf16_stream[ utf16_stream_offset ] < (uint8_t) '0' )
125
0
     || ( utf16_stream[ utf16_stream_offset ] > (uint8_t) '9' ) )
126
0
    {
127
0
      libcerror_error_set(
128
0
       error,
129
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
130
0
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
131
0
       "%s: invalid date time string - unsupported year value.",
132
0
       function );
133
134
0
      return( -1 );
135
0
    }
136
0
    year *= 10;
137
0
    year += utf16_stream[ utf16_stream_offset ] - (uint8_t) '0';
138
0
  }
139
0
  for( utf16_stream_offset = 10;
140
0
       utf16_stream_offset < 14;
141
0
       utf16_stream_offset += 2 )
142
0
  {
143
0
    if( ( utf16_stream[ utf16_stream_offset ] < (uint8_t) '0' )
144
0
     || ( utf16_stream[ utf16_stream_offset ] > (uint8_t) '9' )
145
0
     || ( utf16_stream[ utf16_stream_offset + 1 ] != 0 ) )
146
0
    {
147
0
      libcerror_error_set(
148
0
       error,
149
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
150
0
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
151
0
       "%s: invalid date time string - unsupported month value.",
152
0
       function );
153
154
0
      return( -1 );
155
0
    }
156
0
    month *= 10;
157
0
    month += utf16_stream[ utf16_stream_offset ] - (uint8_t) '0';
158
0
  }
159
0
  for( utf16_stream_offset = 16;
160
0
       utf16_stream_offset < 20;
161
0
       utf16_stream_offset += 2 )
162
0
  {
163
0
    if( ( utf16_stream[ utf16_stream_offset ] < (uint8_t) '0' )
164
0
     || ( utf16_stream[ utf16_stream_offset ] > (uint8_t) '9' )
165
0
     || ( utf16_stream[ utf16_stream_offset + 1 ] != 0 ) )
166
0
    {
167
0
      libcerror_error_set(
168
0
       error,
169
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
170
0
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
171
0
       "%s: invalid date time string - unsupported day of month value.",
172
0
       function );
173
174
0
      return( -1 );
175
0
    }
176
0
    day_of_month *= 10;
177
0
    day_of_month += utf16_stream[ utf16_stream_offset ] - (uint8_t) '0';
178
0
  }
179
0
  for( utf16_stream_offset = 22;
180
0
       utf16_stream_offset < 26;
181
0
       utf16_stream_offset += 2 )
182
0
  {
183
0
    if( ( utf16_stream[ utf16_stream_offset ] < (uint8_t) '0' )
184
0
     || ( utf16_stream[ utf16_stream_offset ] > (uint8_t) '9' )
185
0
     || ( utf16_stream[ utf16_stream_offset + 1 ] != 0 ) )
186
0
    {
187
0
      libcerror_error_set(
188
0
       error,
189
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
190
0
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
191
0
       "%s: invalid date time string - unsupported hours value.",
192
0
       function );
193
194
0
      return( -1 );
195
0
    }
196
0
    hours *= 10;
197
0
    hours += utf16_stream[ utf16_stream_offset ] - (uint8_t) '0';
198
0
  }
199
0
  for( utf16_stream_offset = 28;
200
0
       utf16_stream_offset < 32;
201
0
       utf16_stream_offset += 2 )
202
0
  {
203
0
    if( ( utf16_stream[ utf16_stream_offset ] < (uint8_t) '0' )
204
0
     || ( utf16_stream[ utf16_stream_offset ] > (uint8_t) '9' )
205
0
     || ( utf16_stream[ utf16_stream_offset + 1 ] != 0 ) )
206
0
    {
207
0
      libcerror_error_set(
208
0
       error,
209
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
210
0
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
211
0
       "%s: invalid date time string - unsupported minutes value.",
212
0
       function );
213
214
0
      return( -1 );
215
0
    }
216
0
    minutes *= 10;
217
0
    minutes += utf16_stream[ utf16_stream_offset ] - (uint8_t) '0';
218
0
  }
219
0
  for( utf16_stream_offset = 34;
220
0
       utf16_stream_offset < 38;
221
0
       utf16_stream_offset += 2 )
222
0
  {
223
0
    if( ( utf16_stream[ utf16_stream_offset ] < (uint8_t) '0' )
224
0
     || ( utf16_stream[ utf16_stream_offset ] > (uint8_t) '9' )
225
0
     || ( utf16_stream[ utf16_stream_offset + 1 ] != 0 ) )
226
0
    {
227
0
      libcerror_error_set(
228
0
       error,
229
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
230
0
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
231
0
       "%s: invalid date time string - unsupported seconds value.",
232
0
       function );
233
234
0
      return( -1 );
235
0
    }
236
0
    seconds *= 10;
237
0
    seconds += utf16_stream[ utf16_stream_offset ] - (uint8_t) '0';
238
0
  }
239
0
  for( utf16_stream_offset = 40;
240
0
       utf16_stream_offset < 58;
241
0
       utf16_stream_offset += 2 )
242
0
  {
243
0
    if( ( utf16_stream[ utf16_stream_offset ] < (uint8_t) '0' )
244
0
     || ( utf16_stream[ utf16_stream_offset ] > (uint8_t) '9' )
245
0
     || ( utf16_stream[ utf16_stream_offset + 1 ] != 0 ) )
246
0
    {
247
0
      libcerror_error_set(
248
0
       error,
249
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
250
0
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
251
0
       "%s: invalid date time string - unsupported fraction of second value.",
252
0
       function );
253
254
0
      return( -1 );
255
0
    }
256
0
    fraction_of_second *= 10;
257
0
    fraction_of_second += utf16_stream[ utf16_stream_offset ] - (uint8_t) '0';
258
0
  }
259
0
  if( year < 1600 )
260
0
  {
261
0
    libcerror_error_set(
262
0
     error,
263
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
264
0
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
265
0
     "%s: unsupported year value: %" PRIu16 ".",
266
0
     function,
267
0
     year );
268
269
0
    return( -1 );
270
0
  }
271
0
  if( ( ( ( year % 4 ) == 0 )
272
0
    &&  ( ( year % 100 ) != 0 ) )
273
0
   || ( ( year % 400 ) == 0 ) )
274
0
  {
275
0
    is_leap_year = 1;
276
0
  }
277
0
  switch( month )
278
0
  {
279
0
    case 1:
280
0
    case 3:
281
0
    case 5:
282
0
    case 7:
283
0
    case 8:
284
0
    case 10:
285
0
    case 12:
286
0
      days_per_month = 31;
287
0
      break;
288
289
0
    case 2:
290
0
      if( is_leap_year != 0 )
291
0
      {
292
0
        days_per_month = 29;
293
0
      }
294
0
      else
295
0
      {
296
0
        days_per_month = 28;
297
0
      }
298
0
      break;
299
300
0
    case 4:
301
0
    case 6:
302
0
    case 9:
303
0
    case 11:
304
0
      days_per_month = 30;
305
0
      break;
306
307
0
    default:
308
0
      libcerror_error_set(
309
0
       error,
310
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
311
0
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
312
0
       "%s: unsupported month value: %" PRIu8 ".",
313
0
       function,
314
0
       month );
315
316
0
      return( -1 );
317
0
  }
318
0
  if( ( day_of_month < 1 )
319
0
   || ( day_of_month > days_per_month ) )
320
0
  {
321
0
    libcerror_error_set(
322
0
     error,
323
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
324
0
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
325
0
     "%s: unsupported day of month value: %" PRIu8 ".",
326
0
     function,
327
0
     day_of_month );
328
329
0
    return( -1 );
330
0
  }
331
0
  if( hours > 23 )
332
0
  {
333
0
    libcerror_error_set(
334
0
     error,
335
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
336
0
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
337
0
     "%s: unsupported hours value: %" PRIu8 ".",
338
0
     function,
339
0
     hours );
340
341
0
    return( -1 );
342
0
  }
343
0
  if( minutes > 59 )
344
0
  {
345
0
    libcerror_error_set(
346
0
     error,
347
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
348
0
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
349
0
     "%s: unsupported minutes value: %" PRIu8 ".",
350
0
     function,
351
0
     minutes );
352
353
0
    return( -1 );
354
0
  }
355
0
  if( seconds > 59 )
356
0
  {
357
0
    libcerror_error_set(
358
0
     error,
359
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
360
0
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
361
0
     "%s: unsupported seconds value: %" PRIu8 ".",
362
0
     function,
363
0
     seconds );
364
365
0
    return( -1 );
366
0
  }
367
0
  current_year = 1600;
368
369
0
  while( current_year < ( year - 100 ) )
370
0
  {
371
0
    if( ( current_year % 400 ) == 0 )
372
0
    {
373
0
      safe_filetime += 36525;
374
0
    }
375
0
    else
376
0
    {
377
0
      safe_filetime += 36524;
378
0
    }
379
0
    current_year += 100;
380
0
  }
381
0
  while( current_year < year )
382
0
  {
383
0
    if( ( ( ( current_year % 4 ) == 0 )
384
0
      &&  ( ( current_year % 100 ) != 0 ) )
385
0
     || ( ( current_year % 400 ) == 0 ) )
386
0
    {
387
0
      safe_filetime += 366;
388
0
    }
389
0
    else
390
0
    {
391
0
      safe_filetime += 365;
392
0
    }
393
0
    current_year += 1;
394
0
  }
395
  /* Do not include the number of days in 1600 */
396
0
  safe_filetime -= 366;
397
398
0
  switch( month )
399
0
  {
400
0
    case 12:
401
0
      safe_filetime += 30;
402
403
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
404
0
    case 11:
405
0
      safe_filetime += 31;
406
407
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
408
0
    case 10:
409
0
      safe_filetime += 30;
410
411
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
412
0
    case 9:
413
0
      safe_filetime += 31;
414
415
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
416
0
    case 8:
417
0
      safe_filetime += 31;
418
419
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
420
0
    case 7:
421
0
      safe_filetime += 30;
422
423
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
424
0
    case 6:
425
0
      safe_filetime += 31;
426
427
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
428
0
    case 5:
429
0
      safe_filetime += 30;
430
431
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
432
0
    case 4:
433
0
      safe_filetime += 31;
434
435
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
436
0
    case 3:
437
0
      if( is_leap_year != 0 )
438
0
      {
439
0
        safe_filetime += 29;
440
0
      }
441
0
      else
442
0
      {
443
0
        safe_filetime += 28;
444
0
      }
445
446
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
447
0
    case 2:
448
0
      safe_filetime += 31;
449
450
0
    LIBFWEVT_ATTRIBUTE_FALLTHROUGH;
451
0
    default:
452
0
      safe_filetime += day_of_month - 1;
453
0
  }
454
0
  safe_filetime *= 24;
455
0
  safe_filetime += hours;
456
0
  safe_filetime *= 60;
457
0
  safe_filetime += minutes;
458
0
  safe_filetime *= 60;
459
0
  safe_filetime += seconds;
460
0
  safe_filetime *= 1000000000;
461
0
  safe_filetime += fraction_of_second;
462
0
  safe_filetime /= 100;
463
464
0
  *filetime = safe_filetime;
465
466
0
  return( 1 );
467
0
}
468