Coverage Report

Created: 2022-12-08 06:10

/src/gnupg/common/gettime.c
Line
Count
Source (jump to first uncovered line)
1
/* gettime.c - Wrapper for time functions
2
 * Copyright (C) 1998, 2002, 2007, 2011 Free Software Foundation, Inc.
3
 *
4
 * This file is part of GnuPG.
5
 *
6
 * This file is free software; you can redistribute it and/or modify
7
 * it under the terms of either
8
 *
9
 *   - the GNU Lesser General Public License as published by the Free
10
 *     Software Foundation; either version 3 of the License, or (at
11
 *     your option) any later version.
12
 *
13
 * or
14
 *
15
 *   - the GNU General Public License as published by the Free
16
 *     Software Foundation; either version 2 of the License, or (at
17
 *     your option) any later version.
18
 *
19
 * or both in parallel, as here.
20
 *
21
 * This file is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
28
 */
29
30
#include <config.h>
31
#include <stdlib.h>
32
#include <time.h>
33
#include <ctype.h>
34
#ifdef HAVE_LOCALE_H
35
#include <locale.h>
36
#endif
37
#ifdef HAVE_LANGINFO_H
38
#include <langinfo.h>
39
#endif
40
41
#include "util.h"
42
#include "i18n.h"
43
#include "gettime.h"
44
45
#ifdef HAVE_W32_SYSTEM
46
#include <windows.h>
47
#endif
48
49
#ifdef HAVE_UNSIGNED_TIME_T
50
# define IS_INVALID_TIME_T(a) ((a) == (time_t)(-1))
51
#else
52
  /* Error or 32 bit time_t and value after 2038-01-19.  */
53
62.0k
# define IS_INVALID_TIME_T(a) ((a) < 0)
54
#endif
55
56
57
static unsigned long timewarp;
58
static enum { NORMAL = 0, FROZEN, FUTURE, PAST } timemode;
59
60
/* Correction used to map to real Julian days. */
61
0
#define JD_DIFF 1721060L
62
63
64
/* Wrapper for the time(3).  We use this here so we can fake the time
65
   for tests */
66
time_t
67
gnupg_get_time (void)
68
60.4k
{
69
60.4k
  time_t current = time (NULL);
70
60.4k
  if (current == (time_t)(-1))
71
0
    log_fatal ("time() failed\n");
72
73
60.4k
  if (timemode == NORMAL)
74
60.4k
    return current;
75
0
  else if (timemode == FROZEN)
76
0
    return timewarp;
77
0
  else if (timemode == FUTURE)
78
0
    return current + timewarp;
79
0
  else
80
0
    return current - timewarp;
81
60.4k
}
82
83
84
/* Wrapper around gmtime_r.
85
86
   On systems without gmtime_r this implementation works within gnupg
87
   because we use only one thread a time.  FIXME: An independent
88
   library may use gmtime in one of its own thread (or via
89
   npth_enter/npth_leave) - in this case we run into a problem.  The
90
   solution would be to use a mutex here.  */
91
struct tm *
92
gnupg_gmtime (const time_t *timep, struct tm *result)
93
0
{
94
0
#ifdef HAVE_GMTIME_R
95
0
  return gmtime_r (timep, result);
96
#else
97
  struct tm *tp;
98
99
  tp = gmtime (timep);
100
  if (tp)
101
    memcpy (result, tp, sizeof *result);
102
  return tp;
103
#endif
104
0
}
105
106
107
/* Return the current time (possibly faked) in ISO format. */
108
void
109
gnupg_get_isotime (gnupg_isotime_t timebuf)
110
0
{
111
0
  time_t atime = gnupg_get_time ();
112
0
  struct tm *tp;
113
0
  struct tm tmbuf;
114
115
0
  tp = gnupg_gmtime (&atime, &tmbuf);
116
0
  if (!tp)
117
0
    *timebuf = 0;
118
0
  else
119
0
    snprintf (timebuf, 16, "%04d%02d%02dT%02d%02d%02d",
120
0
              1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday,
121
0
              tp->tm_hour, tp->tm_min, tp->tm_sec);
122
0
}
123
124
125
/* Set the time to NEWTIME so that gnupg_get_time returns a time
126
   starting with this one.  With FREEZE set to 1 the returned time
127
   will never change.  Just for completeness, a value of (time_t)-1
128
   for NEWTIME gets you back to reality.  Note that this is obviously
129
   not thread-safe but this is not required. */
