db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From banda...@apache.org
Subject svn commit: r385610 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/sql/compile/ engine/org/apache/derby/iapi/sql/conn/ engine/org/apache/derby/iapi/sql/dictionary/ engine/org/apache/derby/iapi/util/ engine/org/apache/derby/impl/jdbc/ engi...
Date Mon, 13 Mar 2006 18:28:23 GMT
Author: bandaram
Date: Mon Mar 13 10:28:20 2006
New Revision: 385610

URL: http://svn.apache.org/viewcvs?rev=385610&view=rev
Log:
DERBY-464: Continuation of previous Grant and Revoke checkins. This changes
implement:

1) Schema authorization checks: Only DBA can create a schema with authorization
of another user and regular non-DBA users can only create a schema that matches
their authorizationID in sqlStandard mode.

2) Make database owner authorizationId owner of all system schemas. This is
done in both sqlStandard mode or in legacy mode during database create time.
Need work to test hard upgrade to change system schema owner name to authId
of user doing full database upgrade.

3) Disable switching to sqlStandard mode in soft upgrade mode for 10.2. Since
Grant/Revoke is not enabled in soft upgrade mode in 10.2 release, sqlStandard
mode should be disabled in soft upgrade mode.

4) Disable all authorization checks for DBA user. This includes all object
permission checks and allowing DBA to grant/revoke any privilege on any database
object.

Submitted by Satheesh Bandaram (satheesh@sourcery.org)

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/CompilerContext.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/conn/Authorizer.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementSchemaPermission.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/util/IdUtil.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/TransactionResourceImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DD_Version.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CompilerContextImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateSchemaNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DDLStatementNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericAuthorizer.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/PrivilegeInfo.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RoutinePrivilegeInfo.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/TablePrivilegeInfo.java
    db/derby/code/trunk/java/engine/org/apache/derby/loc/messages_en.properties
    db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/grantRevokeDDL.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/grantRevokeDDL.sql

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/CompilerContext.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/CompilerContext.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/CompilerContext.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/CompilerContext.java
Mon Mar 13 10:28:20 2006
@@ -533,7 +533,7 @@
 	 *
 	 * @param schemaDescriptor
 	 */
-	public void addRequiredSchemaPriv( SchemaDescriptor sd);
+	public void addRequiredSchemaPriv(String schema, String aid, boolean privType);
 
 	/**
 	 * Add a routine execute privilege to the list of used routine privileges.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/conn/Authorizer.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/conn/Authorizer.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/conn/Authorizer.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/conn/Authorizer.java Mon Mar
13 10:28:20 2006
@@ -58,6 +58,10 @@
 	public static final int EXECUTE_PRIV = 6;
 	public static final int PRIV_TYPE_COUNT = 7;
 
+	/* Used to check who can create schemas or who can modify objects in schema */
+	public static final boolean CREATE_SCHEMA_PRIV = false;
+	public static final boolean MODIFY_SCHEMA_PRIV = true;
+
 	/**
 	 * The system authorization ID is defined by the SQL2003 spec as the grantor
 	 * of privileges to object owners.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
Mon Mar 13 10:28:20 2006
@@ -259,6 +259,13 @@
 	public DataDescriptorGenerator	getDataDescriptorGenerator();
 
 	/**
+	 * Get authorizationID of DBA
+	 *
+	 * @return	authorizationID
+	 */
+	public String getAuthorizationDBA();
+
+	/**
  	  *	Get the tabinfo of a system catalog. Paw through the tabinfo arrays looking for the
tabinfo
  	  *	corresponding to this table name.
  	  *

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementSchemaPermission.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementSchemaPermission.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementSchemaPermission.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/StatementSchemaPermission.java
Mon Mar 13 10:28:20 2006
@@ -21,7 +21,6 @@
 package org.apache.derby.iapi.sql.dictionary;
 
 import org.apache.derby.iapi.error.StandardException;
-import org.apache.derby.catalog.UUID;
 import org.apache.derby.iapi.sql.conn.Authorizer;
 import org.apache.derby.iapi.reference.SQLState;
 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
@@ -33,29 +32,45 @@
 
 public class StatementSchemaPermission extends StatementPermission
 {
-	protected UUID schemaUUID;
+	protected String schemaName;
+	protected String aid;
+	protected boolean privType;
 
-	public StatementSchemaPermission(UUID schemaUUID)
+	public StatementSchemaPermission(String schemaName, String aid, boolean privType)
 	{
-		this.schemaUUID = schemaUUID;
+		this.schemaName = schemaName;
+		this.aid 	= aid;
+		this.privType	= privType;
 	}
 
 	/**
-	 * @param tc the TransactionController
-	 * @param dd A DataDictionary
-	 * @param authorizationId A user
+	 * @param tc		the TransactionController
+	 * @param dd 		A DataDictionary
+	 * @param authid	authorizationId
 	 * @param forGrant
 	 *
 	 * @exception StandardException if schema authorization not granted
 	 */
 	public void check(TransactionController tc,
 					   DataDictionary dd,
-					   String authorizationId,
+					   String authid,
 					   boolean forGrant) throws StandardException
 	{
-		SchemaDescriptor sd = dd.getSchemaDescriptor(schemaUUID, tc);
-		if (!authorizationId.equals(sd.getAuthorizationId()))
-			throw StandardException.newException(SQLState.AUTH_NO_ACCESS_NOT_OWNER,
-				 authorizationId, sd.getSchemaName());
+		if (privType == Authorizer.MODIFY_SCHEMA_PRIV)
+		{
+			SchemaDescriptor sd = dd.getSchemaDescriptor(schemaName, tc, true);
+			if (!authid.equals(sd.getAuthorizationId()))
+				throw StandardException.newException(
+					SQLState.AUTH_NO_ACCESS_NOT_OWNER, authid, schemaName);
+		}
+		else
+		{
+			// Non-DBA Users can only create schemas that match their authid
+			// Also allow only DBA to set authid to another user
+			// Note that for DBA, check interface wouldn't be called at all
+			if (!schemaName.equals(authid) || (aid != null && !aid.equals(authid)))
+				throw StandardException.newException(
+					SQLState.AUTH_NOT_DATABASE_OWNER, authid, schemaName);
+		}
 	}
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/util/IdUtil.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/iapi/util/IdUtil.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/util/IdUtil.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/util/IdUtil.java Mon Mar 13 10:28:20
2006
@@ -20,12 +20,16 @@
 
 package org.apache.derby.iapi.util;
 
