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