/*
* 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