+import org.apache.derby.iapi.reference.Attribute;
 import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.reference.Property;
 import org.apache.derby.iapi.error.StandardException;
 import java.io.IOException;
 import java.io.StringReader;
 import java.util.Vector;
 import java.util.HashSet;
+import java.util.Properties;
+
 /**
   Utility class for parsing and producing string representations of
   ids. This class supports both delimited and un-delimited ids.
@@ -463,6 +467,36 @@
 			return mkIdList(a);
 		else
 			return mkIdListAsEntered(a);
+	}
+
+	/**
+	 * Map userName to authorizationId
+	 * 
+	 * @exception StandardException on error
+	 */
+	public static String getUserAuthorizationId(String userName) throws StandardException
+	{
+		try {
+			return parseId(userName);
+		}
+		catch (StandardException se) {
+			throw StandardException.newException(SQLState.AUTH_INVALID_USER_NAME, userName);
+		}
+	}
+
+	/**
+	 * Get user name from URL properties. Handles the case of "" user.
+	 * 
+	 * @exception StandardException on error
+	 */
+	public static String getUserNameFromURLProps(Properties params)
+	{
+		String userName = params.getProperty(Attribute.USERNAME_ATTR,
+							Property.DEFAULT_USER_NAME);
+		if (userName.equals(""))
+			userName = Property.DEFAULT_USER_NAME;
+
+		return userName;
 	}
 
 	/**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/TransactionResourceImpl.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/TransactionResourceImpl.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/TransactionResourceImpl.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/TransactionResourceImpl.java
Mon Mar 13 10:28:20 2006
@@ -37,6 +37,7 @@
 import org.apache.derby.iapi.reference.SQLState;
 import org.apache.derby.iapi.reference.Property;
 import org.apache.derby.iapi.util.StringUtil;
+import org.apache.derby.iapi.util.IdUtil;
 
 import java.util.Properties;
 import java.sql.SQLException;
@@ -142,10 +143,7 @@
 		// into the properties if its getConnection(url, string, string)
 		// interface is used.  Thus, we look there first.
 		// Default to APP.
-		username = info.getProperty(Attribute.USERNAME_ATTR,
-									Property.DEFAULT_USER_NAME);
-		if (username.equals(""))
-			username = Property.DEFAULT_USER_NAME;
+		username = IdUtil.getUserNameFromURLProps(info);
 
 		drdaID = info.getProperty(Attribute.DRDAID_ATTR, null);
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DD_Version.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DD_Version.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DD_Version.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DD_Version.java Mon
Mar 13 10:28:20 2006
@@ -48,6 +48,7 @@
 import org.apache.derby.iapi.services.info.ProductVersionHolder;
 import org.apache.derby.iapi.reference.JDBC30Translation;
 import org.apache.derby.iapi.reference.Limits;
+import org.apache.derby.iapi.util.IdUtil;
 
 import org.apache.derby.iapi.services.uuid.UUIDFactory;
 import org.apache.derby.catalog.UUID;
@@ -203,8 +204,9 @@
 		tc.commit();
 
 		if (performMajorUpgrade) {
-			// real upgrade changes.
-			doFullUpgrade( tc, dictionaryVersion.majorVersionNumber );
+			// real upgrade changes. Get user name of current user.
+			String userName = IdUtil.getUserNameFromURLProps(startParams);
+			doFullUpgrade(tc, dictionaryVersion.majorVersionNumber,IdUtil.getUserAuthorizationId(userName));
 		}
 
 		if (!minorOnly && !isReadOnly) {
@@ -301,10 +303,11 @@
 	  *
 	  * @param	tc	transaction controller
 	  * @param	fromMajorVersionNumber	version of the on-disk database
+	  * @param	aid						AuthorizationID of current user to be made DBA
 	  *
 	  *	@exception StandardException  Standard Cloudscape error policy.
 	  */