130
void
131
gnupg_set_time (time_t newtime, int freeze)
132
0
{
133
0
  time_t current = time (NULL);
134
135
0
  if ( newtime == (time_t)-1 || current == newtime)
136
0
    {
137
0
      timemode = NORMAL;
138
0
      timewarp = 0;
139
0
    }
140
0
  else if (freeze)
141
0
    {
142
0
      timemode = FROZEN;
143
0
      timewarp = newtime == (time_t)-1 ? current : newtime;
144
0
    }
145
0
  else if (newtime > current)
146
0
    {
147
0
      timemode = FUTURE;
148
0
      timewarp = newtime - current;
149
0
    }
150
0
  else
151
0
    {
152
0
      timemode = PAST;
153
0
      timewarp = current - newtime;
154
0
    }
155
0
}
156
157
/* Returns true when we are in timewarp mode */
158
int
159
gnupg_faked_time_p (void)
160
0
{
161
0
  return timemode;
162
0
}
163
164
165
/* This function is used by gpg because OpenPGP defines the timestamp
166
   as an unsigned 32 bit value. */
167
u32
168
make_timestamp (void)
169
60.4k
{
170
60.4k
  time_t t = gnupg_get_time ();
171
60.4k
  return (u32)t;
172
60.4k
}
173
174
175
176
/****************
177
 * Scan a date string and return a timestamp.
178
 * The only supported format is "yyyy-mm-dd"
179
 * Returns 0 for an invalid date.
180
 */
181
u32
182
scan_isodatestr( const char *string )
183
0
{
184
0
    int year, month, day;
185
0
    struct tm tmbuf;
186
0
    time_t stamp;
187
0
    int i;
188
189
0
    if( strlen(string) != 10 || string[4] != '-' || string[7] != '-' )
190
0
  return 0;
191
0
    for( i=0; i < 4; i++ )
192
0
  if( !digitp (string+i) )
193
0
      return 0;
194
0
    if( !digitp (string+5) || !digitp(string+6) )
195
0
  return 0;
196
0
    if( !digitp(string+8) || !digitp(string+9) )
197
0
  return 0;
198
0
    year = atoi(string);
199
0
    month = atoi(string+5);
200
0
    day = atoi(string+8);
201
    /* some basic checks */
202
0
    if( year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 )
203
0
  return 0;
204
0
    memset( &tmbuf, 0, sizeof tmbuf );
205
0
    tmbuf.tm_mday = day;
206
0
    tmbuf.tm_mon = month-1;
207
0
    tmbuf.tm_year = year - 1900;
208
0
    tmbuf.tm_isdst = -1;
209
0
    stamp = mktime( &tmbuf );
210
0
    if( stamp == (time_t)-1 )
211
0
  return 0;
212
0
    return stamp;
213
0
}
214
215
216
int
217
isotime_p (const char *string)
218
0
{
219
0
  const char *s;
220
0
  int i;
221
222
0
  if (!*string)
223
0
    return 0;
224
0
  for (s=string, i=0; i < 8; i++, s++)
225
0
    if (!digitp (s))
226
0
      return 0;
227
0
  if (*s != 'T')
228
0
      return 0;
229
0
  for (s++, i=9; i < 15; i++, s++)
230
0
    if (!digitp (s))
231
0
      return 0;
232
0
  if (*s == 'Z')
233
0
    s++;
234
0
  if ( !(!*s || (isascii (*s) && isspace(*s)) || *s == ':' || *s == ','))
235
0
    return 0;  /* Wrong delimiter.  */
236
237
0
  return 1;
238
0
}
239
240
241
/* Scan a string and return true if the string represents the human
242
   readable format of an ISO time.  This format is:
243
      yyyy-mm-dd[ hh[:mm[:ss]]]
244
   Scanning stops at the second space or at a comma.  If DATE_ONLY is
245
   true the time part is not expected and the scanning stops at the
246
   first space or at a comma. */
