Example #1
0
        private static AppDomain CreateModAppDomain(string modFile)
        {
            //TODO: Should maybe log error
            if (ModAppDomains.ContainsKey(modFile))
            {
                return(ModAppDomains[modFile]);
            }

            string         modDir         = Path.GetDirectoryName(modFile);
            AppDomainSetup appDomainSetup = new AppDomainSetup();

            appDomainSetup.DisallowCodeDownload     = true;
            appDomainSetup.DisallowBindingRedirects = true;
            appDomainSetup.PrivateBinPath           = string.Format("{0};{1}", modDir.Replace("file:\\", ""), Environment.CurrentDirectory);
            //appDomainSetup.PrivateBinPathProbe = null;
            appDomainSetup.ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName;
            appDomainSetup.ApplicationBase = Environment.CurrentDirectory;
            appDomainSetup.DynamicBase     = Environment.CurrentDirectory;

            PermissionSet permissions = new PermissionSet(null);

            //Make sure mods only read from allowed directories
            if (allowedReadDirs.Length > 0)
            {
                FileIOPermission readPermission = new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, allowedReadDirs);
                permissions.AddPermission(readPermission);
            }
            //Allow mod to read from its own directory and game base directory
            modDir = modDir.Replace("file:\\", "");
            FileIOPermission readOwnDirectory = new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, new string[] { Path.GetFullPath(modDir), Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory) });

            permissions.AddPermission(readOwnDirectory);

            //Make sure mods only write to allowed directories
            if (allowedWriteDirs.Length > 0)
            {
                FileIOPermission writePermission = new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Append, allowedWriteDirs);
                permissions.AddPermission(writePermission);
            }

            //Allow mod to run code
            SecurityPermission sp = new SecurityPermission(SecurityPermissionFlag.Execution | SecurityPermissionFlag.Infrastructure | SecurityPermissionFlag.SerializationFormatter | SecurityPermissionFlag.AllFlags);

            permissions.SetPermission(sp);

            //Restrict mods reflection capabilities
            ReflectionPermission rp = new ReflectionPermission(ReflectionPermissionFlag.NoFlags);

            permissions.SetPermission(rp);

            return(AppDomain.CreateDomain(string.Format("{0}AppDomain", Path.GetFileNameWithoutExtension(modFile)), AppDomain.CurrentDomain.Evidence, appDomainSetup, permissions, typeof(Mod).Assembly.Evidence.GetHostEvidence <StrongName>()));
        }
Example #2
0
        internal PolicyStatement CalculatePolicy(Url url)
        {
            URLString uRLString = url.GetURLString();

            if (string.Compare(uRLString.Scheme, "file", StringComparison.OrdinalIgnoreCase) != 0)
            {
                return(null);
            }
            string        directoryName = uRLString.GetDirectoryName();
            PermissionSet permSet       = new PermissionSet(PermissionState.None);

            permSet.SetPermission(new FileIOPermission(this.m_access, Path.GetFullPath(directoryName)));
            return(new PolicyStatement(permSet, PolicyStatementAttribute.Nothing));
        }
Example #3
0
        private PolicyStatement CalculatePolicy(Url url)
        {
            URLString urlString = url.GetURLString();

            if (String.Compare(urlString.Scheme, "file", true, CultureInfo.InvariantCulture) != 0)
            {
                return(null);
            }

            String directory = urlString.GetDirectoryName();

            PermissionSet permSet = new PermissionSet(PermissionState.None);

            permSet.SetPermission(new FileIOPermission(m_access, directory));

            return(new PolicyStatement(permSet, PolicyStatementAttribute.Nothing));
        }
Example #4
0
    // NOTE:  To test, replace www.contoso.com w/ the local string

