Recycle Bin.. Continued

Now that we have handled the Upload and Delete events, let us handle the Restore event. Implemented in a class library which contains similar classes as the earlier library. Again we have a BaseEventSink class which is extended from the default available with the event handler toolkit and the EventSinkdata class 

/*=====================================================================

  File: BaseEventSink.cs

  Summary: Base class for cached event sinks.

---------------------------------------------------------------------

=====================================================================*/

using System;

using System.Xml;

using System.Runtime.InteropServices;

using System.Security.Principal;

using System.IO;

using System.Diagnostics;

using System.Text;

using System.Collections;

using System.Collections.Specialized;

using Microsoft.SharePoint;

using RecycleBin.SharePoint.Configuration;

namespace RecycleBin.SharePoint.EventSink

{

    /// <summary>

    /// Base class for cached event sinks.

    /// Handles impersonation and caching event sink classes to handle multiple events on

    /// the same list.

    /// </summary>

    public class BaseEventSink : Microsoft.SharePoint.IListEventSink

    {

           

        public BaseEventSink()

        {

        }

        /// <summary>

        /// Implementation of the OnEvent method. Windows SharePoint Services will

        /// call this method when an event occurs.

        /// </summary>

        /// <param name="listEvent">The SPListEvent object that describes the event which occured.</param>

        public virtual void OnEvent(Microsoft.SharePoint.SPListEvent listEvent)

        {

                  WriteToFile("The event has been fired. Entered the custom event handler.");

                  WriteToFile("List Event dump:- " + listEvent.ToString());

            if (listEvent == null)

            {

                throw new ArgumentNullException("listEvent", "list event object cannot be null");

            }

           

                  WriteToFile("Getting the cached event sink.");

            BaseEventSink sink = GetCachedEventSink(listEvent);

                  WriteToFile("Got the cached event sink.");

            WindowsImpersonationContext wip = null;

            //Ensure each sink instance handles only one event at a time.

            lock(sink)

            {

                try

                {

                    //Make sure the sink class has the current SPListEvent object

                    sink.EventInfo = listEvent;

                    //Impersonate the appropriate user

                              WriteToFile("Impersonating the administrator");

                    WindowsIdentity id = sink.HandlerIdentity;

                    if (id != null)

                        wip = id.Impersonate();

                              WriteToFile("Impersonation completed.");

                              WriteToFile("Now passing control to the event handler.");

                    sink.HandleEvent();

                   

                }

                finally

                {

                    //Cleanup

               if (wip != null)

                        wip.Undo();

                    if (m_web != null)

                        m_web.Close();

                   

                    //Null out cached SPWeb and SPList objects so that

                    //fresh ones are obtained for the next event.

                    m_web = null;

                    m_list = null;

                }

            }

        }

        /// <summary>

        /// HandleEvent is called when an event occurs. Child classes should perform the main

        /// event handling actions here.

        /// </summary>

            protected void HandleEvent()

            {

                  WriteToFile("Entered the actual event handler.");

                  WriteToFile("The event is of type:- " + EventInfo.Type.ToString());

                  switch (EventInfo.Type)

                  {

                        case SPListEventType.Update:

                        case SPListEventType.Insert:

                              UpdateInsertHandler();

                              break;

                  }

            }

            /// <summary>

            /// Used To restore files from Recycle Bin into the document library of origin

            /// </summary>

            private void UpdateInsertHandler()

            {

                  WriteToFile("Entered the UpdateInserthandler.");

                  string fileLocation = EventInfo.UrlAfter;

                  WriteToFile("Got the file location:- " + fileLocation);

                  SPFile miscFile = EventWeb.GetFile(fileLocation);

// string newFileLoc = fileLocation.Remove(0, strRecycle.Length);

                  string newFileLoc = fileLocation.Remove(0, Configuration.RecycleBinLib.Length+1);

                  WriteToFile("Getting the new location:- " + newFileLoc);

                  try

                  {

                       

// miscFile.MoveTo(string.Concat(strMain,"/",newFileLoc),true);

                        WriteToFile("Moving the file to the new location.");

                  miscFile.MoveTo(string.Concat(Configuration.MainLib,"/",newFileLoc),true);

                        WriteToFile("Move completed.");

     

                  }

                  catch(Exception ex)

                  {

                        PublishException(ex.ToString());

                  }

                 

            }//UpdateInsertHandler

       

