//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Net.PeerToPeer.Collaboration { using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using System.Runtime.InteropServices; using System.Net.Sockets; using System.Net.Mail; using System.ComponentModel; using System.Threading; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; using System.Security.Permissions; using System.IO; /// /// This is the object changed event args class we give back when /// we have have an object changed event fired by native /// public class ObjectChangedEventArgs : EventArgs { private PeerEndPoint m_peerEndPoint; private PeerContact m_peerContact; private PeerChangeType m_peerChangeType; private PeerObject m_peerObject; internal ObjectChangedEventArgs(PeerEndPoint peerEndPoint, PeerContact peerContact, PeerChangeType peerChangeType, PeerObject peerObject) { m_peerEndPoint = peerEndPoint; m_peerContact = peerContact; m_peerChangeType = peerChangeType; m_peerObject = peerObject; } public PeerEndPoint PeerEndPoint { get { return m_peerEndPoint; } } public PeerContact PeerContact { get { return m_peerContact; } } public PeerChangeType PeerChangeType { get { return m_peerChangeType; } } public PeerObject PeerObject { get { return m_peerObject; } } } /// /// This is the presence changed event args class we give back when /// we have have presence changed event fired by native /// public class PresenceChangedEventArgs : EventArgs { private PeerEndPoint m_peerEndPoint; private PeerContact m_peerContact; private PeerChangeType m_peerChangeType; private PeerPresenceInfo m_peerPresenceInfo; internal PresenceChangedEventArgs(PeerEndPoint peerEndPoint, PeerContact peerContact, PeerChangeType peerChangeType, PeerPresenceInfo peerPresenceInfo) { m_peerEndPoint = peerEndPoint; m_peerContact = peerContact; m_peerChangeType = peerChangeType; m_peerPresenceInfo = peerPresenceInfo; } public PeerEndPoint PeerEndPoint { get { return m_peerEndPoint; } } public PeerContact PeerContact { get { return m_peerContact; } } public PeerChangeType PeerChangeType { get { return m_peerChangeType; } } public PeerPresenceInfo PeerPresenceInfo { get { return m_peerPresenceInfo; } } } /// /// This is the event args class we give back when /// we have completed the subscribeasync call /// public class SubscribeCompletedEventArgs : AsyncCompletedEventArgs { private PeerNearMe m_peerNearMe; private PeerContact m_peerContact; internal SubscribeCompletedEventArgs(PeerNearMe peerNearMe, PeerContact peerContact, Exception error, bool cancelled, object userToken) : base(error, cancelled, userToken) { m_peerNearMe = peerNearMe; m_peerContact = peerContact; } public PeerNearMe PeerNearMe { get { return m_peerNearMe; } } public PeerContact PeerContact { get { return m_peerContact; } } } /// /// This is the event args class we give back when /// we have completed the refreshendpoint async call /// public class RefreshDataCompletedEventArgs : AsyncCompletedEventArgs { private PeerEndPoint m_peerEndPoint; internal RefreshDataCompletedEventArgs(PeerEndPoint peerEndPoint, Exception error, bool cancelled, object userToken) : base(error, cancelled, userToken) { m_peerEndPoint = peerEndPoint; } public PeerEndPoint PeerEndPoint { get { return m_peerEndPoint; } } } /// /// This is the event args class we give back when /// we have completed the inviteasync call /// public class InviteCompletedEventArgs : AsyncCompletedEventArgs { private PeerInvitationResponse m_peerInvResponse; internal InviteCompletedEventArgs(PeerInvitationResponse peerInvResponse, Exception error, bool cancelled, object userToken) : base(error, cancelled, userToken) { m_peerInvResponse = peerInvResponse; } public PeerInvitationResponse InviteResponse { get { return m_peerInvResponse; } } } /// /// Has the common interface for PeerNearMe and PeerContact which derive from it /// [Serializable] public abstract class Peer : IDisposable, IEquatable, ISerializable { private PeerEndPointCollection m_peerEndPoints = new PeerEndPointCollection(); private ISynchronizeInvoke m_synchronizingObject; public virtual PeerEndPointCollection PeerEndPoints { get { if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); return m_peerEndPoints; } } public bool IsOnline { get{ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Get Isonline called."); if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); bool isOnline = false; PeerPresenceInfo presenceInfo; foreach (PeerEndPoint peerEndPoint in PeerEndPoints){ presenceInfo = null; try{ presenceInfo = GetPresenceInfo(peerEndPoint); } catch (Exception e){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "Exception thrown {0}", e.Message); } if ((presenceInfo != null) && (presenceInfo.PresenceStatus == PeerPresenceStatus.Online)){ isOnline = true; break; } } Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Leaving Isonline called with {0}.", isOnline); return isOnline; } } /// /// Gets and set the object used to marshall event handlers calls for stand alone /// events /// [Browsable(false), DefaultValue(null), Description(SR.SynchronizingObject)] public ISynchronizeInvoke SynchronizingObject { get { if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); return m_synchronizingObject; } set { if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); m_synchronizingObject = value; } } static internal Guid CurrentApplicationGuid { // // // // [System.Security.SecurityCritical] get{ Guid guid = Guid.Empty; // // Get path and args of app // string path = Path.Combine( Environment.CurrentDirectory, Process.GetCurrentProcess().ProcessName + ".exe"); string arguments = null; string[] argsArray = Environment.GetCommandLineArgs(); int length = argsArray.Length; if (length > 1){ StringBuilder argsBuilder = new StringBuilder(); for (int i = 1; i < length; ++i){ argsBuilder.Append(argsArray[i]); if (i != (length - 1)) argsBuilder.Append(' '); } arguments = argsBuilder.ToString(); } // // Find a matching registered application and return its guid // PeerApplicationCollection peerApplications = PeerCollaboration.GetLocalRegisteredApplications(); foreach (PeerApplication peerApplication in peerApplications){ if ((peerApplication.CommandLineArgs == arguments) && (peerApplication.Path == path)) return peerApplication.Id; } return guid; } } internal Peer(){ OnInviteCompletedDelegate = new SendOrPostCallback(InviteCompletedWaitCallback); } /// /// Constructor to enable serialization /// [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] protected Peer(SerializationInfo serializationInfo, StreamingContext streamingContext):this() { m_peerEndPoints = (PeerEndPointCollection)serializationInfo.GetValue("_PeerEndPoints", typeof(PeerEndPointCollection)); } // // Gets the presence info from collab for a specific endpoint // // // // // // // // // // // // [System.Security.SecurityCritical] public PeerPresenceInfo GetPresenceInfo(PeerEndPoint peerEndPoint) { Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "GetPresenceInfo()called."); if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand(); if (peerEndPoint == null) throw new ArgumentNullException("peerEndPoint"); if (peerEndPoint.EndPoint == null) throw new ArgumentException(SR.GetString(SR.Collab_NoEndPointInPeerEndPoint)); if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Getting presence for the following endpoint."); peerEndPoint.TracePeerEndPoint(); } SafeCollabData presenceInfo = null; PeerPresenceInfo peerPresenceInfo = null; int errorCode; PEER_ENDPOINT pep = new PEER_ENDPOINT(); pep.peerAddress = CollaborationHelperFunctions.ConvertIPEndpointToPEER_ADDRESS(peerEndPoint.EndPoint); // // Pin all the data to pass to native // GCHandle pepName = new GCHandle(); if (peerEndPoint.Name != null){ pepName = GCHandle.Alloc(peerEndPoint.Name, GCHandleType.Pinned); pep.pwzEndpointName = pepName.AddrOfPinnedObject(); } GCHandle peerEP = GCHandle.Alloc(pep, GCHandleType.Pinned); IntPtr ptrPeerEP = peerEP.AddrOfPinnedObject(); // // Refresh data for getting presence info // RefreshIfNeeded(); try{ errorCode = UnsafeCollabNativeMethods.PeerCollabGetPresenceInfo(ptrPeerEP, out presenceInfo); if (errorCode != 0){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerCollabGetPresenceInfo returned with errorcode {0}", errorCode); throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_GetPresenceFailed), errorCode); } IntPtr ptrPeerPresenceInfo = presenceInfo.DangerousGetHandle(); PEER_PRESENCE_INFO ppi = (PEER_PRESENCE_INFO)Marshal.PtrToStructure(ptrPeerPresenceInfo, typeof(PEER_PRESENCE_INFO)); peerPresenceInfo = new PeerPresenceInfo(); peerPresenceInfo.PresenceStatus = ppi.status; peerPresenceInfo.DescriptiveText = ppi.descText; } finally{ if (pepName.IsAllocated) pepName.Free(); if (peerEP.IsAllocated) peerEP.Free(); if (presenceInfo != null) presenceInfo.Dispose(); } Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Leaving GetPresenceInfo()."); return peerPresenceInfo; } // // Gets all the objects for all the endpoints // // // // [System.Security.SecurityCritical] public PeerObjectCollection GetObjects() { PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand(); return InternalGetAllObjects(Guid.Empty, false); } // // Gets specific object for all the endpoints // // // // [System.Security.SecurityCritical] public PeerObjectCollection GetObjects(Guid objectId) { PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand(); return InternalGetAllObjects(objectId, true); } internal abstract void RefreshIfNeeded(); // // // [System.Security.SecurityCritical] private PeerObjectCollection InternalGetAllObjects(Guid objectId, bool guidSupplied) { Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Entering InternalGetAllObjects() with ObjectId {0}.", objectId); Dictionary mergedObjects = new Dictionary(); PeerObjectCollection peerObjectCollection; // // Refresh the data at the endpoint before calling get objs // RefreshIfNeeded(); foreach (PeerEndPoint peerEndPoint in PeerEndPoints) { peerObjectCollection = InternalGetObjects(objectId, guidSupplied, peerEndPoint); // // Special case. If we have already found an endpoint with the user guid then // we just return it // if (guidSupplied && peerObjectCollection.Count != 0) return peerObjectCollection; foreach (PeerObject peerObject in peerObjectCollection) { mergedObjects[peerObject.Id] = peerObject; } } // // Return the object collection from the dictionary // Dictionary.ValueCollection objects = mergedObjects.Values; peerObjectCollection = new PeerObjectCollection(); foreach (PeerObject peerObject in objects) { peerObjectCollection.Add(peerObject); } Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Leaving InternalGetAllObjects(). " + "Returning collection with {0} objects.", peerObjectCollection.Count); return peerObjectCollection; } // // Gets specific objects for an endpoint // // // // // // // // // // // // // // // // // // [System.Security.SecurityCritical] internal static PeerObjectCollection InternalGetObjects(Guid objectId, bool guidSupplied, PeerEndPoint peerEndPoint) { if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Entering InternalGetObjects() with the following PeerEndPoint"); peerEndPoint.TracePeerEndPoint(); } PeerObjectCollection peerObjectColl = new PeerObjectCollection(); SafeCollabEnum handlePeerEnum = null; UInt32 objectCount = 0; int errorCode = 0; GCHandle guidHandle = new GCHandle(); IntPtr guidPtr = IntPtr.Zero; if (guidSupplied){ GUID guid = CollaborationHelperFunctions.ConvertGuidToGUID(objectId); guidHandle = GCHandle.Alloc(guid, GCHandleType.Pinned); guidPtr = guidHandle.AddrOfPinnedObject(); } PEER_ENDPOINT pep = new PEER_ENDPOINT(); pep.peerAddress = CollaborationHelperFunctions.ConvertIPEndpointToPEER_ADDRESS(peerEndPoint.EndPoint); // // Pin data to pass to native // GCHandle pepName = new GCHandle(); if (peerEndPoint.Name != null){ pepName = GCHandle.Alloc(peerEndPoint.Name, GCHandleType.Pinned); pep.pwzEndpointName = pepName.AddrOfPinnedObject(); } GCHandle peerEP = GCHandle.Alloc(pep, GCHandleType.Pinned); IntPtr ptrPeerEP = peerEP.AddrOfPinnedObject(); try{ // // Enumerate through the objects for the endpoint // errorCode = UnsafeCollabNativeMethods.PeerCollabEnumObjects(ptrPeerEP, guidPtr, out handlePeerEnum); if (errorCode != 0){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerCollabEnumObjects returned with errorcode {0}", errorCode); throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_GetObjectsFailed), errorCode); } errorCode = UnsafeCollabNativeMethods.PeerGetItemCount(handlePeerEnum, ref objectCount); if (errorCode != 0){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerGetItemCount returned with errorcode {0}", errorCode); throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_GetObjectsFailed), errorCode); } if (objectCount == 0){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "No PeerObjects found."); return peerObjectColl; } unsafe { SafeCollabData objectArray; errorCode = UnsafeCollabNativeMethods.PeerGetNextItem(handlePeerEnum, ref objectCount, out objectArray); if (errorCode != 0){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerGetNextItem returned with errorcode {0}", errorCode); throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_GetObjectsFailed), errorCode); } IntPtr pPEER_OBJECT = objectArray.DangerousGetHandle(); IntPtr* pObjects = (IntPtr*)pPEER_OBJECT; // // Loop through the applications array from native // for (ulong i = 0; i < objectCount; i++){ PEER_OBJECT* pPeerObject = (PEER_OBJECT*)pObjects[i]; byte[] data = null; if (pPeerObject->data.cbData != 0){ data = new byte[pPeerObject->data.cbData]; Marshal.Copy(pPeerObject->data.pbData, data, 0, (int)pPeerObject->data.cbData); } PeerObject peerObject = new PeerObject(CollaborationHelperFunctions.ConvertGUIDToGuid(pPeerObject->guid), data, (PeerScope)pPeerObject->dwPublicationScope); if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Retrieved following Object"); peerObject.TracePeerObject(); } peerObjectColl.Add(peerObject); } } } finally{ if (guidHandle.IsAllocated) guidHandle.Free(); if (pepName.IsAllocated) pepName.Free(); if (peerEP.IsAllocated) peerEP.Free(); if (handlePeerEnum != null) handlePeerEnum.Dispose(); } Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Leaving InternalGetApplications(). " + "Returning collection with {0} objects.", peerObjectColl.Count); return peerObjectColl; } public abstract PeerInvitationResponse Invite(); public abstract PeerInvitationResponse Invite(PeerApplication applicationToInvite, string message, byte[] invitationData); // // Invites an endpoint with passed data. Includes a contact if it was passed in. // // // // // // // // // // // // // // // // // // // [System.Security.SecurityCritical] internal static PeerInvitationResponse InternalInviteEndPoint(Guid applicationToInviteGuid, string message, byte[] invitationData, PeerEndPoint peerEndPoint, PeerContact peerContact) { if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Entering InternalInviteEndPoint() with the following information."); Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Invitation Message: ", message); if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Verbose) && (invitationData != null)){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "\tInvitation data:"); Logging.DumpData(Logging.P2PTraceSource, TraceEventType.Verbose, Logging.P2PTraceSource.MaxDataSize, invitationData, 0, invitationData.Length); } else Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Invitation Data length: ", (invitationData != null ? invitationData.Length : 0)); if (peerEndPoint != null) peerEndPoint.TracePeerEndPoint(); } PEER_INVITATION pi = new PEER_INVITATION(); pi.applicationId = CollaborationHelperFunctions.ConvertGuidToGUID(applicationToInviteGuid); pi.pwzMessage = message; SafeCollabMemory data = null; pi.applicationData.cbData = (invitationData != null) ? (UInt32)invitationData.Length : 0; // // Marshal Invitation Data // if ((invitationData != null) && (invitationData.Length > 0)) { data = new SafeCollabMemory(invitationData.Length); pi.applicationData.pbData = data.DangerousGetHandle(); Marshal.Copy(invitationData, 0, pi.applicationData.pbData, invitationData.Length); } else pi.applicationData.pbData = IntPtr.Zero; PEER_ENDPOINT pep = new PEER_ENDPOINT(); pep.peerAddress = CollaborationHelperFunctions.ConvertIPEndpointToPEER_ADDRESS(peerEndPoint.EndPoint); // // Pin data to pass to native // GCHandle pepName = new GCHandle(); if (peerEndPoint.Name != null){ pepName = GCHandle.Alloc(peerEndPoint.Name, GCHandleType.Pinned); pep.pwzEndpointName = pepName.AddrOfPinnedObject(); } GCHandle peerEP = GCHandle.Alloc(pep, GCHandleType.Pinned); IntPtr ptrPeerEP = peerEP.AddrOfPinnedObject(); SafeCollabData safeResponse = null; PeerInvitationResponse peerInvResponse = null; int errorCode; try{ // // Make native call with endpoint with/without contact // if (peerContact != null){ // // Generate native contact // SafeCollabMemory safeCredentials = null; PEER_CONTACT pc = CollaborationHelperFunctions.ConvertPeerContactToPEER_CONTACT(peerContact, ref safeCredentials); try{ errorCode = UnsafeCollabNativeMethods.PeerCollabInviteContact(ref pc, ptrPeerEP, ref pi, out safeResponse); } finally{ if (safeCredentials != null) safeCredentials.Dispose(); } } else errorCode = UnsafeCollabNativeMethods.PeerCollabInviteEndpoint(ptrPeerEP, ref pi, out safeResponse); if (errorCode != 0){ if ((errorCode == UnsafeCollabReturnCodes.PEER_E_TIMEOUT) || (errorCode == UnsafeCollabReturnCodes.ERROR_TIMEOUT)){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Timed out. Leaving InternalInviteEndPoint() with InvitationResponseType expired."); return new PeerInvitationResponse(PeerInvitationResponseType.Expired); } Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, ((peerContact != null) ? "PeerCollabInviteContact" : "PeerCollabInviteEndpoint") + " returned with errorcode {0}", errorCode); throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_InviteFailed), errorCode); } if (!safeResponse.IsInvalid){ PEER_INVITATION_RESPONSE pir = (PEER_INVITATION_RESPONSE)Marshal.PtrToStructure(safeResponse.DangerousGetHandle(), typeof(PEER_INVITATION_RESPONSE)); peerInvResponse = new PeerInvitationResponse(pir.action); } } finally{ if (safeResponse != null) safeResponse.Dispose(); if (pepName.IsAllocated) pepName.Free(); if (peerEP.IsAllocated) peerEP.Free(); } Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Leaving InternalInviteEndPoint() with InvitationResponse {0}.", peerInvResponse); return peerInvResponse; } private event EventHandler m_inviteCompleted; public event EventHandler InviteCompleted { add{ if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand(); m_inviteCompleted += value; } remove{ if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand(); m_inviteCompleted -= value; } } public abstract void InviteAsync(Object userToken); public abstract void InviteAsync( PeerApplication applicationToInvite, string message, byte[] invitationData, Object userToken); #region Invite Async variables SendOrPostCallback OnInviteCompletedDelegate; internal Dictionary m_inviteAsyncHelperList = new Dictionary(); #endregion // // // [System.Security.SecurityCritical] internal void InternalInviteAsync(Guid applicationToInviteGuid, string message, byte[] invitationData, PeerEndPointCollection peerEndPoints, PeerContact peerContact, Object userToken) { InviteAsyncHelper inviteAsyncHelper = null; // //The userToken can't be duplicate of what is in the //current list. These are the requriments for the new Async model //that supports multiple outstanding async calls // int newTraceEventId = NewTraceEventId; lock (m_inviteAsyncHelperList){ if (m_inviteAsyncHelperList.ContainsKey(userToken)){ throw new ArgumentException(SR.GetString(SR.DuplicateUserToken)); } inviteAsyncHelper = new InviteAsyncHelper( peerContact, this, peerEndPoints, applicationToInviteGuid, message, invitationData, userToken, newTraceEventId); m_inviteAsyncHelperList[userToken] = inviteAsyncHelper; } try{ // //Start resolution on that resolver // inviteAsyncHelper.InviteAsync(); } catch{ // //If an exception happens clear the userState from the //list so that that token can be reused // lock (m_inviteAsyncHelperList){ m_inviteAsyncHelperList.Remove(userToken); } throw; } } protected virtual void OnInviteCompleted(InviteCompletedEventArgs e) { EventHandler handlerCopy = m_inviteCompleted; if (handlerCopy != null){ handlerCopy(this, e); Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Fired the invite completed event callback."); } } void InviteCompletedWaitCallback(object operationState) { OnInviteCompleted((InviteCompletedEventArgs)operationState); } internal void PrepareToRaiseInviteCompletedEvent(AsyncOperation asyncOP, InviteCompletedEventArgs args) { lock (m_inviteAsyncHelperList){ InviteAsyncHelper helper = m_inviteAsyncHelperList[args.UserState]; if (helper == null){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Critical, 0, "userState for which we are about to call Completed event does not exist in the pending async list"); }else{ Logging.P2PTraceSource.TraceEvent(TraceEventType.Critical, helper.TraceEventId, "userState {0} is being removed from the pending async list", args.UserState.GetHashCode()); m_inviteAsyncHelperList.Remove(args.UserState); } } asyncOP.PostOperationCompleted(OnInviteCompletedDelegate, args); } // // // [System.Security.SecurityCritical] public void InviteAsyncCancel(Object userToken) { if (userToken == null) throw new ArgumentNullException("userToken"); InviteAsyncHelper helper; lock (m_inviteAsyncHelperList){ if (!m_inviteAsyncHelperList.TryGetValue(userToken, out helper)){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Warning, 0, "InviteAsyncCancel called with a userState token that is not in the pending async list - returning"); return; } } Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, helper.TraceEventId, "Proceeding to cancel the pending async"); helper.CancelAsync(userToken); } // // Used to track inviteasynchelpers // private static int s_TraceEventId = 1; internal static int NewTraceEventId { get{ Interlocked.CompareExchange(ref s_TraceEventId, 0, int.MaxValue); Interlocked.Increment(ref s_TraceEventId); return s_TraceEventId; } } public bool Equals(Peer other) { if (other != null){ if (other.PeerEndPoints != null){ return other.PeerEndPoints.Equals(PeerEndPoints); } } return false; } public override string ToString() { return PeerEndPoints.ToString(); } private bool m_Disposed; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } // // // [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.Net.dll is still using pre-v4 security model and needs this demand")] [System.Security.SecurityCritical] [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter, SerializationFormatter = true)] void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { GetObjectData(info, context); } /// /// This is made virtual so that derived types can be implemented correctly /// /// /// [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)] protected virtual void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("_PeerEndPoints", PeerEndPoints); } protected virtual void Dispose(bool disposing) { if (!m_Disposed){ m_Disposed = true; } } } /// /// Helps in all the async invites sent from Peer, PeerContact and PeerNearMe /// internal class InviteAsyncHelper : IDisposable { internal object m_userState; internal SafeCollabInvite m_SafeCollabInvite; internal AutoResetEvent m_InviteEvent = new AutoResetEvent(false); // //The WaitHandle that hooks up a callback to the //event // internal RegisteredWaitHandle m_RegisteredWaitHandle; // //Disposed or not // internal bool m_Disposed; internal bool m_Cancelled; // //Async operation to ensure synchornization //context // AsyncOperation m_AsyncOp; // //A link to the resolver to avoid //circular dependencies and enable GC // WeakReference m_peerWeakReference; // //Lock to make sure things don't mess up stuff // object m_Lock = new Object(); // //EventID or Just a tracking id // int m_TraceEventId; // // Store the latest exception // Exception m_latestException; // // Stores reponses from all endpoints // Collection m_responses = new Collection(); // // Callback called // bool m_Completed; // // Used to ensure only on thread calls the callback // bool m_aboutToFireCallback; object m_aboutToFireCallbackLock = new object(); // // Number of reponses received // int m_numberOfResponses; PeerContact m_peerContact; PeerEndPointCollection m_peerEndPoints; Guid m_applicationId; string m_message; byte[] m_inviteData; internal InviteAsyncHelper( PeerContact peerContact, Peer parentPeer, PeerEndPointCollection peerEndPoints, Guid applicationId, string message, byte[] inviteData, object userState, int NewTraceEventId) { m_userState = userState; m_peerContact = peerContact; m_applicationId = applicationId; m_message = message; m_inviteData = inviteData; m_peerEndPoints = peerEndPoints; m_TraceEventId = NewTraceEventId; m_peerWeakReference = new WeakReference(parentPeer); Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "New InviteAsyncHelper created with TraceEventID {0}", m_TraceEventId); Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "\tPeerContact: {0}, App Guid: {1}, userState {2}, ParentReference {3}", (m_peerContact != null ? m_peerContact.ToString() : "null"), applicationId.ToString(), userState.GetHashCode(), m_peerWeakReference.Target.GetHashCode() ); } // // // // // // // // // // // // [System.Security.SecurityCritical] internal void InviteAsync() { if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "InviteAsync called"); // //First wire up a callback // m_RegisteredWaitHandle = ThreadPool.RegisterWaitForSingleObject(m_InviteEvent, //Event that triggers the callback new WaitOrTimerCallback(InviteCallback), //callback to be called null, //state to be passed -1, //Timeout - aplicable only for timers not for events false //call us everytime the event is set not just one time ); // //Now call the native API to start the resolution //process save the handle for later // PEER_INVITATION pi = new PEER_INVITATION(); pi.applicationId = CollaborationHelperFunctions.ConvertGuidToGUID(m_applicationId); pi.pwzMessage = m_message; SafeCollabMemory data = null; pi.applicationData.cbData = (m_inviteData != null) ? (UInt32)m_inviteData.Length : 0; if ((m_inviteData != null) && (m_inviteData.Length > 0)){ data = new SafeCollabMemory(m_inviteData.Length); pi.applicationData.pbData = data.DangerousGetHandle(); Marshal.Copy(m_inviteData, 0, pi.applicationData.pbData, m_inviteData.Length); } else pi.applicationData.pbData = IntPtr.Zero; foreach (PeerEndPoint peerEndPoint in m_peerEndPoints) { try{ InviteAsyncEndPoint(peerEndPoint, pi); } catch (PeerToPeerException){ if (!m_SafeCollabInvite.IsInvalid && !m_SafeCollabInvite.IsClosed){ m_SafeCollabInvite.Dispose(); } m_RegisteredWaitHandle.Unregister(null); m_RegisteredWaitHandle = null; throw; } } // //Create an async operation with the given //user state // m_AsyncOp = AsyncOperationManager.CreateOperation(m_userState); Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Leaving InviteAsync."); } // // // // // // // // // // // // // [System.Security.SecurityCritical] internal void InviteAsyncEndPoint(PeerEndPoint peerEndPoint, PEER_INVITATION pi) { if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "InviteAsyncEndPoint() is called with the following Info"); peerEndPoint.TracePeerEndPoint(); } PEER_ENDPOINT pep = new PEER_ENDPOINT(); pep.peerAddress = CollaborationHelperFunctions.ConvertIPEndpointToPEER_ADDRESS(peerEndPoint.EndPoint); // // Pin all the data to pass to native // GCHandle pepName = new GCHandle(); if (peerEndPoint.Name != null){ pepName = GCHandle.Alloc(peerEndPoint.Name, GCHandleType.Pinned); pep.pwzEndpointName = pepName.AddrOfPinnedObject(); } GCHandle peerEP = GCHandle.Alloc(pep, GCHandleType.Pinned); IntPtr ptrPeerEP = peerEP.AddrOfPinnedObject(); int errorCode; try{ // // Make native call with endpoint with/without contact // if (m_peerContact != null){ // // Generate native contact // SafeCollabMemory safeCredentials = null; PEER_CONTACT pc = CollaborationHelperFunctions.ConvertPeerContactToPEER_CONTACT(m_peerContact, ref safeCredentials); try{ errorCode = UnsafeCollabNativeMethods.PeerCollabAsyncInviteContact( ref pc, ptrPeerEP, ref pi, m_InviteEvent.SafeWaitHandle, out m_SafeCollabInvite); } finally{ if (safeCredentials != null) safeCredentials.Dispose(); } } else errorCode = UnsafeCollabNativeMethods.PeerCollabAsyncInviteEndpoint(ptrPeerEP, ref pi, m_InviteEvent.SafeWaitHandle, out m_SafeCollabInvite); } finally { if (pepName.IsAllocated) pepName.Free(); if (peerEP.IsAllocated) peerEP.Free(); } if (errorCode != 0){ throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_AsyncInviteFailed), errorCode); } Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Leaving InviteAsyncEndPoint."); } // // Invite callback. Will fire only if i has at least one accepted or when it has all the responses // from all the endpoints // // // // // // // // // // [System.Security.SecurityCritical] internal void InviteCallback(object state, bool timedOut) { Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Entering InviteCallback."); SafeCollabData response = null; int errorCode = 0; InviteCompletedEventArgs inviteCompletedArgs = null; Peer peer = null; bool fireCallback = false; PEER_INVITATION_RESPONSE pir = new PEER_INVITATION_RESPONSE(); try { lock (m_Lock){ if (m_Cancelled || m_Completed){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Invite cancelled({0}) or completed({1}). Returning without doing anything.", m_Cancelled, m_Completed); return; } errorCode = UnsafeCollabNativeMethods.PeerCollabGetInvitationResponse(m_SafeCollabInvite, out response); } if ((errorCode != 0) && (errorCode != UnsafeCollabReturnCodes.PEER_E_TIMEOUT)){ m_latestException = PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_AsyncInviteException), errorCode); Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Got an exception {0}. Storing it in latest exception.", m_latestException); } else { pir = (PEER_INVITATION_RESPONSE)Marshal.PtrToStructure(response.DangerousGetHandle(), typeof(PEER_INVITATION_RESPONSE)); // // Store the responses // lock (m_responses) m_responses.Add(pir.action); if (pir.action == PeerInvitationResponseType.Accepted){ inviteCompletedArgs = new InviteCompletedEventArgs(new PeerInvitationResponse(pir.action), null, false, m_AsyncOp.UserSuppliedState); fireCallback = true; // // Got an accepted. unregister callback to disable all othe other endpoint callbacks // Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Found an accepted. About to fire callback."); m_RegisteredWaitHandle.Unregister(null); } } Interlocked.Increment(ref m_numberOfResponses); if ((!fireCallback) && (m_numberOfResponses == m_peerEndPoints.Count) && (!m_aboutToFireCallback)){ // // Two threads can be here at the same time when all the responses have been // received and only one should be allowed to call the callback // lock (m_aboutToFireCallbackLock){ if (m_aboutToFireCallback) return; m_aboutToFireCallback = true; bool foundDeclined = false; bool foundExpired = false; fireCallback = true; // // Got all responses; make a decision // foreach (PeerInvitationResponseType responseType in m_responses) { if (responseType == PeerInvitationResponseType.Expired){ foundExpired = true; } else if (responseType == PeerInvitationResponseType.Declined){ foundDeclined = true; break; } } // // If at least one is declined, return declined. // if (foundDeclined){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Got a declined invite response."); inviteCompletedArgs = new InviteCompletedEventArgs(new PeerInvitationResponse(PeerInvitationResponseType.Declined), null, false, m_AsyncOp.UserSuppliedState); } else if (foundExpired){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Got an expired invite response but no accepted or declined."); inviteCompletedArgs = new InviteCompletedEventArgs(new PeerInvitationResponse(PeerInvitationResponseType.Expired), null, false, m_AsyncOp.UserSuppliedState); } else{ Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Got all error responses"); inviteCompletedArgs = new InviteCompletedEventArgs(null, (m_latestException != null ? m_latestException : new PeerToPeerException("InviteAsync failure.")), false, m_AsyncOp.UserSuppliedState); } } } // //Last chance to prevent the callback // if (fireCallback){ peer = m_peerWeakReference.Target as Peer; if (!m_Completed && (peer != null)){ lock (m_Lock){ // // Async op may be cancelled already // if (!m_Completed && !m_Cancelled){ // //Mark as completed so that this gets fired only once // m_Completed = true; Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Firing callback with response type {0}.", inviteCompletedArgs.InviteResponse); peer.PrepareToRaiseInviteCompletedEvent(m_AsyncOp, inviteCompletedArgs); } } } } } finally{ if (response != null) response.Dispose(); } Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Leaving InviteCallback."); } // // // // // [System.Security.SecurityCritical] public void ContinueCancelCallback(object state) { Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Entering ContineCancelCallback."); try{ lock (m_Lock){ if (m_Completed) return; m_Cancelled = true; int errorCode = UnsafeCollabNativeMethods.PeerCollabCancelInvitation(m_SafeCollabInvite); if (errorCode != 0){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerCollabCancelInvitation returned with errorcode {0}", errorCode); } m_SafeCollabInvite.Dispose(); } Peer peer = m_peerWeakReference.Target as Peer; if (peer != null){ InviteCompletedEventArgs e = new InviteCompletedEventArgs(null, null, true, m_AsyncOp.UserSuppliedState); peer.PrepareToRaiseInviteCompletedEvent(m_AsyncOp, e); } } catch (ObjectDisposedException ex){ Logging.P2PTraceSource.TraceEvent(TraceEventType.Critical, 0, "Exception while cancelling the call {0}", ex); } Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Leaving ContineCancelCallback."); } // // // [System.Security.SecurityCritical] public void CancelAsync(object state) { // //Defer the work to a callback // ThreadPool.QueueUserWorkItem(new WaitCallback(ContinueCancelCallback), state); } // // // [System.Security.SecurityCritical] public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } // // // // // [System.Security.SecurityCritical] public void Dispose(bool disposing) { if (!m_Disposed){ if (!m_SafeCollabInvite.IsInvalid){ m_SafeCollabInvite.Dispose(); } if (m_RegisteredWaitHandle != null){ m_RegisteredWaitHandle.Unregister(null); m_RegisteredWaitHandle = null; } if (m_InviteEvent != null){ m_InviteEvent.Close(); } } m_Disposed = true; } public override int GetHashCode() { return m_TraceEventId; } internal int TraceEventId { get{ return m_TraceEventId; } } } }