//====START_GENERATED_PROLOG======================================
//
//
//   COMPONENT_NAME: oddataxfer
//
//   CLASSES: none
//
//   ORIGINS: 82,27,94
//
//
//   (C) COPYRIGHT International Business Machines Corp. 1995,1996
//   All Rights Reserved
//   Licensed Materials - Property of IBM
//   US Government Users Restricted Rights - Use, duplication or
//   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
//   	
//   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
//   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
//   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
//   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
//   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
//   OR PERFORMANCE OF THIS SOFTWARE.
//
//====END_GENERATED_PROLOG========================================
//
// @(#) 1.25 nt/src/storage/WLinkSrc.cpp, oddataxfer, od96nt, odnt9712d 10/15/96 15:59:05 [ 3/21/97 17:14:08 ]
/*
	File:		LinkSrc.cpp

	Contains:	Implementation of ODLinkSource class

	Owned by:	Craig Carper

	Copyright:	© 1993 - 1995 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		<41>	 10/8/95	TJ		Fixes Recomended by Refball
		<40>	 10/4/95	jpa		Added missing ODVolatiles [1285799]
		<39>	 10/3/95	TJ		Added Includes so it compiles
		<38>	 10/3/95	TJ		Changes done by RefBall Team
		<37>	  9/8/95	T‚		1281096 FB2:Many constants in ODTypesB
									without kOD prefix!
		<36>	  9/1/95	CC		1278590: Added SetStatus(ODError error).
		<35>	 8/12/95	T‚		1276807 Opt./Bug: use StdTypIO routines for
									portable streaming & smaller footprint
		<34>	  8/3/95	RR		#1257260: Collapse B classes. Remove
									somInit methods. Don't call IsInitialized
									or SubclassResponsibility
		<33>	 7/28/95	CC		1183569: Externalize only when dirty.
		<32>	 7/20/95	CC		1225443: ShowSourceContent: If the section
									isn't registered, register temporarily to
									call GetEditionInfo.
		<31>	 6/12/95	CC		1248090: CloneFromMemoryContainer: Force
									cloning of frame referenced by
									kODPropContentFrame property.
		<30>	 5/26/95	VL		1251403: Multithreading naming support.
		<29>	 5/25/95	jpa		Fixed usage of ODDebug. [1253321]
		<28>	 5/18/95	CC		1238898: Add destFrame argument to
									BeginClone call.
		<27>	 4/27/95	CC		1242555: Removed $5 checkin comment (doh!)
		<26>	 4/14/95	T‚		#1235279 BB: InfoUtil & StdTypIO functions
									should take Environment* and SU* when
									possible
		<25>	 4/11/95	CC		1236452: Added SOM_CATCH to InitLinkSource
									and Release.
		<24>	  3/3/95	CC		1210760 SetAutoUpdate: update edition file
									on change to manual from automatic update.
									1223861 Added GetStatus(); maintain fStatus
									field.
		<23>	  2/7/95	CC		1217192: ReadLinkEdition must call
									somSelf->SetChangeTime AFTER closing the
									edition file.
		<22>	 1/19/95	CC		1210953 Added override of SetSourcePart.
									1193561 Code review changes.
		<21>	12/22/94	CC		1153759 UseLinkEdition - call ODDRaft::
									SetChangedFromPrev().
		<20>	12/20/94	VL		1195012: Make Storage calls be
									marshallable.
		<19>	12/16/94	CC		1203516 Removed ODLink parameter from
									InitLinkSource().
		<18>	 12/8/94	CC		1154711 Use ">= kODSharedWrite" to check
									for write access.
		<17>	10/10/94	CC		1160355 - Don't register with Edition mgr.
									if draft is read only.
		<16>	 10/7/94	CC		1191089 - Cross-document updates get new
									unique ID.
		<15>	 10/4/94	CC		1190862 - Added override of Release().
		<14>	 9/29/94	RA		1189812: Mods for 68K build.
		<13>	 9/23/94	VL		1184272: ContainerID is now a sequence of
									octets.
		<12>	 9/15/94	CC		1186669 - Implemented CloneInto().
		<11>	  9/7/94	CC		#1185114 - Catch throws from KeyValid().
		<10>	 8/26/94	VL		1183174: Use updated cloning APIs.
		 <9>	 8/15/94	T‚		#1180922 Removed most obsolete types from
									StdTypes.idl
		 <8>	 8/10/94	CC		1179943 - Moved platform-independent
									implementation into LinkSrcB.cpp.
		 <7>	  8/5/94	CC		#1179160 - ContentUpdated() takes ODLinkKey
									parameter and sets fContentUpdated instead
									of updating dependents immediately;
										Unlock() updates dependents if contents
									have changed while locked;
										#1153789 (partial implementation) -
									Clear() iterates over values in contents
									property and deletes all data;
		 <6>	  8/3/94	VL		1153123: Storage to ODStor.
		 <5>	 6/24/94	CC		Clean up some easy $5.
		 <4>	 6/24/94	CC		Added call to ShowLinkSourceInfo().
		 <3>	 6/24/94	CC		Changed parameters to InitBaseLinkSource().
		 <2>	 6/21/94	CC		Temporarily remove #include of LinkDlgs.h.
		 <1>	 6/21/94	CC		first checked in

		------------------- CC ---- Converted to SOM

		 <6>	  5/9/94	MB		#1162181: Changes necessary to install MMM.
		 <5>	  4/7/94	CC		SetSourcePart: Allow source part to be nil;
									Externalize: Remove source part property
									source part is nil.  (1154858)
		 <4>	  4/4/94	CC		CloneFromMemoryContainer: Changed
									SetValue() to GetValue() to read
									modification date. (1154449)
		 <3>	 3/28/94	CG		1153547: XMPSessn.h renamed to XMPSessM.h
		 <2>	 3/27/94	T‚		#1153523.  Adjust name of
									kXMPPropRootPartSU to more clearly indicate
									what it is supposed to be used for.
		 <1>	 3/25/94	CC		first checked in

		------------------- CC ---- Moved from Core to Storage subsystem

		 <5>	 3/24/94	CC		Implemented CloneTo() (1153038) and
									GetChangeTime() (115340); renamed
									SetAutoExport() and IsAutoExport() to
									SetAutoUpdate() and IsAutoUpdate()
									(1142938).
		 <4>	 3/14/94	CC		Added SetSourcePart(). (1149440)
		 <3>	  3/1/94	JA		Include ODMemory.h (1147588)
		 <2>	  3/1/94	CC		Replaced calls to HLock, HUnlock, and
									GetHandleSize with XMP equivalents.
		 <9>	  2/8/94	T‚		Throw -> THROW & some code clean up
		 <8>	  2/7/94	T‚		more fixes to compile with PPC Headers
		 <7>	  2/4/94	T‚		fixes to compile with PPC Universal headers
		 <6>	  2/3/94	CG		System Session break out.
		 <5>	  2/1/94	CC		Pass ODCloneKind argument to BeginClone.
		 <4>	 1/28/94	CC		Added ReleaseAll(); InitLinkSource does not
									increment ref count of argument link
									object.
		 <3>	 1/27/94	VL		Used ODStorageUnit::CloneInto.
		 <2>	 1/22/94	CC		Don't call release() in destructor.
		 <1>	 1/21/94	CC		first checked in
		<17>	 1/18/94	CG		Added include for StorgDef.h
		<16>	 1/18/94	CC		Use kXMPScrapTypePart; release draft
									properties on failure in
									CloneToMemoryContainer and
									CloneFromMemoryContainer
		<15>	 1/14/94	CC		Added Lock(), Unlock(), GetTimeStamp(),
									GetStatus(), SetAutoExport(),
									IsAutoExport(), Export(),
									ShowSourceContent(), type ODLinkKey; added
									key parameter to various routines; renamed
									Edition Manager support routines.
		<14>	 1/11/94	T‚		Init... changes
		<13>	12/22/93	CC		Maintain persistent fields fContentSU and
									fChangedDate.  Read/write link content
									from/to edition file by cloning fContentSU
									into a memory container.  Increment link
									ref count for registered sections.  Write
									edition files only on Externalize().
		<12>	12/21/93	VL		Changed ODStorageUnit::GetValueSize to
									StorageUnit::GetSize.
		<11>	12/16/93	CC		Added methods to create, write, and read
									Edition files.  Save Edition Manager
									section records and aliases persistently.
		<10>	12/14/93	T‚		InitLink changes.  Internalize, Initialize
									went away.
		 <9>	 12/8/93	CC		ODValueLink -> ODPartListLink to avoid
									name conflict.  Removed multiple
									constructors.  Added InitPartListLink
		 <8>	11/18/93	T‚		call inherited::InitializePersistent()
									::Externalize()
		 <7>	11/17/93	CC		RegisterDependent calls the LinkUpdated
									method of its part argument.
		 <6>	 9/29/93	VL		Added InitializePersistent.
		 <5>	  7/1/93	NP		Abstract/Concrete breakout.
		 <4>	 6/10/93	NP		Added comments.
		 <3>	 4/28/93	NP		File name changes.
		 <2>	 4/27/93	NP		Now compilable.
		 <1>	 4/21/93	NP		first checked in

	To Do:
	In Progress:
*/

