From 3e92235a777807db2ddb2bea75530fd868e368a7 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Wed, 27 Aug 2014 15:19:44 -0400
Subject: [PATCH] Ticket 47495 - admin express: wrong instance creation time

Bug Description:  Admin express attempted to convert a gmtime value
                  as if it was in the format: YYYYmmddHHMMSS

                  This gave the wrong value in Admin Express.

Fix Description:  Propeperly convert gmtime - reused the same functions
                  389-ds-base uses to convert a gmtime to a nice string.

https://fedorahosted.org/389/ticket/47495

Reviewed by: ?
---
 admserv/cgi-src40/viewdata.c | 130 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 110 insertions(+), 20 deletions(-)

diff --git a/admserv/cgi-src40/viewdata.c b/admserv/cgi-src40/viewdata.c
index 5cba168..a4043fd 100644
--- a/admserv/cgi-src40/viewdata.c
+++ b/admserv/cgi-src40/viewdata.c
@@ -266,9 +266,111 @@ int get_product_url(LDAP *server, char *sie, char **text, char **url) {
   return 1;
 }
 
+/*
+ * These functions: strntoul, read_genTime, and parse_genTime, are taken
+ * from 389-ds-base source (time.c)
+ */
+static unsigned long
+strntoul( char *from, size_t len, int base )
+{
+    unsigned long result;
+    char c = from[ len ];
 
-void output_data(LDAP *server, char *sie) {
+    from[ len ] = '\0';
+    result = strtoul( from, NULL, base );
+    from[ len ] = c;
+
+    return result;
+}
 
+static time_t
+read_genTime(struct berval *from)
+{
+    struct tm t;
+    time_t retTime;
+    time_t diffsec = 0;
+    int i, gflag = 0, havesec = 0;
+
+    memset (&t, 0, sizeof(t));
+    t.tm_isdst = -1;
+    t.tm_year = strntoul (from->bv_val     , 4, 10) - 1900L;
+    t.tm_mon  = strntoul (from->bv_val +  4, 2, 10) - 1;
+    t.tm_mday = strntoul (from->bv_val +  6, 2, 10);
+    t.tm_hour = strntoul (from->bv_val +  8, 2, 10);
+    t.tm_min  = strntoul (from->bv_val + 10, 2, 10);
+    i = 12;
+    /*
+     * the string can end with Z or -xxxx or +xxxx
+     * or before to have the Z/+/- we may have two digits for the seconds.
+     * If there's no Z/+/-, it means it's been expressed as local time
+     * (not standard).
+     */
+    while (from->bv_val[i]) {
+        switch (from->bv_val[i]) {
+        case 'Z':
+        case 'z':
+            gflag = 1;
+            ++i;
+            break;
+        case '+': /* Offset from GMT is on 4 digits */
+            i++;
+            diffsec -= strntoul (from->bv_val + i, 4, 10);
+            gflag = 1;
+            i += 4;
+            break;
+        case '-': /* Offset from GMT is on 4 digits */
+            i++;
+            diffsec += strntoul (from->bv_val + i, 4, 10);
+            gflag = 1;
+            i += 4;
+            break;
+        default:
+            if (havesec){
+                /* Ignore milliseconds */
+                i++;
+            } else {
+                t.tm_sec = strntoul (from->bv_val + i, 2, 10);
+                havesec = 1;
+                i += 2;
+            }
+        } /* end switch */
+    }
+    if (gflag){
+        PRTime pt;
+        PRExplodedTime expt = {0};
+        unsigned long year = strntoul (from->bv_val , 4, 10);
+
+        expt.tm_year = (PRInt16)year;
+        expt.tm_month = t.tm_mon;
+        expt.tm_mday = t.tm_mday;
+        expt.tm_hour = t.tm_hour;
+        expt.tm_min = t.tm_min;
+        expt.tm_sec = t.tm_sec;
+        /* This is a GMT time */
+        expt.tm_params.tp_gmt_offset = 0;
+        expt.tm_params.tp_dst_offset = 0;
+        /* PRTime is expressed in microseconds */
+        pt = PR_ImplodeTime(&expt) / 1000000L;
+        retTime = (time_t)pt;
+        return (retTime + diffsec);
+    } else {
+        return mktime (&t);
+    }
+}
+
+static time_t
+parse_genTime (char* from)
+{
+    struct berval tbv;
+    tbv.bv_val = from;
+    tbv.bv_len = strlen (from);
+
+    return read_genTime(&tbv);
+}
+
+void
+output_data(LDAP *server, char *sie)
+{
   LDAPMessage *entry;
   char **vals;
   int ldapError;
@@ -291,29 +393,17 @@ void output_data(LDAP *server, char *sie) {
 
 
   if((vals = util_ldap_get_values(server, entry, "installationtimestamp"))) {
-    struct tm tm = {0};
+    time_t parsed_time;
     char buf[BIG_LINE];
-    int rc;
-
-    /* only PARSE YYYYmmddHHMMSS */
-    rc = sscanf(vals[0], "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, 
-                &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+    char *inst_time;
 
-    if (rc < 6) {
-        PR_snprintf(buf, sizeof(buf), "Error: date [%s] not in YYYYmmddHHMMSS format", vals[0]);
+    if( (parsed_time = parse_genTime (vals[0])) &&
+        (inst_time = ctime_r(&parsed_time, buf)) )
+    {
+      PR_snprintf(buf, sizeof(buf), "%s %s", inst_time, daylight ? tzname[1] : tzname[0]);
     } else {
-        tm.tm_year -= 1900;  /* the number of years since 1900 */
-        tm.tm_mon  -= 1;     /* The number of month since January, in the range 0 to 11 */
-
-#ifdef LINUX
-        strftime(buf, BIG_LINE, "%b %d, %Y %T %p", &tm);
-        tzset();
-        PR_snprintf(buf, sizeof(buf), "%s %s", buf, daylight ? tzname[1] : tzname[0]);
-#else
-        strftime(buf, sizeof(buf), "%b %d, %Y %T %p %Z", &tm);
-#endif
+      PR_snprintf(buf, sizeof(buf), "Error: date [%s] not in correct format", vals[0]);
     }
-
     util_ldap_value_free(vals);
     vals = NULL;
     fprintf(stdout, (const char*)getResourceString(DBT_OUTPUT_DATA_DATE), buf);
-- 
1.9.3

