xref: /netbsd-src/external/ibm-public/postfix/dist/src/postconf/postconf_edit.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: postconf_edit.c,v 1.1.1.2 2013/09/25 19:06:33 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	postconf_edit 3
6 /* SUMMARY
7 /*	edit main.cf
8 /* SYNOPSIS
9 /*	#include <postconf.h>
10 /*
11 /*	void	edit_parameters(mode, argc, argv)
12 /*	int	mode;
13 /*	int	argc;
14 /*	char	**argv;
15 /* DESCRIPTION
16 /*	Edit the \fBmain.cf\fR configuration file, and update
17 /*	parameter settings with the "\fIname\fR=\fIvalue\fR" pairs
18 /*	given on the command line. The file is copied to a temporary
19 /*	file then renamed into place.
20 /* DIAGNOSTICS
21 /*	Problems are reported to the standard error stream.
22 /* FILES
23 /*	/etc/postfix/main.cf, Postfix configuration parameters
24 /*	/etc/postfix/main.cf.tmp, temporary name
25 /* LICENSE
26 /* .ad
27 /* .fi
28 /*	The Secure Mailer license must be distributed with this software.
29 /* AUTHOR(S)
30 /*	Wietse Venema
31 /*	IBM T.J. Watson Research
32 /*	P.O. Box 704
33 /*	Yorktown Heights, NY 10598, USA
34 /*--*/
35 
36 /* System library. */
37 
38 #include <sys_defs.h>
39 #include <string.h>
40 #include <ctype.h>
41 
42 /* Utility library. */
43 
44 #include <msg.h>
45 #include <mymalloc.h>
46 #include <htable.h>
47 #include <vstring.h>
48 #include <vstring_vstream.h>
49 #include <edit_file.h>
50 #include <readlline.h>
51 #include <stringops.h>
52 
53 /* Global library. */
54 
55 #include <mail_params.h>
56 
57 /* Application-specific. */
58 
59 #include <postconf.h>
60 
61 #define STR(x) vstring_str(x)
62 
63 /* edit_parameters - edit parameter file */
64 
65 void    edit_parameters(int mode, int argc, char **argv)
66 {
67     char   *path;
68     EDIT_FILE *ep;
69     VSTREAM *src;
70     VSTREAM *dst;
71     VSTRING *buf = vstring_alloc(100);
72     VSTRING *key = vstring_alloc(10);
73     char   *cp;
74     char   *edit_key;
75     char   *edit_val;
76     HTABLE *table;
77     struct cvalue {
78 	char   *value;
79 	int     found;
80     };
81     struct cvalue *cvalue;
82     HTABLE_INFO **ht_info;
83     HTABLE_INFO **ht;
84     int     interesting;
85     const char *err;
86 
87     /*
88      * Store command-line parameters for quick lookup.
89      */
90     table = htable_create(argc);
91     while ((cp = *argv++) != 0) {
92 	if (strchr(cp, '\n') != 0)
93 	    msg_fatal("-e, -X, or -# accepts no multi-line input");
94 	while (ISSPACE(*cp))
95 	    cp++;
96 	if (*cp == '#')
97 	    msg_fatal("-e, -X, or -# accepts no comment input");
98 	if (mode & EDIT_MAIN) {
99 	    if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0)
100 		msg_fatal("%s: \"%s\"", err, cp);
101 	} else if (mode & (COMMENT_OUT | EDIT_EXCL)) {
102 	    if (*cp == 0)
103 		msg_fatal("-X or -# requires non-blank parameter names");
104 	    if (strchr(cp, '=') != 0)
105 		msg_fatal("-X or -# requires parameter names only");
106 	    edit_key = cp;
107 	    trimblanks(edit_key, 0);
108 	    edit_val = 0;
109 	} else {
110 	    msg_panic("edit_parameters: unknown mode %d", mode);
111 	}
112 	cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
113 	cvalue->value = edit_val;
114 	cvalue->found = 0;
115 	htable_enter(table, edit_key, (char *) cvalue);
116     }
117 
118     /*
119      * Open a temp file for the result. This uses a deterministic name so we
120      * don't leave behind thrash with random names.
121      */
122     set_config_dir();
123     path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0);
124     if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0)
125 	msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX);
126     dst = ep->tmp_fp;
127 
128     /*
129      * Open the original file for input.
130      */
131     if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) {
132 	/* OK to delete, since we control the temp file name exclusively. */
133 	(void) unlink(ep->tmp_path);
134 	msg_fatal("open %s for reading: %m", path);
135     }
136 
137     /*
138      * Copy original file to temp file, while replacing parameters on the
139      * fly. Issue warnings for names found multiple times.
140      */
141 #define STR(x) vstring_str(x)
142 
143     interesting = 0;
144     while (vstring_get(buf, src) != VSTREAM_EOF) {
145 	for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++)
146 	     /* void */ ;
147 	/* Copy comment, all-whitespace, or empty line. */
148 	if (*cp == '#' || *cp == 0) {
149 	    vstream_fputs(STR(buf), dst);
150 	}
151 	/* Copy, skip or replace continued text. */
152 	else if (cp > STR(buf)) {
153 	    if (interesting == 0)
154 		vstream_fputs(STR(buf), dst);
155 	    else if (mode & COMMENT_OUT)
156 		vstream_fprintf(dst, "#%s", STR(buf));
157 	}
158 	/* Copy or replace start of logical line. */
159 	else {
160 	    vstring_strncpy(key, cp, strcspn(cp, " \t\r\n="));
161 	    cvalue = (struct cvalue *) htable_find(table, STR(key));
162 	    if ((interesting = !!cvalue) != 0) {
163 		if (cvalue->found++ == 1)
164 		    msg_warn("%s: multiple entries for \"%s\"", path, STR(key));
165 		if (mode & EDIT_MAIN)
166 		    vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value);
167 		else if (mode & COMMENT_OUT)
168 		    vstream_fprintf(dst, "#%s", cp);
169 	    } else {
170 		vstream_fputs(STR(buf), dst);
171 	    }
172 	}
173     }
174 
175     /*
176      * Generate new entries for parameters that were not found.
177      */
178     if (mode & EDIT_MAIN) {
179 	for (ht_info = ht = htable_list(table); *ht; ht++) {
180 	    cvalue = (struct cvalue *) ht[0]->value;
181 	    if (cvalue->found == 0)
182 		vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value);
183 	}
184 	myfree((char *) ht_info);
185     }
186 
187     /*
188      * When all is well, rename the temp file to the original one.
189      */
190     if (vstream_fclose(src))
191 	msg_fatal("read %s: %m", path);
192     if (edit_file_close(ep) != 0)
193 	msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX);
194 
195     /*
196      * Cleanup.
197      */
198     myfree(path);
199     vstring_free(buf);
200     vstring_free(key);
201     htable_free(table, myfree);
202 }
203