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