-	private	void	doFullUpgrade(TransactionController tc, int fromMajorVersionNumber)
+	private	void	doFullUpgrade(TransactionController tc, int fromMajorVersionNumber, String
aid)
 		throws StandardException
 	{
 		// Only supports upgrade from Derby 10.0 releases onwards
@@ -360,11 +363,17 @@
                 tc, 
                 bootingDictionary.getSystemUtilSchemaDescriptor().getUUID());
 
+			if (SanityManager.DEBUG)
+				SanityManager.ASSERT((aid != null), "Failed to get new DBA authorization");
+
 			// Add new system catalogs created for grant and revoke
 			bootingDictionary.upgradeMakeCatalog(tc, DataDictionary.SYSTABLEPERMS_CATALOG_NUM);
 			bootingDictionary.upgradeMakeCatalog(tc, DataDictionary.SYSCOLPERMS_CATALOG_NUM);
 			bootingDictionary.upgradeMakeCatalog(tc, DataDictionary.SYSROUTINEPERMS_CATALOG_NUM);
 			bootingDictionary.upgradeMakeCatalog(tc, DataDictionary.SYSREQUIREDPERM_CATALOG_NUM);
+
+			// Change system schemas to be owned by aid
+			bootingDictionary.updateSystemSchemaAuthorization(aid, tc);
         }
         
 	}

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
Mon Mar 13 10:28:20 2006
@@ -132,6 +132,7 @@
 import org.apache.derby.iapi.services.locks.ShExLockable;
 import org.apache.derby.iapi.services.locks.ShExQual;
 import org.apache.derby.iapi.util.StringUtil;
+import org.apache.derby.iapi.util.IdUtil;
 
 import java.util.Calendar;
 import java.util.Date;
@@ -328,6 +329,8 @@
 	/** Dictionary version of the currently running engine */
 	private DD_Version  softwareVersion;
 
+	private String authorizationDBA;
+
 	/*
 	** This property and value are written into the database properties
 	** when the database is created, and are used to determine whether
@@ -636,7 +639,9 @@
 			DataDescriptorGenerator ddg = getDataDescriptorGenerator();
 	
 			if (create) {
-
+				String userName = IdUtil.getUserNameFromURLProps(startParams);
+				authorizationDBA = IdUtil.getUserAuthorizationId(userName);
+			
 				// create any required tables.
 				createDictionaryTables(startParams, bootingTC, ddg);
 				//create procedures for network server metadata
@@ -662,8 +667,14 @@
 			} else {
 				// Get the ids for non-core tables
 				loadDictionaryTables(bootingTC, ddg, startParams);
+				SchemaDescriptor sd = locateSchemaRow(SchemaDescriptor.IBM_SYSTEM_SCHEMA_NAME,
+								 bootingTC);
+				authorizationDBA = sd.getAuthorizationId();
 			}
 					
+			if (SanityManager.DEBUG)
+				SanityManager.ASSERT((authorizationDBA != null), "Failed to get DBA authorization");
+
 			/* Commit & destroy the create database */
 			bootingTC.commit();
 			cm.getContext(ExecutionContext.CONTEXT_ID).popMe(); // done with ctx
