xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/mail_version.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
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 
mail_version_int(const char * strval)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 
mail_version_worker(MAIL_VERSION * mp,char * cp)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 
mail_version_parse(const char * string,const char ** why)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 
mail_version_free(MAIL_VERSION * mp)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 
get_mail_version(void)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 
check_mail_version(const char * version_string)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 
parse_sample(const char * sample)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 
main(int argc,char ** argv)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