247
int
248
isotime_human_p (const char *string, int date_only)
249
0
{
250
0
  const char *s;
251
0
  int i;
252
253
0
  if (!*string)
254
0
    return 0;
255
0
  for (s=string, i=0; i < 4; i++, s++)
256
0
    if (!digitp (s))
257
0
      return 0;
258
0
  if (*s != '-')
259
0
    return 0;
260
0
  s++;
261
0
  if (!digitp (s) || !digitp (s+1) || s[2] != '-')
262
0
    return 0;
263
0
  i = atoi_2 (s);
264
0
  if (i < 1 || i > 12)
265
0
    return 0;
266
0
  s += 3;
267
0
  if (!digitp (s) || !digitp (s+1))
268
0
    return 0;
269
0
  i = atoi_2 (s);
270
0
  if (i < 1 || i > 31)
271
0
    return 0;
272
0
  s += 2;
273
0
  if (!*s || *s == ',')
274
0
    return 1; /* Okay; only date given.  */
275
0
  if (!spacep (s))
276
0
    return 0;
277
0
  if (date_only)
278
0
    return 1; /* Okay; only date was requested.  */
279
0
  s++;
280
0
  if (spacep (s))
281
0
    return 1; /* Okay, second space stops scanning.  */
282
0
  if (!digitp (s) || !digitp (s+1))
283
0
    return 0;
284
0
  i = atoi_2 (s);
285
0
  if (i < 0 || i > 23)
286
0
    return 0;
287
0
  s += 2;
288
0
  if (!*s || *s == ',')
289
0
    return 1; /* Okay; only date and hour given.  */
290
0
  if (*s != ':')
291
0
    return 0;
292
0
  s++;
293
0
  if (!digitp (s) || !digitp (s+1))
294
0
    return 0;
295
0
  i = atoi_2 (s);
296
0
  if (i < 0 || i > 59)
297
0
    return 0;
298
0
  s += 2;
299
0
  if (!*s || *s == ',')
300
0
    return 1; /* Okay; only date, hour and minute given.  */
301
0
  if (*s != ':')
302
0
    return 0;
303
0
  s++;
304
0
  if (!digitp (s) || !digitp (s+1))
305
0
    return 0;
306
0
  i = atoi_2 (s);
307
0
  if (i < 0 || i > 60)
308
0
    return 0;
309
0
  s += 2;
310
0
  if (!*s || *s == ',' || spacep (s))
311
0
    return 1; /* Okay; date, hour and minute and second given.  */
312
313
0
  return 0; /* Unexpected delimiter.  */
314
0
}
315
316
/* Convert a standard isotime or a human readable variant into an
317
   isotime structure.  The allowed formats are those described by
318
   isotime_p and isotime_human_p.  The function returns 0 on failure
319
   or the length of the scanned string on success.  */
320
size_t
321
string2isotime (gnupg_isotime_t atime, const char *string)
322
0
{
323
0
  gnupg_isotime_t dummyatime;
324
325
0
  if (!atime)
326
0
    atime = dummyatime;
327
328
0
  atime[0] = 0;
329
0
  if (isotime_p (string))
330
0
    {
331
0
      memcpy (atime, string, 15);
332
0
      atime[15] = 0;
333
0
      return 15;
334
0
    }
335
0
  if (!isotime_human_p (string, 0))
336
0
    return 0;
337
0
  atime[0] = string[0];
338
0
  atime[1] = string[1];
339
0
  atime[2] = string[2];
340
0
  atime[3] = string[3];
341
0
  atime[4] = string[5];
342
0
  atime[5] = string[6];
343
0
  atime[6] = string[8];
344
0
  atime[7] = string[9];
345
0
  atime[8] = 'T';
346
0
  memset (atime+9, '0', 6);
347
0
  atime[15] = 0;
348
0
  if (!spacep (string+10))
349
0
    return 10;
350
0
  if (spacep (string+11))
351
0
    return 11; /* As per def, second space stops scanning.  */
352
0
  atime[9] = string[11];
353
0
  atime[10] = string[12];
354
0
  if (string[13] != ':')
355
0
    return 13;
356
0
  atime[11] = string[14];
357
0
  atime[12] = string[15];
358
0
  if (string[16] != ':')
359
0
    return 16;
360
0
  atime[13] = string[17];
361
0
  atime[14] = string[18];
362
0
  return 19;
363
0
}
364
365
366
/* Scan an ISO timestamp and return an Epoch based timestamp.  The
367
   only supported format is "yyyymmddThhmmss[Z]" delimited by white
368
   space, nul, a colon or a comma.  Returns (time_t)(-1) for an
369
   invalid string.  */
370
time_t
371
isotime2epoch (const char *string)
372
0
{
373
0
  int year, month, day, hour, minu, sec;
374
0
  struct tm tmbuf;
375
376
0
  if (!isotime_p (string))
377
0
    return (time_t)(-1);
378
379
0
  year  = atoi_4 (string);
380
0
  month = atoi_2 (string + 4);
381
0
  day   = atoi_2 (string + 6);
382
0
  hour  = atoi_2 (string + 9);
383
0
  minu  = atoi_2 (string + 11);
384
0
  sec   = atoi_2 (string + 13);
385
386
  /* Basic checks.  */
387
0
  if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31
388
0
      || hour > 23 || minu > 59 || sec > 61 )
389
0
    return (time_t)(-1);
390
391
0
  memset (&tmbuf, 0, sizeof tmbuf);
392
0
  tmbuf.tm_sec  = sec;
