Author: viethen Date: Thu Mar 22 14:02:15 2012 New Revision: 2944 Log: Add a little utility program needed within the shell scripts for the webif GUI. Added: trunk/pairing/util/ trunk/pairing/util/pisa-pairing-date.c Modified: trunk/Makefile.am trunk/pairing/README Modified: trunk/Makefile.am ============================================================================== --- trunk/Makefile.am Thu Mar 22 13:55:33 2012 (r2943) +++ trunk/Makefile.am Thu Mar 22 14:02:15 2012 (r2944) @@ -71,6 +71,7 @@ pairing-legacy/management \ pairing-legacy/passgen \ pairing-legacy/send \ + pairing/util/pisa-pairing-date \ pisacd/pisacdconf \ pisasd/pisasdconf \ test/checkhipd \ @@ -149,6 +150,8 @@ pairing_legacy_passgen_SOURCES = pairing-legacy/hash.c \ pairing-legacy/passgen.c +pairing_util_pisa_pairing_date = pairing/util/pisa-pairing-date.c + pisabeacon_pisabeacon_SOURCES = pisabeacon/pisabeacon.c pisacd_pisacd_SOURCES = pisacd/cdconf.c \ Modified: trunk/pairing/README ============================================================================== --- trunk/pairing/README Thu Mar 22 13:55:33 2012 (r2943) +++ trunk/pairing/README Thu Mar 22 14:02:15 2012 (r2944) @@ -11,7 +11,11 @@ webif contains a number of scripts in order to extend the webif GUI of a Trust Point with pairing functionality - +util contains a few small utility programs that will be called + by the shell scripts from the webif directory; these + utility programs were written in C, to provide + functionality that was hard to achieve with shell scripts + alone C. Viethen, August 2011 Added: trunk/pairing/util/pisa-pairing-date.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/pairing/util/pisa-pairing-date.c Thu Mar 22 14:02:15 2012 (r2944) @@ -0,0 +1,222 @@ +/** + * @file + * This program will verify a date string of the format yyyy-mm-dd for + * validity. It will indicate success or failure by an appropriate result + * code. + * + * If the given date is valid, and if an optional offset value is provided, + * this offset will be interpreted as a number of months. The date will be + * moved into the future by this indicated number of months. The "day" of + * the date will then be set to correctly indicate the last day of the + * respective calender month. The offset value must be between 0 and 99. + * + * A date thus modified will be output to stdout, in the same yyyy-mm-dd + * format. + * + * Examples: 2012-03-31, with offset 6, will yield 2012-09-30. + * 2012-02-29, with offset 12, will yield 2013-02-28. + * 2011-01-14, with offset 6, will yield 2011-07-31. + * 2012-06-03, with offset 0, will yield 2012-06-30. + * + * @brief verify and increment date strings in yyyy-mm-dd format. + * @author Christoph Viethen <christoph.viethen@xxxxxxxxxxxxxx> + */ +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#define __USE_MISC +#include <time.h> + +static void print_patched_date(char *input_date_string, char *offset_string) +{ + int offset; + char year_string[5] = { 0 }; + char month_string[3] = { 0 }; + int year, month; + struct tm first_of_following_month; + struct tm last_of_target_month; + time_t timestamp = 0; + + if (input_date_string == NULL || offset_string == NULL) { + return; + } + + if (sscanf(offset_string, "%d", &offset) != 1) { + return; + } + + /* extract year and month from the date string which was input */ + strncpy(year_string, input_date_string, 4); + strncpy(month_string, input_date_string + 5, 2); + + year = atoi(year_string); + month = atoi(month_string); + + /* add the offset plus one more month, yielding the month following + * the actual target ... */ + month += (offset + 1); + + if (month > 12) { + year += (month - 1) / 12; + month = ((month - 1) % 12) + 1; + } + + /* ... and express the first day of that following month */ + memset(&first_of_following_month, 0, sizeof(struct tm)); + first_of_following_month.tm_year = year - 1900; + first_of_following_month.tm_mon = month - 1; + first_of_following_month.tm_mday = 1; + first_of_following_month.tm_isdst = -1; + + /* Calculate the timestamp of that day, 00:00:00 GMT */ + timestamp = timegm(&first_of_following_month); + + if (timestamp == -1 || timestamp == 0) { + return; + } + + /* Go one second back, which should be equivalent to + * 23:59:59 GMT on the last day of the target month */ + timestamp--; + + if (gmtime_r(×tamp, &last_of_target_month) == NULL) { + return; + } + + printf("%04d-%02d-%02d\n", last_of_target_month.tm_year + 1900, last_of_target_month.tm_mon + 1, last_of_target_month.tm_mday); +} + +static bool verify_offset(const char *offset_string) +{ + int len = 0; + + if (offset_string == NULL) { + return false; + } + + /* allow one leading '+' */ + if (offset_string[0] == '+') { + offset_string++; + } + + /* safely verify the length - count up to 3 chars, so strings which + * are too long can be distinguished from strings which have the + * desirable length of 2 chars max */ + while (len < 3 && offset_string[len] != 0) { + len++; + } + + /* ensure offset string consists of no more than 2 (decimal) digits */ + if (len < 3 && len > 0) { + for (int i = 0; i < len; i++) { + if (offset_string[i] < '0' || offset_string[i] > '9') { + return false; + } + } + return true; + } + + return false; +} + +static bool verify_date_semantics(char *date_string) +{ + struct tm input_date; + int year, month, day; + + if (date_string == NULL) { + return false; + } + + if (sscanf(date_string, "%4d-%2d-%2d", &year, &month, &day) != 3) { + return false; + } + + memset(&input_date, 0, sizeof(struct tm)); + input_date.tm_year = year - 1900; + input_date.tm_mon = month - 1; + input_date.tm_mday = day; +// input_date.tm_isdst = -1; /* don't know whether DST in force or not */ + + /* Use timegm() to convert a broken-down date into a timestamp value - + * conveniently, timegm() will normalize the values in the tm structure + * so they make sense, e.g., a 35th of January will be converted to + * the 4th of February. (If the date is "too broken", for whatever + * that means, timegm() will return -1.) */ + if (timegm(&input_date) == -1) { + return false; + } + + /* find out whether normalization was performed - if so, the specified + * date contained semantic errors */ + if (year != input_date.tm_year + 1900 || month != input_date.tm_mon + 1 + || day != input_date.tm_mday) { + return false; + } + + return true; +} + +static bool verify_date_format(char *date_string) +{ + int i = 0; + char year_string[5] = { 0 }; + char month_string[3] = { 0 }; + char day_string[3] = { 0 }; + const char *digits = "0123456789"; + + if (date_string == NULL) { + return false; + } + + /* safely verify the length - count up to 11 chars, so strings which + * are too long can be distinguished from strings which have the + * desirable length of 10 chars */ + while (i < 11 && date_string[i] != 0) { + i++; + } + + if (i != 10) { + return false; + } + + /* verify that digits and hyphens are in the right places */ + if (date_string[4] != '-' || date_string[7] != '-') { + return false; + } + + strncpy(year_string, date_string, 4); + strncpy(month_string, date_string + 5, 2); + strncpy(day_string, date_string + 8, 2); + + if (strspn(year_string, digits) != 4 || strspn(month_string, digits) != 2 + || strspn(day_string, digits) != 2) { + return false; + } + + return true; +} + +int main(int argc, char *argv[]) +{ + int exit_status = EXIT_FAILURE; + + if (argc == 2 || argc == 3) { + if (verify_date_format(argv[1]) == true && + verify_date_semantics(argv[1]) == true) { + exit_status = EXIT_SUCCESS; + + if (argc == 3) { + if (verify_offset(argv[2]) == true) { + print_patched_date(argv[1], argv[2]); + } else { + exit_status = EXIT_FAILURE; + } + } + } + } + + exit(exit_status); +} -- This is the pisa developer mailing list. Please also subscribe to the main pisa list at: //www.freelists.org/list/pisa