@@ -1134,6 +1145,16 @@
 	}
 
 	/**
+	 * Get authorizationID of DBA
+	 *
+	 * @return	authorizationID
+	 */
+	public String getAuthorizationDBA()
+	{
+		return authorizationDBA;
+	}
+
+	/**
 	 * Get a DataValueFactory, through which we can create
 	 * data value objects.
 	 *
@@ -5355,6 +5376,78 @@
 	}
 
 	/**
+	 * Update all system schemas to have new authorizationId. This is needed
+	 * while upgrading pre-10.2 databases to 10.2 or later versions. From 10.2,
+	 * all system schemas would be owned by database owner's authorizationId.
+	 *
+	 * @param aid							AuthorizationID of DBA
+	 * @param tc							TransactionController to use
+	 *
+	 * @exception StandardException		Thrown on failure
+	 */
+	public void updateSystemSchemaAuthorization(String aid,
+												TransactionController tc)
+		throws StandardException
+	{
+		updateSchemaAuth(SchemaDescriptor.STD_SYSTEM_SCHEMA_NAME, aid, tc);
+		updateSchemaAuth(SchemaDescriptor.IBM_SYSTEM_SCHEMA_NAME, aid, tc);
+
+		updateSchemaAuth(SchemaDescriptor.IBM_SYSTEM_CAT_SCHEMA_NAME, aid, tc);
+		updateSchemaAuth(SchemaDescriptor.IBM_SYSTEM_FUN_SCHEMA_NAME, aid, tc);
+		updateSchemaAuth(SchemaDescriptor.IBM_SYSTEM_PROC_SCHEMA_NAME, aid, tc);
+		updateSchemaAuth(SchemaDescriptor.IBM_SYSTEM_STAT_SCHEMA_NAME, aid, tc);
+		updateSchemaAuth(SchemaDescriptor.IBM_SYSTEM_NULLID_SCHEMA_NAME, aid, tc);
+
+		updateSchemaAuth(SchemaDescriptor.STD_SQLJ_SCHEMA_NAME, aid, tc);
+		updateSchemaAuth(SchemaDescriptor.STD_SYSTEM_DIAG_SCHEMA_NAME, aid, tc);
+		updateSchemaAuth(SchemaDescriptor.STD_SYSTEM_UTIL_SCHEMA_NAME, aid, tc);
+	}
+
+	/**
+	 * Update authorizationId of specified schemaName
+	 *
+	 * @param schemaName			Schema Name of system schema
+	 * @param authorizationId		authorizationId of new schema owner
+	 * @param tc					The TransactionController to use
+	 *
+	 * @exception StandardException		Thrown on failure
+	 */
+	public void updateSchemaAuth(String schemaName,
+								 String authorizationId,
+								 TransactionController tc)
+		throws StandardException
+	{
+		ExecIndexRow				keyRow;
+		DataValueDescriptor			schemaNameOrderable;
+		TabInfo						ti = coreInfo[SYSSCHEMAS_CORE_NUM];
+
+		/* Use schemaNameOrderable in both start 
+		 * and stop position for index 1 scan. 
+		 */
+		schemaNameOrderable = dvf.getVarcharDataValue(schemaName);
+
+		/* Set up the start/stop position for the scan */
+		keyRow = (ExecIndexRow) exFactory.getIndexableRow(1);
+		keyRow.setColumn(1, schemaNameOrderable);
+
+		SYSSCHEMASRowFactory	rf = (SYSSCHEMASRowFactory) ti.getCatalogRowFactory();
+		ExecRow row = rf.makeEmptyRow();
+
+		row.setColumn(SYSSCHEMASRowFactory.SYSSCHEMAS_SCHEMAAID,
+					  dvf.getVarcharDataValue(authorizationId));
+
+		boolean[] bArray = {false, true};
+
+		int[] colsToUpdate = {SYSSCHEMASRowFactory.SYSSCHEMAS_SCHEMAAID};
+
+		ti.updateRow(keyRow, row,
+					 SYSSCHEMASRowFactory.SYSSCHEMAS_INDEX2_ID,
+					 bArray,
+					 colsToUpdate,
+					 tc);
+	}
+
+	/**
 	 * Update the conglomerateNumber for an array of ConglomerateDescriptors.
 	 * In case of more than one ConglomerateDescriptor, they are for duplicate
 	 * indexes sharing one conglomerate.
@@ -6139,7 +6232,7 @@
             new SchemaDescriptor(
                 this, 
                 convertIdToLower ? schema_name.toLowerCase() : schema_name, 
-                SchemaDescriptor.SA_USER_NAME,
+                authorizationDBA,
                 uuidFactory.recreateUUID(schema_uuid),
                 true);
 
@@ -8103,7 +8196,7 @@
         return new SchemaDescriptor(
                 this,
                 name,
-                SchemaDescriptor.SA_USER_NAME,
+                authorizationDBA,
                 uuidFactory.recreateUUID(uuid),
                 true);
     }
@@ -8112,7 +8205,7 @@
     {
         return new SchemaDescriptor(this,
                                         name,
-                                        SchemaDescriptor.SA_USER_NAME,
+                						authorizationDBA,
                                         (UUID) null,
                                         false);
     }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CompilerContextImpl.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CompilerContextImpl.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CompilerContextImpl.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CompilerContextImpl.java
Mon Mar 13 10:28:20 2006
@@ -786,12 +786,13 @@
 	 *
 	 * @param SchemaDescriptor
 	 */