393
0
  tmbuf.tm_min  = minu;
394
0
  tmbuf.tm_hour = hour;
395
0
  tmbuf.tm_mday = day;
396
0
  tmbuf.tm_mon  = month-1;
397
0
  tmbuf.tm_year = year - 1900;
398
0
  tmbuf.tm_isdst = -1;
399
0
  return timegm (&tmbuf);
400
0
}
401
402
403
/* Convert an Epoch time to an iso time stamp. */
404
void
405
epoch2isotime (gnupg_isotime_t timebuf, time_t atime)
406
0
{
407
0
  if (atime == (time_t)(-1))
408
0
    *timebuf = 0;
409
0
  else
410
0
    {
411
0
      struct tm *tp;
412
0
#ifdef HAVE_GMTIME_R
413
0
      struct tm tmbuf;
414
415
0
      tp = gmtime_r (&atime, &tmbuf);
416
#else
417
      tp = gmtime (&atime);
418
#endif
419
0
      snprintf (timebuf, 16, "%04d%02d%02dT%02d%02d%02d",
420
0
                1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday,
421
0
                tp->tm_hour, tp->tm_min, tp->tm_sec);
422
0
    }
423
0
}
424
425
426
/* Parse a short ISO date string (YYYY-MM-DD) into a TM structure.
427
   Returns 0 on success.  */
428
int
429
isodate_human_to_tm (const char *string, struct tm *t)
430
0
{
431
0
  int year, month, day;
432
433
0
  if (!isotime_human_p (string, 1))
434
0
    return -1;
435
436
0
  year  = atoi_4 (string);
437
0
  month = atoi_2 (string + 5);
438
0
  day   = atoi_2 (string + 8);
439
440
  /* Basic checks.  */
441
0
  if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31)
442
0
    return -1;
443
444
0
  memset (t, 0, sizeof *t);
445
0
  t->tm_sec  = 0;
446
0
  t->tm_min  = 0;
447
0
  t->tm_hour = 0;
448
0
  t->tm_mday = day;
449
0
  t->tm_mon  = month-1;
450
0
  t->tm_year = year - 1900;
451
0
  t->tm_isdst = -1;
452
0
  return 0;
453
0
}
454
455
456
/* This function is a copy of gpgme/src/conversion.c:_gpgme_timegm.
457
   If you change it, then update the other one too.  */
458
#ifdef HAVE_W32_SYSTEM
459
static time_t
460
_win32_timegm (struct tm *tm)
461
{
462
  /* This one is thread safe.  */
463
  SYSTEMTIME st;
464
  FILETIME ft;
465
  unsigned long long cnsecs;
466
467
  st.wYear   = tm->tm_year + 1900;
468
  st.wMonth  = tm->tm_mon  + 1;
469
  st.wDay    = tm->tm_mday;
470
  st.wHour   = tm->tm_hour;
471
  st.wMinute = tm->tm_min;
472
  st.wSecond = tm->tm_sec;
473
  st.wMilliseconds = 0; /* Not available.  */
474
  st.wDayOfWeek = 0;    /* Ignored.  */
475
476
  /* System time is UTC thus the conversion is pretty easy.  */
477
  if (!SystemTimeToFileTime (&st, &ft))
478
    {
479
      gpg_err_set_errno (EINVAL);
480
      return (time_t)(-1);
481
    }
482
483
  cnsecs = (((unsigned long long)ft.dwHighDateTime << 32)
484
      | ft.dwLowDateTime);
485
  cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01.  */
486
  return (time_t)(cnsecs / 10000000ULL);
487
}
488
#endif
489
490
491
/* Parse the string TIMESTAMP into a time_t.  The string may either be
492
   seconds since Epoch or in the ISO 8601 format like
493
   "20390815T143012".  Returns 0 for an empty string or seconds since
494
   Epoch. Leading spaces are skipped. If ENDP is not NULL, it will
495
   point to the next non-parsed character in TIMESTRING.
496
497
   This function is a copy of
498
   gpgme/src/conversion.c:_gpgme_parse_timestamp.  If you change it,
499
   then update the other one too.  */