#define VARIABLE_MACROS

#ifndef __TIME_H__
#include <time.h>
#endif

#define ODLinkSource_Class_Source
#include <LinkSrc.xih>

#ifndef SOM_ODLink_xh
#include <Link.xh>
#endif

#ifndef _EXCEPT_
#include <Except.h>
#endif

#ifndef _ODMEMORY_
#include <ODMemory.h>
#endif

#ifndef _CONSTDEF_
#include <ConstDf.h>
#endif

#ifndef SOM_ODLinkManager_xh
#include <LinkMgr.xh>
#endif

#ifndef SOM_ODPart_xh
#include <Part.xh>
#endif

#ifndef SOM_ODSession_xh
#include <ODSessn.xh>
#endif

#ifndef SOM_ODContainer_xh
#include <ODCtr.xh>
#endif

#ifndef SOM_ODDocument_xh
#include <Document.xh>
#endif

#ifndef SOM_ODDraft_xh
#include <Draft.xh>
#endif

#ifndef SOM_ODStorageSystem_xh
#include <ODStor.xh>
#endif

#ifndef _PLFMFILE_
#include <PlfmFile.h>
#endif

#ifndef SOM_ODStorageUnit_xh
#include <StorageU.xh>
#endif

#ifndef SOM_ODTranslation_xh
#include <Translt.xh>
#endif

#ifndef SOM_Module_OpenDoc_StdProps_defined
#include <StdProps.xh>
#endif

#ifndef SOM_Module_OpenDoc_StdTypes_defined
#include <StdTypes.xh>
#endif

#ifndef SOM_DocumentManager_xh
#include <DocMgr.xh>
#endif

#ifndef _ISOSTR_
#include <ISOStr.h>
#endif

#ifndef __ERRORS__
#include <Errors.h>
#endif

#ifndef _ODUTILS_
#include <ODUtils.h>
#endif

#ifndef _DOCUTILS_
#include <DocUtils.h>
#endif

#ifndef _ODDEBUG_
#include <ODDebug.h>
#endif

#ifndef _BARRAY_
#include <BArray.h>
#endif

#ifndef _STDTYPIO_
#include <StdTypIO.h>
#endif

#ifndef _STORUTIL_
#include <StorUtil.h>
#endif

#ifndef _TEMPOBJ_
#include "TempObj.h"
#endif

#ifndef _ODUTILW_
#include "ODUtilW.h"
#endif

#ifndef _OLEWRAP_H_
#include "OleWrap.h"
#endif

#ifndef _OLEDATA_H_
#include "OleData.h"
#endif

#ifndef _OLEXFER_H_
#include "OleXfer.h"
#endif

#ifndef _OLEMGRW_ 
#include "OleMgrW.h"   
#endif   

#include <wcstr.h> // for wcslen function

#pragma segment ODLinkSource

// forward declarations for functions
void CopyFromMemoryContainer(Environment *ev,
		ODStorageUnit* contentSU,
		ODHandle containerHandle);

//==============================================================================
// LinkSrcAdviseSink
//==============================================================================

class LinkSrcAdviseSink : public IAdviseSink
{
public:
	ULONG               fcRef;

protected:
	ODLinkSource		*_fLinkSrc;
	HWND				fhwndNotify;	// Bogus window handle
	
public:
	LinkSrcAdviseSink(ODLinkSource*, LPUNKNOWN, HINSTANCE); // HINSTANCE added rrk 24 apr 96 
	~LinkSrcAdviseSink(void);
	
	STDMETHODIMP QueryInterface(REFIID, LPVOID*);
	STDMETHODIMP_(ULONG) AddRef(void);
	STDMETHODIMP_(ULONG) Release(void);
	
	STDMETHODIMP_(void)  OnDataChange(LPFORMATETC, LPSTGMEDIUM);
	STDMETHODIMP_(void)  OnViewChange(DWORD, LONG);
	STDMETHODIMP_(void)  OnRename(LPMONIKER);
	STDMETHODIMP_(void)  OnSave(void);
	STDMETHODIMP_(void)  OnClose(void);
};

//==============================================================================
// ODLinkSource
//==============================================================================

//------------------------------------------------------------------------------
// ODLinkSource: somUninit
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourcesomUninit(ODLinkSource *somSelf)
{
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","somUninit");

	if (_fdwConn)
 		((IDataObject*)_fpDataObj)->DUnadvise(_fdwConn);

#ifdef LATER
	if (_fpmkFull)
		OleStdRelease((IUnknown*)_fpmkFull);
	if (_fpmkRelative)
		OleStdRelease((IUnknown*)_fpmkRelative);
	if (_fpDataObj)
		OleStdRelease((IUnknown*)_fpDataObj);
	if (_fpAdviseSink)
		OleStdRelease((IUnknown*)_fpAdviseSink);
#else
	if (_fpmkFull)
                ((LPUNKNOWN)_fpmkFull)->Release();
	if (_fpmkRelative)
                ((LPUNKNOWN)_fpmkRelative)->Release();
	if (_fpDataObj)
                ((LPUNKNOWN)_fpDataObj)->Release();
	if (_fpAdviseSink)
                ((LPUNKNOWN)_fpAdviseSink)->Release();
#endif

	if(_fPromiseKeeper)
		delete _fPromiseKeeper;

        parent_somUninit(somSelf);
}

