/*++ /* NAME /* mail_version 3 /* SUMMARY /* time-dependent probe sender addresses /* SYNOPSIS /* #include /* /* 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 #include #include /* Utility library. */ #include #include #include #include /* Global library. */ #include /* 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 #include #include #include #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