500
time_t
501
parse_timestamp (const char *timestamp, char **endp)
502
0
{
503
  /* Need to skip leading spaces, because that is what strtoul does
504
     but not our ISO 8601 checking code. */
505
0
  while (*timestamp && *timestamp== ' ')
506
0
    timestamp++;
507
0
  if (!*timestamp)
508
0
    return 0;
509
510
0
  if (strlen (timestamp) >= 15 && timestamp[8] == 'T')
511
0
    {
512
0
      struct tm buf;
513
0
      int year;
514
515
0
      year = atoi_4 (timestamp);
516
0
      if (year < 1900)
517
0
        return (time_t)(-1);
518
519
0
      if (endp)
520
0
        *endp = (char*)(timestamp + 15);
521
522
      /* Fixme: We would better use a configure test to see whether
523
         mktime can handle dates beyond 2038. */
524
0
      if (sizeof (time_t) <= 4 && year >= 2038)
525
0
        return (time_t)2145914603; /* 2037-12-31 23:23:23 */
526
527
0
      memset (&buf, 0, sizeof buf);
528
0
      buf.tm_year = year - 1900;
529
0
      buf.tm_mon = atoi_2 (timestamp+4) - 1;
530
0
      buf.tm_mday = atoi_2 (timestamp+6);
531
0
      buf.tm_hour = atoi_2 (timestamp+9);
532
0
      buf.tm_min = atoi_2 (timestamp+11);
533
0
      buf.tm_sec = atoi_2 (timestamp+13);
534
535
#ifdef HAVE_W32_SYSTEM
536
      return _win32_timegm (&buf);
537
#else
538
0
#ifdef HAVE_TIMEGM
539
0
      return timegm (&buf);
540
#else
541
      {
542
        time_t tim;
543
544
        putenv ("TZ=UTC");
545
        tim = mktime (&buf);
546
#ifdef __GNUC__
547
#warning fixme: we must somehow reset TZ here.  It is not threadsafe anyway.
548
#endif
549
        return tim;
550
      }
551
#endif /* !HAVE_TIMEGM */
552
0
#endif /* !HAVE_W32_SYSTEM */
553
0
    }
554
0
  else
555
0
    return (time_t)strtoul (timestamp, endp, 10);
556
0
}
557
558
559
560
u32
561
add_days_to_timestamp( u32 stamp, u16 days )
562
0
{
563
0
    return stamp + days*86400L;
564
0
}
565
566
567
/****************
568
 * Return a string with a time value in the form: x Y, n D, n H
569
 */
570
571
const char *
572
strtimevalue( u32 value )
573
1.19k
{
574
1.19k
    static char buffer[30];
575
1.19k
    unsigned int years, days, hours, minutes;
576
577
1.19k
    value /= 60;
578
1.19k
    minutes = value % 60;
579
1.19k
    value /= 60;
580
1.19k
    hours = value % 24;
581
1.19k
    value /= 24;
582
1.19k
    days = value % 365;
583
1.19k
    value /= 365;
584
1.19k
    years = value;
585
586
1.19k
    sprintf(buffer,"%uy%ud%uh%um", years, days, hours, minutes );
587
1.19k
    if( years )
588
624
  return buffer;
589
572
    if( days )
590
233
  return strchr( buffer, 'y' ) + 1;
591
339
    return strchr( buffer, 'd' ) + 1;
592
572
}
593
594
595
596
/* Return a malloced string with the time elapsed between NOW and
597
   SINCE.  May return NULL on error. */
598
char *
599
elapsed_time_string (time_t since, time_t now)
600
0
{
601
0
  char *result;
602
0
  double diff;
603
0
  unsigned long value;
604
0
  unsigned int days, hours, minutes, seconds;
605
606
0
  if (!now)
607
0
    now = gnupg_get_time ();
608
609
0
  diff = difftime (now, since);
610
0
  if (diff < 0)
611
0
    return xtrystrdup ("time-warp");
612
613
0
  seconds = (unsigned long)diff % 60;
614
0
  value = (unsigned long)(diff / 60);
615
0
  minutes = value % 60;
616
0
  value /= 60;
617
0
  hours = value % 24;
618
0
  value /= 24;
619
0
  days = value % 365;
620
621
0
  if (days)
622
0
    result = xtryasprintf ("%ud%uh%um%us", days, hours, minutes, seconds);
623
0
  else if (hours)
624
0
    result = xtryasprintf ("%uh%um%us", hours, minutes, seconds);
625
0
  else if (minutes)
626
0
    result = xtryasprintf ("%um%us", minutes, seconds);
627
0
  else
628
0
    result = xtryasprintf ("%us", seconds);
629
630
0
  return result;
631
0
}
632
633
634
/*
635
 * Note: this function returns GMT
636
 */
