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