C# Copy files across domains

Recently, I was in need of a means to move a set of files from one server to another. A straight forward task that can be accomplished with the System.IO.File.Copy Method. Unfortunately, my situation wasn’t that simple.  In my case, the source and destination servers were on different domains.  The service I created was running on the source server and the Copy Method would fail unless I could somehow authenticate the service on the destination server.   The answer to my problem was Impersonation. In order to complete the file transfer I needed to impersonate a valid user context on the destination server.  Thankfully, Impersonation can be accomplished by using the System.Security.Principal .WindowsIdentity.Impersonate Method.  The tricky part of this process is getting a   System.Security.Principal.WindowsIdentity for the user to impersonate on the destination domain. Fortunately, the LogonUser Function, found in advapi32.dll, can be used to receive a user token for an authenticated user which can be to reference the WindowsIdentity to impersonate.  Here is a sample based on the code I used:

public static class Class1
    {
        public enum fileaction
        {
            move,
            copy
        }

        // Import the LogonUser method from the advapi32.dll
        [DllImport ( "advapi32.dll", SetLastError = true )]
        public static extern bool LogonUser(
            string lpszUsername,
            string lpszDomain,
            string lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            out IntPtr phToken
            );

        /// <summary>
        /// This method 
        /// </summary>
        /// <param name="user">The user object to impersonate</param>
        /// <param name="sourcepath">The source path for the files to action</param>
        /// <param name="destpath">The destinattion path for the files to action</param>
        /// <param name="searchpattern">The searchpattern to match for the files to action from the source path</param>
        /// <param name="action">The action to perform on the files</param>
        public static void CMIt( UserInfo user, string sourcepath, string destpath, string searchpattern, fileaction action )
        {
            const int LOGON32_LOGON_INTERACTIVE = 2;
            const int LOGON32_LOGON_NETWORK = 3;
            const int LOGON32_LOGON_BATCH = 4;
            const int LOGON32_LOGON_SERVICE = 5;
            const int LOGON32_LOGON_UNLOCK = 7;
            const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
            const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

            try
            {
                IntPtr admin_token;
                WindowsIdentity wid_current = WindowsIdentity.GetCurrent ();
                WindowsIdentity wid_admin;
                WindowsImpersonationContext wic;

                //ensure a trailing backslash for the path passed
                sourcepath = BackslashIt ( sourcepath );
                destpath = BackslashIt ( destpath );

                // Get the list of files
                string[] theFilesToCopy = System.IO.Directory.GetFiles ( sourcepath, searchpattern );

                // Get the user token 
                if ( LogonUser ( user.Username, user.Domain, user.Password, LOGON32_LOGON_NEW_CREDENTIALS, 0, out admin_token ) )
                {
                    wid_admin = new WindowsIdentity ( admin_token );
                    // Impersonate the user
                    wic = wid_admin.Impersonate ();

                    foreach ( string currentFile in theFilesToCopy )
                    {
                        string filename = System.IO.Path.GetFileName ( currentFile );
                        string sourceFile = currentFile;
                        string destFile = destpath + filename;


                        // Perform the action
                        switch ( action )
                        {
                            case fileaction.move:
                                File.Move ( sourceFile, destFile );
                                break;
                            case fileaction.copy:
                                File.Copy ( sourceFile, destFile, false );
                                break;
                            default:
                                throw new Exception ( "Invalid action." );
                        }
                    }
                    wic.Undo ();
                }
            }
            catch
            {
                int ret = Marshal.GetLastWin32Error ();
            }
        }

        /// <summary>
        /// This method ensures that a string has a trailing backslah
        /// </summary>
        /// <param name="s">A string that is to have a trailing backslash</param>
        /// <returns>String with a trailing backslash</returns>
        public static string BackslashIt( string s )
        {
            string t = s.EndsWith ( "\\" ) ? s : s + "\\";
            return t;
        }
    }

The UserInfo object referenced is a quick class I created to hold a list of users’ credentials:

public class UserInfo
    {
        private string username;
        public string Username
        {
            get
            {
                return username;
            }
            set
            {
                username = value;
            }
        }

        private string password;
        public string Password
        {
            get
            {
                return password;
            }
            set
            {
                password = value;
            }
        }

        private string domain;
        public string Domain
        {
            get
            {
                return domain;
            }
            set
            {
                domain = value;
            }
        }


        public UserInfo( string username, string password, string domain )
        {
            this.username = username;
            this.password = password;
            this.domain = domain;
        }
    }



   

SQL: Retrieve a List of Columns in an Index

SQL server Object Views allow for the retrieval of object information within a database. You can query a list of the columns in a table as well as retrieve a list of a table's indexes. Taking this information a bit further you can retrieve a list of the columns contained in an index.

This information is helpful for determining if it is possible to REBUILD a SQL index online (REBUILD WITH ONLINE = ON). Indexes cannot be rebuilt online (fails) if it is an XML index, Spatial index or if it contains Large object data type columns (image, text, ntext, varchar(max), nvarchar(max), varbinary(max), and xml). I used this query as part of a an index maintenance plan to determine if it an index supported an online rebuild.

-- ##################################################
-- get a list of columns in an index
-- ##################################################    
SELECT
	ao.object_id 
	,ao.name AS [object_name] 
	,i.index_id 
	,i.name AS index_name
	,c.column_id
	,c.name AS column_name
	,c.max_length 
	,c.system_type_id
	,s.name
FROM 
	sys.all_objects ao LEFT JOIN sys.indexes i 
		ON (i.object_id = ao.object_id)
	LEFT JOIN sys.index_columns ic 
		ON (ao.object_id = ic.object_id) 
		AND (i.index_id = ic.index_id)
	LEFT JOIN sys.columns c 
		ON (ic.object_id = c.object_id) 
		AND (ic.column_id = c.column_id)
	LEFT JOIN sys.types s 
		ON (c.system_type_id = s.system_type_id)
WHERE 
	(ao.type = N'U')
 
-- ##################################################



   

SQL: Retrieve Table Columns

Expanding on yesterday's post on SQL Server Catalog Views, the following example gets a list of columns in a table within SQL Server Database.

-- ##################################################
-- get a list of columns in a table
-- ##################################################    
SELECT 
	ao.object_id
	,ao.name
	,c.column_id
	,c.name
	,c.system_type_id
	,s.name AS type_name
FROM 
	-- sys.tables can be used to retrieve a list of tables of type 'U'
	sys.all_objects ao LEFT JOIN sys.columns c 
		ON (ao.object_id = c.object_id)
	LEFT JOIN sys.types s 
		ON (c.system_type_id = s.system_type_id)
WHERE 
	(ao.type = N'U')

-- ##################################################