//<snippet1>

    public static Object GetFile(String fileURL, XmlResolver resolver)
    {
        // Generate the default PermissionSet using the file URL.
        Evidence      evidence      = XmlSecureResolver.CreateEvidenceForUrl(fileURL);
        PermissionSet myPermissions = SecurityManager.ResolvePolicy(evidence);

        // Modify the PermissionSet to only allow access to http://www.contoso.com.
        // Create a WebPermission which only allows access to http://www.contoso.com.
        WebPermission myWebPermission = new WebPermission(NetworkAccess.Connect, "http://www.contoso.com");

        // Replace the existing WebPermission in myPermissions with the updated WebPermission.
        myPermissions.SetPermission(myWebPermission);

        // Use the modified PermissionSet to construct the XmlSecureResolver.
        XmlSecureResolver sResolver = new XmlSecureResolver(resolver, myPermissions);

        // Get the object.
        Uri fullUri = sResolver.ResolveUri(null, fileURL);

        return(sResolver.GetEntity(fullUri, null, null));
    }
        public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack,
                                               IMessage requestMsg,
                                               ITransportHeaders requestHeaders, Stream requestStream,
                                               out IMessage responseMsg, out ITransportHeaders responseHeaders,
                                               out Stream responseStream)
        {
            if (requestMsg != null)
            {
                // The message has already been deserialized so delegate to the next sink.
                return(_nextSink.ProcessMessage(
                           sinkStack,
                           requestMsg, requestHeaders, requestStream,
                           out responseMsg, out responseHeaders, out responseStream));
            }

            if (requestHeaders == null)
            {
                throw new ArgumentNullException("requestHeaders");
            }

            BaseTransportHeaders wkRequestHeaders = requestHeaders as BaseTransportHeaders;

            ServerProcessing processing;

            responseHeaders = null;
            responseStream  = null;

            String verb        = null;
            String contentType = null;

            bool bCanServiceRequest = true;

            // determine the content type
            String contentTypeHeader = null;

            if (wkRequestHeaders != null)
            {
                contentTypeHeader = wkRequestHeaders.ContentType;
            }
            else
            {
                contentTypeHeader = requestHeaders["Content-Type"] as String;
            }
            if (contentTypeHeader != null)
            {
                String charsetValue;
                HttpChannelHelper.ParseContentType(contentTypeHeader,
                                                   out contentType, out charsetValue);
            }

            // check to see if Content-Type matches
            if ((contentType != null) &&
                (String.CompareOrdinal(contentType, CoreChannel.BinaryMimeType) != 0))
            {
                bCanServiceRequest = false;
            }

            // check for http specific verbs
            if (_protocol == Protocol.Http)
            {
                verb = (String)requestHeaders["__RequestVerb"];
                if (!verb.Equals("POST") && !verb.Equals("M-POST"))
                {
                    bCanServiceRequest = false;
                }
            }

            // either delegate or return an error message if we can't service the request
            if (!bCanServiceRequest)
            {
                // delegate to next sink if available
                if (_nextSink != null)
                {
                    return(_nextSink.ProcessMessage(sinkStack, null, requestHeaders, requestStream,
                                                    out responseMsg, out responseHeaders, out responseStream));
                }
                else
                {
                    // send back an error message
                    if (_protocol == Protocol.Http)
                    {
                        // return a client bad request error
                        responseHeaders = new TransportHeaders();
                        responseHeaders["__HttpStatusCode"]   = "400";
                        responseHeaders["__HttpReasonPhrase"] = "Bad Request";
                        responseStream = null;
                        responseMsg    = null;
                        return(ServerProcessing.Complete);
                    }
                    else
                    {
                        // The transport sink will catch this and do something here.
                        throw new RemotingException(
                                  CoreChannel.GetResourceString("Remoting_Channels_InvalidRequestFormat"));
                    }
                }
            }


            try
            {
                String objectUri = null;

                bool   bIsCustomErrorEnabled = true;
                object oIsCustomErrorEnabled = requestHeaders["__CustomErrorsEnabled"];
                if (oIsCustomErrorEnabled != null && oIsCustomErrorEnabled is bool)
                {
                    bIsCustomErrorEnabled = (bool)oIsCustomErrorEnabled;
                }
                CallContext.SetData("__CustomErrorsEnabled", bIsCustomErrorEnabled);

                if (wkRequestHeaders != null)
                {
                    objectUri = wkRequestHeaders.RequestUri;
                }
                else
                {
                    objectUri = (String)requestHeaders[CommonTransportKeys.RequestUri];
                }

                if (objectUri != lastUri && RemotingServices.GetServerTypeForUri(objectUri) == null)
                {
                    throw new RemotingException(
                              CoreChannel.GetResourceString("Remoting_ChnlSink_UriNotPublished"));
                }
                else
                {
                    lastUri = objectUri;
                }

                PermissionSet currentPermissionSet = null;
                if (this.TypeFilterLevel != TypeFilterLevel.Full)
                {
                    currentPermissionSet = new PermissionSet(PermissionState.None);
                    currentPermissionSet.SetPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter));
                }

                try {
                    if (currentPermissionSet != null)
                    {
                        currentPermissionSet.PermitOnly();
                    }

                    // Deserialize Request - Stream to IMessage
                    requestMsg = CoreChannel.DeserializeBinaryRequestMessage(objectUri, requestStream, _strictBinding, this.TypeFilterLevel);
                }
                finally {
                    if (currentPermissionSet != null)
                    {
                        CodeAccessPermission.RevertPermitOnly();
                    }
                }
                requestStream.Close();

                if (requestMsg == null)
                {
                    throw new RemotingException(CoreChannel.GetResourceString("Remoting_DeserializeMessage"));
                }


                // Dispatch Call
                sinkStack.Push(this, null);
                processing =
                    _nextSink.ProcessMessage(sinkStack, requestMsg, requestHeaders, null,
                                             out responseMsg, out responseHeaders, out responseStream);
                // make sure that responseStream is null
                if (responseStream != null)
                {
                    throw new RemotingException(
                              CoreChannel.GetResourceString("Remoting_ChnlSink_WantNullResponseStream"));
                }

                switch (processing)
                {
                case ServerProcessing.Complete:
                {
                    if (responseMsg == null)
                    {
                        throw new RemotingException(CoreChannel.GetResourceString("Remoting_DispatchMessage"));
                    }

                    sinkStack.Pop(this);

                    SerializeResponse(sinkStack, responseMsg,
                                      ref responseHeaders, out responseStream);
                    break;
                } // case ServerProcessing.Complete

                case ServerProcessing.OneWay:
                {
                    sinkStack.Pop(this);
                    break;
                } // case ServerProcessing.OneWay:

                case ServerProcessing.Async:
                {
                    sinkStack.Store(this, null);
                    break;
                } // case ServerProcessing.Async
                } // switch (processing)
            }
            catch (Exception e)
            {
                processing  = ServerProcessing.Complete;
                responseMsg = new ReturnMessage(e, (IMethodCallMessage)(requestMsg == null?new ErrorMessage():requestMsg));
                //

                CallContext.SetData("__ClientIsClr", true);
                responseStream = (MemoryStream)CoreChannel.SerializeBinaryMessage(responseMsg, _includeVersioning);
                CallContext.FreeNamedDataSlot("__ClientIsClr");
                responseStream.Position = 0;
                responseHeaders         = new TransportHeaders();

                if (_protocol == Protocol.Http)
                {
                    responseHeaders["Content-Type"] = CoreChannel.BinaryMimeType;
                }
            }
            finally{
                CallContext.FreeNamedDataSlot("__CustomErrorsEnabled");
            }

            return(processing);
        } // ProcessMessage
        internal PolicyStatement CalculatePolicy(Url url) {
            URLString urlString = url.GetURLString();
            if (String.Compare(urlString.Scheme, "file", StringComparison.OrdinalIgnoreCase) != 0)
                return null;

            string directory = urlString.GetDirectoryName();
            PermissionSet permSet = new PermissionSet(PermissionState.None);
            permSet.SetPermission(new FileIOPermission(m_access, System.IO.Path.GetFullPath(directory)));

            return new PolicyStatement(permSet, PolicyStatementAttribute.Nothing);
        }