//------------------------------------------------------------------------------
// ODLinkSource: InitLinkSource
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourceInitLinkSource(ODLinkSource *somSelf, Environment *ev,
		ODStorageUnit* storageUnit,
		ODPart* sourcePart)
{
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","InitLinkSource");

	SOM_TRY
	  _fSourceNeedsClosing = kODFalse; // rrk 6 sep 96

	/* Moved from somInit. SOM itself sets fields to zero
	_fSection = (SectionHandle) kODNULL;
	_fStatus = 0;
	_fDirty = kODFalse;
	*/
	somSelf->InitBaseLinkSource(ev, storageUnit, sourcePart);

	somSelf->SetLinkSourceDirty(ev);

	// rrk 19 apr 96
	//	_fSession	= gpODShell->GetSession( ev );
	_fSession = somSelf->GetStorageUnit(ev)->GetSession(ev);

        // rrk 6 jun 96 currentkey never initialized
	_fCurrentKey = 42; // Hey, that's the value Novell used!
	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

//------------------------------------------------------------------------------
// ODLinkSource: InitLinkSourceFromStorage
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourceInitLinkSourceFromStorage(ODLinkSource *somSelf, Environment *ev,
		ODStorageUnit* storageUnit)
{
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","InitLinkSourceFromStorage");

	HRESULT			hr;
	FORMATETC		fe;
	ODPlatformType	platformType;
	ODLinkKey key;

	SOM_TRY
	  _fSourceNeedsClosing = kODFalse; // rrk 6 sep 96

	/* Moved from somInit. SOM itself sets fields to zero
	_fSection = (SectionHandle) kODNULL;
	_fStatus = 0;
	_fDirty = kODFalse;
	*/
	somSelf->InitBaseLinkSourceFromStorage(ev, storageUnit);

	/* Read in the full and relative monikers
	 */
  	if (storageUnit->Exists(ev,kODPropMonikerFull,kODMoniker, 0))
		somSelf->RestoreMoniker(ev,storageUnit,kODPropMonikerFull,&_fpmkFull);
  	if (storageUnit->Exists(ev,kODPropMonikerRelative,kODMoniker, 0))
		somSelf->RestoreMoniker(ev,storageUnit,kODPropMonikerRelative,&_fpmkRelative);

  
        if (_fpmkFull) {
	  // We have a moniker -- this means that the real source of this link is in another process.
	  // It may or may not be running right now, but we want to bring it up to see if there have
	  // been any changes.  The "simple" way to do this is to bind the moniker, but that gets us
	  // into trouble -- the registry says that the OleWrapper can run InProc, so that's what Ole
	  // tries to do.  This results in serious ugliness as we try to start a second document in
	  // our document shell.  
	  //
	  // So instead of the simple way, we do things the hard way:
	  //  1) Query the RunningObjectTable to see if the target is currently running.  To do this
	  //     requires that we:
	  //       a) get the running object table
	  //       b) the the DisplayName for our full moniker (file!item)
	  //       c) convert it to ASCII
	  //       d) strip off the !item part by looking for "ODLink," set up in WLinkSpc.cpp
	  //       e) create a file moniker from the remaining left-hand moniker
	  //       f) ask the ROT to GetObject on this moniker.
	  //  2) If the GetObject fails, the thing must not be running -- we try to bring it up:
	  //       a) Get the CLSID from the moniker via GetClassFile (should be OleWrapper, but we do it anyway..)
	  //       b) Launch a local server via CoCreateInstance.  This is the whole reason we are doing all this
	  //          happy stuff - to get a local server.
	  //       c) Ask the IPersistFile interface of our new local server to perform a Load of the file.
	  //  3) At this point we proceed with common code - executed whether the source was running or not.
	  //     We bind the moniker, set up the advise sink, etc......
	        LPMONIKER  pmkFile = NULL;
	        LPBC       pbcFull;
                LPOLESTR   psz = NULL;   
		LPOLESTR   pszFile = NULL;
         	LPRUNNINGOBJECTTABLE prot;	
		LPUNKNOWN punk;
		CLSID theCLSID;
		_fSourceDocumentIPersistFile = NULL;

        	hr = GetRunningObjectTable((DWORD) NULL, &prot);
                CreateBindCtx(0, (LPBC FAR*)&pbcFull);  
                ((LPMONIKER)_fpmkFull)->GetDisplayName(pbcFull, NULL, &psz);    
				/* Convert Unicode to ANSII
									 */
	        ODULong cbString  = (wcslen(psz)+1) * sizeof(OLECHAR);
		LPSTR pszTemp		= (char *)ODNewPtr(cbString);
		if (pszTemp)
		   {
		      W2A((LPOLESTR)psz,pszTemp,cbString);
		   }
#if DEBUG
		PRINT("InitLinkSourceFromStorage: GetDisplayName on full moniker yields:\n\t\"%s\".\n", pszTemp);
#endif
		wchar_t* xx = wcsstr(psz, OLESTR("ODLink"));
		size_t fileLength = xx - psz;
#if DEBUG
                PRINT("InitLinkSourceFromStorage: ODLink found at position %d.\n", xx);
                PRINT("InitLinkSourceFromStorage: difference is %d.\n", fileLength);
#endif
	        cbString  = (fileLength * sizeof(OLECHAR));
		pszFile = (LPOLESTR)ODNewPtrClear(cbString);
		wcsncpy(pszFile, psz, fileLength-1);

        	hr = CreateFileMoniker(pszFile, &pmkFile);
#if DEBUG
		ULONG numEaten;
                PRINT("InitLinkSourceFromStorage: CreateFileMoniker returned %x; numEaten is %d, pmkFile is %x.\n", hr, numEaten, pmkFile);
#endif
		hr = prot->GetObject(pmkFile, &punk);
                if (FAILED(hr)) {  // try to bring up the source document
#ifdef USE_OLESHELL
		  hr = GetClassFile(pszFile, &theCLSID);
		  if (SUCCEEDED(hr)) {
		     hr = CoCreateInstance(theCLSID, 
					   NULL, 
					   CLSCTX_LOCAL_SERVER, 
					   IID_IPersistFile, 
					   &_fSourceDocumentIPersistFile);
		     if (SUCCEEDED(hr)) {
	               _fSourceNeedsClosing = kODTrue; // rrk 6 sep 96
		       hr = ((LPPERSISTFILE)_fSourceDocumentIPersistFile)->Load(pszFile, 
									      STGM_READWRITE | STGM_SHARE_DENY_READ );
		       if (FAILED(hr)) {
#ifdef DEBUG
			 PRINT("InitLinkSourceFromStorage: IPersistFile::Load failed: HRESULT %x.\n", hr);
#endif
                         return;
                       }
                       else {
#ifdef DEBUG
			 PRINT("InitLinkSourceFromStorage: IPersistFile::Load succeeded.\n");
#endif
                       }
		     }
                     else {
#ifdef DEBUG
		       PRINT("InitLinkSourceFromStorage: CoCreateInstance failed: HRESULT %x.\n", hr);
#endif
                       return;
		       }
                   }
		   else {
#ifdef DEBUG
		     PRINT("InitLinkSourceFromStorage: GetClassFile failed: HRESULT %x.\n", hr);
#endif
                     return;
		     }
#else  
		  STARTUPINFO si;
                  ZeroMemory(&si, sizeof(si));
		  si.dwFlags = STARTF_USESHOWWINDOW;
		  si.wShowWindow = SW_HIDE;
                  si.cb = sizeof(si);
		  PROCESS_INFORMATION pi;
		  BOOL bInheritHandles = TRUE;
		  DWORD dwCreationFlags = kODNULL;
		  W2A((LPOLESTR)pszFile,pszTemp,cbString);

		  char cmdline[100];
		  strcpy(cmdline, "docshell.exe -f ");
		  strcat(cmdline, pszTemp);
		  BOOL rc = CreateProcess(NULL,
					  cmdline,
					  NULL,
					  NULL,
					  bInheritHandles,
					  dwCreationFlags,
					  NULL,                       // use caller's environment block
					  NULL,                       // use caller's current drive and directory
					  &si,                        // pointer to startup information
					  &pi);                       // pointer to (received) process information
		  if (!rc) {
#ifdef DEBUG
		    PRINT("InitLinkSourceFromStorage: CreateProcess failed.\n");
		    DWORD errRc = GetLastError();
		    PRINT("InitLinkSourceFromStorage: GetLasteError returned %x.\n", errRc);
#endif
                    return;
                  }

                  DWORD dwRC  = WaitForInputIdle(pi.hProcess, INFINITE);
		  if (dwRC == -1) {
#ifdef DEBUG
		    PRINT("InitLinkSourceFromStorage: WaitForInputIdle failed.\n");
#endif
                    return;
                  }
#endif // USE_OLESHELL
		  }

        	_fSession = somSelf->GetStorageUnit(ev)->GetSession(ev);
         

#ifdef DEBUG
		PRINT("!----> LinkSource::InitLinkSourceFromStorage about to bind to moniker...\n");
#endif 
		hr	= BindMoniker((LPMONIKER)_fpmkFull,NULL,IID_IDataObject,(void**)&_fpDataObj);
#ifdef DEBUG
		PRINT("... moniker bound.\n");
#endif

		if(SUCCEEDED(hr))
		  {
		    somSelf->Lock(ev, 0, &key);

		    ((IUnknown*)_fpmkFull)->AddRef();

		    /* Create our advise sink, if it doesn't exist
		     */
		    if(!_fpAdviseSink)
		      {
			_fpAdviseSink	= (LPVOID)new LinkSrcAdviseSink(somSelf,
									NULL,
									_fSession->GetWindowSystemData(ev)->instance);
			if(_fpAdviseSink)
			  ((IUnknown*)_fpAdviseSink)->AddRef();
		      }

		    /* Make sure we are notified if the data object is modified
		     */
		    platformType = _fSession->GetTranslation(ev)->GetPlatformTypeFromISOType(ev,kODApplePart);
		    SETDEFAULTFORMATETC(fe,(unsigned long)platformType,TYMED_HGLOBAL);
		    if(_fpAdviseSink)
		      ((IDataObject*)_fpDataObj)->DAdvise(&fe,
							  ADVF_NODATA,
							    (IAdviseSink*)_fpAdviseSink,		
							    &_fdwConn);			
						
		    /* Initialize our content storage unit
		     */
		    somSelf->ReadMoniker(ev, key);

		    somSelf->SetLinkSourceDirty(ev);
		   
		    somSelf->Unlock(ev, key);
		  }

		else {
#ifdef DEBUG
		  PRINT("Error on BindMoniker: %08lx.\n", hr);
		  LPVOID lpBuffer;
		  FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
			     NULL,
			     hr,
			     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			     (LPTSTR) &lpBuffer, 0 , NULL);
		  PRINT("Error was: \"%s\".\n", lpBuffer);
#endif
	          if (_fSourceNeedsClosing && _fSourceDocumentIPersistFile) {
	             //  We were brought up the source document, but can't bind to the moniker.  Take it down.
                     LPOLEOBJECT linkOleObject;
                     HRESULT hr = ((LPPERSISTFILE)_fSourceDocumentIPersistFile)->QueryInterface(IID_IOleObject, 
										      (LPVOID*) &linkOleObject);

	            if (SUCCEEDED(hr))
	              hr = linkOleObject->Close(OLECLOSE_SAVEIFDIRTY);
		  }
		}
	}
	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}
  
