/*
* Accessor.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.Runtime.InteropServices;
namespace MOSSPH
{
///
/// Manages the extraction of properties and content from either a Container or an Item.
/// For a Container, IFilter will emit additional URLs for subsequent reentry.
/// For an Item, IFilter will emit the actual content and properties of the item.
///
public class Accessor : IUrlAccessor, IFilter
{
private ContentEnumerator m_ContentEnumerator = null;
private ArrayList m_Properties = null;
private string m_CurrentText = null;
private ChunkInfo m_CurrentValue = null;
private bool m_bDirectory = false;
private uint m_ulChunkId = 0;
private uint m_lcid = 1033; // TODO. Get this from somewhere.
///
/// Uses pce to prepare the accessor for subsequent calls.
///
///
///
public HRESULT Init(ContentEnumerator pce)
{
DateTime enterTime = Logging.Enter(typeof(Accessor), "Init");
Logging.Information("Accessor::Init(" + pce.Url + ")");
HRESULT hr = (HRESULT)SCODE.S_OK;
string sOperation = null;
try
{
m_ContentEnumerator = pce; // Save my copy of the content enumerator.
if ((m_ContentEnumerator.Type == ContentEnumerator.UriType.Container))
{
m_bDirectory = true;
}
else
{
m_bDirectory = false;
// Download the content for use later.
m_ContentEnumerator.EnsureContent();
sOperation = "adding properties";
// Build property list to chunk-out later.
m_Properties = new ArrayList();
// LaunchUrl
m_Properties.Add(new ChunkInfo(SearchProtocolGuids.PSGUID_QUERY, 9, m_ContentEnumerator.LaunchUrl));
// HREF display text.
m_Properties.Add(new ChunkInfo(SearchProtocolGuids.PSGUID_DAV, "dav:displayname", m_ContentEnumerator.Title)); // TODO. Need to find the right property for the text of the href.
// IDPath.
m_Properties.Add(new ChunkInfo(SearchProtocolGuids.PSGUID_DAV, "urn:schemas-microsoft-com:sharepoint:portal:IDPath", m_ContentEnumerator.IDPath)); // JohnKoz
// Area Guid.
// TODO. Optional. If your content is associated in some way with a portal area, fetch that Area ID here and return it to the indexer.
m_Properties.Add(new ChunkInfo(SearchProtocolGuids.PSGUID_DAV, "urn:schemas-microsoft-com:publishing:Category", m_ContentEnumerator.AreaID.ToString()));
}
}
catch (Exception e)
{
hr = HRESULT.E_FAIL;
Logging.Exception("Accessor::Init (" + sOperation + ")", e);
}
Logging.Leave(typeof(Accessor), "Init", hr.ToString(), enterTime);
return hr;
}
///
/// Explicitly cleans up any resources the accessor allocated.
/// Technically this isn't needed, but old habits die hard.
///
public void Close()
{
Logging.Trace("Accessor::Close enter");
try
{
m_CurrentText = null;
m_CurrentValue = null;
m_Properties = null;
if (m_ContentEnumerator != null)
{
m_ContentEnumerator.Terminate();
m_ContentEnumerator = null;
}
m_bDirectory = false;
}
catch (Exception e)
{
Logging.Exception("Accessor::Close", e);
}
Logging.Trace("Accessor::Close leave");
}
///
/// If asked, returns a reference to the underlying ContentEnumerator used to initialize this accessor.
///
public ContentEnumerator Content
{
get
{
return m_ContentEnumerator;
}
}
#region IUrlAccessor Members
public void AddRequestParameter(ref PROPSPEC pSpec, ref PROPVARIANT pVar)
{
Logging.Trace("Accessor::AddRequestParameter called");
throw new NotImplementedException();
}
public void GetDocFormat(System.IntPtr wszDocFormat, uint dwSize, out uint pdwLength)
{
Logging.Trace("Accessor::GetDocFormat enter");
HRESULT hr = HRESULT.E_NOTIMPL;
uint dwLength = 0;
try
{
if (!m_bDirectory)
{
dwLength = (uint)COMInterop.CopyStringToIntPtr(m_ContentEnumerator.ContentType, wszDocFormat);
hr = (HRESULT)SCODE.S_OK;
}
}
catch (Exception e)
{
Logging.Exception("Accessor::GetDocFormat", e);
}
pdwLength = dwLength;
Logging.Trace("Accessor::GetDocFormat leave (" + hr.ToString() + ")");
if (hr != (HRESULT)SCODE.S_OK)
Marshal.ThrowExceptionForHR((int)hr);
}
public void GetCLSID(out Guid pClsid)
{
Logging.Trace("Accessor::GetCLSID enter");
HRESULT hr = HRESULT.E_NOTIMPL;
pClsid = Guid.Empty;
Logging.Trace("Accessor::GetCLSID leave (" + hr.ToString() + ")");
if (hr != (HRESULT)SCODE.S_OK)
Marshal.ThrowExceptionForHR((int)hr);
}
public void GetHost(System.IntPtr wszHost, uint dwSize, out uint pdwLength)
{
Logging.Trace("Accessor::GetHost called");
pdwLength = 0;
throw new NotImplementedException();
}
///
/// Tells the filter daemon if this piece of content is a container or item.
///
public void IsDirectory()
{
Logging.Trace("Accessor::IsDirectory called (" + m_bDirectory.ToString() + ")");
HRESULT hr = (HRESULT)SCODE.S_OK;
if (!m_bDirectory)
hr = (HRESULT)SCODE.S_FALSE;
if (hr != (HRESULT)SCODE.S_OK)
throw new RETURN_HRESULT(hr);
}
public void GetSize(out ulong pllSize)
{
Logging.Trace("Accessor::GetSize enter");
ulong ulSize = 0;
if (!m_bDirectory)
{
ulSize = m_ContentEnumerator.Size;
Logging.Information("Size: " + ulSize.ToString());
}
pllSize = ulSize;
Logging.Trace("Accessor::GetSize leave");
} // Always return a size. For directories it's zero.
public void GetLastModified(out FILETIME pftLastModified)
{
Logging.Trace("Accessor::GetLastModified enter");
HRESULT hr = HRESULT.E_NOTIMPL;
FILETIME ftLastModified = new FILETIME();
try
{
ftLastModified = COMInterop.DateTimeToFileTime(m_ContentEnumerator.LastModified);
Logging.Trace(m_ContentEnumerator.Url + ", content date: " + m_ContentEnumerator.LastModified.ToString());
hr = (HRESULT)SCODE.S_OK;
}
catch (Exception e)
{
Logging.Exception("Accessor::GetLastModified", e);
}
pftLastModified = ftLastModified;
Logging.Trace("Accessor::GetLastModified leave");
if (hr != (HRESULT)SCODE.S_OK)
Marshal.ThrowExceptionForHR((int)hr);
}
public void GetFileName(System.IntPtr wszFileName, uint dwSize, out uint pdwLength)
{
Logging.Trace("Accessor::GetFileName called");
pdwLength = 0;
throw new NotImplementedException();
}
public void GetSecurityDescriptor(System.IntPtr pSD, uint dwSize, out uint pdwLength)
{
Logging.Trace("Accessor::GetSecurityDescriptor enter");
HRESULT hr = HRESULT.E_NOTIMPL;
uint dwLength = 0;
byte[] bDescriptor = m_ContentEnumerator.SecurityDescriptor;
if (bDescriptor != null && bDescriptor.Length > 0)
{
dwLength = (uint)bDescriptor.Length;
if (dwLength <= dwSize)
{
Logging.Information("Returning security descriptor, len: " + dwLength.ToString());
Marshal.Copy(bDescriptor, 0, pSD, (int)dwLength);
hr = (HRESULT)SCODE.S_OK;
}
else
{
hr = HRESULT.ERROR_INSUFFICIENT_BUFFER;
}
}
pdwLength = dwLength;
Logging.Trace("Accessor::GetSecurityDescriptor leave");
if (hr != (HRESULT)SCODE.S_OK)
throw new RETURN_HRESULT(hr);
}
public void GetRedirectedURL(System.IntPtr wszRedirectedUrl, uint dwSize, out uint pdwLength)
{
Logging.Trace("Accessor::GetRedirectedURL called");
pdwLength = 0;
throw new NotImplementedException();
}
public void GetSecurityProvider(out Guid pSPClsid)
{
Logging.Trace("Accessor::GetSecurityProvider called");
pSPClsid = Guid.Empty;
throw new NotImplementedException();
}
public void BindToStream(out System.Runtime.InteropServices.ComTypes.IStream ppStream)
{
Logging.Trace("Accessor::BindToStream enter");
HRESULT hr = HRESULT.E_NOTIMPL;
System.Runtime.InteropServices.ComTypes.IStream stream = null;
try
{
if (!m_bDirectory)
{
stream = m_ContentEnumerator.Content;
if (stream != null)
hr = (HRESULT)SCODE.S_OK;
}
}
catch (Exception e)
{
Logging.Exception("Accessor::BindToStream", e);
}
ppStream = stream; // Give the filter daemon back a stream to read the file.
Logging.Trace("Accessor::BindToStream leave (" + hr.ToString() + ")");
if (hr != (HRESULT)SCODE.S_OK)
Marshal.ThrowExceptionForHR((int)hr);
}
public void BindToFilter(out IFilter ppFilter)
{
Logging.Trace("Accessor::BindToFilter called");
// Need a filter for either directory or item.
ppFilter = this as IFilter;
}
#endregion
#region IFilter Members
public IFilterReturnCodes Init(IFILTER_INIT grfFlags, uint cAttributes, FULLPROPSPEC[] aAttributes, ref uint pFlags)
{
Logging.Trace("Accessor::IFilter.Init enter");
while (cAttributes > 0)
{
--cAttributes;
Logging.Trace("Guid: " + aAttributes[cAttributes].guidPropSet.ToString());
Logging.Trace("PSKIND: " + aAttributes[cAttributes].psProperty.ulKind.ToString("D6"));
switch (aAttributes[cAttributes].psProperty.ulKind)
{
case PSKIND.PROPID:
Logging.Trace("PropID: " + aAttributes[cAttributes].psProperty.data.propid.ToString());
break;
case PSKIND.LPWSTR:
Logging.Trace("String: " + aAttributes[cAttributes].psProperty.data.lpwstr);
break;
}
}
Logging.Trace("Accessor::IFilter.Init leave");
return IFilterReturnCodes.S_OK;
}
public IFilterReturnCodes GetChunk(ref STAT_CHUNK pStat)
{
Logging.Trace("Accessor::GetChunk enter");
IFilterReturnCodes hr = IFilterReturnCodes.FILTER_E_END_OF_CHUNKS;
pStat = new STAT_CHUNK();
if (m_bDirectory)
{
// Next Container or Item.
m_CurrentText = m_ContentEnumerator.GetNextItem(); //spool out all items within containter first
if (m_CurrentText == null || m_CurrentText == string.Empty) // No more items, try the next container.
{
string dontcare = m_ContentEnumerator.GetNextContainer();
m_CurrentText = m_ContentEnumerator.GetNextItem(); //spool out all items within containter first
}
if (m_CurrentText != null && m_CurrentText.Length > 0) // If we still have an item, process it.
{
pStat.idChunk = ++m_ulChunkId;
pStat.breakType = CHUNK_BREAKTYPE.CHUNK_EOS;
pStat.flags = CHUNKSTATE.CHUNK_TEXT;
pStat.locale = m_lcid;
pStat.attribute.guidPropSet = SearchProtocolGuids.IID_GathererPropset;
pStat.attribute.psProperty.ulKind = PSKIND.PROPID;
pStat.attribute.psProperty.data.propid = (uint)GathererProperties.PID_GTHR_DIRLINK;
pStat.idChunkSource = 0;
pStat.cwcStartSource = 0;
pStat.cwcLenSource = 0;
hr = (IFilterReturnCodes)SCODE.S_OK;
}
}
else
{
if (m_Properties.Count > 0)
{
// Pop a value off the collection.
m_CurrentValue = (ChunkInfo)m_Properties[m_Properties.Count - 1];
m_Properties.Remove(m_CurrentValue);
pStat.idChunk = ++m_ulChunkId;
pStat.breakType = CHUNK_BREAKTYPE.CHUNK_EOS;
pStat.flags = CHUNKSTATE.CHUNK_VALUE;
pStat.locale = m_lcid;
pStat.attribute = m_CurrentValue.PropSpec;
pStat.idChunkSource = 0;
pStat.cwcStartSource = 0;
pStat.cwcLenSource = 0;
hr = (IFilterReturnCodes)SCODE.S_OK;
}
}
Logging.Trace("Accessor::GetChunk leave (" + hr.ToString() + ")");
return hr;
}
public IFilterReturnCodes GetText(ref uint pcwcBuffer, System.IntPtr awcBuffer)
{
Logging.Trace("Accessor::GetText Enter");
IFilterReturnCodes hr = IFilterReturnCodes.FILTER_E_NO_TEXT;
uint cwcBuffer = 0;
if (m_CurrentText != null)
{
Logging.Information("Queuing url: " + m_CurrentText);
cwcBuffer = (uint)COMInterop.CopyStringToIntPtr(m_CurrentText, awcBuffer);
hr = IFilterReturnCodes.FILTER_S_LAST_TEXT;
m_CurrentText = null;
}
pcwcBuffer = cwcBuffer;
Logging.Trace("Accessor::GetText, returning (" + hr.ToString() + ")");
return hr;
}
public IFilterReturnCodes GetValue(ref IntPtr ppPropValue)
{
Logging.Trace("Accessor::GetValue enter (" + ppPropValue.ToString() + ")");
IFilterReturnCodes hr = IFilterReturnCodes.FILTER_E_NO_VALUES;
if (!m_bDirectory)
{
if (m_CurrentValue != null)
{
string sOperation = "Fetching PropVariant";
PROPVARIANT pv = m_CurrentValue.PropVariant;
Logging.Information("Adding Value: " + pv.pwszVal);
try
{
sOperation = "Allocating structure";
ppPropValue = Marshal.AllocCoTaskMem(Marshal.SizeOf(pv));
sOperation = "Coping structure to unmanaged memory";
Marshal.StructureToPtr(pv, ppPropValue, false);
hr = (IFilterReturnCodes)SCODE.S_OK;
}
catch (Exception e)
{
Logging.Exception(sOperation, e);
hr = IFilterReturnCodes.E_FAIL;
}
m_CurrentValue = null;
}
else
hr = IFilterReturnCodes.FILTER_E_NO_MORE_VALUES;
}
Logging.Trace("Accessor::GetValue leave (" + hr.ToString() + ")");
return hr;
}
public IFilterReturnCodes BindRegion(FILTERREGION origPos, ref Guid riid, ref UIntPtr ppunk)
{
Logging.Trace("Accessor::BindRegion called");
IFilterReturnCodes hr = (IFilterReturnCodes)HRESULT.E_NOTIMPL;
return hr;
}
#endregion
}
public class ChunkInfo
{
public Guid GUID;
public FULLPROPSPEC PropSpec;
public PROPVARIANT PropVariant;
public ChunkInfo(Guid guid, UInt32 propid, string sVal) : this(guid, PSKIND.PROPID, propid, VARENUM.VT_LPWSTR, sVal)
{
}
public ChunkInfo(Guid guid, string lpwstr, string sVal) : this(guid, PSKIND.LPWSTR, lpwstr, VARENUM.VT_LPWSTR, sVal)
{
}
public ChunkInfo(Guid guid, PSKIND ulKind, object prop, VARENUM vt, object sVal)
{
PropSpec.guidPropSet = guid;
PropSpec.psProperty = new PROPSPEC();
PropSpec.psProperty.ulKind = ulKind;
switch (ulKind)
{
case PSKIND.PROPID:
PropSpec.psProperty.data.propid = (uint)prop;
break;
case PSKIND.LPWSTR:
PropSpec.psProperty.data.lpwstr = Marshal.StringToCoTaskMemUni((string)prop);
break;
}
PropVariant = new PROPVARIANT();
PropVariant.vt = vt;
switch (vt)
{
case VARENUM.VT_LPWSTR:
PropVariant.pwszVal = (string)sVal;
break;
}
}
}
}
// JohnKoz