637
const char *
638
strtimestamp (u32 stamp)
639
474
{
640
474
  static char buffer[11+5];
641
474
  struct tm *tp;
642
474
  time_t atime = stamp;
643
644
474
  if (IS_INVALID_TIME_T (atime))
645
0
    {
646
0
      strcpy (buffer, "????" "-??" "-??");
647
0
    }
648
474
  else
649
474
    {
650
474
      tp = gmtime( &atime );
651
474
      snprintf (buffer, sizeof buffer, "%04d-%02d-%02d",
652
474
                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
653
474
    }
654
474
  return buffer;
655
474
}
656
657
658
/*
659
 * Note: this function returns GMT
660
 */
661
const char *
662
isotimestamp (u32 stamp)
663
1.46k
{
664
1.46k
  static char buffer[25+5];
665
1.46k
  struct tm *tp;
666
1.46k
  time_t atime = stamp;
667
668
1.46k
  if (IS_INVALID_TIME_T (atime))
669
0
    {
670
0
      strcpy (buffer, "????" "-??" "-??" " " "??" ":" "??" ":" "??");
671
0
    }
672
1.46k
  else
673
1.46k
    {
674
1.46k
      tp = gmtime ( &atime );
675
1.46k
      snprintf (buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d",
676
1.46k
                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
677
1.46k
                tp->tm_hour, tp->tm_min, tp->tm_sec);
678
1.46k
    }
679
1.46k
  return buffer;
680
1.46k
}
681
682
683
/****************
684
 * Note: this function returns local time
685
 */
686
const char *
687
asctimestamp (u32 stamp)
688
60.0k
{
689
60.0k
  static char buffer[80];
690
60.0k
#if defined (HAVE_STRFTIME) && defined (HAVE_NL_LANGINFO)
691
60.0k
  static char fmt[80];
692
60.0k
#endif
693
60.0k
  struct tm *tp;
694
60.0k
  time_t atime = stamp;
695
696
60.0k
  if (IS_INVALID_TIME_T (atime))
697
0
    {
698
0
      strcpy (buffer, "????" "-??" "-??");
699
0
      return buffer;
700
0
    }
701
702
60.0k
  tp = localtime( &atime );
703
60.0k
#ifdef HAVE_STRFTIME
704
60.0k
# if defined(HAVE_NL_LANGINFO)
705
60.0k
  mem2str( fmt, nl_langinfo(D_T_FMT), DIM(fmt)-3 );
706
60.0k
  if (!strstr( fmt, "%Z" ))
707
60.0k
    strcat( fmt, " %Z");
708
  /* NOTE: gcc -Wformat-noliteral will complain here.  I have found no
709
     way to suppress this warning.  */
710
60.0k
  strftime (buffer, DIM(buffer)-1, fmt, tp);
711
# else
712
#  if HAVE_W32_SYSTEM
713
  {
714
    static int done;
715
716
    if (!done)
717
      {
718
        /* The locale names as used by Windows are in the form
719
         * "German_Germany.1252" or "German_Austria.1252" with
720
         * alternate names similar to Unix, e.g. "de-DE".  However
721
         * that is the theory.  On current Windows and Mingw the
722
         * alternate names do not work.  We would need a table to map
723
         * them from the short names as provided by gpgrt to the long
724
         * names and append some code page.  For now we use "" and
725
         * take the locale from the user's system settings.  Thus the
726
         * standard Unix envvars don't work for time and may mismatch
727
         * with the string translations.  The new UCRT available since
728
         * 2018 has a lot of additional support but that will for sure
729
         * break other things.  We should move to ISO strings to get
730
         * rid of such problems.  */
731
        setlocale (LC_TIME, "");
732
        done = 1;
733
        /* log_debug ("LC_ALL  now '%s'\n", setlocale (LC_ALL, NULL)); */
734
        /* log_debug ("LC_TIME now '%s'\n", setlocale (LC_TIME, NULL)); */
735
      }
736
  }
737
#  endif
738
   /* FIXME: we should check whether the locale appends a " %Z" These
739
    * locales from glibc don't put the " %Z": fi_FI hr_HR ja_JP lt_LT
740
    * lv_LV POSIX ru_RU ru_SU sv_FI sv_SE zh_CN.  */
741
  strftime (buffer, DIM(buffer)-1, "%c %Z", tp);
742
# endif
743
60.0k
  buffer[DIM(buffer)-1] = 0;
744
#else
745
  mem2str( buffer, asctime(tp), DIM(buffer) );
746
#endif
747
60.0k
  return buffer;
748
60.0k
}
749
750
751
/* Return the timestamp STAMP in RFC-2822 format.  This is always done
752
 * in the C locale.  We return the gmtime to avoid computing the
753
 * timezone. The caller must release the returned string.
754
 *
755
 * Example: "Mon, 27 Jun 2016 1:42:00 +0000".
756
 */
757
char *
758
rfctimestamp (u32 stamp)
759
0
{
760
0
  time_t atime = stamp;
761
0
  struct tm tmbuf, *tp;
762
763
764
0
  if (IS_INVALID_TIME_T (atime))
765
0
    {
766
0
      gpg_err_set_errno (EINVAL);
767
0
      return NULL;
768
0
    }
769
770
0
  tp = gnupg_gmtime (&atime, &tmbuf);
771
0
  if (!tp)
772
0
    return NULL;
773
0
  return xtryasprintf ("%.3s, %02d %.3s %04d %02d:%02d:%02d +0000",
774
0
                       &"SunMonTueWedThuFriSat"[(tp->tm_wday%7)*3],
775
0
                       tp->tm_mday,
776
0
                       &"JanFebMarAprMayJunJulAugSepOctNovDec"
777
0
                       [(tp->tm_mon%12)*3],
778
0
                       tp->tm_year + 1900,
779
0
                       tp->tm_hour,
780
0
                       tp->tm_min,
781
0
                       tp->tm_sec);
782
0
}
783
784
785
static int
786
days_per_year (int y)
787
0
{
788
0
  int s ;
789
790
0
  s = !(y % 4);
791
0
  if ( !(y % 100))
792
0
    if ((y%400))
793
0
      s = 0;
794
0
  return s ? 366 : 365;
795
0
}
796
797
static int
798
days_per_month (int y, int m)
799
0
{
800
0
  int s;
801
802
0
  switch(m)
803
0
    {
804
0
    case 1: case 3: case 5: case 7: case 8: case 10: case 12:
805
0
      return 31 ;
806
0
    case 2:
807
0
      s = !(y % 4);
808
0
      if (!(y % 100))
809
0
        if ((y % 400))
810
0
          s = 0;
811
0
      return s? 29 : 28 ;
812
0
    case 4: case 6: case 9: case 11:
813
0
      return 30;
814
0
    }
815
0
  BUG();
816
0
}
817
818
819
/* Convert YEAR, MONTH and DAY into the Julian date.  We assume that
820
   it is already noon.  We do not support dates before 1582-10-15. */
821
static unsigned long
822
date2jd (int year, int month, int day)
823
0
{
824
0
  unsigned long jd;
825
826
0
  jd = 365L * year + 31 * (month-1) + day + JD_DIFF;
827
0
  if (month < 3)
828
0
    year-- ;
829
0
  else
830
0
    jd -= (4 * month + 23) / 10;
831
832
0
  jd += year / 4 - ((year / 100 + 1) *3) / 4;
833
834
0
  return jd ;
835
0
}
836
837
/* Convert a Julian date back to YEAR, MONTH and DAY.  Return day of
838
   the year or 0 on error.  This function uses some more or less
839
   arbitrary limits, most important is that days before 1582 are not
840
   supported. */
841
static int
842
jd2date (unsigned long jd, int *year, int *month, int *day)
843
0
{
844
0
  int y, m, d;
845
0
  long delta;
846
847
0
  if (!jd)
848
0
    return 0 ;
849
0
  if (jd < 1721425 || jd > 2843085)
850
0
    return 0;
851
852
0
  y = (jd - JD_DIFF) / 366;
853
0
  d = m = 1;
854
855
0
  while ((delta = jd - date2jd (y, m, d)) > days_per_year (y))
856
0
    y++;
857
858
0
  m = (delta / 31) + 1;
859
0
  while( (delta = jd - date2jd (y, m, d)) > days_per_month (y,m))
860
0
    if (++m > 12)
861
0
      {
862
0
        m = 1;
863
0
        y++;
864
0
      }
865
866
0
  d = delta + 1 ;
867
0
  if (d > days_per_month (y, m))
868
0
    {
869
0
      d = 1;
870
0
      m++;
871
0
    }
872
0
  if (m > 12)
873
0
    {
874
0
      m = 1;
875
0
      y++;
876
0
    }
877
878
0
  if (year)
879
0
    *year = y;
880
0
  if (month)
881
0
    *month = m;
882
0
  if (day)
883
0
    *day = d ;
884
885
0
  return (jd - date2jd (y, 1, 1)) + 1;
886
0
}
887
888
889
/* Check that the 15 bytes in ATIME represent a valid ISO time.  Note
890
   that this function does not expect a string but a plain 15 byte
891
   isotime buffer. */
892
gpg_error_t
893
check_isotime (const gnupg_isotime_t atime)
894
0
{
895
0
  int i;
896
0
  const char *s;
897
898
0
  if (!*atime)
899
0
    return gpg_error (GPG_ERR_NO_VALUE);
900
901
0
  for (s=atime, i=0; i < 8; i++, s++)
902
0
    if (!digitp (s))
903
0
      return gpg_error (GPG_ERR_INV_TIME);
904
0
  if (*s != 'T')
905
0
      return gpg_error (GPG_ERR_INV_TIME);
906
0
  for (s++, i=9; i < 15; i++, s++)
907
0
    if (!digitp (s))
908
0
      return gpg_error (GPG_ERR_INV_TIME);
909
0
  return 0;
910
0
}
911
912
913
/* Dump the ISO time T to the log stream without a LF.  */
914
void
915
dump_isotime (const gnupg_isotime_t t)
916
0
{
917
0
  if (!t || !*t)
918
0
    log_printf ("%s", _("[none]"));
919
0
  else
920
0
    log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s",
921
0
                t, t+4, t+6, t+9, t+11, t+13);
922
0
}
923
924
925
/* Copy one ISO date to another, this is inline so that we can do a
926
   minimal sanity check.  A null date (empty string) is allowed.  */
