diff options
Diffstat (limited to 'src/global/mail_version.c')
-rw-r--r-- | src/global/mail_version.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/src/global/mail_version.c b/src/global/mail_version.c new file mode 100644 index 0000000..0ded035 --- /dev/null +++ b/src/global/mail_version.c @@ -0,0 +1,258 @@ +/*++ +/* NAME +/* mail_version 3 +/* SUMMARY +/* time-dependent probe sender addresses +/* SYNOPSIS +/* #include <mail_version.h> +/* +/* typedef struct { +/* char *program; /* postfix */ +/* int major; /* 2 */ +/* int minor; /* 9 */ +/* int patch; /* patchlevel or -1 */ +/* char *snapshot; /* null or snapshot info */ +/* } MAIL_VERSION; +/* +/* MAIL_VERSION *mail_version_parse(version_string, why) +/* const char *version_string; +/* const char **why; +/* +/* void mail_version_free(mp) +/* MAIL_VERSION *mp; +/* +/* const char *get_mail_version() +/* +/* int check_mail_version(version_string) +/* const char *version_string; +/* DESCRIPTION +/* This module understands the format of Postfix version strings +/* (for example the default value of "mail_version"), and +/* provides support to compare the compile-time version of a +/* Postfix program with the run-time version of a Postfix +/* library. Apparently, some distributions don't use proper +/* so-number versioning, causing programs to fail erratically +/* after an update replaces the library but not the program. +/* +/* A Postfix version string consists of two or three parts +/* separated by a single "-" character: +/* .IP \(bu +/* The first part is a string with the program name. +/* .IP \(bu +/* The second part is the program version: either two or three +/* non-negative integer numbers separated by single "." +/* character. Stable releases have a major version, minor +/* version and patchlevel; experimental releases (snapshots) +/* have only major and minor version numbers. +/* .IP \(bu +/* The third part is ignored with a stable release, otherwise +/* it is a string with the snapshot release date plus some +/* optional information. +/* +/* mail_version_parse() parses a version string. +/* +/* get_mail_version() returns the version string (the value +/* of DEF_MAIL_VERSION) that is compiled into the library. +/* +/* check_mail_version() compares the caller's version string +/* (usually the value of DEF_MAIL_VERSION) that is compiled +/* into the caller, and logs a warning when the strings differ. +/* DIAGNOSTICS +/* In the case of a parsing error, mail_version_parse() returns +/* a null pointer, and sets the why argument to a string with +/* problem details. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <stdlib.h> +#include <errno.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <stringops.h> +#include <split_at.h> + +/* Global library. */ + +#include <mail_version.h> + +/* mail_version_int - convert integer */ + +static int mail_version_int(const char *strval) +{ + char *end; + int intval; + long longval; + + errno = 0; + intval = longval = strtol(strval, &end, 10); + if (*strval == 0 || *end != 0 || errno == ERANGE || longval != intval) + intval = (-1); + return (intval); +} + +/* mail_version_worker - do the parsing work */ + +static const char *mail_version_worker(MAIL_VERSION *mp, char *cp) +{ + char *major_field; + char *minor_field; + char *patch_field; + + /* + * Program name. + */ + if ((mp->program = mystrtok(&cp, "-")) == 0) + return ("no program name"); + + /* + * Major, minor, patchlevel. If this is a stable release, then we ignore + * text after the patchlevel, in case there are vendor extensions. + */ + if ((major_field = mystrtok(&cp, "-")) == 0) + return ("missing major version"); + + if ((minor_field = split_at(major_field, '.')) == 0) + return ("missing minor version"); + if ((mp->major = mail_version_int(major_field)) < 0) + return ("bad major version"); + patch_field = split_at(minor_field, '.'); + if ((mp->minor = mail_version_int(minor_field)) < 0) + return ("bad minor version"); + + if (patch_field == 0) + mp->patch = -1; + else if ((mp->patch = mail_version_int(patch_field)) < 0) + return ("bad patchlevel"); + + /* + * Experimental release. If this is not a stable release, we take + * everything to the end of the string. + */ + if (patch_field != 0) + mp->snapshot = 0; + else if ((mp->snapshot = mystrtok(&cp, "")) == 0) + return ("missing snapshot field"); + + return (0); +} + +/* mail_version_parse - driver */ + +MAIL_VERSION *mail_version_parse(const char *string, const char **why) +{ + MAIL_VERSION *mp; + char *saved_string; + const char *err; + + mp = (MAIL_VERSION *) mymalloc(sizeof(*mp)); + saved_string = mystrdup(string); + if ((err = mail_version_worker(mp, saved_string)) != 0) { + *why = err; + myfree(saved_string); + myfree((void *) mp); + return (0); + } else { + return (mp); + } +} + +/* mail_version_free - destroy version information */ + +void mail_version_free(MAIL_VERSION *mp) +{ + myfree(mp->program); + myfree((void *) mp); +} + +/* get_mail_version - return parsed mail version string */ + +const char *get_mail_version(void) +{ + return (DEF_MAIL_VERSION); +} + +/* check_mail_version - compare caller version with library version */ + +void check_mail_version(const char *version_string) +{ + if (strcmp(version_string, DEF_MAIL_VERSION) != 0) + msg_warn("Postfix library version mis-match: wanted %s, found %s", + version_string, DEF_MAIL_VERSION); +} + +#ifdef TEST + +#include <unistd.h> +#include <vstring.h> +#include <vstream.h> +#include <vstring_vstream.h> + +#define STR(x) vstring_str(x) + +/* parse_sample - parse a sample string from argv or stdin */ + +static void parse_sample(const char *sample) +{ + MAIL_VERSION *mp; + const char *why; + + mp = mail_version_parse(sample, &why); + if (mp == 0) { + vstream_printf("ERROR: %s: %s\n", sample, why); + } else { + vstream_printf("program: %s\t", mp->program); + vstream_printf("major: %d\t", mp->major); + vstream_printf("minor: %d\t", mp->minor); + if (mp->patch < 0) + vstream_printf("snapshot: %s\n", mp->snapshot); + else + vstream_printf("patch: %d\n", mp->patch); + mail_version_free(mp); + } + vstream_fflush(VSTREAM_OUT); +} + +/* main - the main program */ + +int main(int argc, char **argv) +{ + VSTRING *inbuf = vstring_alloc(1); + int have_tty = isatty(0); + + if (argc > 1) { + while (--argc > 0 && *++argv) + parse_sample(*argv); + } else { + for (;;) { + if (have_tty) { + vstream_printf("> "); + vstream_fflush(VSTREAM_OUT); + } + if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0) + break; + if (have_tty == 0) + vstream_printf("> %s\n", STR(inbuf)); + if (*STR(inbuf) == 0 || *STR(inbuf) == '#') + continue; + parse_sample(STR(inbuf)); + } + } + vstring_free(inbuf); + return (0); +} + +#endif |