//------------------------------------------------------------------------------
// ODLinkSource: Release (OVERRIDE)
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourceRelease(ODLinkSource *somSelf, Environment *ev)
{
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","Release");

	SOM_TRY

	parent_Release(somSelf, ev);

#if ODDebug
	somPrintf("ODLinkSource %d::Release: Ref count is %d\n", somSelf->GetID(ev), somSelf->GetRefCount(ev));
#endif

	if ( somSelf->GetRefCount(ev) == 0 )
		somSelf->GetStorageUnit(ev)->GetDraft(ev)->ReleaseLinkSource(ev, somSelf);

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

//------------------------------------------------------------------------------
// ODLinkSource: Externalize (OVERRIDE)
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourceExternalize(ODLinkSource *somSelf, Environment *ev)
{
	BOOL				rc			= FALSE;
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","Externalize");

	SOM_TRY

    parent_Externalize(somSelf,ev);

	if ( _fDirty )
	{

		ODStorageUnit* su = somSelf->GetStorageUnit(ev);


		/* Save the full and relative monikers, if they exist (Windows specific)
		 */
		if (_fpmkFull)
		  {
		    rc	= somSelf->StoreMoniker(ev,su,kODPropMonikerFull,_fpmkFull);
		    if (_fpmkRelative && rc)
		             rc	= somSelf->StoreMoniker(ev,su,kODPropMonikerRelative,_fpmkRelative);
		  }

		if (!rc)
		  {
		    if (su->Exists(ev,kODPropMonikerFull, 0, 0))
		      {
  			su->Focus(ev,kODPropMonikerFull, kODPosUndefined,
				  (ODValueType) 0, (ODValueIndex) 0, kODPosUndefined);
  			su->Remove(ev);
		      }
		    if (su->Exists(ev,kODPropMonikerRelative, 0, 0))
		      {
  			su->Focus(ev,kODPropMonikerRelative, kODPosUndefined,
				  (ODValueType) 0, (ODValueIndex) 0, kODPosUndefined);
  			su->Remove(ev);
		      }
		  }
	}

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

//------------------------------------------------------------------------------
// ODLinkSource: CloneInto (OVERRIDE)
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourceCloneInto(ODLinkSource *somSelf, Environment *ev,
		ODDraftKey key,
		ODStorageUnit* toSU,
		ODFrame* scopeFrame)
{
	BOOL				rc			= FALSE;
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","CloneInto");

	SOM_TRY

        parent_CloneInto(somSelf, ev, key, toSU, scopeFrame);
	/* Save the full and relative monikers, if they exist (Windows specific)
	 */
 	if (_fpmkFull)
	{
		rc	= somSelf->StoreMoniker(ev,toSU,kODPropMonikerFull,_fpmkFull);
		if (_fpmkRelative && rc)
			rc	= somSelf->StoreMoniker(ev,toSU,kODPropMonikerRelative,_fpmkRelative);
	}

	if (!rc)
  	{
  		if (toSU->Exists(ev,kODPropMonikerFull, 0, 0))
  		{
  			toSU->Focus(ev,kODPropMonikerFull, kODPosUndefined,
					  (ODValueType) 0, (ODValueIndex) 0, kODPosUndefined);
  			toSU->Remove(ev);
  		}
  		if (toSU->Exists(ev,kODPropMonikerRelative, 0, 0))
  		{
  			toSU->Focus(ev,kODPropMonikerRelative, kODPosUndefined,
					  (ODValueType) 0, (ODValueIndex) 0, kODPosUndefined);
  			toSU->Remove(ev);
  		}
  	}

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

//------------------------------------------------------------------------------
// ODLinkSource: ReleaseAll (OVERRIDE)
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourceReleaseAll(ODLinkSource *somSelf, Environment *ev)
{
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","ReleaseAll");

	SOM_TRY

    parent_ReleaseAll(somSelf,ev);


	if (_fpmkFull)
	{

	  if (_fSourceNeedsClosing && _fSourceDocumentIPersistFile) {
	     //  We were responsible for bringing the source document up, so we will now take it down.
             LPOLEOBJECT linkOleObject;
             HRESULT hr = ((LPPERSISTFILE)_fSourceDocumentIPersistFile)->QueryInterface(IID_IOleObject, 
										      (LPVOID*) &linkOleObject);
#if DEBUG
	     PRINT("ODLinkSource:ReleaseAll: QueryInterface yields hr %x, linkOleObject %x.\n", hr, linkOleObject);
#endif
	       if (SUCCEEDED(hr)) {
	         hr = linkOleObject->Close(OLECLOSE_SAVEIFDIRTY);
#if DEBUG
	         PRINT("ODLinkSource:ReleaseAll: Close yields hr %x.\n", hr);
#endif
	       }
	  }

          ((LPUNKNOWN)_fpmkFull)->Release();
	  _fpmkFull		= NULL;
	}
	if (_fpmkRelative)
	{
          ((LPUNKNOWN)_fpmkRelative)->Release();
	  _fpmkRelative	= NULL;
	}
	ODDraftPermissions permissions = somSelf->GetStorageUnit(ev)->GetDraft(ev)->GetPermissions(ev);
	if (HAS_WRITE_ACCESS(permissions)) {
	  somSelf->Release(ev); 
	}

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

//------------------------------------------------------------------------------
// ODLinkSource: SetAutoUpdate (OVERRIDE)
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourceSetAutoUpdate(ODLinkSource *somSelf, Environment *ev,
		ODBoolean automatic)
{
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","SetAutoUpdate");

	SOM_TRY

    parent_SetAutoUpdate(somSelf, ev, automatic);

	if ( automatic == kODFalse )
	{
		ODLinkManager* linkManager = somSelf->GetStorageUnit(ev)->GetSession(ev)->GetLinkManager(ev);
		ODLink* link = kODNULL;
		
		TRY
			link = somSelf->GetLink(ev);
		CATCH_ALL
		ENDTRY

		if ( link && linkManager->ExportOnSave(ev, link, kODFalse) )
		{
		}
	}

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

//------------------------------------------------------------------------------
// ODLinkSource: ShowSourceContent (OVERRIDE)
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourceShowSourceContent(ODLinkSource *somSelf, Environment *ev)
{
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","ShowSourceContent");

	SOM_TRY

       	parent_ShowSourceContent(somSelf, ev);

	SOM_CATCH_ALL
	{
#if ODDebug
		somPrintf("ODLinkSource %d: ShowSourceContent :Error %d caught\n", somSelf->GetID(ev), ErrorCode());
#endif

	}
	SOM_ENDTRY
	return;
}

//------------------------------------------------------------------------------
// ODLinkSource: SetSourcePart (OVERRIDE)
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourceSetSourcePart(ODLinkSource *somSelf, Environment *ev,
		ODStorageUnit* sourcePartSU)
{
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","SetSourcePart");

	SOM_TRY

	ODLinkManager* linkManager = somSelf->GetStorageUnit(ev)->GetSession(ev)->GetLinkManager(ev);

	// If there is no source part for this link, delete the edition file when the
	// draft is saved; is there is a source part, make sure the edition file
	// is still maintained.
	linkManager->DeleteOnSave(ev, somSelf->GetLink(ev), (sourcePartSU == kODNULL));

        parent_SetSourcePart(somSelf, ev, sourcePartSU);

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

//------------------------------------------------------------------------------
// ODLinkSource: SetLinkSourceDirty (OVERRIDE)
//------------------------------------------------------------------------------

SOM_Scope void  SOMLINK ODLinkSourceSetLinkSourceDirty(ODLinkSource *somSelf, Environment *ev)

{
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","SetLinkSourceDirty");

	SOM_TRY

	  _fDirty = kODTrue;

	  ODDraft* draft = somSelf->GetStorageUnit(ev)->GetDraft(ev);
	  if ( draft->GetPermissions(ev) != kODDPReadOnly )
	    draft->SetChangedFromPrev(ev);

	SOM_CATCH_ALL

	SOM_ENDTRY

}


//------------------------------------------------------------------------------
// ODLinkSource: CloneFromMemoryContainer
//------------------------------------------------------------------------------
// This method does not call it's draft's SetChangeFromPrev() method so that the
// user is not prompted to save changes if the only change was to update a link
// from an edition file.


SOM_Scope void  SOMLINK ODLinkSourceCloneFromMemoryContainer(ODLinkSource *somSelf, Environment *ev,
		ODStorageUnit* contentSU,
		ODHandle containerHandle)
{
	ODLinkSourceData *somThis = ODLinkSourceGetData(somSelf);
	ODLinkSourceMethodDebug("ODLinkSource","CloneFromMemoryContainer");
	// start rrk 22 jul 96 - this stuff doesn't work - doesn't get called in local case.
#if 0
	ODLinkKey key;
	somSelf->Lock(ev, 0, &key);
        somSelf->Clear(ev, somSelf->GetUpdateID(ev), key);
       
                        ODStorageUnit* myContentSU = somSelf->GetContentStorageUnit(ev, key);

                        // Existing properties inhibit cloning in the same property, so remove all properties
                        // from the content storage unit before cloning.
                        myContentSU->Focus(ev, (ODPropertyName) kODNULL, kODPosAll, kODTypeAll, 0, kODPosUndefined);
                        ODULong numProperties = contentSU->CountProperties(ev);
                        ODULong i;
                        for (i = 1; i <= numProperties; i++) {
                                myContentSU->Focus(ev, (ODPropertyName) kODNULL, kODPosNextSib, kODTypeAll, 0, kODPosUndefined);
                                myContentSU->Remove(ev);
                                }

	somSelf->Unlock(ev, key);	     
#endif
	// end 22 jul 96 rrk
	CopyFromMemoryContainer(ev, contentSU, containerHandle);
}

//------------------------------------------------------------------------------
// FUNCTION: CloneFromMemoryContainer
//------------------------------------------------------------------------------
// This function was extracted from CloneFromMemoryContainer and made available
// as a global function so that OLE interop code can access it.
void CopyFromMemoryContainer(Environment *ev,
		ODStorageUnit* contentSU,
		ODHandle containerHandle)
{

	SOM_TRY

	ODStorageUnitID		rootID;
	ODDraft*			myDraft = contentSU->GetDraft(ev);
	ODSession*			session = contentSU->GetSession(ev);
	
	TempODContainer container = GetMemoryContainer(ev, session, containerHandle, kODBentoMemoryContainer);

	TempODDocument document = container->AcquireDocument(ev, kODDefaultDocument);
		
	TempODDraft draft = document->AcquireBaseDraft(ev, kODDPExclusiveWrite);
		
	{ TempODStorageUnit draftProperties = draft->AcquireDraftProperties(ev);
	  rootID = ODGetStrongSURefProp(ev, draftProperties, kODPropRootPartSU, kODStrongStorageUnitRef);
	}
	
	TempODStorageUnit rootSU = draft->AcquireStorageUnit(ev, rootID);

	ODDraftKey draftKey = draft->BeginClone(ev, myDraft, kODNULL, kODCloneAll);
	TRY
		draft->Clone(ev, draftKey, rootID, contentSU->GetID(ev), 0);	

		if ( ODSUExistsThenFocus(ev, rootSU, kODPropContentFrame, kODWeakStorageUnitRef) )
		{
			ODID contentFrameID = ODGetWeakSURefProp(ev, rootSU, kODPropContentFrame, kODWeakStorageUnitRef);
			draft->Clone(ev, draftKey, contentFrameID, kODNULLID, 0);				
		}		
	CATCH_ALL
		draft->AbortClone(ev, draftKey);
		RERAISE;
	ENDTRY
	draft->EndClone(ev, draftKey);

	SOM_CATCH_ALL

	SOM_ENDTRY
	  return;
}

/* METHOD: UseMoniker
 *
 * DESCRIPTION:
 *   Use the given moniker as the source that we are linked to
 */
SOM_Scope void SOMLINK ODLinkSourceUseMoniker(
	ODLinkSource	*somSelf,
	Environment		*ev,
	void			*pmkFull,
	ODLinkKey 		key)
{
	ODLinkSourceData	*somThis	= ODLinkSourceGetData(somSelf);

	//#ifdef LATER // rrk 18 apr 96 until wrapper is ready
	//OleWrapper		*pWrap			= gpODShell->GetActiveDocShell( ev )->GetOleWrapper( ev );
	OleWrapper                      *pWrap  = ODOLEManager::GetShell()->GetActiveDocumentManager(ev)->GetOleWrapper(ev);
	//#endif
	LPMONIKER		pmkLinkSource	= (LPMONIKER)pmkFull;
	HRESULT			hr;
	FORMATETC		fe;
	ODPlatformType	platformType;

   	somSelf->KeyValid(ev,key);
        somSelf->SetCurrentKey(ev, key); // rrk 24 jun 96
   	ASSERT(pmkFull != kODNULL, kODErrCannotCreateLink);		// Internal error

        // rrk 24 jun 96 - if we do not have any left-hand moniker (pWrap->fpMoniker) then
	//                 we just return.  We don't want to put any link information in
	//                 the data transfer object.
	// rrk 13 Aug 96 - do not use fpMoniker 
	IMoniker *theMoniker;
	hr = pWrap->fpIOleObject->GetMoniker(OLEGETMONIKER_FORCEASSIGN,
					     OLEWHICHMK_OBJFULL,
                       			     &theMoniker);
	if ( FAILED(hr) || (theMoniker == kODNULL) ) {
	   WARNMSG_DEBUG(WARN_INDEX(-1),"ODLinkSource: Did not get a moniker from the Ole Wrapper");
	   return;
	}
	/* Now bind the moniker to extract an IDataObject that we
	 * can talk with, whether the link source was OLE or OpenDoc
	 */
#ifdef DEBUG
	PRINT("!----> LinkSource::UseMoniker about to bind to moniker...\n");
#endif
	hr	= BindMoniker(pmkLinkSource,NULL,IID_IDataObject,(void**)&_fpDataObj);
#ifdef DEBUG
	PRINT("... moniker bound.\n");
#endif

	if(SUCCEEDED(hr))
	{
		/* Store away the full and relative monikers to the real link source
		 */
		_fpmkFull		= pmkFull;
		((IUnknown*)_fpmkFull)->AddRef();
#ifndef LATER // rrk 18 april wait until olewrapper complete.  The real file moniker is based on the name of
	     //              the document.  We'll just invent a dummy name for now.
       	           // rrk 4 jun 96 OK, try this code now.

		if(pWrap)
			hr	= ((IMoniker*)_fpmkFull)->RelativePathTo(theMoniker,
									 (IMoniker **)&_fpmkRelative);
#else
		LPMONIKER tempMoniker;
		CreateFileMoniker((LPCOLESTR)"TemporaryFileMoniker", &tempMoniker);
		hr = ((IMoniker*)_fpmkFull)->RelativePathTo((IMoniker*)tempMoniker,
							    (IMoniker **)&_fpmkRelative);
		tempMoniker->Release();
#endif

		/* Create our advise sink, if it doesn't exist
		 */
		if(!_fpAdviseSink)
		{
			_fpAdviseSink	= (LPVOID)new LinkSrcAdviseSink(somSelf,
						     NULL,
						     _fSession->GetWindowSystemData(ev)->instance); // rrk 24 apr 96
			if(_fpAdviseSink)
				((IUnknown*)_fpAdviseSink)->AddRef();
		}

		/* Make sure we are notified if the data object is modified
		 */
		platformType = _fSession->GetTranslation(ev)->GetPlatformTypeFromISOType(ev,kODApplePart);
// rrk 1 Apr 96 platformType is a pointer, and the compiler does not want to chop off the first 16 bits.
//              It doesn't mind throwing away the first 16 bits of a long.
// 		SETDefFormatEtc(fe,(CLIPFORMAT)platformType,TYMED_HGLOBAL);
 		SETDEFAULTFORMATETC(fe,(unsigned long)platformType,TYMED_HGLOBAL);
		if(_fpAdviseSink)
			((IDataObject*)_fpDataObj)->DAdvise(&fe,
											   ADVF_NODATA,
											   (IAdviseSink*)_fpAdviseSink,		
											   &_fdwConn);			
						
		/* Initialize our content storage unit
		 */
		   somSelf->ReadMoniker(ev, key);

		   somSelf->SetLinkSourceDirty(ev);
	}
#ifdef DEBUG
	else {
	      PRINT("Error on BindMoniker: %08lx.\n", hr);
	      LPVOID lpBuffer;
	      FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
			     NULL,
			     hr,
			     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			     (LPTSTR) &lpBuffer, 0 , NULL);
	      PRINT("Error was: \"%s\".\n", lpBuffer);
	}
#endif

}

/* METHOD: ReadMoniker
 *
 * DESCRIPTION:
 * 	 Get an update from the link source OLE object...
 */
SOM_Scope void SOMLINK ODLinkSourceReadMoniker(
	ODLinkSource	*somSelf,
	Environment		*ev,
	ODLinkKey        key)

{			
	ODLinkSourceData	*somThis	= ODLinkSourceGetData(somSelf);
	ODStorageUnit	*contentSU 		= _fContentSU;
	IDataObject		*pIDataObject	= (IDataObject*)_fpDataObj;
	LPENUMFORMATETC	pEnumFE		  	= NULL;
	ODPlatformType	platformType;
	HRESULT			hr;
        STGMEDIUM		stm;
        FORMATETC		fe;
	ODType			theISOType;
	ULONG			cfe;
	BOOL                    bLocked = FALSE; // assume we do not get the lock in here.

	ASSERT(_fpDataObj != kODNULL, kODErrNotImportedLink);

	/* Existing properties inhibit cloning in the same property, so remove
	 * all properties from the content storage unit before updating the
	 * link information
	 */
        // rrk 6 jun 96 _fContentSU is another one of those pesky members being maintained only in the
	//              base class now.  Call the base class, but what to use for a key?
        // 9 jul 96 - if the key was passed in as null, we are not locked.  Lock now, but unlock
	//            before attempted UpdateDependents, which acquires and releases lock.
	if (key == kODNULL) {
	  somSelf->Lock(ev, 0, &key);
          bLocked = TRUE;
	}
	ODUpdateID updateID = somSelf->GetStorageUnit(ev)->GetSession(ev)->UniqueUpdateID(ev); // rrk 22 jul
	somSelf->Clear(ev, updateID, key); // rrk 22 jul ?
        contentSU = somSelf->GetContentStorageUnit(ev, key);
	
	
	contentSU->Focus(ev, (ODPropertyName) kODNULL, kODPosAll, kODTypeAll, 0, kODPosUndefined);
	ODULong numProperties = contentSU->CountProperties(ev);
	ODULong i;
	for (i = 0; i < numProperties; i++)
	{
		contentSU->Focus(ev, (ODPropertyName) kODNULL, kODPosNextSib, kODTypeAll, 0, kODPosUndefined);
		contentSU->Remove(ev);
	}

	/* If the data object we're linked to has the 'Apple:ScrapType:odcn' type
	 * at the moment, we can directly import the OpenDoc clipboard as a
	 * memory Bento container.
	 *
	 * Otherwise we must individually put up promises for the various data
	 * types represented in the data object.
	 */
	platformType = _fSession->GetTranslation(ev)->GetPlatformTypeFromISOType(ev,kODApplePart);
// rrk 1 Apr 96 platformType is a pointer, and the compiler does not want to chop off the first 16 bits.
//              It doesn't mind throwing away the first 16 bits of a long.
//	SETDefFormatEtc(fe,(CLIPFORMAT)platformType,TYMED_HGLOBAL);
	SETDEFAULTFORMATETC(fe,(unsigned long)platformType,TYMED_HGLOBAL);
	hr			 = pIDataObject->GetData(&fe,&stm);
	if (SUCCEEDED(hr))
	{
		/* Now try to take the global memory segment we got back
		 * from OLE2 and try to resurrect the Bento container within
		 * it that contains all the clipboard information
		 */
		TRY
	//---------------------------------------------------------------------------
	// The following code differs somewhat from Novell's implementation.  See the comments
	// in WClipbd.cpp for a discussion. 14 jun 96
	//---------------------------------------------------------------------------
				        ODULong dataSize  = GlobalSize(stm.hGlobal);
					TempODHandle tempHandle = (ODHandle)ODNewHandle(dataSize);

		                        ODPtr data	= GlobalLock(stm.hGlobal);
		                        if (data) {
                                            LPSTR lpstrTo = (LPSTR)ODLockHandle((ODHandle)tempHandle);
					    memcpy(lpstrTo, data, dataSize);
			                    ODUnlockHandle((ODHandle) lpstrTo);
                                	}
					GlobalUnlock(stm.hGlobal);

  			somSelf->CloneFromMemoryContainer(ev,contentSU,(ODHandle)tempHandle);
  		CATCH_ALL
  			WASSERTMSG_DEBUG(kODFalse,"Throw from ReadEditionFile");
        ENDTRY

		/* Release the storage medium that we were given
		 */
		ReleaseStgMedium(&stm);
	}
	else
	{
		/* Otherwise we have to create a storage unit containing promises of
		 * everything that is in the data transfer object
		 */
		
		/* Enumerate all the FORMATETC's in our linked-to data object
		 */
		pIDataObject->EnumFormatEtc(DATADIR_GET,&pEnumFE);
		if (pEnumFE)		
		{
			/* Create a new instance of the PromiseKeeper which has
			 * to honour all requests to fulfill promises on the storage units
			 * we hand out to OpenDoc that really came from an OLE source
			 * process...
			 */

			if(_fPromiseKeeper)
				delete _fPromiseKeeper;
			_fPromiseKeeper	= new OLEPromiseKeeper();
			_fPromiseKeeper->InitOLEPromiseKeeper(ev,pIDataObject);

			/* For all the formats available from the IDataObject
			*/
			while ( !FAILED(pEnumFE->Next(1,&fe,&cfe)) && cfe)
			{
				/* Get the ISO type for this FORMATETC, and write it into
				 * the content list of the clipboard storage unit. Shove a
				 * promise in, so that we don't have to actually get the data
				 * from the other process until the object reads the data.
				 */
			  // rrk 1 apr 96 - VACPP doesn't like to convert CLIPFORMAT to ODPlatformType, because 
			  //                ODPlatformType is a pointer.  Convert to 32-bits first.
				theISOType = _fSession->GetTranslation(ev)->GetISOTypeFromPlatformType(
																	ev,
																	(ODPlatformType) fe.cfFormat,
																	kODPlatformDataType);
				if (theISOType != (ODType)kODNULL)
				{
					contentSU->AddProperty(ev,kODPropContents);

					StorageUnitSetPromiseValue(contentSU, ev,theISOType,0,0,kODNULL,_fPromiseKeeper);

	
					ODDisposePtr(theISOType);
				}
			}
			pEnumFE->Release();
		}
	}

  	/* Tell everyone that new link data is available
	 */
	if(contentSU)
	{
	  // rrk 1 Apr 96 need to look at this update ID business - this is almost certainly no correct...Done.
	  //		_fChangeID++;				/* LATER: ????? */
	  //	_fLink->UpdateDependents(ev,_fChangeID);
          ODUpdateID updateID = somSelf->GetUpdateID(ev);
          somSelf->SetUpdateID(ev,++updateID,key);
          // set the time stamp to the current time.
	  ODTime nowTime;
	  time(&nowTime);
	  somSelf->SetChangeTime(ev, nowTime, key);
	  if (bLocked) somSelf->Unlock(ev, key); // if we had to lock ourselves, release now.
	  // rrk 6 jun 96 - Again, we do not maintain _fLink in the derived class.  Use base class
	  //          _fLink->UpdateDependents(ev);
	  somSelf->GetLink(ev)->UpdateDependents(ev);
	}
}

/* METHOD: StoreMoniker
 *
 * DESCRIPTION:
 *   Save the given OLE moniker under the specified property name
 */
SOM_Scope BOOL SOMLINK ODLinkSourceStoreMoniker(
	ODLinkSource	*somSelf,
	Environment		*ev,
	ODStorageUnit	*su,
	ODPropertyName	property,
	LPVOID			pmk)
{
	ODLinkSourceData	*somThis	= ODLinkSourceGetData(somSelf);
    HRESULT    		    hr  		= ResultFromScode(E_FAIL);
	STATSTG				Status;
	ODULong 			size, *lValue;
	HGLOBAL				hGlobal;
	IStream				*pStream;

    /* Create a memory stream to save our monikers into
     */
	hGlobal	= GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,0);
	if (hGlobal)
	{
		hr	= CreateStreamOnHGlobal(hGlobal,FALSE,&pStream);
		if (SUCCEEDED(hr))
		{
			/* Serialize the full moniker
			 */
			hr	= OleSaveToStream((IMoniker*)pmk,pStream);
			if (SUCCEEDED(hr))
			{
				pStream->Stat(&Status,STGTY_STREAM);
				size	= Status.cbSize.LowPart;
				lValue	= (ODULong*)GlobalLock(hGlobal);
				if (!su->Exists(ev,property, kODMoniker, 0))
					su->AddProperty(ev,property)->AddValue(ev,kODMoniker);
				su->Focus(ev,property, kODPosUndefined,
						  kODMoniker, (ODValueIndex)0, kODPosUndefined);
				StorageUnitSetValue(su, ev,size,(ODValue*)lValue);
				GlobalUnlock(hGlobal);
			}	
			pStream->Release();
		}
		GlobalFree(hGlobal);
  	}
	return (SUCCEEDED(hr));
}

/* METHOD: RestoreMoniker
 *
 * DESCRIPTION:
 *   Load the OLE moniker from the given property within a storage unit
 */
SOM_Scope void SOMLINK ODLinkSourceRestoreMoniker(
	ODLinkSource	*somSelf,
	Environment		*ev,
	ODStorageUnit	*storageUnit,
	ODPropertyName	property,
	LPVOID			*ppmk)
{
	ODLinkSourceData	*somThis	= ODLinkSourceGetData(somSelf);
    HRESULT         	hr  		= ResultFromScode(E_FAIL);
	HGLOBAL				hGlobal;
	char				*pBuffer;
	IStream				*pStream;
	ODULong 			size;

	/* Release any pre-existing moniker
	 */
	if (*ppmk)
	{
#ifdef LATER
		OleStdRelease((IUnknown*)(*ppmk));
#else
                ((LPUNKNOWN)*ppmk)->Release();
#endif
		*ppmk	= NULL;
	}

	/* Focus on the kODMoniker value and read it out
	 */
	storageUnit->Focus(ev,property,kODPosUndefined,
					   kODMoniker,(ODValueIndex)0, kODPosUndefined);
	size	= storageUnit->GetSize(ev);
	hGlobal	= GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,size);
	if (hGlobal)
	{
		pBuffer	= (char *)GlobalLock(hGlobal);
		if(pBuffer)
		{
			StorageUnitGetValue(storageUnit, ev,size,(ODValue)pBuffer);

			/* Now create a stream based on the serialized moniker
			 */
			hr	= CreateStreamOnHGlobal(hGlobal,FALSE,&pStream);
			if (SUCCEEDED(hr))
			{
				/* Load the moniker that is contained within this
				 * stream
				 */
				hr	= OleLoadFromStream(pStream,
										IID_IMoniker,
										(void**)ppmk);
				pStream->Release();
			}
			GlobalUnlock(hGlobal);
		}
		GlobalFree(hGlobal);
	}
}

/* METHOD: OnDataChange
 *
 * DESCRIPTION:
 *   Refresh the link data in this process.
 */
SOM_Scope void SOMLINK ODLinkSourceOnDataChange(
	ODLinkSource	*somSelf,
	Environment		*ev)
{
	ODLinkSourceData	*somThis		= ODLinkSourceGetData(somSelf);
#if 0
	ODLinkKey key;
	if (somSelf->Lock(ev, 0, &key)) {
	   somSelf->ReadMoniker(ev, key);
	   somSelf->Unlock(ev, key);
	}
#endif
	somSelf->ReadMoniker(ev, 0); // pass in a zero key; we have not locked the link source
}

/* METHOD: OnClose
 *
 * DESCRIPTION:
 *   DataObject is going away. Dispose of our reference to it, and our
 *   advise sink.
 */
SOM_Scope void SOMLINK ODLinkSourceOnClose(
	ODLinkSource	*somSelf,
	Environment		*ev)
{
	ODLinkSourceData	*somThis		= ODLinkSourceGetData(somSelf);

	if (_fdwConn)
 		((IDataObject*)_fpDataObj)->DUnadvise(_fdwConn);

	if(_fpDataObj)
	{
#ifdef LATER
		OleStdRelease((IUnknown*)_fpDataObj);
#else
                ((LPUNKNOWN)_fpDataObj)->Release();
#endif
		_fpDataObj		= NULL;
	}
	if(_fpAdviseSink)
	{
#ifdef LATER
		OleStdRelease((IUnknown*)_fpAdviseSink);
#else
                ((LPUNKNOWN)_fpAdviseSink)->Release();
#endif
		_fpAdviseSink	= NULL;
	}
}

/* METHOD: OnRename
 *
 * DESCRIPTION:
 *   Source DataObject's identifying moniker was changed.
 */
SOM_Scope void SOMLINK ODLinkSourceOnRename(
	ODLinkSource	*somSelf,
	Environment		*ev,
	LPVOID			pmk)
{
	ODLinkSourceData	*somThis		= ODLinkSourceGetData(somSelf);

	HRESULT hr;

	//#ifdef LATER // rrk 18 apr 96 until wrapper is ready
	//	OleWrapper	*pWrap		= gpODShell->GetActiveDocShell( ev )->GetOleWrapper( ev );
	OleWrapper              *pWrap  = ODOLEManager::GetShell()->GetActiveDocumentManager(ev)->GetOleWrapper(ev);
	//#endif
	LPMONIKER	pmkFull		= (IMoniker*)pmk;

        // rrk 24 jun 96 - if we do not have any left-hand moniker (pWrap->fpMoniker) then
	//                 we just return.  We don't want to put any link information in
	//                 the data transfer object.
	// rrk 13 Aug 96 - do not use fpMoniker 
	IMoniker *theMoniker;
	hr = pWrap->fpIOleObject->GetMoniker(OLEGETMONIKER_FORCEASSIGN,
					     OLEWHICHMK_OBJFULL,
                       			     &theMoniker);
	if ( FAILED(hr) || (theMoniker == kODNULL) ) {
	   WARNMSG_DEBUG(WARN_INDEX(-1),"ODLinkSource: Did not get a moniker from the Ole Wrapper");
	   return;
	}
	/* Release our existing moniker
	 */
	if (_fpmkFull)
	{
#ifdef LATER
		OleStdRelease((IUnknown*)_fpmkFull);
#else
                ((LPUNKNOWN)_fpmkFull)->Release();
#endif
		_fpmkFull		= NULL;
	}
	if (_fpmkRelative)
	{
#ifdef LATER
		OleStdRelease((IUnknown*)_fpmkRelative);
#else
                ((LPUNKNOWN)_fpmkRelative)->Release();
#endif
		_fpmkRelative	= NULL;
	}

	/* Now adopt the new moniker
	 */
	if (pmkFull)
	{
		/* Store away the full and relative monikers to the real link source
		 */
		_fpmkFull		= pmkFull;
		((IUnknown*)_fpmkFull)->AddRef();
#ifndef LATER // rrk 18 apr 96 until wrapper is ready
	           // rrk 4 jun 96 OK, try this code now.

		if(pWrap)
			((IMoniker*)_fpmkFull)->RelativePathTo(theMoniker,
												  (IMoniker **)&_fpmkRelative);
#else
		LPMONIKER tempMoniker;
		CreateFileMoniker((LPCOLESTR)"TemporaryFileMoniker", &tempMoniker);
		((IMoniker*)_fpmkFull)->RelativePathTo((IMoniker*)tempMoniker,
						       (IMoniker **)&_fpmkRelative);
		tempMoniker->Release();
#endif
	}
}

/* METHOD: GetLocked
 *
 * DESCRIPTION:
 *   Access the fLocked variable
 */
SOM_Scope ODBoolean SOMLINK ODLinkSourceGetLocked(
	ODLinkSource	*somSelf,
	Environment		*ev)
{
	ODLinkSourceData	*somThis	= ODLinkSourceGetData(somSelf);

	return	_fLocked;
}

/* METHOD: SetLocked
 *
 * DESCRIPTION:
 *   Set the fLocked variable
 */
SOM_Scope void SOMLINK ODLinkSourceSetLocked(
	ODLinkSource	*somSelf,
	Environment		*ev,
	ODBoolean		bLocked)
{
	ODLinkSourceData	*somThis	= ODLinkSourceGetData(somSelf);

	_fLocked = bLocked;
}

/* METHOD: GetCurrentKey
 *
 * DESCRIPTION:
 *   Access the fCurrentKey variable
 */
SOM_Scope ODLinkKey SOMLINK ODLinkSourceGetCurrentKey(
	ODLinkSource	*somSelf,
	Environment		*ev)
{
	ODLinkSourceData	*somThis	= ODLinkSourceGetData(somSelf);

	return	_fCurrentKey;
}

/* METHOD: SetCurrentKey
 *
 * DESCRIPTION:
 *   Set the fCurrentKey variable
 */
SOM_Scope void SOMLINK ODLinkSourceSetCurrentKey(
	ODLinkSource	*somSelf,
	Environment		*ev,
	ODLinkKey		CurrentKey)
{
	ODLinkSourceData	*somThis	= ODLinkSourceGetData(somSelf);

	_fCurrentKey = CurrentKey;
}



/* ########################## LinkSrcAdviseSink ########################### */

#define WM_ONCLOSE		WM_USER
#define WM_ONRENAME		WM_USER + 1
#define WM_ONDATACHANGE	WM_USER + 2

/* FUNCTION: AdviseSinkWndProc
 *
 * DESCRIPTION:
 *   Make asynchronous calls to ODLinkSource to tell it that the OLE
 *   data object it is using was changed. This is necessary because of the
 *   limitation that you can't call IDataObject::GetData during
 *   IAdviseSink::OnDataChange. Gee whiz, doesn't OLE suck !
 */
ODSLong CALLBACK AdviseSinkWndProc(
	ODPlatformWindow	hWnd,		// window handle
	UINT				message,	// type of message
	UINT				wParam,		// additional information
	ODSLong				lParam)		// additional information
{
	LPMONIKER			pmk			= (LPMONIKER)lParam;
	ODLinkSource		*pLinkSrc	= (ODLinkSource*)GetWindowLong(hWnd,0);
	Environment			*ev			= somGetGlobalEnvironment();

	switch (message)
	{
		case WM_CREATE:
		case WM_NCCREATE:
			SetWindowLong(hWnd, 0, (LONG)((LPCREATESTRUCT)lParam)->lpCreateParams);	
			return TRUE;

		case WM_ONCLOSE:
			pLinkSrc->OnClose(ev);
			break;
	
		case WM_ONDATACHANGE:
			pLinkSrc->OnDataChange(ev);
			break;
	
		case WM_ONRENAME:
			pLinkSrc->OnRename(ev,pmk);
			pmk->Release();
			break;

		default:
			return (DefWindowProc(hWnd, message, wParam, lParam));
	}
	return	0L;
}

/* CONSTRUCTOR: LinkSrcAdviseSink::LinkSrcAdviseSink
 *
 * DESCRIPTION:
 */
LinkSrcAdviseSink::LinkSrcAdviseSink(
	ODLinkSource	*pLinkSource,	/* IN: Link source we report to */
	LPUNKNOWN	pUnkOuter,	/* IN: Controlling unknown */
        HINSTANCE        hInst)          /* IN: HINSTANCE for the shell */
{
  	Environment		*ev		= somGetGlobalEnvironment();
	// rrk 26 apr 96 hInst is now passed in to this constructor.
	//HINSTANCE		hInst	= gpODShell->GetShellInstance(ev);
	WNDCLASS  		wc;
	ODUnused(pUnkOuter);

	fcRef	 			= 0;				// for debugging only
	_fLinkSrc     		= pLinkSource;

	/* Register a bogus window class if necessary and create our link window
	 */
	if (!GetClassInfo(hInst,"ODLinkWindow",&wc))
	{
		wc.style	     	= CS_DBLCLKS;
		wc.lpfnWndProc   	= AdviseSinkWndProc;
		wc.cbClsExtra    	= 0;
		wc.cbWndExtra    	= 4;
		wc.hInstance     	= hInst;
		wc.hIcon	     	= 0;
		wc.hCursor	     	= LoadCursor(NULL,IDC_ARROW);
		wc.hbrBackground 	= 0;
		wc.lpszMenuName  	= NULL;
		wc.lpszClassName 	= "ODLinkWindow";
		RegisterClass(&wc);
	}
	fhwndNotify			= CreateWindowEx(0L,
										 "ODLinkWindow",						
									     "Link Window",		
									     WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
									     0,0,0,0,
									     NULL,								
									     NULL,								
									     hInst,				
									     (void*)_fLinkSrc);			
}

/* DESTRUCTOR: pIAdviseSink::~LinkSrcAdviseSink
 *
 * DESCRIPTION:
 */
LinkSrcAdviseSink::~LinkSrcAdviseSink(void)
{
	if (fhwndNotify)
		DestroyWindow(fhwndNotify);
}

/* ########################## IUnknown interface ########################## */

/* METHOD: LinkSrcAdviseSink::QueryInterface
 *
 * DESCRIPTION:
 *   IUnknown method QueryInterface for LinkSrcAdviseSink
 */
STDMETHODIMP LinkSrcAdviseSink::QueryInterface(
	REFIID riid, 			/* IN: IID of the interface to be queried */
	LPVOID* ppv)			/* OUT: interface object for the given IID */
{
	ODUnused(riid);
	ODUnused(ppv);

   if (  IsEqualIID(riid, IID_IAdviseSink)
	  || IsEqualIID(riid, IID_IUnknown)
	  )
        *ppv	= (LPVOID)this;
	else
		*ppv	= NULL;

	/* AddRef any interface that we return to the caller !
	 */
    if (*ppv)
        ((LPUNKNOWN)*ppv)->AddRef();

    return *ppv ? NOERROR : ResultFromScode(E_NOINTERFACE);
}

/* METHOD: LinkSrcAdviseSink::AddRef
 *
 * DESCRIPTION:
 *	 IUnknown method AddRef for LinkSrcAdviseSink
 */
STDMETHODIMP_(ULONG) LinkSrcAdviseSink::AddRef(void)
{
    return ++fcRef;
}

/* METHOD: LinkSrcAdviseSink::Release
 *
 * DESCRIPTION:
 *	 IUnknown method Release for LinkSrcAdviseSink
 */
STDMETHODIMP_(ULONG) LinkSrcAdviseSink::Release()
{
    ULONG       			cRefT;

    cRefT	= --fcRef;
    if (!fcRef)
		delete	this;
    return cRefT;
}

/* ####################### IAdviseSink interface ########################## */

/* METHOD: LinkSrcAdviseSink::OnDataChange
 *
 * DESCRIPTION:
 *	 Not implemented and not needed, we do not establish a data update link
 *	 using IDataObject::Advise. We use only OnViewChange
 */
STDMETHODIMP_(void) LinkSrcAdviseSink::OnDataChange(
	LPFORMATETC		pFEIn,		
    LPSTGMEDIUM		pSTM)		
{
	ODUnused(pFEIn);
	ODUnused(pSTM);

	PostMessage(fhwndNotify,WM_ONDATACHANGE,0, 0L);
}

/* METHOD: LinkSrcAdviseSink::OnViewChange
 *
 * DESCRIPTION:
 *	 Notifies the advise sink that presentation data has changed in the
 *	 data object to which we're connected and that the container
 *	 should thus repaint itself in the near future
 */
STDMETHODIMP_(void) LinkSrcAdviseSink::OnViewChange(
	DWORD dwAspect,		/* Aspect that has changed */
	LONG lindex)		/* Piece that has changed (-1 => everything) */
{
	ODUnused(lindex);
	ODUnused(dwAspect);
}

/* METHOD: LinkSrcAdviseSink::OnRename
 *
 * DESCRIPTION:
 *	 Informs the advise sink that a linked object has been renamed.
 */
STDMETHODIMP_(void) LinkSrcAdviseSink::OnRename(
	LPMONIKER		pmk)		
{
	pmk->AddRef();
	PostMessage(fhwndNotify,WM_ONRENAME,0, (DWORD)pmk);
}

/* METHOD: LinkSrcAdviseSink::OnSave
 *
 * DESCRIPTION:
 *	 Informs the advise sink that the OLE object has been saved
 *	 persistently. The primary purpose of this is for containers
 *	 that want to make optimizations for objects that are not in a
 *	 saved state, so on this you have to disable such optimizations.
 *
 *	 Not implemented.
 */
STDMETHODIMP_(void) LinkSrcAdviseSink::OnSave()
{
}

/* METHOD: LinkSrcAdviseSink::OnClose
 *
 * DESCRIPTION:
 *	 Informs the advise sink that the OLE object has closed
 */
STDMETHODIMP_(void) LinkSrcAdviseSink::OnClose()
{
	PostMessage(fhwndNotify,WM_ONCLOSE,0, 0L);
}