-	public void addRequiredSchemaPriv(SchemaDescriptor sd)
+	public void addRequiredSchemaPriv(String schemaName, String aid, boolean privType)
 	{
-		if( requiredSchemaPrivileges == null || sd == null)
+		if( requiredSchemaPrivileges == null || schemaName == null)
 			return;
 
-		StatementSchemaPermission key = new StatementSchemaPermission(sd.getUUID());
+		StatementSchemaPermission key = new 
+				StatementSchemaPermission(schemaName, aid, privType);
 
 		requiredSchemaPrivileges.put(key, key);
 	}

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateSchemaNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateSchemaNode.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateSchemaNode.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateSchemaNode.java
Mon Mar 13 10:28:20 2006
@@ -31,6 +31,9 @@
 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
 import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
 
+import org.apache.derby.iapi.sql.compile.CompilerContext;
+import org.apache.derby.iapi.sql.conn.Authorizer;
+
 import org.apache.derby.iapi.error.StandardException;
 
 import org.apache.derby.iapi.services.monitor.Monitor;
@@ -98,6 +101,20 @@
 		}
 	}
 
+	/**
+	 * Bind this createSchemaNode. Main work is to create a StatementPermission
+	 * object to require CREATE_SCHEMA_PRIV at execution time.
+	 */
+	public QueryTreeNode bind() throws StandardException
+	{
+		super.bind();
+
+		CompilerContext cc = getCompilerContext();
+		cc.addRequiredSchemaPriv(name, aid, Authorizer.CREATE_SCHEMA_PRIV);
+
+		return this;
+	}
+	
 	public String statementToString()
 	{
 		return "CREATE SCHEMA";

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DDLStatementNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DDLStatementNode.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DDLStatementNode.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DDLStatementNode.java
Mon Mar 13 10:28:20 2006
@@ -27,6 +27,7 @@
 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
+import org.apache.derby.iapi.sql.conn.Authorizer;
 import org.apache.derby.iapi.reference.SQLState;
 import org.apache.derby.iapi.error.StandardException;
 
@@ -227,15 +228,18 @@
 		if (sd == null) {
 			/* Disable creating schemas starting with SYS */
 			if (schemaName.startsWith("SYS"))
-				throw StandardException.newException(SQLState.LANG_NO_USER_DDL_IN_SYSTEM_SCHEMA,
-					statementToString(), schemaName);
+				throw StandardException.newException(
+					SQLState.LANG_NO_USER_DDL_IN_SYSTEM_SCHEMA,
+					statementToString(),
+					schemaName);
 
 			sd  = new SchemaDescriptor(getDataDictionary(), schemaName,
 				(String) null, (UUID)null, false);
 		}
 
 		if (ownerCheck)
-			getCompilerContext().addRequiredSchemaPriv(sd);
+			getCompilerContext().addRequiredSchemaPriv(sd.getSchemaName(), null,
+						Authorizer.MODIFY_SCHEMA_PRIV);
 
 		/*
 		** Catch the system schema here.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericAuthorizer.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericAuthorizer.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericAuthorizer.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericAuthorizer.java
Mon Mar 13 10:28:20 2006
@@ -143,13 +143,15 @@
 			if (SanityManager.DEBUG)
 				SanityManager.THROWASSERT("Bad operation code "+operation);
 		}
-
         if( activation != null)
         {
-			List requiredPermissionsList = activation.getPreparedStatement().getRequiredPermissionsList();
-            if( requiredPermissionsList != null && ! requiredPermissionsList.isEmpty())
+            List requiredPermissionsList = activation.getPreparedStatement().getRequiredPermissionsList();
+            DataDictionary dd = lcc.getDataDictionary();
+
+            // DBA can access any object. Ignore requiredPermissionsList for DBA
+            if( requiredPermissionsList != null && ! requiredPermissionsList.isEmpty()
&& 
+				!authorizationId.equals(dd.getAuthorizationDBA()))
             {
-                DataDictionary dd = lcc.getDataDictionary();
                 TransactionController tc = activation.getTransactionController();
                 for( Iterator iter = requiredPermissionsList.iterator();
                      iter.hasNext();)

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/conn/GenericLanguageConnectionContext.java
Mon Mar 13 10:28:20 2006
@@ -309,7 +309,11 @@
 									Property.DEFAULT_CONNECTION_MODE_PROPERTY);
 		if (modeS != null &&
 			 StringUtil.SQLEqualsIgnoreCase(modeS, Property.SQL_STANDARD_ACCESS))
+		{
+			// Raise error if DD version is not less than 10.2
+			getDataDictionary().checkVersion(DataDictionary.DD_VERSION_DERBY_10_2, "sqlAuthorization");
 			usesSqlPermissions = true;
+		}
 
 		setRunTimeStatisticsMode(logQueryPlan);
 
@@ -331,7 +335,7 @@
 	{
 		//
 		//Creating the authorizer authorizes the connection.
-		authorizer = new GenericAuthorizer(getAuthorizationId(userName),this, sqlConnection);
+		authorizer = new GenericAuthorizer(IdUtil.getUserAuthorizationId(userName),this, sqlConnection);
 
 		//we can ignore the following if this is a database connection
 		//associated with internal thread such as logSniffer and StageTrunc
@@ -3098,19 +3102,6 @@
 	public Activation getLastActivation()
 	{
 		return (Activation)acts.lastElement();
-	}
-
-	/**
-		Set the authorization identifier for this user.
-	*/
-	protected String getAuthorizationId(String userName)
-		throws StandardException {
-		try {
-			return IdUtil.parseId(userName);
-		}
-		catch (StandardException se) {
-			throw StandardException.newException(SQLState.AUTH_INVALID_USER_NAME, userName);
-		}
 	}
 
 	public StringBuffer appendErrorInfo() {

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/PrivilegeInfo.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/PrivilegeInfo.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/PrivilegeInfo.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/PrivilegeInfo.java Mon
Mar 13 10:28:20 2006
@@ -23,6 +23,7 @@
 import org.apache.derby.catalog.UUID;
 import org.apache.derby.iapi.sql.dictionary.TupleDescriptor;
 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;
 import org.apache.derby.iapi.services.sanity.SanityManager;
 import org.apache.derby.iapi.sql.Activation;
 import org.apache.derby.iapi.reference.SQLState;
@@ -49,24 +50,28 @@
 		throws StandardException;
 
 	/**
-	 * Determines whether a user is the owner of an object (table, function, or procedure).
+	 * Determines whether a user is the owner of an object
+	 * (table, function, or procedure).
 	 *
 	 * @param user
 	 * @param objectDescriptor
 	 * @param sd
+	 * @param DataDictionary
 	 *
 	 * @exception StandardException if user does not own the object
 	 */
 	protected void checkOwnership( String user,
 								   TupleDescriptor objectDescriptor,
-								   SchemaDescriptor sd)
+								   SchemaDescriptor sd,
+								   DataDictionary dd)
 		throws StandardException
 	{
-		if( ! user.equals( sd.getAuthorizationId()))
-			throw StandardException.newException( SQLState.AUTH_NOT_OWNER,
-												  user,
-												  objectDescriptor.getDescriptorType(),
-												  sd.getSchemaName(),
-												  objectDescriptor.getDescriptorName());
+		if (!user.equals(sd.getAuthorizationId()) &&
+				!user.equals(dd.getAuthorizationDBA()))
+			throw StandardException.newException(SQLState.AUTH_NOT_OWNER,
+									  user,
+									  objectDescriptor.getDescriptorType(),
+									  sd.getSchemaName(),
+									  objectDescriptor.getDescriptorName());
 	}
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RoutinePrivilegeInfo.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RoutinePrivilegeInfo.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RoutinePrivilegeInfo.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RoutinePrivilegeInfo.java
Mon Mar 13 10:28:20 2006
@@ -66,7 +66,8 @@
 		// Check that the current user has permission to grant the privileges.
 		checkOwnership( currentUser,
 						aliasDescriptor,
-						dd.getSchemaDescriptor( aliasDescriptor.getSchemaUUID(), tc));
+						dd.getSchemaDescriptor( aliasDescriptor.getSchemaUUID(), tc),
+						dd);
 		
 		DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/TablePrivilegeInfo.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/TablePrivilegeInfo.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/TablePrivilegeInfo.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/TablePrivilegeInfo.java
