77 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
88 * Portions Copyright (c) 1994, Regents of the University of California
99 *
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 $
1111 *
1212 *-------------------------------------------------------------------------
1313 */
1919#include "parser/keywords.h"
2020
2121
22+ #define supports_grant_options (version ) ((version) >= 70400)
23+
24+ static bool parseAclArray (const char * acls , char * * * itemarray , int * nitems );
2225static bool parseAclItem (const char * item , const char * type , const char * name ,
2326 int remoteVersion ,
2427 PQExpBuffer grantee , PQExpBuffer grantor ,
2528 PQExpBuffer privs , PQExpBuffer privswgo );
29+ static char * copyAclUserName (PQExpBuffer output , char * input );
2630static void AddAcl (PQExpBuffer aclbuf , const char * keyword );
27- #define supports_grant_options (version ) ((version) >= 70400)
2831
2932
3033/*
@@ -185,15 +188,23 @@ buildACLCommands(const char *name, const char *type,
185188 int remoteVersion ,
186189 PQExpBuffer sql )
187190{
188- char * aclbuf ,
189- * tok ;
191+ char * * aclitems ;
192+ int naclitems ;
193+ int i ;
190194 PQExpBuffer grantee , grantor , privs , privswgo ;
191195 PQExpBuffer firstsql , secondsql ;
192196 bool found_owner_privs = false;
193197
194198 if (strlen (acls ) == 0 )
195199 return true; /* object has default permissions */
196200
201+ if (!parseAclArray (acls , & aclitems , & naclitems ))
202+ {
203+ if (aclitems )
204+ free (aclitems );
205+ return false;
206+ }
207+
197208 grantee = createPQExpBuffer ();
198209 grantor = createPQExpBuffer ();
199210 privs = createPQExpBuffer ();
@@ -217,27 +228,10 @@ buildACLCommands(const char *name, const char *type,
217228 appendPQExpBuffer (firstsql , "REVOKE ALL ON %s %s FROM PUBLIC;\n" ,
218229 type , name );
219230
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 ++ )
225233 {
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 ,
241235 grantee , grantor , privs , privswgo ))
242236 return false;
243237
@@ -327,7 +321,6 @@ buildACLCommands(const char *name, const char *type,
327321 type , name , fmtId (owner ));
328322 }
329323
330- free (aclbuf );
331324 destroyPQExpBuffer (grantee );
332325 destroyPQExpBuffer (grantor );
333326 destroyPQExpBuffer (privs );
@@ -337,15 +330,105 @@ buildACLCommands(const char *name, const char *type,
337330 destroyPQExpBuffer (firstsql );
338331 destroyPQExpBuffer (secondsql );
339332
333+ free (aclitems );
334+
340335 return true;
341336}
342337
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+ }
343419
344420/*
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).
349432 *
350433 * Note: for cross-version compatibility, it's important to use ALL when
351434 * appropriate.
@@ -365,19 +448,19 @@ parseAclItem(const char *item, const char *type, const char *name,
365448
366449 buf = strdup (item );
367450
368- /* user name is string up to = */
369- eqpos = strchr ( buf , '=' );
370- if (! eqpos )
451+ /* user or group name is string up to = */
452+ eqpos = copyAclUserName ( grantee , buf );
453+ if (* eqpos != '=' )
371454 return false;
372- * eqpos = '\0' ;
373- printfPQExpBuffer (grantee , "%s" , buf );
374455
375456 /* grantor may be listed after / */
376457 slpos = strchr (eqpos + 1 , '/' );
377458 if (slpos )
378459 {
379- * slpos = '\0' ;
380- printfPQExpBuffer (grantor , "%s" , slpos + 1 );
460+ * slpos ++ = '\0' ;
461+ slpos = copyAclUserName (grantor , slpos );
462+ if (* slpos != '\0' )
463+ return false;
381464 }
382465 else
383466 resetPQExpBuffer (grantor );
@@ -457,6 +540,38 @@ parseAclItem(const char *item, const char *type, const char *name,
457540 return true;
458541}
459542
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+ }
460575
461576/*
462577 * Append a privilege keyword to a keyword list, inserting comma if needed.
0 commit comments