7
7
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
8
8
* Portions Copyright (c) 1994, Regents of the University of California
9
9
*
10
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v 1.5 2003/07/24 15:52:53 petere Exp $
10
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v 1.6 2003/07/31 17:21:57 tgl Exp $
11
11
*
12
12
*-------------------------------------------------------------------------
13
13
*/
19
19
#include "parser/keywords.h"
20
20
21
21
22
+ #define supports_grant_options (version ) ((version) >= 70400)
23
+
24
+ static bool parseAclArray (const char * acls ,char * * * itemarray ,int * nitems );
22
25
static bool parseAclItem (const char * item ,const char * type ,const char * name ,
23
26
int remoteVersion ,
24
27
PQExpBuffer grantee ,PQExpBuffer grantor ,
25
28
PQExpBuffer privs ,PQExpBuffer privswgo );
29
+ static char * copyAclUserName (PQExpBuffer output ,char * input );
26
30
static void AddAcl (PQExpBuffer aclbuf ,const char * keyword );
27
- #define supports_grant_options (version ) ((version) >= 70400)
28
31
29
32
30
33
/*
@@ -185,15 +188,23 @@ buildACLCommands(const char *name, const char *type,
185
188
int remoteVersion ,
186
189
PQExpBuffer sql )
187
190
{
188
- char * aclbuf ,
189
- * tok ;
191
+ char * * aclitems ;
192
+ int naclitems ;
193
+ int i ;
190
194
PQExpBuffer grantee ,grantor ,privs ,privswgo ;
191
195
PQExpBuffer firstsql ,secondsql ;
192
196
bool found_owner_privs = false;
193
197
194
198
if (strlen (acls )== 0 )
195
199
return true;/* object has default permissions */
196
200
201
+ if (!parseAclArray (acls ,& aclitems ,& naclitems ))
202
+ {
203
+ if (aclitems )
204
+ free (aclitems );
205
+ return false;
206
+ }
207
+
197
208
grantee = createPQExpBuffer ();
198
209
grantor = createPQExpBuffer ();
199
210
privs = createPQExpBuffer ();
@@ -217,27 +228,10 @@ buildACLCommands(const char *name, const char *type,
217
228
appendPQExpBuffer (firstsql ,"REVOKE ALL ON %s %s FROM PUBLIC;\n" ,
218
229
type ,name );
219
230
220
- /* Make a working copy of acls so we can use strtok */
221
- aclbuf = strdup (acls );
222
-
223
- /* Scan comma-separated ACL items */
224
- for (tok = strtok (aclbuf ,"," );tok != NULL ;tok = strtok (NULL ,"," ))
231
+ /* Scan individual ACL items */
232
+ for (i = 0 ;i < naclitems ;i ++ )
225
233
{
226
- size_t toklen ;
227
-
228
- /*
229
- * Token may start with '{' and/or '"'. Actually only the start
230
- * of the string should have '{', but we don't verify that.
231
- */
232
- if (* tok == '{' )
233
- tok ++ ;
234
- if (* tok == '"' )
235
- tok ++ ;
236
- toklen = strlen (tok );
237
- while (toklen >=0 && (tok [toklen - 1 ]== '"' || tok [toklen - 1 ]== '}' ))
238
- tok [toklen -- - 1 ]= '\0' ;
239
-
240
- if (!parseAclItem (tok ,type ,name ,remoteVersion ,
234
+ if (!parseAclItem (aclitems [i ],type ,name ,remoteVersion ,
241
235
grantee ,grantor ,privs ,privswgo ))
242
236
return false;
243
237
@@ -327,7 +321,6 @@ buildACLCommands(const char *name, const char *type,
327
321
type ,name ,fmtId (owner ));
328
322
}
329
323
330
- free (aclbuf );
331
324
destroyPQExpBuffer (grantee );
332
325
destroyPQExpBuffer (grantor );
333
326
destroyPQExpBuffer (privs );
@@ -337,15 +330,105 @@ buildACLCommands(const char *name, const char *type,
337
330
destroyPQExpBuffer (firstsql );
338
331
destroyPQExpBuffer (secondsql );
339
332
333
+ free (aclitems );
334
+
340
335
return true;
341
336
}
342
337
338
+ /*
339
+ * Deconstruct an ACL array (or actually any 1-dimensional Postgres array)
340
+ * into individual items.
341
+ *
342
+ * On success, returns true and sets *itemarray and *nitems to describe
343
+ * an array of individual strings. On parse failure, returns false;
344
+ * *itemarray may exist or be NULL.
345
+ *
346
+ * NOTE: free'ing itemarray is sufficient to deallocate the working storage.
347
+ */
348
+ static bool
349
+ parseAclArray (const char * acls ,char * * * itemarray ,int * nitems )
350
+ {
351
+ int inputlen ;
352
+ char * * items ;
353
+ char * strings ;
354
+ int curitem ;
355
+
356
+ /*
357
+ * We expect input in the form of "{item,item,item}" where any item
358
+ * is either raw data, or surrounded by double quotes (in which case
359
+ * embedded characters including backslashes and quotes are backslashed).
360
+ *
361
+ * We build the result as an array of pointers followed by the actual
362
+ * string data, all in one malloc block for convenience of deallocation.
363
+ * The worst-case storage need is not more than one pointer and one
364
+ * character for each input character (consider "{,,,,,,,,,,}").
365
+ */
366
+ * itemarray = NULL ;
367
+ * nitems = 0 ;
368
+ inputlen = strlen (acls );
369
+ if (inputlen < 2 || acls [0 ]!= '{' || acls [inputlen - 1 ]!= '}' )
370
+ return false;/* bad input */
371
+ items = (char * * )malloc (inputlen * (sizeof (char * )+ sizeof (char )));
372
+ if (items == NULL )
373
+ return false;/* out of memory */
374
+ * itemarray = items ;
375
+ strings = (char * ) (items + inputlen );
376
+
377
+ acls ++ ;/* advance over initial '{' */
378
+ curitem = 0 ;
379
+ while (* acls != '}' )
380
+ {
381
+ if (* acls == '\0' )
382
+ return false;/* premature end of string */
383
+ items [curitem ]= strings ;
384
+ while (* acls != '}' && * acls != ',' )
385
+ {
386
+ if (* acls == '\0' )
387
+ return false;/* premature end of string */
388
+ if (* acls != '"' )
389
+ * strings ++ = * acls ++ ;/* copy unquoted data */
390
+ else
391
+ {
392
+ /* process quoted substring */
393
+ acls ++ ;
394
+ while (* acls != '"' )
395
+ {
396
+ if (* acls == '\0' )
397
+ return false;/* premature end of string */
398
+ if (* acls == '\\' )
399
+ {
400
+ acls ++ ;
401
+ if (* acls == '\0' )
402
+ return false;/* premature end of string */
403
+ }
404
+ * strings ++ = * acls ++ ;/* copy quoted data */
405
+ }
406
+ acls ++ ;
407
+ }
408
+ }
409
+ * strings ++ = '\0' ;
410
+ if (* acls == ',' )
411
+ acls ++ ;
412
+ curitem ++ ;
413
+ }
414
+ if (acls [1 ]!= '\0' )
415
+ return false;/* bogus syntax (embedded '}') */
416
+ * nitems = curitem ;
417
+ return true;
418
+ }
343
419
344
420
/*
345
- * This will take an aclitem string of privilege code letters and
346
- * parse it into grantee, grantor, and privilege information. The
347
- * privilege information is split between privileges with grant option
348
- * (privswgo) and without (privs).
421
+ * This will parse an aclitem string, having the general form
422
+ *username=privilegecodes/grantor
423
+ * or
424
+ *group groupname=privilegecodes/grantor
425
+ * (the /grantor part will not be present if pre-7.4 database).
426
+ *
427
+ * The returned grantee string will be the dequoted username or groupname
428
+ * (preceded with "group " in the latter case). The returned grantor is
429
+ * the dequoted grantor name or empty. Privilege characters are decoded
430
+ * and split between privileges with grant option (privswgo) and without
431
+ * (privs).
349
432
*
350
433
* Note: for cross-version compatibility, it's important to use ALL when
351
434
* appropriate.
@@ -365,19 +448,19 @@ parseAclItem(const char *item, const char *type, const char *name,
365
448
366
449
buf = strdup (item );
367
450
368
- /* user name is string up to = */
369
- eqpos = strchr ( buf , '=' );
370
- if (! eqpos )
451
+ /* useror group name is string up to = */
452
+ eqpos = copyAclUserName ( grantee , buf );
453
+ if (* eqpos != '=' )
371
454
return false;
372
- * eqpos = '\0' ;
373
- printfPQExpBuffer (grantee ,"%s" ,buf );
374
455
375
456
/* grantor may be listed after / */
376
457
slpos = strchr (eqpos + 1 ,'/' );
377
458
if (slpos )
378
459
{
379
- * slpos = '\0' ;
380
- printfPQExpBuffer (grantor ,"%s" ,slpos + 1 );
460
+ * slpos ++ = '\0' ;
461
+ slpos = copyAclUserName (grantor ,slpos );
462
+ if (* slpos != '\0' )
463
+ return false;
381
464
}
382
465
else
383
466
resetPQExpBuffer (grantor );
@@ -457,6 +540,38 @@ parseAclItem(const char *item, const char *type, const char *name,
457
540
return true;
458
541
}
459
542
543
+ /*
544
+ * Transfer a user or group name starting at *input into the output buffer,
545
+ * dequoting if needed. Returns a pointer to just past the input name.
546
+ * The name is taken to end at an unquoted '=' or end of string.
547
+ */
548
+ static char *
549
+ copyAclUserName (PQExpBuffer output ,char * input )
550
+ {
551
+ resetPQExpBuffer (output );
552
+ while (* input && * input != '=' )
553
+ {
554
+ if (* input != '"' )
555
+ appendPQExpBufferChar (output ,* input ++ );
556
+ else
557
+ {
558
+ input ++ ;
559
+ while (* input != '"' )
560
+ {
561
+ if (* input == '\0' )
562
+ return input ;/* really a syntax error... */
563
+ /*
564
+ * There is no quoting convention here, thus we can't cope
565
+ * with usernames containing double quotes. Keep this code
566
+ * in sync with putid() in backend's acl.c.
567
+ */
568
+ appendPQExpBufferChar (output ,* input ++ );
569
+ }
570
+ input ++ ;
571
+ }
572
+ }
573
+ return input ;
574
+ }
460
575
461
576
/*
462
577
* Append a privilege keyword to a keyword list, inserting comma if needed.