927
void
928
gnupg_copy_time (gnupg_isotime_t d, const gnupg_isotime_t s)
929
0
{
930
0
  if (*s)
931
0
    {
932
0
      if ((strlen (s) != 15 || s[8] != 'T'))
933
0
        BUG();
934
0
      memcpy (d, s, 15);
935
0
      d[15] = 0;
936
0
    }
937
0
  else
938
0
    *d = 0;
939
0
}
940
941
942
/* Add SECONDS to ATIME.  SECONDS may not be negative and is limited
943
   to about the equivalent of 62 years which should be more then
944
   enough for our purposes. */
945
gpg_error_t
946
add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds)
947
0
{
948
0
  gpg_error_t err;
949
0
  int year, month, day, hour, minute, sec, ndays;
950
0
  unsigned long jd;
951
952
0
  err = check_isotime (atime);
953
0
  if (err)
954
0
    return err;
955
956
0
  if (nseconds < 0 || nseconds >= (0x7fffffff - 61) )
957
0
    return gpg_error (GPG_ERR_INV_VALUE);
958
959
0
  year  = atoi_4 (atime+0);
960
0
  month = atoi_2 (atime+4);
961
0
  day   = atoi_2 (atime+6);
962
0
  hour  = atoi_2 (atime+9);
963
0
  minute= atoi_2 (atime+11);
964
0
  sec   = atoi_2 (atime+13);
965
966
0
  if (year <= 1582) /* The julian date functions don't support this. */
967
0
    return gpg_error (GPG_ERR_INV_VALUE);
968
969
0
  sec    += nseconds;
970
0
  minute += sec/60;
971
0
  sec    %= 60;
972
0
  hour   += minute/60;
973
0
  minute %= 60;
974
0
  ndays  = hour/24;
975
0
  hour   %= 24;
976
977
0
  jd = date2jd (year, month, day) + ndays;
978
0
  jd2date (jd, &year, &month, &day);
979
980
0
  if (year > 9999 || month > 12 || day > 31
981
0
      || year < 0 || month < 1 || day < 1)
982
0
    return gpg_error (GPG_ERR_INV_VALUE);
983
984
0
  snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d",
985
0
            year, month, day, hour, minute, sec);