        /// <summary>

        /// The SPListEvent object that describes the current event.

        /// </summary>

        protected virtual SPListEvent EventInfo

        {

            get

            {

                return m_ListEvent;

            }

            set

            {

                m_ListEvent = value;

                m_web = null;

                m_list = null;

                m_fileUrl = null;

                m_sinkData = null;

            }

        }

       

        /// <summary>

        /// The SPWeb object for the web site that contains the list

        /// that the event occured on.

        /// </summary>

        protected virtual SPWeb EventWeb

        {

            get

            {

                if (m_web == null)

                {

                    m_web = m_ListEvent.Site.OpenWeb();

               }

                return m_web;

            }

        }

        /// <summary>

        /// The SPList object for the list the event occured on.

        /// </summary>

        protected virtual SPList EventList

        {

            get

            {

     if (m_list == null)

                {

                    m_list = EventWeb.Lists[EventInfo.ListID];

                }

                return m_list;

            }

        }

        /// <summary>

        /// The url of the file the event occured on.

        /// Equals UrlAfter if UrlAfter is not empty. Otherwise equals

        /// UrlBefore.

        /// </summary>

        protected string EventFileUrl

        {

            get

            {

                if (m_fileUrl == null)

                {

                    string webUrl = m_ListEvent.WebUrl == null ? "<NULL>" : m_ListEvent.WebUrl;

                    string webRelUrl = m_ListEvent.UrlAfter;

                    if (webRelUrl == null || webRelUrl.Length == 0)

                    {

                        webRelUrl = (m_ListEvent.UrlBefore == null || m_ListEvent.UrlBefore.Length == 0 ?

                            "<NULL>" : m_ListEvent.UrlBefore);

                    }

                    m_fileUrl = String.Format("{0}/{1}", webUrl, webRelUrl);

                }

                return m_fileUrl;

            }

        }

       

        protected string Data

        {

            get

            {

                if (m_sinkData == null)

                {

                    m_sinkData = m_ListEvent.SinkData;

                }

                return m_sinkData;

            }

        }

            # region Custom Code

            /// <summary>

            /// Converts XML string retrieved from SinkData property into an object

            /// </summary>

            protected EventSinkData Configuration

            {

                  get

                  {

                        try

                        {

                              m_ConfigData = EventSinkData.GetInstance(Data);

                              return m_ConfigData;

                        }

                        catch(Exception ex)

                        {

                              PublishException(ex.ToString());

                        }

      return null;

                  }

            }//Configuration

            /// <summary>

            /// Makes sure that the proper folder structure is in place for copying, moving or inserting a file in to the mirror

            /// document library or the Recycle Bin document library.

            /// </summary>

            /// <param name="web">

            /// Web where the folder structure needs to be built or ensured

            /// </param>

            /// <param name="finalUrl">

            /// Url containing the structure that needs to be built, must contain the trailing "/" if it is not a link to a file.

            /// If it is a link to a file the file name will be stripped and the folder structure ensured.

            /// </param>

            /// <returns>

            /// File path that has been built

            /// </returns>

            protected static string EnsureParentFolder(SPWeb web, string finalUrl)

            {

                  finalUrl = web.GetFile(finalUrl).Url;

                  int x = finalUrl.LastIndexOf("/");

                  string epf = String.Empty;

                  if(x <= -1)

                        return epf;

                  epf = finalUrl.Substring(0, x);

                  SPFolder folder = web.GetFolder(epf);

                  if(folder.Exists)

                        return epf;

            SPFolder curFolder = web.RootFolder;

                  string [] folders = epf.Split('/');

                  for(int i = 0; i < folders.Length; i ++)

                  {

                        curFolder = curFolder.SubFolders.Add(folders[i]);

                  }

                  return epf;

            }//EnsureParentFolder

            protected static void PublishException(string errors)

            {

                  if(errors.Length > 0)

                  {

                        if(!EventLog.SourceExists(SOURCENAME))

                              EventLog.CreateEventSource(SOURCENAME, LOGNAME);

                        EventLog el = new EventLog();

                        el.Source = LOGNAME;

                        el.WriteEntry(errors, EventLogEntryType.Error);

                  }

            }

            # endregion

            # region Windows Impersonation Code

        /// <summary>

        /// The WindowsIdentity class of the identity the Event Sink should impersonate.

        /// </summary>

