1 /*
2 * Copyright (c) 2010-2013 Balabit
3 * Copyright (c) 2010-2013 Balázs Scheidler
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * As an additional exemption you are allowed to compile & link against the
20 * OpenSSL libraries as published by the OpenSSL project. See the file
21 * COPYING for details.
22 *
23 */
24
25 #include "cfg-lexer-subst.h"
26 #include "cfg-args.h"
27 #include "cfg-grammar.h"
28
29 #include <string.h>
30 #include <stdlib.h>
31
32 typedef enum _CfgLexerStringTrackState
33 {
34 CLS_NOT_STRING,
35 CLS_WITHIN_STRING,
36 CLS_WITHIN_STRING_QUOTE,
37 CLS_WITHIN_STRING_QUOTED_CHARACTER,
38 CLS_WITHIN_QSTRING,
39 } CfgLexerStringTrackState;
40
41 /* substitute backtick references into lexer input */
42 struct _CfgLexerSubst
43 {
44 CfgArgs *globals;
45 CfgArgs *defs;
46 CfgArgs *args;
47 CfgLexerStringTrackState string_state;
48 GString *result_buffer;
49 };
50
51 static const gchar *
_lookup_value(CfgLexerSubst * self,const gchar * name)52 _lookup_value(CfgLexerSubst *self, const gchar *name)
53 {
54 const gchar *arg;
55
56 if (self->args && (arg = cfg_args_get(self->args, name)))
57 ;
58 else if (self->defs && (arg = cfg_args_get(self->defs, name)))
59 ;
60 else if (self->globals && (arg = cfg_args_get(self->globals, name)))
61 ;
62 else if ((arg = g_getenv(name)))
63 ;
64 else
65 arg = NULL;
66
67 return arg;
68 }
69
70 static CfgLexerStringTrackState
_track_string_state(CfgLexerSubst * self,CfgLexerStringTrackState last_state,const gchar * p)71 _track_string_state(CfgLexerSubst *self, CfgLexerStringTrackState last_state, const gchar *p)
72 {
73 switch (last_state)
74 {
75 case CLS_NOT_STRING:
76 if (*p == '"')
77 return CLS_WITHIN_STRING;
78 else if (*p == '\'')
79 return CLS_WITHIN_QSTRING;
80 return CLS_NOT_STRING;
81 case CLS_WITHIN_STRING:
82 case CLS_WITHIN_STRING_QUOTED_CHARACTER:
83 if (*p == '\\')
84 return CLS_WITHIN_STRING_QUOTE;
85 else if (*p == '"')
86 return CLS_NOT_STRING;
87 return CLS_WITHIN_STRING;
88 case CLS_WITHIN_STRING_QUOTE:
89 return CLS_WITHIN_STRING_QUOTED_CHARACTER;
90 case CLS_WITHIN_QSTRING:
91 if (*p == '\'')
92 return CLS_NOT_STRING;
93 return CLS_WITHIN_QSTRING;
94 default:
95 g_assert_not_reached();
96 break;
97 }
98 g_assert_not_reached();
99 }
100
101 static gchar *
_extract_string_literal(const gchar * value)102 _extract_string_literal(const gchar *value)
103 {
104 CfgLexer *lexer;
105 gint token, look_ahead_token;
106 CFG_STYPE yylval, look_ahead_yylval;
107 CFG_LTYPE yylloc, look_ahead_yylloc;
108 gchar *result = NULL;
109
110 lexer = cfg_lexer_new_buffer(configuration, value, strlen(value));
111 token = cfg_lexer_lex(lexer, &yylval, &yylloc);
112 if (token == LL_STRING)
113 {
114 look_ahead_token = cfg_lexer_lex(lexer, &look_ahead_yylval, &look_ahead_yylloc);
115 if (!look_ahead_token)
116 result = g_strdup(yylval.cptr);
117
118 cfg_lexer_free_token(&look_ahead_yylval);
119 }
120 cfg_lexer_free_token(&yylval);
121
122 cfg_lexer_free(lexer);
123 return result;
124 }
125
126 static gboolean
_encode_as_string(CfgLexerSubst * self,const gchar * value,GError ** error)127 _encode_as_string(CfgLexerSubst *self, const gchar *value, GError **error)
128 {
129 gint i;
130
131 g_return_val_if_fail(error == NULL || (*error) == NULL, FALSE);
132 for (i = 0; value[i]; i++)
133 {
134 gchar p = value[i];
135
136 if (p == '"')
137 g_string_append(self->result_buffer, "\\\"");
138 else if (p == '\n')
139 g_string_append(self->result_buffer, "\\n");
140 else if (p == '\r')
141 g_string_append(self->result_buffer, "\\r");
142 else if (p == '\\')
143 g_string_append(self->result_buffer, "\\\\");
144 else
145 g_string_append_c(self->result_buffer, p);
146 }
147 return TRUE;
148 }
149
150 static gboolean
_encode_as_qstring(CfgLexerSubst * self,const gchar * value,GError ** error)151 _encode_as_qstring(CfgLexerSubst *self, const gchar *value, GError **error)
152 {
153 gint i;
154
155 g_return_val_if_fail(error == NULL || (*error) == NULL, FALSE);
156 for (i = 0; value[i]; i++)
157 {
158 gchar p = value[i];
159
160 if (p == '\'')
161 {
162 g_set_error(error, CFG_LEXER_ERROR, CFG_LEXER_CANNOT_REPRESENT_APOSTROPHES_IN_QSTRINGS,
163 "cannot represent apostrophes within apostroph-enclosed string");
164 return FALSE;
165 }
166 g_string_append_c(self->result_buffer, p);
167 }
168 return TRUE;
169 }
170
171 static gboolean
_append_value(CfgLexerSubst * self,const gchar * value,GError ** error)172 _append_value(CfgLexerSubst *self, const gchar *value, GError **error)
173 {
174 g_return_val_if_fail(error == NULL || (*error) == NULL, FALSE);
175 gboolean result = TRUE;
176
177 if (self->string_state == CLS_NOT_STRING)
178 {
179 g_string_append(self->result_buffer, value);
180 }
181 else
182 {
183 gchar *string_literal;
184
185 string_literal = _extract_string_literal(value);
186 if (string_literal)
187 {
188 switch (self->string_state)
189 {
190 case CLS_WITHIN_STRING:
191 result = _encode_as_string(self, string_literal, error);
192 break;
193 case CLS_WITHIN_QSTRING:
194 result = _encode_as_qstring(self, string_literal, error);
195 break;
196 default:
197 g_assert_not_reached();
198 break;
199 }
200 g_free(string_literal);
201 }
202 else
203 g_string_append(self->result_buffer, value);
204 }
205 return result;
206 }
207
208 gchar *
cfg_lexer_subst_invoke(CfgLexerSubst * self,const gchar * input,gssize input_len,gsize * output_length,GError ** error)209 cfg_lexer_subst_invoke(CfgLexerSubst *self, const gchar *input, gssize input_len, gsize *output_length, GError **error)
210 {
211 gboolean backtick = FALSE;
212 const gchar *p, *ref_start = input;
213 GString *result;
214
215 g_return_val_if_fail(error == NULL || (*error) == NULL, FALSE);
216
217 if (input_len < 0)
218 input_len = strlen(input);
219
220 result = g_string_sized_new(32);
221 self->result_buffer = result;
222 p = input;
223 while (p - input < input_len)
224 {
225 self->string_state = _track_string_state(self, self->string_state, p);
226
227 if (!backtick && (*p) == '`')
228 {
229 if (self->string_state == CLS_WITHIN_STRING_QUOTED_CHARACTER)
230 {
231 g_set_error(error, CFG_LEXER_ERROR, CFG_LEXER_BACKTICKS_CANT_BE_SUBSTITUTED_AFTER_BACKSLASH,
232 "cannot subsitute backticked values right after a string quote character");
233 goto error;
234 }
235 /* start of reference */
236 backtick = TRUE;
237 ref_start = p + 1;
238 }
239 else if (backtick && (*p) == '`')
240 {
241 /* end of reference */
242 backtick = FALSE;
243
244 if (ref_start == p)
245 {
246 /* empty ref, just include a ` character */
247 g_string_append_c(result, '`');
248 }
249 else
250 {
251 const gchar *value;
252 gchar *name;
253
254 name = g_strndup(ref_start, p - ref_start);
255 value = _lookup_value(self, name);
256 g_free(name);
257 if (!_append_value(self, value ? : "", error))
258 goto error;
259 }
260 }
261 else if (!backtick)
262 g_string_append_c(result, *p);
263
264 p++;
265 }
266 self->result_buffer = NULL;
267
268 if (backtick)
269 {
270 g_set_error(error, CFG_LEXER_ERROR, CFG_LEXER_MISSING_BACKTICK_PAIR, "missing closing backtick (`) character");
271 goto error;
272 }
273
274 *output_length = result->len;
275 return g_string_free(result, FALSE);
276 error:
277 g_string_free(result, TRUE);
278 return NULL;
279
280 }
281
282 CfgLexerSubst *
cfg_lexer_subst_new(CfgArgs * globals,CfgArgs * defs,CfgArgs * args)283 cfg_lexer_subst_new(CfgArgs *globals, CfgArgs *defs, CfgArgs *args)
284 {
285 CfgLexerSubst *self = g_new0(CfgLexerSubst, 1);
286
287 self->globals = globals;
288 self->defs = defs;
289 self->args = args;
290 return self;
291 }
292
293 void
cfg_lexer_subst_free(CfgLexerSubst * self)294 cfg_lexer_subst_free(CfgLexerSubst *self)
295 {
296 cfg_args_unref(self->globals);
297 cfg_args_unref(self->defs);
298 cfg_args_unref(self->args);
299 g_free(self);
300 }
301
302 gchar *
cfg_lexer_subst_args_in_input(CfgArgs * globals,CfgArgs * defs,CfgArgs * args,const gchar * input,gssize input_length,gsize * output_length,GError ** error)303 cfg_lexer_subst_args_in_input(CfgArgs *globals, CfgArgs *defs, CfgArgs *args, const gchar *input, gssize input_length,
304 gsize *output_length, GError **error)
305 {
306 CfgLexerSubst *subst = cfg_lexer_subst_new(cfg_args_ref(globals), cfg_args_ref(defs), cfg_args_ref(args));
307 gchar *result;
308
309 result = cfg_lexer_subst_invoke(subst, input, input_length, output_length, error);
310 cfg_lexer_subst_free(subst);
311 return result;
312 }
313