986
0
  return 0;
987
0
}
988
989
990
gpg_error_t
991
add_days_to_isotime (gnupg_isotime_t atime, int ndays)
992
0
{
993
0
  gpg_error_t err;
994
0
  int year, month, day, hour, minute, sec;
995
0
  unsigned long jd;
996
997
0
  err = check_isotime (atime);
998
0
  if (err)
999
0
    return err;
1000
1001
0
  if (ndays < 0 || ndays >= 9999*366 )
1002
0
    return gpg_error (GPG_ERR_INV_VALUE);
1003
1004
0
  year  = atoi_4 (atime+0);
1005
0
  month = atoi_2 (atime+4);
1006
0
  day   = atoi_2 (atime+6);
1007
0
  hour  = atoi_2 (atime+9);
1008
0
  minute= atoi_2 (atime+11);
1009
0
  sec   = atoi_2 (atime+13);
1010
1011
0
  if (year <= 1582) /* The julian date functions don't support this. */
1012
0
    return gpg_error (GPG_ERR_INV_VALUE);
1013
1014
0
  jd = date2jd (year, month, day) + ndays;
1015
0
  jd2date (jd, &year, &month, &day);
1016
1017
0
  if (year > 9999 || month > 12 || day > 31
1018
0
      || year < 0 || month < 1 || day < 1)
1019
0
    return gpg_error (GPG_ERR_INV_VALUE);
1020
1021
0
  snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d",
1022
0
            year, month, day, hour, minute, sec);
1023
0
  return 0;
1024
0
}