@@ -158,6 +158,210 @@ quote_identifier(const char *s)
158
158
}
159
159
160
160
161
+ /*
162
+ * Append the given string to the shell command being built in the buffer,
163
+ * with suitable shell-style quoting to create exactly one argument.
164
+ *
165
+ * Forbid LF or CR characters, which have scant practical use beyond designing
166
+ * security breaches. The Windows command shell is unusable as a conduit for
167
+ * arguments containing LF or CR characters. A future major release should
168
+ * reject those characters in CREATE ROLE and CREATE DATABASE, because use
169
+ * there eventually leads to errors here.
170
+ */
171
+ void
172
+ appendShellString (PQExpBuffer buf , const char * str )
173
+ {
174
+ const char * p ;
175
+
176
+ #ifndef WIN32
177
+ appendPQExpBufferChar (buf , '\'' );
178
+ for (p = str ; * p ; p ++ )
179
+ {
180
+ if (* p == '\n' || * p == '\r' )
181
+ {
182
+ fprintf (stderr ,
183
+ _ ("shell command argument contains a newline or carriage return: \"%s\"\n" ),
184
+ str );
185
+ exit (EXIT_FAILURE );
186
+ }
187
+
188
+ if (* p == '\'' )
189
+ appendPQExpBufferStr (buf , "'\"'\"'" );
190
+ else
191
+ appendPQExpBufferChar (buf , * p );
192
+ }
193
+ appendPQExpBufferChar (buf , '\'' );
194
+ #else /* WIN32 */
195
+ int backslash_run_length = 0 ;
196
+
197
+ /*
198
+ * A Windows system() argument experiences two layers of interpretation.
199
+ * First, cmd.exe interprets the string. Its behavior is undocumented,
200
+ * but a caret escapes any byte except LF or CR that would otherwise have
201
+ * special meaning. Handling of a caret before LF or CR differs between
202
+ * "cmd.exe /c" and other modes, and it is unusable here.
203
+ *
204
+ * Second, the new process parses its command line to construct argv (see
205
+ * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
206
+ * backslash-double quote sequences specially.
207
+ */
208
+ appendPQExpBufferStr (buf , "^\"" );
209
+ for (p = str ; * p ; p ++ )
210
+ {
211
+ if (* p == '\n' || * p == '\r' )
212
+ {
213
+ fprintf (stderr ,
214
+ _ ("shell command argument contains a newline or carriage return: \"%s\"\n" ),
215
+ str );
216
+ exit (EXIT_FAILURE );
217
+ }
218
+
219
+ /* Change N backslashes before a double quote to 2N+1 backslashes. */
220
+ if (* p == '"' )
221
+ {
222
+ while (backslash_run_length )
223
+ {
224
+ appendPQExpBufferStr (buf , "^\\" );
225
+ backslash_run_length -- ;
226
+ }
227
+ appendPQExpBufferStr (buf , "^\\" );
228
+ }
229
+ else if (* p == '\\' )
230
+ backslash_run_length ++ ;
231
+ else
232
+ backslash_run_length = 0 ;
233
+
234
+ /*
235
+ * Decline to caret-escape the most mundane characters, to ease
236
+ * debugging and lest we approach the command length limit.
237
+ */
238
+ if (!((* p >= 'a' && * p <= 'z' ) ||
239
+ (* p >= 'A' && * p <= 'Z' ) ||
240
+ (* p >= '0' && * p <= '9' )))
241
+ appendPQExpBufferChar (buf , '^' );
242
+ appendPQExpBufferChar (buf , * p );
243
+ }
244
+
245
+ /*
246
+ * Change N backslashes at end of argument to 2N backslashes, because they
247
+ * precede the double quote that terminates the argument.
248
+ */
249
+ while (backslash_run_length )
250
+ {
251
+ appendPQExpBufferStr (buf , "^\\" );
252
+ backslash_run_length -- ;
253
+ }
254
+ appendPQExpBufferStr (buf , "^\"" );
255
+ #endif /* WIN32 */
256
+ }
257
+
258
+
259
+ /*
260
+ * Append the given string to the buffer, with suitable quoting for passing
261
+ * the string as a value, in a keyword/pair value in a libpq connection
262
+ * string
263
+ */
264
+ void
265
+ appendConnStrVal (PQExpBuffer buf , const char * str )
266
+ {
267
+ const char * s ;
268
+ bool needquotes ;
269
+
270
+ /*
271
+ * If the string is one or more plain ASCII characters, no need to quote
272
+ * it. This is quite conservative, but better safe than sorry.
273
+ */
274
+ needquotes = true;
275
+ for (s = str ; * s ; s ++ )
276
+ {
277
+ if (!((* s >= 'a' && * s <= 'z' ) || (* s >= 'A' && * s <= 'Z' ) ||
278
+ (* s >= '0' && * s <= '9' ) || * s == '_' || * s == '.' ))
279
+ {
280
+ needquotes = true;
281
+ break ;
282
+ }
283
+ needquotes = false;
284
+ }
285
+
286
+ if (needquotes )
287
+ {
288
+ appendPQExpBufferChar (buf , '\'' );
289
+ while (* str )
290
+ {
291
+ /* ' and \ must be escaped by to \' and \\ */
292
+ if (* str == '\'' || * str == '\\' )
293
+ appendPQExpBufferChar (buf , '\\' );
294
+
295
+ appendPQExpBufferChar (buf , * str );
296
+ str ++ ;
297
+ }
298
+ appendPQExpBufferChar (buf , '\'' );
299
+ }
300
+ else
301
+ appendPQExpBufferStr (buf , str );
302
+ }
303
+
304
+
305
+ /*
306
+ * Append a psql meta-command that connects to the given database with the
307
+ * then-current connection's user, host and port.
308
+ */
309
+ void
310
+ appendPsqlMetaConnect (PQExpBuffer buf , const char * dbname )
311
+ {
312
+ const char * s ;
313
+ bool complex ;
314
+
315
+ /*
316
+ * If the name is plain ASCII characters, emit a trivial "\connect "foo"".
317
+ * For other names, even many not technically requiring it, skip to the
318
+ * general case. No database has a zero-length name.
319
+ */
320
+ complex = false;
321
+ for (s = dbname ; * s ; s ++ )
322
+ {
323
+ if (* s == '\n' || * s == '\r' )
324
+ {
325
+ fprintf (stderr ,
326
+ _ ("database name contains a newline or carriage return: \"%s\"\n" ),
327
+ dbname );
328
+ exit (EXIT_FAILURE );
329
+ }
330
+
331
+ if (!((* s >= 'a' && * s <= 'z' ) || (* s >= 'A' && * s <= 'Z' ) ||
332
+ (* s >= '0' && * s <= '9' ) || * s == '_' || * s == '.' ))
333
+ {
334
+ complex = true;
335
+ }
336
+ }
337
+
338
+ appendPQExpBufferStr (buf , "\\connect " );
339
+ if (complex )
340
+ {
341
+ PQExpBufferData connstr ;
342
+
343
+ initPQExpBuffer (& connstr );
344
+ appendPQExpBuffer (& connstr , "dbname=" );
345
+ appendConnStrVal (& connstr , dbname );
346
+
347
+ appendPQExpBuffer (buf , "-reuse-previous=on " );
348
+
349
+ /*
350
+ * As long as the name does not contain a newline, SQL identifier
351
+ * quoting satisfies the psql meta-command
10000
parser. Prefer not to
352
+ * involve psql-interpreted single quotes, which behaved differently
353
+ * before PostgreSQL 9.2.
354
+ */
355
+ appendPQExpBufferStr (buf , quote_identifier (connstr .data ));
356
+
357
+ termPQExpBuffer (& connstr );
358
+ }
359
+ else
360
+ appendPQExpBufferStr (buf , quote_identifier (dbname ));
361
+ appendPQExpBufferChar (buf , '\n' );
362
+ }
363
+
364
+
161
365
/*
162
366
* get_user_info()
163
367
* (copied from initdb.c) find the current user
0 commit comments