/* * Copyright (c) 1998-2001, 2003 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * */ #include "sm/generic.h" SM_RCSID("@(#)$Id: arpadate.c,v 1.7 2006/10/06 02:51:35 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/time.h" #include "sm/str.h" /* ** ARPADATE -- Create date in RFC 2822 format ** ** Parameters: ** when -- time_t ** udate -- (pointer to) str for RFC2822 date field (output) ** ** Returns: ** pointer to an RFC 2822 date field */ #define MIN_per_HOUR 60 /* minutes in an hour */ #define MIN_per_DAY (24 * MIN_per_HOUR) /* minutes in a day */ #define SEC_per_MIN 60 /* seconds in a minute */ sm_ret_T arpadate(time_t *when, sm_str_P udate) { char *p; int off, i; #if MTA_USE_PTHREADS struct tm tm; #endif struct tm gmt; struct tm *lt; sm_ret_T ret; char *ud, ct[26]; /* ctime_r(3) */ #define APPCHAR(ch) \ do \ { \ if ((ret = sm_str_put(udate, (uchar) (ch))) != SM_SUCCESS) \ return ret; \ } while (0) #define APPCHARP \ do \ { \ APPCHAR(*p); \ p++; \ } while (0) /* ** Get current time. ** This will be used if a null argument is passed and ** to resolve the timezone. */ SM_REQUIRE(when != NULL); #if HAVE_CTIME_R # if SM_CTIME_R_API == 2 (void) ctime_r(when, ct); # elif SM_CTIME_R_API == 3 (void) ctime_r(when, ct, sizeof(ct)); # else OOPS: unknown ctime_r() API # endif ud = ct; #else /* HAVE_CTIME_R */ OOPS: missing ctime_r() write a replacement function in librepl/ #endif /* HAVE_CTIME_R */ /* ** Crack the UNIX date line in a singularly unoriginal way. */ p = &ud[0]; /* weekday (%a) */ APPCHARP; APPCHARP; APPCHARP; APPCHAR(','); APPCHAR(' '); p = &ud[8]; /* day of month (%e) */ APPCHARP; /* might be blank */ APPCHARP; APPCHAR(' '); p = &ud[4]; /* month (%b) */ APPCHARP; APPCHARP; APPCHARP; APPCHAR(' '); p = &ud[20]; /* year (%G) */ APPCHARP; APPCHARP; APPCHARP; APPCHARP; APPCHAR(' '); p = &ud[11]; /* HH:MM:SS */ for (i = 8; i > 0; i--) APPCHARP; /* ** Should really get the timezone from the time in "ud" (which ** is only different if a non-null arg was passed which is different ** from the current time), but for all practical purposes, returning ** the current local zone will do (it's all that is ever needed). */ gmt = *gmtime(when); #if MTA_USE_PTHREADS lt = localtime_r(when, &tm); #else /* MTA_USE_PTHREADS */ lt = localtime(when); #endif /* MTA_USE_PTHREADS */ off = (lt->tm_hour - gmt.tm_hour) * MIN_per_HOUR + lt->tm_min - gmt.tm_min; /* assume that offset isn't more than a day ... */ if (lt->tm_year < gmt.tm_year) off -= MIN_per_DAY; else if (lt->tm_year > gmt.tm_year) off += MIN_per_DAY; else if (lt->tm_yday < gmt.tm_yday) off -= MIN_per_DAY; else if (lt->tm_yday > gmt.tm_yday) off += MIN_per_DAY; /* seconds can be 0 - 60 now */ if (lt->tm_sec <= gmt.tm_sec - SEC_per_MIN) off -= 1; else if (lt->tm_sec >= gmt.tm_sec + SEC_per_MIN) off += 1; APPCHAR(' '); if (off == 0) { APPCHAR('+'); APPCHAR('0'); APPCHAR('0'); APPCHAR('0'); APPCHAR('0'); } else { if (off < 0) { off = -off; APPCHAR('-'); } else APPCHAR('+'); if (off >= 24*60) /* should be impossible */ off = 23*60+59; /* if not, insert silly value */ APPCHAR((off / 600) + '0'); APPCHAR((off / 60) % 10 + '0'); off %= 60; APPCHAR((off / 10) + '0'); APPCHAR((off % 10) + '0'); } /* APPCHAR('\0'); */ return SM_SUCCESS; }