gpg: Fix bug parsing a zero length user id.
[gnupg.git] / common / gettime.c
index 1ccbbc6..788743f 100644 (file)
@@ -1,14 +1,24 @@
 /* gettime.c - Wrapper for time functions
- *     Copyright (C) 1998, 2002, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 2002, 2007, 2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
- * GnuPG is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either
  *
- * GnuPG is distributed in the hope that it will be useful,
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
@@ -31,7 +41,7 @@
 
 #ifdef HAVE_UNSIGNED_TIME_T
 # define IS_INVALID_TIME_T(a) ((a) == (time_t)(-1))
-#else 
+#else
   /* Error or 32 bit time_t and value after 2038-01-19.  */
 # define IS_INVALID_TIME_T(a) ((a) < 0)
 #endif
@@ -46,8 +56,8 @@ static enum { NORMAL = 0, FROZEN, FUTURE, PAST } timemode;
 
 /* Wrapper for the time(3).  We use this here so we can fake the time
    for tests */
-time_t 
-gnupg_get_time () 
+time_t
+gnupg_get_time ()
 {
   time_t current = time (NULL);
   if (timemode == NORMAL)
@@ -66,15 +76,15 @@ void
 gnupg_get_isotime (gnupg_isotime_t timebuf)
 {
   time_t atime = gnupg_get_time ();
-    
+
   if (atime == (time_t)(-1))
     *timebuf = 0;
-  else 
+  else
     {
       struct tm *tp;
 #ifdef HAVE_GMTIME_R
       struct tm tmbuf;
-      
+
       tp = gmtime_r (&atime, &tmbuf);
 #else
       tp = gmtime (&atime);
@@ -128,7 +138,7 @@ gnupg_faked_time_p (void)
 
 /* This function is used by gpg because OpenPGP defines the timestamp
    as an unsigned 32 bit value. */
-u32 
+u32
 make_timestamp (void)
 {
   time_t t = gnupg_get_time ();
@@ -179,29 +189,162 @@ scan_isodatestr( const char *string )
     return stamp;
 }
 
-/* Scan am ISO timestamp and return an Epoch based timestamp.  The only
-   supported format is "yyyymmddThhmmss" delimited by white space, nul, a
-   colon or a comma.  Returns (time_t)(-1) for an invalid string.  */
-time_t
-isotime2epoch (const char *string)
+
+int
+isotime_p (const char *string)
 {
   const char *s;
-  int year, month, day, hour, minu, sec;
-  struct tm tmbuf;
   int i;
 
   if (!*string)
-    return (time_t)(-1);
+    return 0;
   for (s=string, i=0; i < 8; i++, s++)
     if (!digitp (s))
-      return (time_t)(-1);
+      return 0;
   if (*s != 'T')
-      return (time_t)(-1);
+      return 0;
   for (s++, i=9; i < 15; i++, s++)
     if (!digitp (s))
-      return (time_t)(-1);
+      return 0;
   if ( !(!*s || (isascii (*s) && isspace(*s)) || *s == ':' || *s == ','))
-    return (time_t)(-1);  /* Wrong delimiter.  */
+    return 0;  /* Wrong delimiter.  */
+
+  return 1;
+}
+
+
+/* Scan a string and return true if the string represents the human
+   readable format of an ISO time.  This format is:
+      yyyy-mm-dd[ hh[:mm[:ss]]]
+   Scanning stops at the second space or at a comma.  */
+int
+isotime_human_p (const char *string)
+{
+  const char *s;
+  int i;
+
+  if (!*string)
+    return 0;
+  for (s=string, i=0; i < 4; i++, s++)
+    if (!digitp (s))
+      return 0;
+  if (*s != '-')
+    return 0;
+  s++;
+  if (!digitp (s) || !digitp (s+1) || s[2] != '-')
+    return 0;
+  i = atoi_2 (s);
+  if (i < 1 || i > 12)
+    return 0;
+  s += 3;
+  if (!digitp (s) || !digitp (s+1))
+    return 0;
+  i = atoi_2 (s);
+  if (i < 1 || i > 31)
+    return 0;
+  s += 2;
+  if (!*s || *s == ',')
+    return 1; /* Okay; only date given.  */
+  if (!spacep (s))
+    return 0;
+  s++;
+  if (spacep (s))
+    return 1; /* Okay, second space stops scanning.  */
+  if (!digitp (s) || !digitp (s+1))
+    return 0;
+  i = atoi_2 (s);
+  if (i < 0 || i > 23)
+    return 0;
+  s += 2;
+  if (!*s || *s == ',')
+    return 1; /* Okay; only date and hour given.  */
+  if (*s != ':')
+    return 0;
+  s++;
+  if (!digitp (s) || !digitp (s+1))
+    return 0;
+  i = atoi_2 (s);
+  if (i < 0 || i > 59)
+    return 0;
+  s += 2;
+  if (!*s || *s == ',')
+    return 1; /* Okay; only date, hour and minute given.  */
+  if (*s != ':')
+    return 0;
+  s++;
+  if (!digitp (s) || !digitp (s+1))
+    return 0;
+  i = atoi_2 (s);
+  if (i < 0 || i > 60)
+    return 0;
+  s += 2;
+  if (!*s || *s == ',' || spacep (s))
+    return 1; /* Okay; date, hour and minute and second given.  */
+
+  return 0; /* Unexpected delimiter.  */
+}
+
+/* Convert a standard isotime or a human readable variant into an
+   isotime structure.  The allowed formats are those described by
+   isotime_p and isotime_human_p.  The function returns 0 on failure
+   or the length of the scanned string on success.  */
+size_t
+string2isotime (gnupg_isotime_t atime, const char *string)
+{
+  gnupg_isotime_t dummyatime;
+
+  if (!atime)
+    atime = dummyatime;
+
+  atime[0] = 0;
+  if (isotime_p (string))
+    {
+      memcpy (atime, string, 15);
+      atime[15] = 0;
+      return 15;
+    }
+  if (!isotime_human_p (string))
+    return 0;
+  atime[0] = string[0];
+  atime[1] = string[1];
+  atime[2] = string[2];
+  atime[3] = string[3];
+  atime[4] = string[5];
+  atime[5] = string[6];
+  atime[6] = string[8];
+  atime[7] = string[9];
+  atime[8] = 'T';
+  memset (atime+9, '0', 6);
+  atime[15] = 0;
+  if (!spacep (string+10))
+    return 10;
+  if (spacep (string+11))
+    return 11; /* As per def, second space stops scanning.  */
+  atime[9] = string[11];
+  atime[10] = string[12];
+  if (string[13] != ':')
+    return 13;
+  atime[11] = string[14];
+  atime[12] = string[15];
+  if (string[16] != ':')
+    return 16;
+  atime[13] = string[17];
+  atime[14] = string[18];
+  return 19;
+}
+
+
+/* Scan an ISO timestamp and return an Epoch based timestamp.  The only
+   supported format is "yyyymmddThhmmss" delimited by white space, nul, a
+   colon or a comma.  Returns (time_t)(-1) for an invalid string.  */
+time_t
+isotime2epoch (const char *string)
+{
+  int year, month, day, hour, minu, sec;
+  struct tm tmbuf;
+
+  if (!isotime_p (string))
+    return (time_t)(-1);
 
   year  = atoi_4 (string);
   month = atoi_2 (string + 4);
@@ -233,12 +376,12 @@ epoch2isotime (gnupg_isotime_t timebuf, time_t atime)
 {
   if (atime == (time_t)(-1))
     *timebuf = 0;
-  else 
+  else
     {
       struct tm *tp;
 #ifdef HAVE_GMTIME_R
       struct tm tmbuf;
-      
+
       tp = gmtime_r (&atime, &tmbuf);
 #else
       tp = gmtime (&atime);
@@ -287,6 +430,45 @@ strtimevalue( u32 value )
 }
 
 
+
+/* Return a malloced string with the time elapsed between NOW and
+   SINCE.  May return NULL on error. */
+char *
+elapsed_time_string (time_t since, time_t now)
+{
+  char *result;
+  double diff;
+  unsigned long value;
+  unsigned int days, hours, minutes, seconds;
+
+  if (!now)
+    now = gnupg_get_time ();
+
+  diff = difftime (now, since);
+  if (diff < 0)
+    return xtrystrdup ("time-warp");
+
+  seconds = (unsigned long)diff % 60;
+  value = (unsigned long)(diff / 60);
+  minutes = value % 60;
+  value /= 60;
+  hours = value % 24;
+  value /= 24;
+  days = value % 365;
+
+  if (days)
+    result = xtryasprintf ("%ud%uh%um%us", days, hours, minutes, seconds);
+  else if (hours)
+    result = xtryasprintf ("%uh%um%us", hours, minutes, seconds);
+  else if (minutes)
+    result = xtryasprintf ("%um%us", minutes, seconds);
+  else
+    result = xtryasprintf ("%us", seconds);
+
+  return result;
+}
+
+
 /*
  * Note: this function returns GMT
  */
@@ -296,7 +478,7 @@ strtimestamp (u32 stamp)
   static char buffer[11+5];
   struct tm *tp;
   time_t atime = stamp;
-    
+
   if (IS_INVALID_TIME_T (atime))
     {
       strcpy (buffer, "????" "-??" "-??");
@@ -320,7 +502,7 @@ isotimestamp (u32 stamp)
   static char buffer[25+5];
   struct tm *tp;
   time_t atime = stamp;
-  
+
   if (IS_INVALID_TIME_T (atime))
     {
       strcpy (buffer, "????" "-??" "-??" " " "??" ":" "??" ":" "??");
@@ -364,7 +546,7 @@ asctimestamp (u32 stamp)
   /* NOTE: gcc -Wformat-noliteral will complain here.  I have found no
      way to suppress this warning.  */
   strftime (buffer, DIM(buffer)-1, fmt, tp);
-# elif defined(HAVE_W32CE_SYSTEM) 
+# elif defined(HAVE_W32CE_SYSTEM)
   /* tzset is not available but %Z nevertheless prints a default
      nonsense timezone ("WILDABBR").  Thus we don't print the time
      zone at all.  */
@@ -400,7 +582,7 @@ static int
 days_per_month (int y, int m)
 {
   int s;
-    
+
   switch(m)
     {
     case 1: case 3: case 5: case 7: case 8: case 10: case 12:
@@ -460,19 +642,19 @@ jd2date (unsigned long jd, int *year, int *month, int *day)
   m = (delta / 31) + 1;
   while( (delta = jd - date2jd (y, m, d)) > days_per_month (y,m))
     if (++m > 12)
-      { 
+      {
         m = 1;
         y++;
       }
 
   d = delta + 1 ;
   if (d > days_per_month (y, m))
-    { 
+    {
       d = 1;
       m++;
     }
   if (m > 12)
-    { 
+    {
       m = 1;
       y++;
     }
@@ -499,7 +681,7 @@ check_isotime (const gnupg_isotime_t atime)
 
   if (!*atime)
     return gpg_error (GPG_ERR_NO_VALUE);
-  
+
   for (s=atime, i=0; i < 8; i++, s++)
     if (!digitp (s))
       return gpg_error (GPG_ERR_INV_TIME);
@@ -566,7 +748,7 @@ add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds)
   sec   = atoi_2 (atime+13);
 
   if (year <= 1582) /* The julian date functions don't support this. */
-    return gpg_error (GPG_ERR_INV_VALUE); 
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   sec    += nseconds;
   minute += sec/60;
@@ -575,14 +757,14 @@ add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds)
   minute %= 60;
   ndays  = hour/24;
   hour   %= 24;
-  
+
   jd = date2jd (year, month, day) + ndays;
   jd2date (jd, &year, &month, &day);
 
   if (year > 9999 || month > 12 || day > 31
       || year < 0 || month < 1 || day < 1)
-    return gpg_error (GPG_ERR_INV_VALUE); 
-    
+    return gpg_error (GPG_ERR_INV_VALUE);
+
   snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d",
             year, month, day, hour, minute, sec);
   return 0;
@@ -611,15 +793,15 @@ add_days_to_isotime (gnupg_isotime_t atime, int ndays)
   sec   = atoi_2 (atime+13);
 
   if (year <= 1582) /* The julian date functions don't support this. */
-    return gpg_error (GPG_ERR_INV_VALUE); 
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   jd = date2jd (year, month, day) + ndays;
   jd2date (jd, &year, &month, &day);
 
   if (year > 9999 || month > 12 || day > 31
       || year < 0 || month < 1 || day < 1)
-    return gpg_error (GPG_ERR_INV_VALUE); 
-    
+    return gpg_error (GPG_ERR_INV_VALUE);
+
   snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d",
             year, month, day, hour, minute, sec);
   return 0;