Home | History | Annotate | Line # | Download | only in global
      1 /*	$NetBSD: mail_version.c,v 1.2 2017/02/14 01:16:45 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	mail_version 3
      6 /* SUMMARY
      7 /*	time-dependent probe sender addresses
      8 /* SYNOPSIS
      9 /*	#include <mail_version.h>
     10 /*
     11 /*	typedef struct {
     12 /*	    char   *program;		/* postfix */
     13 /*	    int     major;		/* 2 */
     14 /*	    int     minor;		/* 9 */
     15 /*	    int     patch;		/* patchlevel or -1 */
     16 /*	    char   *snapshot;		/* null or snapshot info */
     17 /*	} MAIL_VERSION;
     18 /*
     19 /*	MAIL_VERSION *mail_version_parse(version_string, why)
     20 /*	const char *version_string;
     21 /*	const char **why;
     22 /*
     23 /*	void	mail_version_free(mp)
     24 /*	MAIL_VERSION *mp;
     25 /*
     26 /*	const char *get_mail_version()
     27 /*
     28 /*	int	check_mail_version(version_string)
     29 /*	const char *version_string;
     30 /* DESCRIPTION
     31 /*	This module understands the format of Postfix version strings
     32 /*	(for example the default value of "mail_version"), and
     33 /*	provides support to compare the compile-time version of a
     34 /*	Postfix program with the run-time version of a Postfix
     35 /*	library. Apparently, some distributions don't use proper
     36 /*	so-number versioning, causing programs to fail erratically
     37 /*	after an update replaces the library but not the program.
     38 /*
     39 /*	A Postfix version string consists of two or three parts
     40 /*	separated by a single "-" character:
     41 /* .IP \(bu
     42 /*	The first part is a string with the program name.
     43 /* .IP \(bu
     44 /*	The second part is the program version: either two or three
     45 /*	non-negative integer numbers separated by single "."
     46 /*	character. Stable releases have a major version, minor
     47 /*	version and patchlevel; experimental releases (snapshots)
     48 /*	have only major and minor version numbers.
     49 /* .IP \(bu
     50 /*	The third part is ignored with a stable release, otherwise
     51 /*	it is a string with the snapshot release date plus some
     52 /*	optional information.
     53 /*
     54 /*	mail_version_parse() parses a version string.
     55 /*
     56 /*	get_mail_version() returns the version string (the value
     57 /*	of DEF_MAIL_VERSION) that is compiled into the library.
     58 /*
     59 /*	check_mail_version() compares the caller's version string
     60 /*	(usually the value of DEF_MAIL_VERSION) that is compiled
     61 /*	into the caller, and logs a warning when the strings differ.
     62 /* DIAGNOSTICS
     63 /*	In the case of a parsing error, mail_version_parse() returns
     64 /*	a null pointer, and sets the why argument to a string with
     65 /*	problem details.
     66 /* LICENSE
     67 /* .ad
     68 /* .fi
     69 /*	The Secure Mailer license must be distributed with this software.
     70 /* AUTHOR(S)
     71 /*	Wietse Venema
     72 /*	IBM T.J. Watson Research
     73 /*	P.O. Box 704
     74 /*	Yorktown Heights, NY 10598, USA
     75 /*--*/
     76 
     77 /* System library. */
     78 
     79 #include <sys_defs.h>
     80 #include <stdlib.h>
     81 #include <errno.h>
     82 
     83 /* Utility library. */
     84 
     85 #include <msg.h>
     86 #include <mymalloc.h>
     87 #include <stringops.h>
     88 #include <split_at.h>
     89 
     90 /* Global library. */
     91 
     92 #include <mail_version.h>
     93 
     94 /* mail_version_int - convert integer */
     95 
     96 static int mail_version_int(const char *strval)
     97 {
     98     char   *end;
     99     int     intval;
    100     long    longval;
    101 
    102     errno = 0;
    103     intval = longval = strtol(strval, &end, 10);
    104     if (*strval == 0 || *end != 0 || errno == ERANGE || longval != intval)
    105 	intval = (-1);
    106     return (intval);
    107 }
    108 
    109 /* mail_version_worker - do the parsing work */
    110 
    111 static const char *mail_version_worker(MAIL_VERSION *mp, char *cp)
    112 {
    113     char   *major_field;
    114     char   *minor_field;
    115     char   *patch_field;
    116 
    117     /*
    118      * Program name.
    119      */
    120     if ((mp->program = mystrtok(&cp, "-")) == 0)
    121 	return ("no program name");
    122 
    123     /*
    124      * Major, minor, patchlevel. If this is a stable release, then we ignore
    125      * text after the patchlevel, in case there are vendor extensions.
    126      */
    127     if ((major_field = mystrtok(&cp, "-")) == 0)
    128 	return ("missing major version");
    129 
    130     if ((minor_field = split_at(major_field, '.')) == 0)
    131 	return ("missing minor version");
    132     if ((mp->major = mail_version_int(major_field)) < 0)
    133 	return ("bad major version");
    134     patch_field = split_at(minor_field, '.');
    135     if ((mp->minor = mail_version_int(minor_field)) < 0)
    136 	return ("bad minor version");
    137 
    138     if (patch_field == 0)
    139 	mp->patch = -1;
    140     else if ((mp->patch = mail_version_int(patch_field)) < 0)
    141 	return ("bad patchlevel");
    142 
    143     /*
    144      * Experimental release. If this is not a stable release, we take
    145      * everything to the end of the string.
    146      */
    147     if (patch_field != 0)
    148 	mp->snapshot = 0;
    149     else if ((mp->snapshot = mystrtok(&cp, "")) == 0)
    150 	return ("missing snapshot field");
    151 
    152     return (0);
    153 }
    154 
    155 /* mail_version_parse - driver */
    156 
    157 MAIL_VERSION *mail_version_parse(const char *string, const char **why)
    158 {
    159     MAIL_VERSION *mp;
    160     char   *saved_string;
    161     const char *err;
    162 
    163     mp = (MAIL_VERSION *) mymalloc(sizeof(*mp));
    164     saved_string = mystrdup(string);
    165     if ((err = mail_version_worker(mp, saved_string)) != 0) {
    166 	*why = err;
    167 	myfree(saved_string);
    168 	myfree((void *) mp);
    169 	return (0);
    170     } else {
    171 	return (mp);
    172     }
    173 }
    174 
    175 /* mail_version_free - destroy version information */
    176 
    177 void    mail_version_free(MAIL_VERSION *mp)
    178 {
    179     myfree(mp->program);
    180     myfree((void *) mp);
    181 }
    182 
    183 /* get_mail_version - return parsed mail version string */
    184 
    185 const char *get_mail_version(void)
    186 {
    187     return (DEF_MAIL_VERSION);
    188 }
    189 
    190 /* check_mail_version - compare caller version with library version */
    191 
    192 void    check_mail_version(const char *version_string)
    193 {
    194     if (strcmp(version_string, DEF_MAIL_VERSION) != 0)
    195 	msg_warn("Postfix library version mis-match: wanted %s, found %s",
    196 		 version_string, DEF_MAIL_VERSION);
    197 }
    198 
    199 #ifdef TEST
    200 
    201 #include <unistd.h>
    202 #include <vstring.h>
    203 #include <vstream.h>
    204 #include <vstring_vstream.h>
    205 
    206 #define STR(x) vstring_str(x)
    207 
    208 /* parse_sample - parse a sample string from argv or stdin */
    209 
    210 static void parse_sample(const char *sample)
    211 {
    212     MAIL_VERSION *mp;
    213     const char *why;
    214 
    215     mp = mail_version_parse(sample, &why);
    216     if (mp == 0) {
    217 	vstream_printf("ERROR: %s: %s\n", sample, why);
    218     } else {
    219 	vstream_printf("program: %s\t", mp->program);
    220 	vstream_printf("major: %d\t", mp->major);
    221 	vstream_printf("minor: %d\t", mp->minor);
    222 	if (mp->patch < 0)
    223 	    vstream_printf("snapshot: %s\n", mp->snapshot);
    224 	else
    225 	    vstream_printf("patch: %d\n", mp->patch);
    226 	mail_version_free(mp);
    227     }
    228     vstream_fflush(VSTREAM_OUT);
    229 }
    230 
    231 /* main - the main program */
    232 
    233 int     main(int argc, char **argv)
    234 {
    235     VSTRING *inbuf = vstring_alloc(1);
    236     int     have_tty = isatty(0);
    237 
    238     if (argc > 1) {
    239 	while (--argc > 0 && *++argv)
    240 	    parse_sample(*argv);
    241     } else {
    242 	for (;;) {
    243 	    if (have_tty) {
    244 		vstream_printf("> ");
    245 		vstream_fflush(VSTREAM_OUT);
    246 	    }
    247 	    if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0)
    248 		break;
    249 	    if (have_tty == 0)
    250 		vstream_printf("> %s\n", STR(inbuf));
    251 	    if (*STR(inbuf) == 0 || *STR(inbuf) == '#')
    252 		continue;
    253 	    parse_sample(STR(inbuf));
    254 	}
    255     }
    256     vstring_free(inbuf);
    257     return (0);
    258 }
    259 
    260 #endif
    261