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 ,
2326int remoteVersion ,
2427PQExpBuffer grantee ,PQExpBuffer grantor ,
2528PQExpBuffer 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,
185188int remoteVersion ,
186189PQExpBuffer sql )
187190{
188- char * aclbuf ,
189- * tok ;
191+ char * * aclitems ;
192+ int naclitems ;
193+ int i ;
190194PQExpBuffer grantee ,grantor ,privs ,privswgo ;
191195PQExpBuffer firstsql ,secondsql ;
192196bool found_owner_privs = false;
193197
194198if (strlen (acls )== 0 )
195199return true;/* object has default permissions */
196200
201+ if (!parseAclArray (acls ,& aclitems ,& naclitems ))
202+ {
203+ if (aclitems )
204+ free (aclitems );
205+ return false;
206+ }
207+
197208grantee = createPQExpBuffer ();
198209grantor = createPQExpBuffer ();
199210privs = createPQExpBuffer ();
@@ -217,27 +228,10 @@ buildACLCommands(const char *name, const char *type,
217228appendPQExpBuffer (firstsql ,"REVOKE ALL ON %s %s FROM PUBLIC;\n" ,
218229type ,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 ,
241235grantee ,grantor ,privs ,privswgo ))
242236return false;
243237
@@ -327,7 +321,6 @@ buildACLCommands(const char *name, const char *type,
327321type ,name ,fmtId (owner ));
328322}
329323
330- free (aclbuf );
331324destroyPQExpBuffer (grantee );
332325destroyPQExpBuffer (grantor );
333326destroyPQExpBuffer (privs );
@@ -337,15 +330,105 @@ buildACLCommands(const char *name, const char *type,
337330destroyPQExpBuffer (firstsql );
338331destroyPQExpBuffer (secondsql );
339332
333+ free (aclitems );
334+
340335return 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
366449buf = strdup (item );
367450
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 != '=' )
371454return false;
372- * eqpos = '\0' ;
373- printfPQExpBuffer (grantee ,"%s" ,buf );
374455
375456/* grantor may be listed after / */
376457slpos = strchr (eqpos + 1 ,'/' );
377458if (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}
382465else
383466resetPQExpBuffer (grantor );
@@ -457,6 +540,38 @@ parseAclItem(const char *item, const char *type, const char *name,
457540return 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.