Mon Mar 13 10:28:20 2006
@@ -90,7 +90,7 @@
 		TransactionController tc = lcc.getTransactionExecute();
 
 		// Check that the current user has permission to grant the privileges.
-		checkOwnership( currentUser, td, td.getSchemaDescriptor());
+		checkOwnership( currentUser, td, td.getSchemaDescriptor(), dd);
 		
 		DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages_en.properties
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages_en.properties?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages_en.properties (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages_en.properties Mon Mar 13
10:28:20 2006
@@ -1059,6 +1059,7 @@
 2850B=User ''{0}'' does not have execute permission on {1} ''{2}''.''{3}'' for grant.
 2850C=User ''{0}'' is not the owner of {1} ''{2}''.''{3}''.
 2850D=User ''{0}'' can not perform the operation in schema ''{1}''.
+2850E=User ''{0}'' can not create schema ''{1}''. Only database owner could issue this statement.
 04501.C=Database connection refused.
 
 

Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
(original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
Mon Mar 13 10:28:20 2006
@@ -1367,6 +1367,7 @@
 	String AUTH_NO_EXECUTE_PERMISSION_FOR_GRANT                        = "2850B";
 	String AUTH_NOT_OWNER                                              = "2850C";
 	String AUTH_NO_ACCESS_NOT_OWNER                                    = "2850D";
+	String AUTH_NOT_DATABASE_OWNER                                     = "2850E";
 
 	/*
 	** Dependency manager

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/grantRevokeDDL.out
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/grantRevokeDDL.out?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/grantRevokeDDL.out
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/grantRevokeDDL.out
Mon Mar 13 10:28:20 2006
@@ -130,8 +130,8 @@
 ij(SATCONNECTION)> select * from sys.sysschemas where schemaname not like 'SYS%';
 SCHEMAID                            |SCHEMANAME                                         
                                                                            |AUTHORIZATIONID
                                                                                         
                      
 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-xxxxFILTERED-UUIDxxxx|NULLID                                                            
                                                             |DBA                        
                                                                                         
          
-xxxxFILTERED-UUIDxxxx|SQLJ                                                              
                                                             |DBA                        
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|NULLID                                                            
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SQLJ                                                              
                                                             |SATHEESH                   
                                                                                         
          
 xxxxFILTERED-UUIDxxxx|APP                                                               
                                                             |APP                        
                                                                                         
          
 xxxxFILTERED-UUIDxxxx|SATHEESH                                                          
                                                             |SATHEESH                   
                                                                                         
          
 xxxxFILTERED-UUIDxxxx|AUTH_TEST                                                         
                                                             |SATHEESH                   
                                                                                         
          
@@ -337,4 +337,86 @@
 4          
 4          
 2 rows selected
-ij(SWIPERCONNECTION)> 
+ij(SWIPERCONNECTION)> -- Test schema creation authorization checks
+set connection swiperConnection;
+ij(SWIPERCONNECTION)> -- Negative tests. Should all fail
+create schema myFriend;
+ERROR 2850E: User 'SWIPER' can not create schema 'MYFRIEND'. Only database owner could issue
this statement.
+ij(SWIPERCONNECTION)> create schema mySchema authorization me;
+ERROR 2850E: User 'SWIPER' can not create schema 'MYSCHEMA'. Only database owner could issue
this statement.
+ij(SWIPERCONNECTION)> create schema myschema authorization swiper;
+ERROR 2850E: User 'SWIPER' can not create schema 'MYSCHEMA'. Only database owner could issue
this statement.
+ij(SWIPERCONNECTION)> connect 'grantRevokeDDL;user=sam';
+ij(CONNECTION0)> create schema sam authorization swiper;
+ERROR 2850E: User 'SAM' can not create schema 'SAM'. Only database owner could issue this
statement.
+ij(CONNECTION0)> -- Should pass
+create schema authorization sam;
+0 rows inserted/updated/deleted
+ij(CONNECTION0)> connect 'grantRevokeDDL;user=george';
+ij(CONNECTION1)> create schema george;
+0 rows inserted/updated/deleted
+ij(CONNECTION1)> -- Now try as DBA (satheesh)
+set connection satConnection;
+ij(SATCONNECTION)> create schema myFriend;
+0 rows inserted/updated/deleted
+ij(SATCONNECTION)> create schema mySchema authorization me;
+0 rows inserted/updated/deleted
+ij(SATCONNECTION)> create schema authorization testSchema;
+0 rows inserted/updated/deleted
+ij(SATCONNECTION)> select * from sys.sysschemas;
+SCHEMAID                            |SCHEMANAME                                         
                                                                            |AUTHORIZATIONID
                                                                                         
                      
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+xxxxFILTERED-UUIDxxxx|SYSIBM                                                            
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SYS                                                               
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SYSCAT                                                            
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SYSFUN                                                            
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SYSPROC                                                           
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SYSSTAT                                                           
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|NULLID                                                            
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SQLJ                                                              
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SYSCS_DIAG                                                        
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SYSCS_UTIL                                                        
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|APP                                                               
                                                             |APP                        
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SATHEESH                                                          
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|AUTH_TEST                                                         
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|MYDODO                                                            
                                                             |DODO                       
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|DERBY                                                             
                                                             |DERBY                      
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SWIPER                                                            
                                                             |SWIPER                     
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|SAM                                                               
                                                             |SAM                        
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|GEORGE                                                            
                                                             |GEORGE                     
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|MYFRIEND                                                          
                                                             |SATHEESH                   
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|MYSCHEMA                                                          
                                                             |ME                         
                                                                                         
          
+xxxxFILTERED-UUIDxxxx|TESTSCHEMA                                                        
                                                             |TESTSCHEMA                 
                                                                                         
          
+21 rows selected
+ij(SATCONNECTION)> -- Check if DBA can ignore all privilege checks
+set connection swiperConnection;
+ij(SWIPERCONNECTION)> set schema swiper;
+0 rows inserted/updated/deleted
+ij(SWIPERCONNECTION)> create table swiperTab (i int, j int);
+0 rows inserted/updated/deleted
+ij(SWIPERCONNECTION)> insert into swiperTab values (1,1);
+1 row inserted/updated/deleted
+ij(SWIPERCONNECTION)> revoke select on swiperTab from satheesh;
+0 rows inserted/updated/deleted
+ij(SWIPERCONNECTION)> revoke insert on swiperTab from satheesh;
+0 rows inserted/updated/deleted
+ij(SWIPERCONNECTION)> set connection satConnection;
+ij(SATCONNECTION)> -- Should still work, as satheesh is DBA
+select * from swiper.swiperTab;
+I          |J          
+-----------------------
+1          |1          
+1 row selected
+ij(SATCONNECTION)> insert into swiper.swiperTab values (2,2);
+1 row inserted/updated/deleted
+ij(SATCONNECTION)> select * from swiper.swiperTab;
+I          |J          
+-----------------------
+1          |1          
+2          |2          
+2 rows selected
+ij(SATCONNECTION)> grant select on swiper.swiperTab to sam;
+0 rows inserted/updated/deleted
+ij(SATCONNECTION)> revoke insert on swiper.swiperTab from satheesh;
+0 rows inserted/updated/deleted
+ij(SATCONNECTION)> 

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/grantRevokeDDL.sql
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/grantRevokeDDL.sql?rev=385610&r1=385609&r2=385610&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/grantRevokeDDL.sql
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/grantRevokeDDL.sql
Mon Mar 13 10:28:20 2006
@@ -261,3 +261,52 @@
 
 select f_abs(-4) from sys.systables where tablename like 'SYSTAB%';
 
+-- Test schema creation authorization checks
+
+set connection swiperConnection;
+
+-- Negative tests. Should all fail
+create schema myFriend;
+create schema mySchema authorization me;
+create schema myschema authorization swiper;
+
+connect 'grantRevokeDDL;user=sam';
+create schema sam authorization swiper;
+
+-- Should pass
+create schema authorization sam;
+
+connect 'grantRevokeDDL;user=george';
+create schema george;
+
+-- Now try as DBA (satheesh)
+set connection satConnection;
+
+create schema myFriend;
+create schema mySchema authorization me;
+create schema authorization testSchema;
+
+select * from sys.sysschemas;
+
+-- Check if DBA can ignore all privilege checks
+
+set connection swiperConnection;
+
+set schema swiper;
+create table swiperTab (i int, j int);
+insert into swiperTab values (1,1);
+
+revoke select on swiperTab from satheesh;
+
+revoke insert on swiperTab from satheesh;
+
+set connection satConnection;
+
+-- Should still work, as satheesh is DBA
+select * from swiper.swiperTab;
+insert into swiper.swiperTab values (2,2);
+select * from swiper.swiperTab;
+
+grant select on swiper.swiperTab to sam;
+revoke insert on swiper.swiperTab from satheesh;
+



Mime
View raw message