Example #7
0
 public static void PermissionSetDemo()
 {
     Console.WriteLine("Executing PermissionSetDemo");
     try
     {
         //<Snippet2>
         // Open a new PermissionSet.
         PermissionSet ps1 = new PermissionSet(PermissionState.None);
         Console.WriteLine("Adding permission to open a file from a file dialog box.");
         //<Snippet3>
         // Add a permission to the permission set.
         ps1.AddPermission(
             new FileDialogPermission(FileDialogPermissionAccess.Open));
         //</Snippet3>
         Console.WriteLine("Demanding permission to open a file.");
         ps1.Demand();
         Console.WriteLine("Demand succeeded.");
         //</Snippet2>
         Console.WriteLine("Adding permission to save a file from a file dialog box.");
         ps1.AddPermission(
             new FileDialogPermission(FileDialogPermissionAccess.Save));
         Console.WriteLine("Demanding permission to open and save a file.");
         ps1.Demand();
         Console.WriteLine("Demand succeeded.");
         Console.WriteLine("Adding permission to read environment variable USERNAME.");
         ps1.AddPermission(
             new EnvironmentPermission(EnvironmentPermissionAccess.Read, "USERNAME"));
         ps1.Demand();
         Console.WriteLine("Demand succeeded.");
         Console.WriteLine("Adding permission to read environment variable COMPUTERNAME.");
         ps1.AddPermission(
             new EnvironmentPermission(EnvironmentPermissionAccess.Read, "COMPUTERNAME"));
         //<Snippet4>
         // Demand all the permissions in the set.
         Console.WriteLine("Demand all permissions.");
         ps1.Demand();
         //</Snippet4>
         Console.WriteLine("Demand succeeded.");
         //<Snippet5>
         // Display the number of permissions in the set.
         Console.WriteLine("Number of permissions = " + ps1.Count);
         //</Snippet5>
         //<Snippet6>
         // Display the value of the IsSynchronized property.
         Console.WriteLine("IsSynchronized property = " + ps1.IsSynchronized);
         //</Snippet6>
         //<Snippet7>
         // Display the value of the IsReadOnly property.
         Console.WriteLine("IsReadOnly property = " + ps1.IsReadOnly);
         //</Snippet7>
         //<Snippet8>
         // Display the value of the SyncRoot property.
         Console.WriteLine("SyncRoot property = " + ps1.SyncRoot);
         //</Snippet8>
         //<Snippet9>
         // Display the result of a call to the ContainsNonCodeAccessPermissions method.
         // Gets a value indicating whether the PermissionSet contains permissions
         // that are not derived from CodeAccessPermission.
         // Returns true if the PermissionSet contains permissions that are not
         // derived from CodeAccessPermission; otherwise, false.
         Console.WriteLine("ContainsNonCodeAccessPermissions method returned " +
                           ps1.ContainsNonCodeAccessPermissions());
         //</Snippet9>
         //<Snippet10>
         Console.WriteLine("Value of the permission set ToString = \n" + ps1.ToString());
         //</Snippet10>
         PermissionSet ps2 = new PermissionSet(PermissionState.None);
         //<Snippet11>
         // Create a second permission set and compare it to the first permission set.
         ps2.AddPermission(
             new EnvironmentPermission(EnvironmentPermissionAccess.Read, "USERNAME"));
         ps2.AddPermission(
             new EnvironmentPermission(EnvironmentPermissionAccess.Write, "COMPUTERNAME"));
         IEnumerator list = ps1.GetEnumerator();
         Console.WriteLine("Permissions in first permission set:");
         while (list.MoveNext())
         {
             Console.WriteLine(list.Current.ToString());
         }
         Console.WriteLine("Second permission IsSubsetOf first permission = " + ps2.IsSubsetOf(ps1));
         //</Snippet11>
         //<Snippet12>
         // Display the intersection of two permission sets.
         PermissionSet ps3 = ps2.Intersect(ps1);
         Console.WriteLine("The intersection of the first permission set and "
                           + "the second permission set = " + ps3.ToString());
         //</Snippet12>
         // Create a new permission set.
         PermissionSet ps4 = new PermissionSet(PermissionState.None);
         ps4.AddPermission(
             new FileIOPermission(FileIOPermissionAccess.Read,
                                  "C:\\Temp\\Testfile.txt"));
         ps4.AddPermission(
             new FileIOPermission(FileIOPermissionAccess.Read |
                                  FileIOPermissionAccess.Write | FileIOPermissionAccess.Append,
                                  "C:\\Temp\\Testfile.txt"));
         //<Snippet13>
         // Display the union of two permission sets.
         PermissionSet ps5 = ps3.Union(ps4);
         Console.WriteLine("The union of permission set 3 and permission set 4 = "
                           + ps5.ToString());
         //</Snippet13>
         //<Snippet15>
         // Remove FileIOPermission from the permission set.
         ps5.RemovePermission(typeof(FileIOPermission));
         Console.WriteLine("The last permission set after removing FileIOPermission = "
                           + ps5.ToString());
         //</Snippet15>
         //<Snippet16>
         // Change the permission set using SetPermission.
         ps5.SetPermission(new EnvironmentPermission(EnvironmentPermissionAccess.AllAccess, "USERNAME"));
         Console.WriteLine("Permission set after SetPermission = " + ps5.ToString());
         //</Snippet16>
         //<Snippet17>
         // Display result of ToXml and FromXml operations.
         PermissionSet ps6 = new PermissionSet(PermissionState.None);
         ps6.FromXml(ps5.ToXml());
         Console.WriteLine("Result of ToFromXml = " + ps6.ToString() + "\n");
         //</Snippet17>
         //<Snippet18>
         // Display results of PermissionSet.GetEnumerator.
         IEnumerator psEnumerator = ps1.GetEnumerator();
         while (psEnumerator.MoveNext())
         {
             Console.WriteLine(psEnumerator.Current);
         }
         //</Snippet18>
         //<Snippet19>
         // Check for an unrestricted permission set.
         PermissionSet ps7 = new PermissionSet(PermissionState.Unrestricted);
         Console.WriteLine("Permission set is unrestricted = " + ps7.IsUnrestricted());
         //</Snippet19>
         //<Snippet20>
         // Create and display a copy of a permission set.
         ps7 = ps5.Copy();
         Console.WriteLine("Result of copy = " + ps7.ToString());
         //</Snippet20>
     }
     catch (Exception e)
     {
         Console.WriteLine(e.Message.ToString());
     }
 }
        public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg, out ITransportHeaders responseHeaders, out Stream responseStream)
        {
            ServerProcessing complete;

            if (requestMsg != null)
            {
                return(this._nextSink.ProcessMessage(sinkStack, requestMsg, requestHeaders, requestStream, out responseMsg, out responseHeaders, out responseStream));
            }
            if (requestHeaders == null)
            {
                throw new ArgumentNullException("requestHeaders");
            }
            BaseTransportHeaders headers = requestHeaders as BaseTransportHeaders;

            responseHeaders = null;
            responseStream  = null;
            string str         = null;
            string str2        = null;
            bool   flag        = true;
            string contentType = null;

            if (headers != null)
            {
                contentType = headers.ContentType;
            }
            else
            {
                contentType = requestHeaders["Content-Type"] as string;
            }
            if (contentType != null)
            {
                string str4;
                HttpChannelHelper.ParseContentType(contentType, out str2, out str4);
            }
            if ((str2 != null) && (string.Compare(str2, "text/xml", StringComparison.Ordinal) != 0))
            {
                flag = false;
            }
            if (this._protocol == Protocol.Http)
            {
                str = (string)requestHeaders["__RequestVerb"];
                if (!str.Equals("POST") && !str.Equals("M-POST"))
                {
                    flag = false;
                }
            }
            if (!flag)
            {
                if (this._nextSink != null)
                {
                    return(this._nextSink.ProcessMessage(sinkStack, null, requestHeaders, requestStream, out responseMsg, out responseHeaders, out responseStream));
                }
                if (this._protocol != Protocol.Http)
                {
                    throw new RemotingException(CoreChannel.GetResourceString("Remoting_Channels_InvalidRequestFormat"));
                }
                responseHeaders = new TransportHeaders();
                responseHeaders["__HttpStatusCode"]   = "400";
                responseHeaders["__HttpReasonPhrase"] = "Bad Request";
                responseStream = null;
                responseMsg    = null;
                return(ServerProcessing.Complete);
            }
            bool bClientIsClr = true;

            try
            {
                string str7;
                string uRI = null;
                if (headers != null)
                {
                    uRI = headers.RequestUri;
                }
                else
                {
                    uRI = (string)requestHeaders["__RequestUri"];
                }
                if (RemotingServices.GetServerTypeForUri(uRI) == null)
                {
                    throw new RemotingException(CoreChannel.GetResourceString("Remoting_ChnlSink_UriNotPublished"));
                }
                if (this._protocol == Protocol.Http)
                {
                    string str6 = (string)requestHeaders["User-Agent"];
                    if (str6 != null)
                    {
                        if (str6.IndexOf("MS .NET Remoting") == -1)
                        {
                            bClientIsClr = false;
                        }
                    }
                    else
                    {
                        bClientIsClr = false;
                    }
                }
                bool   data = true;
                object obj2 = requestHeaders["__CustomErrorsEnabled"];
                if ((obj2 != null) && (obj2 is bool))
                {
                    data = (bool)obj2;
                }
                CallContext.SetData("__CustomErrorsEnabled", data);
                Header[]      channelHeaders = this.GetChannelHeaders(requestHeaders, out str7);
                PermissionSet set            = null;
                if (this.TypeFilterLevel != System.Runtime.Serialization.Formatters.TypeFilterLevel.Full)
                {
                    set = new PermissionSet(PermissionState.None);
                    set.SetPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter));
                }
                try
                {
                    if (set != null)
                    {
                        set.PermitOnly();
                    }
                    requestMsg = CoreChannel.DeserializeSoapRequestMessage(requestStream, channelHeaders, this._strictBinding, this.TypeFilterLevel);
                }
                finally
                {
                    if (set != null)
                    {
                        CodeAccessPermission.RevertPermitOnly();
                    }
                }
                requestStream.Close();
                if (requestMsg == null)
                {
                    throw new RemotingException(CoreChannel.GetResourceString("Remoting_DeserializeMessage"));
                }
                if ((str7 != null) && !SoapServices.IsSoapActionValidForMethodBase(str7, ((IMethodMessage)requestMsg).MethodBase))
                {
                    throw new RemotingException(string.Format(CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Soap_InvalidSoapAction"), new object[] { str7 }));
                }
                sinkStack.Push(this, null);
                complete = this._nextSink.ProcessMessage(sinkStack, requestMsg, requestHeaders, null, out responseMsg, out responseHeaders, out responseStream);
                if (responseStream != null)
                {
                    throw new RemotingException(CoreChannel.GetResourceString("Remoting_ChnlSink_WantNullResponseStream"));
                }
                switch (complete)
                {
                case ServerProcessing.Complete:
                    if (responseMsg == null)
                    {
                        throw new RemotingException(CoreChannel.GetResourceString("Remoting_DispatchMessage"));
                    }
                    break;

                case ServerProcessing.OneWay:
                    sinkStack.Pop(this);
                    return(complete);

                case ServerProcessing.Async:
                    sinkStack.Store(this, null);
                    return(complete);

                default:
                    return(complete);
                }
                sinkStack.Pop(this);
                this.SerializeResponse(sinkStack, responseMsg, bClientIsClr, ref responseHeaders, out responseStream);
                return(complete);
            }
            catch (Exception exception)
            {
                complete    = ServerProcessing.Complete;
                responseMsg = new ReturnMessage(exception, (requestMsg == null) ? ((IMethodCallMessage) new System.Runtime.Remoting.Channels.Http.ErrorMessage()) : ((IMethodCallMessage)requestMsg));
                CallContext.SetData("__ClientIsClr", bClientIsClr);
                responseStream = (MemoryStream)CoreChannel.SerializeSoapMessage(responseMsg, this._includeVersioning);
                CallContext.FreeNamedDataSlot("__ClientIsClr");
                responseStream.Position = 0L;
                responseHeaders         = new TransportHeaders();
                if (this._protocol == Protocol.Http)
                {
                    responseHeaders["__HttpStatusCode"]   = "500";
                    responseHeaders["__HttpReasonPhrase"] = "Internal Server Error";
                    responseHeaders["Content-Type"]       = "text/xml; charset=\"utf-8\"";
                }
            }
            finally
            {
                CallContext.FreeNamedDataSlot("__CustomErrorsEnabled");
            }
            return(complete);
        }