/* * ContentEnumerator.cs * * Microsoft Office SharePoint Server Managed Protocol Handler * * Author: John Kozell (johnkoz@microsoft.com) * Microsoft Coroporation */ using System; using System.Collections; using System.IO; using System.Text; using System.Net; using System.Web; using System.Runtime.InteropServices; using System.Data; using System.Data.SqlClient; namespace MOSSPH { /// /// This class abstracts access to the protocol handlers underlying content, as addressed by a content source URL. /// It is capable of enumerating containers OR items/rows. /// public class ContentEnumerator : Uri, System.Runtime.InteropServices.ComTypes.IStream { public enum UriType { Unknown, Container, Item } private const int S_FALSE = 1; private const string USER_AGENT = "MOSSPH"; // TODO. Private variables to hold references to your content. private IEnumerator m_ContainerEnumerator = null; private IEnumerator m_ItemEnumerator = null; private string m_IDPath = null; private byte[] m_SecurityDescriptor = null; private Stream m_Stream = null; // Holds the contents of the downloaded page. private ulong m_Size = 0; // How big the downloaded page is. private string m_ContentType = "txt"; private UriType m_type = UriType.Unknown; private string m_dbname = ""; private string m_server = ""; private string m_enumstoredProc = ""; private string m_itemstoredProc = ""; private string m_itemID = ""; private string m_itemType = ""; private bool m_bValidContainer = false; private DataSet m_dsContent = new DataSet("content"); private const string PHCODE = "myph"; /// /// Accepts a URL as the address to the desired content source. /// /// // ok assuming URI's are //myph://servername/dbname/enumproc/contentproc/fake.myp?ID=55&TYPE=NA no ID for enum public ContentEnumerator(string sUrl) : base(sUrl) { if (sUrl.ToLower().Contains("fake.txt?id=")) m_type = UriType.Item; else if (sUrl.ToLower().Contains("fake.txt")) m_type = UriType.Container; else throw new ApplicationException("Unknown content type"); string tURL = sUrl.Substring(sUrl.IndexOf("//") + 2); // trim off the excess string[] aURL = tURL.Split("/".ToCharArray()); m_server = aURL[0]; m_dbname = aURL[1]; m_enumstoredProc = aURL[2]; m_itemstoredProc = aURL[3]; string[] parms = this.Query.Split("&".ToCharArray()); Hashtable ht = new Hashtable(); for (int i = 0; i < parms.Length; i++) { string[] brk = parms[i].Split("=".ToCharArray()); ht[Microsoft.JScript.GlobalObject.decodeURI(brk[0]).ToLower()] = Microsoft.JScript.GlobalObject.decodeURI(brk[1]); } if (Type == UriType.Item) { m_itemID = ht["id"].ToString(); m_itemType = ht["type"].ToString(); } Logging.Trace("ContentEnumerator created"); Logging.Information("Creating ContentEnumerator for: " + sUrl); } /// /// Evaluates the underlying URL and decides if it points to a Container or Item. /// public UriType Type { get { return m_type; } } /// /// This method will initialize the enumeration constructs based on the "Type" of the URL. /// /// // public int Init() { DateTime enterTime = Logging.Enter(typeof(ContentEnumerator), "Init"); string sOperation = "Open Content Source"; // Alternative to multiple try/catch's. try { RunStoredProc(); switch (Type) { case UriType.Container: // TODO. Use the URL to connect to the content container and initialize the enumerators. // Containers can contain other containers as well as items, so we need both enumerators here. m_ContainerEnumerator = m_dsContent.Tables.GetEnumerator(); m_ItemEnumerator = null; break; case UriType.Item: break; } } catch (Exception e) { // Use sOperation to log what step the exception was thrown. Logging.Exception(sOperation, e); } Logging.Leave(typeof(ContentEnumerator), "Init", null, enterTime); return 0; } /// /// Content type is interesting only for items. /// public string ContentType { get { string contenttype = null; switch (Type) { case UriType.Item: contenttype = m_ContentType; break; } return contenttype; } } public void EnsureContent() { //need to initialize a stream here. String contstr = m_dsContent.Tables[0].Rows[0]["CONTENT"].ToString(); // in this case your Stored proc combines all your searchable data into one field. byte[] byteArray; UnicodeEncoding uniEncoding = new UnicodeEncoding(); // Create the data to write to the stream. byte[] firstString = uniEncoding.GetBytes(contstr); m_Stream = new MemoryStream(firstString); } public void RunStoredProc() { SqlConnection conn = null; try { conn = new SqlConnection("Server=" + m_server + ";DataBase=" + m_dbname + ";Integrated Security=SSPI"); conn.Open(); SqlCommand cmd; if (Type == UriType.Container) { cmd = new SqlCommand(m_enumstoredProc, conn); } else { cmd = new SqlCommand(m_itemstoredProc, conn); cmd.Parameters.Add(new SqlParameter("@ItemID",m_itemID)); cmd.Parameters.Add(new SqlParameter("@ItemType",m_itemType)); } cmd.CommandType = CommandType.StoredProcedure; SqlDataAdapter da = new System.Data.SqlClient.SqlDataAdapter(cmd); da.SelectCommand.CommandType = CommandType.StoredProcedure; da.Fill(m_dsContent); conn.Close(); // alternatively, conn.Dispose } finally { if (conn != null) { conn.Close(); } } } public void Terminate() { m_SecurityDescriptor = null; m_ItemEnumerator = null; m_ContainerEnumerator = null; Logging.Information("ContentEnumerator terminated"); } #region Properties /// /// Simply returns the text version of the underlying URL. /// public string Url { get { return ToString(); } } /// /// Fetch the DateTime of the last modification to the container or item. /// An invalid type will return a MinValue for the DateTime. /// public DateTime LastModified { get { DateTime lastmodified = Convert.ToDateTime("01.01.1901"); switch (Type) { case UriType.Container: lastmodified = DateTime.Now; // TODO. Exctract last modified date of the container. break; case UriType.Item: try { lastmodified = (DateTime) m_dsContent.Tables[0].Rows[0]["LASTUPDATE"]; } catch (Exception ee) { lastmodified = Convert.ToDateTime("01.01.1901"); } break; } return lastmodified; } } /// /// Return the size of an item. Containers always have a size of zero (0). /// public ulong Size { get { ulong size = 0; switch (Type) { case UriType.Item: try { size = (ulong) m_dsContent.Tables[0].Rows[0]["SIZE"]; } catch (Exception ee) { size = 0; } break; } return size; } } /// /// Provides an IStream interface to read the item content. /// public System.Runtime.InteropServices.ComTypes.IStream Content { get { // This object implements IStream, so we just return a reference to ourself. return this; } } /// /// Content type is interesting only for items. /// public string ContentType2 { get { string contenttype = null; switch (Type) { case UriType.Item: contenttype = m_ContentType; break; } return contenttype; } } /// /// This is the anchor for the URL presented to the user during in their search results. /// The assumption is that it will be different than the Content Source URL privided to the indexer. /// public string LaunchUrl { get { string url = Url; switch (Type) { // how are you planning on access the content after retrieving. Need to create a display content page. case UriType.Container: url = "http://www.microsoft.com"; // TODO. Construct a user navigable URL to your container. break; case UriType.Item: url = "http://www.msn.com"; // TODO. Construct a user navigable URL to your item. break; } return url; } } /// /// The human readable name for the container or item. /// public string Title { get { string title = null; switch (Type) { case ContentEnumerator.UriType.Container: title = "Some Container"; // TODO. Fetch the name of your container. break; case ContentEnumerator.UriType.Item: try { title = m_dsContent.Tables[0].Rows[0]["TITLE"].ToString(); } catch (Exception ee) { title = ee.Message; } break; } return title; } } /// /// True if the URL provided points to a content continer, otherwise False. /// public bool IsContainer { get { return Type == UriType.Container; } } /// /// Where is this container or item logically implemented in a portal area hierarchy. /// In the form of "HomeAreaGuidl:SubAreaGuid1:SubAreaGuid2..." /// public string IDPath { get { if (m_IDPath == null) { m_IDPath = ""; // TODO. Construct your IDPath if necessary. } return m_IDPath; } } /// /// If this piece of content is logically associated with an area, return the area's ID. /// public Guid AreaID { get { return Guid.Empty; // TODO. Fetch the ID of the desired area. } } /// /// Returns a relative Win32 security descriptor to the current Container or Item, as a byte array. /// public byte[] SecurityDescriptor { get { if (m_SecurityDescriptor == null) { DateTime startTime = Logging.Enter(typeof(ContentEnumerator), "SecurityDescriptor-create"); // lets say for the our example here that the stored proc will return a list of users that //are allowed to access the items and they are in domain\user format already :) String owner = m_dsContent.Tables[0].Rows[0]["OWNER"].ToString(); ArrayList al = new ArrayList(); if (m_dsContent.Tables.Count == 2) // second one is security list for (int i = 0;i /// Rewindes the enumeration back to the first container. /// public void ResetContainer() { if (m_ContainerEnumerator != null) { m_ContainerEnumerator.Reset(); } else { Logging.Warning("Logic error. Calling ResetContainer without an enumerator"); } } /// /// Returns the URL of the current sub-container. /// public string CurrentContainer { get { string currentContainer = string.Empty; return currentContainer; } } /// /// Advances to the next sub-container and returns its URL. /// /// public string GetNextContainer() { if (m_ContainerEnumerator != null) { this.m_bValidContainer = m_ContainerEnumerator.MoveNext(); if (this.m_bValidContainer) this.m_ItemEnumerator = ((System.Data.DataTable)m_ContainerEnumerator.Current).Rows.GetEnumerator(); else m_ItemEnumerator = null; } else { Logging.Warning("Logic error, trying to GetNextContainer without an enumerator"); } return string.Empty; } #endregion // Container enumeration #region Item enumeration // TODO. Modify the functions in this region so that the functions perform an itteration through the URLs // of items of the current container (if any). It is possible to have an IEnumerator that // returns an object for which you can derive a URL. /// /// Rewindes the enumeration back to the first item. /// public void ResetItems() { if (m_ItemEnumerator != null) m_ItemEnumerator.Reset(); } /// /// Returns the URL of the current item. /// public string CurrentItem { get { string currentItem = string.Empty; if (m_ItemEnumerator != null) { try { DataRow dr = (System.Data.DataRow)m_ItemEnumerator.Current; //lets assume there is a column call ID for simplicity string id = dr["ID"].ToString(); string itype = dr["ITEMTYPE"].ToString(); // this allows the same stored proceedure to crawl multiple types. currentItem = PHCODE + "://" + m_server + "/" + m_dbname + "/" + m_enumstoredProc + "/" + m_itemstoredProc + "/fake.myp?ID=" + id; } catch { } } return currentItem; } } /// /// Advances to the next item and returns its URL. /// /// public string GetNextItem() { string sReturn = string.Empty; if (m_ItemEnumerator != null) { if (m_ItemEnumerator.MoveNext()) { sReturn = CurrentItem; } } return sReturn; } #endregion // Item enumeration #region IStream members void System.Runtime.InteropServices.ComTypes.IStream.Commit(int grfCommitFlags) { Logging.Trace("ContentEnumerator::Commit called"); } void System.Runtime.InteropServices.ComTypes.IStream.Clone(out System.Runtime.InteropServices.ComTypes.IStream ppstm) { Logging.Trace("ContentEnumerator::Clone called"); ppstm = null; } void System.Runtime.InteropServices.ComTypes.IStream.LockRegion(long libOffset, long cb, int dwLockType) { Logging.Trace("ContentEnumerator::LockRegion called"); } /// /// This is the only method the filter daemon calls from the IStream interface. /// The read method will manage the extraction of the content from the m_Stream member /// previously fetched during EnsureContent. /// /// /// /// public void Read(byte[] pv, int cb, System.IntPtr pcbRead) { Logging.Trace("ContentEnumerator::Read enter"); int hr = S_FALSE; string sOperation = null; try { sOperation = "Read"; int iRead = m_Stream.Read(pv, 0, (int)cb); if (iRead > 0) { // Log some text. sOperation = "Log sample of byte recieved"; char[] bufferSample = new char[200]; for (int i = 0; i < bufferSample.Length; i++) bufferSample[i] = (char)pv[i]; Logging.Trace("Read " + iRead.ToString() + " bytes: " + new string(bufferSample)); hr = 0; } sOperation = "Marshal #bytes read back to caller"; Marshal.WriteInt32(pcbRead, iRead); } catch (Exception e) { Logging.Exception(sOperation, e); hr = S_FALSE; } Logging.Trace("ContentEnumerator::Read leave"); if (hr != 0) { Marshal.ThrowExceptionForHR(hr); } } public void Revert() { Logging.Trace("ContentEnumerator::Revert called"); } public void CopyTo(System.Runtime.InteropServices.ComTypes.IStream pstm, long cb, System.IntPtr pcbRead, System.IntPtr pcbWritten) { Logging.Trace("ContentEnumerator::CopyTo called"); } public void Seek(long dlibMove, int dwOrigin, System.IntPtr plibNewPosition) { Logging.Trace("ContentEnumerator::Seek called"); } void System.Runtime.InteropServices.ComTypes.IStream.UnlockRegion(long libOffset, long cb, int dwLockType) { Logging.Trace("ContentEnumerator::UnlockRegion called"); } void System.Runtime.InteropServices.ComTypes.IStream.SetSize(long libNewSize) { Logging.Trace("ContentEnumerator::SetSize called"); } void System.Runtime.InteropServices.ComTypes.IStream.Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag) { Logging.Trace("ContentEnumerator::Stat called"); pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG(); } public void Write(byte[] pv, int cb, System.IntPtr pcbWritten) { Logging.Trace("ContentEnumerator::Write called"); } #endregion // IStream members #region Optional components #endregion // Optional components. } } // JohnKoz