        protected virtual WindowsIdentity HandlerIdentity

        {

            get

            {

                        m_Identity = CreateIdentity(Configuration.UserName,

                        Configuration.Domain,

                        Configuration.Password);

                       

                        return m_Identity;

            }

        }

        /// <summary>

        /// Helper method to handle creating a WindowsIdentity object from a username / domain / password.

        /// </summary>

        /// <param name="User">The username of the account to impersonate.</param>

        /// <param name="Domain">The domain of the account to impersonate.</param>

        /// <param name="Password">The password of the account to impersonate.</param>

        /// <returns></returns>

        public static WindowsIdentity CreateIdentity(string User, string Domain, string Password)

        {

            // The Windows NT user token.

            IntPtr tokenHandle = new IntPtr(0);

       

            const int LOGON32_PROVIDER_DEFAULT = 0;

            const int LOGON32_LOGON_NETWORK_CLEARTEXT = 3;

            tokenHandle = IntPtr.Zero;

            // Call LogonUser to obtain a handle to an access token.

            bool returnValue = LogonUser(User, Domain, Password,

                LOGON32_LOGON_NETWORK_CLEARTEXT, LOGON32_PROVIDER_DEFAULT,

      ref tokenHandle);

            if (false == returnValue)

            {

                int ret = Marshal.GetLastWin32Error();

                throw new Exception("LogonUser failed with error code: " + ret);

            }

            //The WindowsIdentity class makes a new copy of the token.

            //It also handles calling CloseHandle for the copy.

            WindowsIdentity id = new WindowsIdentity(tokenHandle);

            CloseHandle(tokenHandle);

            return id;

        }

            # endregion

        /// <summary>

        /// Get the cached event sink object to handle this event.

        /// </summary>

        /// <returns>The cached IListEventSink object that should handle the current event.</returns>

        private BaseEventSink GetCachedEventSink(SPListEvent evt)

        {

            BaseEventSink sink = null;

           

            //Syncrhonize both reads and writes to the cache.

            //Even though the Hashtable class is threadsafe for reads, we don't want to have

            //multiple sink instances per list. Otherwise, if many events occur on a list

            //right off the bat, you could get many instances that all have to initialize

            //themselves.

            lock(SinkCache.SyncRoot)

            {

                sink = SinkCache[evt.ListID] as BaseEventSink;

           

                //The cached sink is only fit if it is the same type and has the same data

                //as the current sink.

                if (sink == null ||

                    ! sink.GetType().Equals(this.GetType()) ||

                    evt.SinkData != sink.Data)

                {

                    //Update the cache, throw away the old sink if it exists.

                    SinkCache[evt.ListID] = this;

                    sink = this;

                }

            }

            return sink;

        }

            # region Win32 API calls

       

        [DllImport("advapi32.dll", SetLastError=true)]

        private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,

            int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("kernel32.dll", CharSet=CharSet.Auto)]

        private extern static bool CloseHandle(IntPtr handle);

            # endregion

        protected SPListEvent m_ListEvent;

        private SPWeb m_web;

        private SPList m_list;

        private string m_fileUrl;

        private string m_sinkData;

            // Custom variables start

           

            private EventSinkData m_ConfigData;

            protected static Hashtable SinkCache = new Hashtable();

            private const string SOURCENAME = "Recycle Bin";

            private const string LOGNAME = "Recyclebin.EventSink";

// private string strMain = "TEST";

// private string strRecycle = "Recycle Bin";

            private WindowsIdentity m_Identity = null;

            // Custom variables end

            // Logging

            private static void WriteToFile(String input)

            {

                  try

                  {

                        string filePath=@"C:\sharepoint\Log.txt";

                        FileInfo logFile = new FileInfo(filePath);

                        if(logFile.Exists)

                        {

                              if (logFile.Length >= 100000)

                                    File.Delete(filePath);

                        }

                        FileStream fs = new FileStream(filePath,FileMode.OpenOrCreate, FileAccess.ReadWrite);

                        StreamWriter w = new StreamWriter(fs);

                        w.BaseStream.Seek(0,SeekOrigin.End);

                        w.WriteLine(input);

                        w.WriteLine("--------------------------------------------------------------");

                        w.Flush();

                        w.Close();

                  }

                  catch(System.Exception ex)

                  {

                        PublishException(ex.ToString());

                  }

            }

       

    }

}