Exemple #1
0
        }         // end ParseArguments

        /// <summary>
        /// Register all RestPlugins to the RestBot static plugin dictionary
        /// </summary>
        /// <param name="assembly">Given assembly to search for</param>
        static void RegisterAllCommands(Assembly assembly)
        {
            foreach (Type t in assembly.GetTypes())
            {
                try
                {
                    if (t.IsSubclassOf(typeof(RestPlugin)))
                    {
                        ConstructorInfo?info = t.GetConstructor(Type.EmptyTypes); // ask for parameter-less constructor for this class, if it exists (gwyneth 20220425)
                        if (info == null)
                        {
                            // Not a serious warning, some plugins might be incorrectly configured but still work well
                            DebugUtilities.WriteWarning($"Couldn't get constructor without parameters for plugin {t.GetType().Name}!");
                        }
                        else
                        {
                            RestPlugin plugin = (RestPlugin)info.Invoke(new object[0]);
                            RestBot.AddPlugin(plugin);
                        }
                    }
                }
                catch (Exception e)
                {
                    DebugUtilities.WriteError(e.Message);
                }
            }
        }
        static void Reaper_Elapsed(object?sender, System.Timers.ElapsedEventArgs e)
        {
            if (Program.Sessions == null)
            {
                // No sessions to worry about, we can return safely.
                return;
            }

            // If the configuration says we shouldn't run the Reaper, return. (gwyneth 20220412)
            if (Program.config != null && Program.config.plugin.reaper != true)
            {
                return;
            }

            List <UUID> kc = new List <UUID>();

            foreach (UUID key in Program.Sessions.Keys) //why not just lock() ? :P
            {
                kc.Add(key);
            }
            //Do reaping
            foreach (UUID key in kc)
            {
                if (Program.Sessions.ContainsKey(key))
                {
                    DateTime t  = DateTime.Now;
                    TimeSpan ts = t.Subtract(Program.Sessions[key].LastAccessed);
                    if (ts > SESSION_TIMEOUT)
                    {
                        DebugUtilities.WriteWarning("Session expiring, " + key.ToString() + ", timespan=" + ts.ToString());
                        Program.DisposeSession(key);
                    }
                }
            }
        }
Exemple #3
0
        }         // end Avatars_OnDirPeopleReply()

        /// <summary>
        /// name2key (avatar name to UUID)
        /// </summary>
        /// <param name="b">RESTbot object</param>
        /// <param name="name">Name of avatar to check</param>
        /// <returns>UUID of corresponding avatar, if it exists</returns>
        public static UUID getKey(RestBot b, String name)
        {
            DebugUtilities.WriteInfo("getKey(): Looking up key for " + name);
            b.Client.Directory.DirPeopleReply += Avatars_OnDirPeopleReply;
            name = name.ToLower();
            DebugUtilities.WriteDebug("getKey(): Looking up: " + name);
            DebugUtilities
            .WriteDebug("getKey(): Key not in cache, requesting directory lookup");                     // how do you know? (gwyneth 20220128)
            lock (KeyLookupEvents)
            {
                KeyLookupEvents.Add(name, new AutoResetEvent(false));
            }
            DebugUtilities
            .WriteDebug("getKey(): Lookup Event added, KeyLookupEvents now has a total of " +
                        KeyLookupEvents.Count.ToString() +
                        " entries");
            DirFindQueryPacket find = new DirFindQueryPacket();

            find.AgentData.AgentID    = b.Client.Self.AgentID;          // was Network and not Self
            find.AgentData.SessionID  = b.Client.Self.SessionID;
            find.QueryData.QueryFlags = 1;

            //find.QueryData.QueryText = Helpers.StringToField(name);
            find.QueryData.QueryText  = Utils.StringToBytes(name);
            find.QueryData.QueryID    = new UUID("00000000000000000000000000000001");
            find.QueryData.QueryStart = 0;

            b.Client.Network.SendPacket((Packet)find);
            DebugUtilities
            .WriteDebug("getKey(): Packet sent - KLE has " +
                        KeyLookupEvents.Count.ToString() +
                        " entries.. now waiting");
            if (!KeyLookupEvents[name].WaitOne(15000, true))
            {
                DebugUtilities
                .WriteWarning("getKey(): timed out on avatar name lookup for " +
                              name);
            }
            DebugUtilities.WriteDebug("getKey(): Waiting done!");
            lock (KeyLookupEvents)
            {
                KeyLookupEvents.Remove(name);
            }
            DebugUtilities
            .WriteDebug($"getKey(): Done with KLE, now has {KeyLookupEvents.Count.ToString()} entries");
            UUID response = new UUID();             // hopefully this sets the response to UUID.Zero first... (gwyneth 20220128)

            if (avatarKeys.ContainsKey(name))
            {
                response = avatarKeys[name];
                lock (avatarKeys)
                {
                    avatarKeys.Remove(name);
                }
            }
            b.Client.Directory.DirPeopleReply -= Avatars_OnDirPeopleReply;
            return(response);
        }         // end getKey()
Exemple #4
0
 /// <summary>
 /// Callback for when this bot gets disconnected, attempting to connect again in 5 minutes.
 /// </summary>
 /// <param name="sender">Sender object</param>
 /// <param name="e">Arguments for the disconnected event</param>
 /// <remarks>rewrote to show message</remarks>
 void Network_OnDisconnected(object?sender, DisconnectedEventArgs e)
 {
     if (e.Reason != NetworkManager.DisconnectType.ClientInitiated)
     {
         myStatus = Status.Reconnecting;
         DebugUtilities.WriteWarning($"{sessionid.ToString()} was disconnected ({e.Message.ToString()}), but I'm logging back in again in 5 minutes.");
         ReloginTimer.Stop();
         ReloginTimer.Interval = 5 * 60 * 1000;
         ReloginTimer.Start();
     }
 }
Exemple #5
0
        }         // end getName()

        /// <summary>
        /// Loop through all (pending) replies for UUID/Avatar names
        /// and process them if they contain any key we're looking for.
        /// </summary>
        /// <param name="sender">parameter ignored</param>
        /// <param name="e">List of UUID/Avatar names</param>
        /// <returns>void</returns>
        /// <remarks>using new Directory functionality</remarks>
        public static void Avatars_OnDirPeopleReply(
            object?sender,
            DirPeopleReplyEventArgs e
            )
        {
            if (e.MatchedPeople.Count < 1)
            {
                DebugUtilities
                .WriteWarning("Avatars_OnDirPeopleReply() - Error: empty people directory reply");
            }
            else
            {
                int replyCount = e.MatchedPeople.Count;

                DebugUtilities
                .WriteInfo("Avatars_OnDirPeopleReply() - Processing " +
                           replyCount.ToString() +
                           " DirPeople replies");
                for (int i = 0; i < replyCount; i++)
                {
                    string avatarName =
                        e.MatchedPeople[i].FirstName + " " + e.MatchedPeople[i].LastName;
                    UUID avatarKey = e.MatchedPeople[i].AgentID;
                    DebugUtilities
                    .WriteDebug("Avatars_OnDirPeopleReply() -  Reply " +
                                (i + 1).ToString() +
                                " of " +
                                replyCount.ToString() +
                                " Key : " +
                                avatarKey.ToString() +
                                " Name : " +
                                avatarName);

                    if (!avatarKeys.ContainsKey(avatarName))
                    {
                        /* || avatarKeys[avatarName] == null )	 // apparently dictionary entries cannot be null */
                        lock (avatarKeys)
                        {
                            avatarKeys[avatarName.ToLower()] = avatarKey;
                        }
                    }

                    lock (KeyLookupEvents)
                    {
                        if (KeyLookupEvents.ContainsKey(avatarName.ToLower()))
                        {
                            KeyLookupEvents[avatarName.ToLower()].Set();
                            DebugUtilities.WriteDebug(avatarName.ToLower() + " KLE set!");
                        }
                    }
                }
            }
        }         // end Avatars_OnDirPeopleReply()
        /*
         * // obsoleted packet call
         * public void Avatars_OnDirPeopleReply(Packet packet, Simulator simulator)
         * {
         *      DirPeopleReplyPacket reply = (DirPeopleReplyPacket)packet;
         *      DebugUtilities.WriteDebug("Got DirPeopleReply!");
         *      if (reply.QueryReplies.Length < 1) {
         *              DebugUtilities.WriteWarning(session + " {MethodName + " Error - empty people directory reply");
         *      } else {
         *              int replyCount = reply.QueryReplies.Length;
         *              DebugUtilities.WriteInfo(session + " {MethodName + " Proccesing {replyCount.ToString() + " DirPeople replies");
         *              for ( int i = 0 ; i <  replyCount ; i++ ) {
         *                      string avatarName = Utils.BytesToString(reply.QueryReplies[i].FirstName) + " {Utils.BytesToString(reply.QueryReplies[i].LastName);
         *                      UUID avatarKey = reply.QueryReplies[i].AgentID;
         *                      DebugUtilities.WriteDebug(session + " {MethodName + " Reply {(i + 1).ToString() + " of {replyCount.ToString() + " Key : {avatarKey.ToString() + " Name : {avatarName);
         *
         *                      if ( !avatarKeys.ContainsKey(avatarName) ) // || avatarKeys[avatarName] == null ) { // apparently dictionary entries cannot be null
         *                              lock ( avatarKeys ) {
         *                                      avatarKeys[avatarName.ToLower()] = avatarKey;
         *                              }
         *                      }
         *
         *                      lock(KeyLookupEvents)
         *                      {
         *                               if ( KeyLookupEvents.ContainsKey(avatarName.ToLower())) {
         *                                               KeyLookupEvents[avatarName.ToLower()].Set();
         *                                      DebugUtilities.WriteDebug(avatarName.ToLower() + " KLE set!");
         *                               }
         *                      }
         *              }
         *      }
         * } */
        /// <summary>
        /// Loop through all (pending) replies for UUID/Avatar names
        /// and process them if they contain any key we're looking for.
        /// </summary>
        /// <param name="sender">parameter ignored</param>
        /// <param name="e">List of UUID/Avatar names</param>
        /// <returns>void</returns>
        /// <remarks>using new Directory functionality</remarks>
        public void Avatars_OnDirPeopleReply(
            object?sender,
            DirPeopleReplyEventArgs e
            )
        {
            if (e.MatchedPeople.Count < 1)
            {
                DebugUtilities
                .WriteWarning($"{session} {MethodName} Error - empty people directory reply");
            }
            else
            {
                int replyCount = e.MatchedPeople.Count;

                DebugUtilities
                .WriteInfo($"{session} {MethodName} Processing {replyCount.ToString()} DirPeople replies");
                for (int i = 0; i < replyCount; i++)
                {
                    string avatarName = e.MatchedPeople[i].FirstName + e.MatchedPeople[i].LastName;
                    UUID   avatarKey  = e.MatchedPeople[i].AgentID;
                    DebugUtilities
                    .WriteDebug($"{session} {MethodName} Reply {(i + 1).ToString()} of {replyCount.ToString()} Key: {avatarKey.ToString()} Name: {avatarName}");

                    if (!avatarKeys.ContainsKey(avatarName))
                    {
                        /* || avatarKeys[avatarName] == null )	 // apparently dictionary entries cannot be null */
                        lock (avatarKeys)
                        {
                            avatarKeys[avatarName.ToLower()] = avatarKey;
                        }
                    }

                    lock (KeyLookupEvents)
                    {
                        if (KeyLookupEvents.ContainsKey(avatarName.ToLower()))
                        {
                            KeyLookupEvents[avatarName.ToLower()].Set();
                            DebugUtilities.WriteDebug($"{avatarName.ToLower()} KLE set!");
                        }
                    }
                }
            }
        }
Exemple #7
0
        }         // end Avatars_OnAvatarNames()

        /// <summary>
        /// key2name (given an avatar UUID, returns the avatar name, if it exists)
        /// </summary>
        /// <param name="b">RESTbot object</param>
        /// <param name="key">UUID of avatar to check</param>
        /// <returns>Name of the avatar if it exists; String.Empty if not</returns>
        public static string getName(RestBot b, UUID id)
        {
            DebugUtilities
            .WriteInfo("getName(): Looking up name for " + id.ToString());
            b.Client.Avatars.UUIDNameReply += Avatars_OnAvatarNames;
            lock (NameLookupEvents)
            {
                NameLookupEvents.Add(id, new AutoResetEvent(false));
            }

            b.Client.Avatars.RequestAvatarName(id);

            if (!NameLookupEvents[id].WaitOne(15000, true))
            {
                DebugUtilities
                .WriteWarning("getName(): timed out on avatar name lookup");
            }
            lock (NameLookupEvents)
            {
                NameLookupEvents.Remove(id);
            }

            // C# 8+ is stricter with null assignments.
            // string? response = null;	// technically this cannot ever be null, so it doesn't make sense...
            string response = String.Empty;

            if (avatarNames.ContainsKey(id))
            {
                response = avatarNames[id];                 // .Name removed
                lock (avatarNames)
                {
                    avatarNames.Remove(id);
                }
            }

            /*          else
             * {
             *      response = String.Empty;
             * } */
            b.Client.Avatars.UUIDNameReply -= Avatars_OnAvatarNames;
            return(response);
        }         // end getName()
        /// <summary>
        /// key2name (given an avatar UUID, returns the avatar name, if it exists)
        /// </summary>
        /// <param name="b">RESTbot object</param>
        /// <param name="key">UUID of avatar to check</param>
        /// <returns>Name of the avatar if it exists; String.Empty if not</returns>
        private string getName(RestBot b, UUID id)
        {
            DebugUtilities
            .WriteInfo($"{session.ToString()} {MethodName} Looking up name for {id.ToString()}");
            lock (NameLookupEvents)
            {
                NameLookupEvents.Add(id, new AutoResetEvent(false));
            }

            b.Client.Avatars.RequestAvatarName(id);

            if (!NameLookupEvents[id].WaitOne(15000, true))
            {
                DebugUtilities
                .WriteWarning($"{session.ToString()} {MethodName} timed out on avatar name lookup");
            }
            lock (NameLookupEvents)
            {
                NameLookupEvents.Remove(id);
            }

            // C# 8+ is stricter with null assignments.
            // string? response = null;	// technically this cannot ever be null, so it doesn't make sense...
            string response = String.Empty;

            if (avatarNames.ContainsKey(id))
            {
                response = avatarNames[id];                 // .Name removed
                lock (avatarNames)
                {
                    avatarNames.Remove(id);
                }
            }

            /*			else
             * {
             *      response = String.Empty;
             * } */
            return(response);
        }
Exemple #9
0
        /// <summary>
        /// Activates a group for a 'bot, given the group key.
        /// </summary>
        /// <param name="b">A currently active RestBot</param>
        /// <param name="groupUUID">UUID of the group to activate</param>
        /// <returns>Activated group name, or empty if activation failed</returns>
        private string activateGroup(RestBot b, UUID groupUUID)
        {
            DebugUtilities.WriteInfo(session.ToString() + " " + MethodName + " Activating group " + groupUUID.ToString());
            EventHandler <PacketReceivedEventArgs> pcallback = AgentDataUpdateHandler;

            b.Client.Network.RegisterCallback(PacketType.AgentDataUpdate, pcallback);
            b.Client.Groups.ActivateGroup(groupUUID);

            if (!GroupsEvent.WaitOne(15000, true))
            {
                DebugUtilities.WriteWarning(session + " " + MethodName + " timed out on setting active group");
            }

            // ignore everything and just reset the event
            b.Client.Network.UnregisterCallback(PacketType.AgentDataUpdate, pcallback);
            GroupsEvent.Reset();

            if (String.IsNullOrEmpty(activeGroup))
            {
                DebugUtilities.WriteWarning(session + " " + MethodName + " Failed to activate the group " + groupUUID);
                return("");                     // maybe we ought to return something else? (gwyneth 20220127)
            }
            return(activeGroup);                // guaranteed *not* to be null, nor empty! (gwyneth 20220127)
        }
Exemple #10
0
        /// <summary>
        /// Internal function that wil set the bot's group to a certain group UUID.
        /// </summary>
        /// <param name="b">A currently active RestBot</param>
        /// <param name="Parameters">A dictionary containing the UUID for a group.</param>
        /// <returns>String with the group name, or null if group not found</returns>
        private string?activateGroup(RestBot b, UUID groupUUID)
        {
            DebugUtilities.WriteInfo($"{session.ToString()} {MethodName} Activating group {groupUUID.ToString()}");
            EventHandler <PacketReceivedEventArgs> pcallback = AgentDataUpdateHandler;

            b.Client.Network.RegisterCallback(PacketType.AgentDataUpdate, pcallback);
            b.Client.Groups.ActivateGroup(groupUUID);

            if (!GroupsEvent.WaitOne(15000, true))
            {
                DebugUtilities.WriteWarning($"{session.ToString()} {MethodName} timed out on setting active group");
            }

            // ignore everything and just reset the event
            b.Client.Network.UnregisterCallback(PacketType.AgentDataUpdate, pcallback);
            GroupsEvent.Reset();

            if (String.IsNullOrEmpty(activeGroup))
            {
                DebugUtilities.WriteWarning($"{session.ToString()} {MethodName} Failed to activate the group {groupUUID.ToString()}");
            }

            return(activeGroup);
        }
Exemple #11
0
        /// <summary>
        /// Process a request (assuming it exists)
        /// </summary>
        /// <param name="headers">Request headers (including path, etc.)</param>
        /// <param name="body">Request body (will usually have all parameters from POST)</param>
        public static string DoProcessing(RequestHeaders headers, string body)
        {
            // Abort if we don't even have a valid configuration; too many things depend on it... (gwyneth 20220213)
            if (Program.config == null)
            {
                return("<error>No valid configuration loaded, aborting</error>");
            }

            //Setup variables
            DebugUtilities.WriteInfo($"New request - {headers.RequestLine.Path}");
            //Split the URL
            string[] parts = headers.RequestLine.Path.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length < 1)
            {
                return("<error>invalidmethod</error>");
            }
            string Method = parts[0];
            /// <summary>Process the request params from POST, URL</summary>
            Dictionary <string, string> Parameters = RestBot.HandleDataFromRequest(headers, body);
            string debugparams = String.Empty;
            string debugparts  = String.Empty;

            foreach (KeyValuePair <string, string> kvp in Parameters)
            {
                debugparams = debugparams + "[" + kvp.Key + "=" + kvp.Value + "] ";
            }
            DebugUtilities.WriteDebug($"Parameters (total: {Parameters.Count()}) - '{debugparams}'");
            foreach (string s in parts)
            {
                debugparts = debugparts + "[ " + s + " ]";
            }
            DebugUtilities.WriteDebug($"Parts (total: {parts.Count()}) - '{debugparts}'");
            if (Method == "establish_session")
            {
                DebugUtilities.WriteDebug("We have an `establish_session` method.");
                // Alright, we're going to try to establish a session
                // Start location is optional (gwyneth 20220421)
                if (parts.Length >= 2 && parts[1] == Program.config.security.serverPass &&
                    Parameters.ContainsKey("first") && Parameters.ContainsKey("last") && Parameters.ContainsKey("pass"))
                {
                    DebugUtilities.WriteDebug("Found required parameters for establish_session");
                    if (Sessions != null)
                    {
                        foreach (KeyValuePair <UUID, Session> ss in Sessions)
                        {
                            if (ss.Value != null && ss.Value.Bot != null)
                            {
                                DebugUtilities.WriteSpecial($"Avatar check: [{ss.Value.Bot.First.ToLower()}/{ss.Value.Bot.Last.ToLower()}] = [{Parameters["first"].ToLower()}/{Parameters["last"].ToLower()}]");
                                if (Parameters["first"].ToLower() == ss.Value.Bot.First.ToLower() &&
                                    Parameters["last"].ToLower() == ss.Value.Bot.Last.ToLower()
                                    )
                                {
                                    DebugUtilities.WriteWarning($"Already running avatar {Parameters["first"]} {Parameters["last"]}");

                                    /// <value>Temporary string to construct a full response, if possible; if not, we catch the
                                    /// exception and return a much shorter version</value>
                                    /// <remarks>This is a hack. The issue is that we're probably acessing nullable
                                    /// elements without checking. (gwyneth 20220428)</remarks>
                                    string returnString = "";

                                    try
                                    {
                                        // Attempt to get a
                                        returnString = $@"<existing_session>true</existing_session>
	<session_id>{ss.Key.ToString()}</session_id>
	<key>{ss.Value.Bot.Client.Self.AgentID.ToString()}</key>
	<name>{ss.Value.Bot.Client.Self.FirstName} {ss.Value.Bot.Client.Self.LastName}</name>
	<FirstName>{ss.Value.Bot.Client.Self.FirstName}</FirstName>
	<LastName>{ss.Value.Bot.Client.Self.LastName}</LastName>
	<status>{ss.Value.Bot.myStatus.ToString()}</status>
	<uptime>{ss.Value.Bot.getUptimeISO8601()}</uptime>
	<start>{ss.Value.Bot.Start}</start>
	<CurrentSim>{ss.Value.Bot.Client.Network.CurrentSim.ToString()}</CurrentSim>
	<Position>{ss.Value.Bot.Client.Self.SimPosition.X},{ss.Value.Bot.Client.Self.SimPosition.Y},{ss.Value.Bot.Client.Self.SimPosition.Z}</Position>
	<Rotation>{ss.Value.Bot.Client.Self.SimRotation.X},{ss.Value.Bot.Client.Self.SimRotation.Y},{ss.Value.Bot.Client.Self.SimRotation.Z},{ss.Value.Bot.Client.Self.SimRotation.W}</Rotation>
";
                                    }
                                    catch (Exception e)
                                    {
                                        DebugUtilities.WriteError($"Could not generate full response, error was: '{e.Message}'; falling back to the simple, minimalistic answer");
                                        returnString = $"<existing_session>true</existing_session><session_id>{ss.Key.ToString()}</session_id>";
                                    }
                                    return(returnString);
                                }
                            }
                        }
                    }
                    else
                    {
                        DebugUtilities.WriteDebug("No available sessions...");
                    }
                    UUID    id = UUID.Random();
                    Session s  = new Session();
                    s.ID           = id;
                    s.Hostname     = headers.Hostname;
                    s.LastAccessed = DateTime.Now;
                    // Needs the $1$ for the md5 on the login for LibreMetaverse
                    if (!Parameters["pass"].StartsWith("$1$"))
                    {
                        Parameters["pass"] = "******" + Parameters["pass"];
                    }
                    // check if user has provided us with a starting location (default is to use the last location)
                    // (gwyneth 20220420)
                    string gridLocation = Parameters.ContainsKey("start") ? Parameters["start"] : "last";
                    s.Bot = new RestBot(s.ID, Parameters["first"], Parameters["last"], Parameters["pass"], gridLocation);

                    if (Sessions != null)
                    {
                        lock (Sessions)
                        {
                            Sessions.Add(id, s);
                        }
                    }
                    else
                    {
                        // no "else", we have no dictionary
                        DebugUtilities.WriteWarning("Possible issue: we have null Sessions when adding, which shouldn't happen");
                    }
                    RestBot.LoginReply reply = s.Bot.Login();
                    if (reply.wasFatal)
                    {
                        if (Sessions != null)
                        {
                            lock (Sessions)
                            {
                                if (Sessions.ContainsKey(id))
                                {
                                    Sessions.Remove(id);
                                }
                            }
                        }
                        else
                        {
                            // no "else", we have no dictionary
                            DebugUtilities.WriteWarning("Possible issue: we have null Sessions when removing, which shouldn't happen");
                        }
                    }
                    return(reply.xmlReply);
                }
                else
                {
                    String result = String.Empty;
                    if (parts.Length < 2)
                    {
                        result = "Missing a part.";
                    }
                    if (!Parameters.ContainsKey("first"))
                    {
                        result = result + " Missing 'first' arg.";
                    }
                    if (!Parameters.ContainsKey("last"))
                    {
                        result = result + " Missing 'last' arg.";
                    }
                    if (!Parameters.ContainsKey("pass"))
                    {
                        result = result + " Missing 'pass' arg.";
                    }
                    return($"<error>arguments: {result}</error>");
                }
            }
            // Note: formerly undocumented functionality!! (gwyneth 20220414)
            else if (Method == "server_quit")
            {
                if (parts.Length < 2)
                {
                    return($"<error>{Method}: missing 'pass' arg.</error>");
                }
                if (parts[1] == Program.config.security.serverPass)
                {
                    if (Sessions != null)
                    {
                        foreach (KeyValuePair <UUID, Session> s in Sessions)
                        {
                            lock (Sessions) DisposeSession(s.Key);
                        }
                        StillRunning = false;
                        // note: a caveat of this undocumented method is that it requires a _new_
                        // incoming request to actually kill the server... could be a ping, though. (gwyneth 20220414)
                        return("<status>success - all bot sessions were logged out and a request was made for queued shutdown</status>");
                    }
                    else
                    {
                        // it's fine if there are no sessions (gwyneth 20220414)
                        return("<status>success - no sessions were active</status>");
                    }
                }
                else
                {
                    // wrong password sent! (gwyneth 20220414)
                    return($"<error>{Method}: server authentication failure</error>");
                }
            }
            else if (Method == "ping")
            {
                if (parts.Length < 2)
                {
                    return($"<error>{Method}: missing 'pass' arg.</error>");
                }
                if (parts[1] == Program.config.security.serverPass)
                {
                    return($"<{Method}>I'm alive!</{Method}>");
                }
                else
                {
                    // wrong password sent! (gwyneth 20220414)
                    return($"<error>{Method}: server authentication failure</error>");
                }
            }
            else if (Method == "session_list")
            {
                if (parts.Length < 2)
                {
                    return("<error>missing 'pass' arg.</error>");
                }
                if (parts[1] == Program.config.security.serverPass)
                {
                    bool check = false;
                    if (Program.Sessions.Count != 0)                     // no sessions? that's fine, no need to abort
                    {
                        check = true;
                    }

                    string response = $"<{Method}>";
                    if (check)                          // optimisation: if empty, no need to run the foreach (gwyneth 20220424)
                    {
                        foreach (KeyValuePair <OpenMetaverse.UUID, RESTBot.Session> kvp in Program.Sessions)
                        {
                            if (kvp.Value.Bot != null && kvp.Value.Bot.Client != null && kvp.Value.Bot.Client.Self != null && kvp.Value.Bot.Client.Network != null)
                            {
                                response += $@"
	<session>
		<session_id>{kvp.Key.ToString()}</session_id>
		<key>{kvp.Value.Bot.Client.Self.AgentID.ToString()}</key>
		<name>{kvp.Value.Bot.Client.Self.FirstName} {kvp.Value.Bot.Client.Self.LastName}</name>
		<FirstName>{kvp.Value.Bot.Client.Self.FirstName}</FirstName>
		<LastName>{kvp.Value.Bot.Client.Self.LastName}</LastName>
		<status>{kvp.Value.Bot.myStatus.ToString()}</status>
		<uptime>{kvp.Value.Bot.getUptimeISO8601()}</uptime>
		<start>{kvp.Value.Bot.Start}</start>
		<CurrentSim>{kvp.Value.Bot.Client.Network.CurrentSim.ToString()}</CurrentSim>
		<Position>{kvp.Value.Bot.Client.Self.SimPosition.X},{kvp.Value.Bot.Client.Self.SimPosition.Y},{kvp.Value.Bot.Client.Self.SimPosition.Z}</Position>
		<Rotation>{kvp.Value.Bot.Client.Self.SimRotation.X},{kvp.Value.Bot.Client.Self.SimRotation.Y},{kvp.Value.Bot.Client.Self.SimRotation.Z},{kvp.Value.Bot.Client.Self.SimRotation.W}</Rotation>
	</session>"    ;
                            }
                            else
                            {
                                // Somehow, we have a session ID that has no bot assigned;
                                // this should never be the case, but... (gwyneth 20220426)
                                response += $"<session><session_id>{kvp.Key.ToString()}</session_id><key>{UUID.Zero.ToString()}</key></session>";
                            }
                        }
                    }
                    else
                    {
                        response += "no sessions";
                    }
                    response += $"</{Method}>";
                    return(response);
                }
                else
                {
                    // wrong password sent! (gwyneth 20220414)
                    return($"<error>{Method}: server authentication failure</error>");
                }
            }
            else if (Method == "stats")
            {
                if (parts.Length < 2)
                {
                    return($"<error>{Method}: missing 'pass' arg.</error>");
                }
                if (parts[1] == Program.config.security.serverPass)
                {
                    string response = "<stats><bots>" + ((Sessions != null) ? Sessions.Count.ToString() : "0") + "</bots>"
                                      + "<uptime>" + (DateTime.Now - uptime) + "</uptime></stats>";
                    return(response);
                }
                else
                {
                    return($"<error>{Method}: server authentication failure</error>");
                }
            }

            //Only a method? pssh.
            if (parts.Length == 1)
            {
                return("<error>no session key found</error>");
            }

            UUID sess = new UUID();

            try
            {
                sess = new UUID(parts[1]);
            }
            catch (FormatException)
            {
                return("<error>cannot parse the session key</error>");
            }
            catch (Exception e)
            {
                DebugUtilities.WriteError(e.Message);
            }

            //Session checking
            if (!ValidSession(sess, headers.Hostname))
            {
                return("<error>invalidsession</error>");
            }

            //YEY PROCESSING
            RestBot?r = null;

            if (Sessions != null)
            {
                r = Sessions[sess].Bot;
            }

            if (r == null)
            {
                return($"<error>no RestBot found for session {sess.ToString()}</error>");
            }
            //Last accessed for plugins
            if (Sessions != null)
            {
                Sessions[sess].LastAccessed = DateTime.Now;
            }
            //Pre-error checking
            if (r.myStatus != RestBot.Status.Connected) //Still logging in?
            {
                return($"<error>{r.myStatus.ToString()}</error>");
            }
            else if (!r.Client.Network.Connected) //Disconnected?
            {
                return("<error>clientdisconnected</error>");
            }
            else if (Method == "exit")
            {
                DisposeSession(sess);
                return("<disposed>true</disposed>");
            }
            else if (Method == "stats")
            {
                string response = "<bots>" + ((Sessions != null) ? Sessions.Count.ToString() : "NaN") + "</bots>";
                response += "<uptime>" + (DateTime.Now - uptime) + "</uptime>";
                return(response);
            }

            return(r.DoProcessing(Parameters, parts));
        } // end DoProcessing
Exemple #12
0
        /// <summary>
        /// Login block
        /// </summary>
        public LoginReply Login()
        {
            LoginReply response = new LoginReply();

            DebugUtilities.WriteSpecial("Login block was called in Login()");
            if (Client.Network.Connected)
            {
                DebugUtilities.WriteError("Uhm, Login() was called when we where already connected. Hurr");
                return(new LoginReply());
            }

            //Client.Network.LoginProgress +=
            //    delegate(object? sender, LoginProgressEventArgs e)
            //    {
            //        DebugUtilities.WriteDebug($"Login {e.Status}: {e.Message}");
            //        if (e.Status == LoginStatus.Success)
            //        {
            //            DebugUtilities.WriteSpecial("Logged in successfully");
            //            myStatus = Status.Connected;
            //            response.wasFatal = false;
            //            response.xmlReply = "<success><session_id>" + sessionid.ToString() + "</session_id></success>";
            //        }
            //        else if (e.Status == LoginStatus.Failed)
            //        {
            //            DebugUtilities.WriteError("$There was an error while connecting: {Client.Network.LoginErrorKey}");
            //            response.wasFatal = true;
            //            response.xmlReply = "<error></error>";
            //        }
            //    };
            // Optimize the throttle
            Client.Throttle.Wind  = 0;
            Client.Throttle.Cloud = 0;
            Client.Throttle.Land  = 1000000;
            Client.Throttle.Task  = 1000000;

            // we add this check here because LOGIN_SERVER should never be assigned null (gwyneth 20220213)
            if (Program.config != null && Program.config.networking.loginuri != null)
            {
                Client.Settings.LOGIN_SERVER = Program.config.networking.loginuri;                      // could be String.Empty, so we check below...
            }
            else if (RESTBot.XMLConfig.Configuration.defaultLoginURI != null)
            {
                Client.Settings.LOGIN_SERVER = RESTBot.XMLConfig.Configuration.defaultLoginURI;                 // could ALSO be String.Empty, so we check below...
            }
            else
            {
                Client.Settings.LOGIN_SERVER = String.Empty;
            }

            // Any of the above _might_ have set LOGIN_SERVER to an empty string, so we check first if we have
            // something inside the string. (gwyneth 20220213)
            // To-do: validate the URL first? It's not clear if .NET 6 already does that at some point...
            if (Client.Settings.LOGIN_SERVER == String.Empty)
            {
                // we don't know where to login to!
                response.wasFatal = true;
                response.xmlReply = "<error fatal=\"true\">No login URI provided</error>";
                DebugUtilities.WriteError("No login URI provided; aborting...");
                return(response);
            }
            DebugUtilities.WriteDebug($"Login URI: {Client.Settings.LOGIN_SERVER}");

            LoginParams loginParams =
                Client.Network.DefaultLoginParams(First, Last, MD5Password, "RestBot", Program.Version);

            loginParams.Start = Start;

            if (Client.Network.Login(loginParams))
            {
                DebugUtilities.WriteSpecial($"{First} {Last} logged in successfully");
                myStatus          = Status.Connected;
                response.wasFatal = false;
                response.xmlReply =
                    $@"<success>
	<session_id>{sessionid.ToString()}</session_id>
	<key>{Client.Self.AgentID.ToString()}</key>
	<name>{Client.Self.FirstName} {Client.Self.LastName}</name>
	<FirstName>{Client.Self.FirstName}</FirstName>
	<LastName>{Client.Self.LastName}</LastName>
	<CurrentSim>{Client.Network.CurrentSim.ToString()}</CurrentSim>
	<Position>{Client.Self.SimPosition.X},{Client.Self.SimPosition.Y},{Client.Self.SimPosition.Z}</Position>
	<Rotation>{Client.Self.SimRotation.X},{Client.Self.SimRotation.Y},{Client.Self.SimRotation.Z},{Client.Self.SimRotation.W}</Rotation>
</success>";
            }
            else
            {
                DebugUtilities
                .WriteError($"There was an error while connecting: {Client.Network.LoginErrorKey}");
                switch (Client.Network.LoginErrorKey)
                {
                case "connect":
                case "key":
                case "disabled":
                    response.wasFatal = true;
                    response.xmlReply =
                        $"<error fatal=\"true\">{Client.Network.LoginMessage}</error>";
                    break;

                case "presence":
                case "timed out":
                case "god":
                    DebugUtilities
                    .WriteWarning("Nonfatal error while logging in.. this may be normal");
                    response.wasFatal = false;
                    response.xmlReply =
                        $"<error fatal=\"false\">{Client.Network.LoginMessage}</error><retry>10</retry><session_id>{sessionid}</session_id>";

                    DebugUtilities
                    .WriteSpecial("Relogin attempt will be made in 10 minutes");
                    ReloginTimer.Interval = 10 * 60 * 1000;                             //10 minutes
                    ReloginTimer.Start();
                    break;

                default:
                    DebugUtilities
                    .WriteError($"{sessionid.ToString()} UNKNOWN ERROR {Client.Network.LoginErrorKey} WHILE ATTEMPTING TO LOGIN");
                    response.wasFatal = true;
                    response.xmlReply =
                        $"<error fatal=\"true\">Unknown error '{Client.Network.LoginErrorKey}' has occurred.</error>";
                    break;
                }

                if (response.wasFatal == false)
                {
                    myStatus = Status.Reconnecting;
                }
            }

            //Client.Network.BeginLogin(loginParams);
            return(response);
        }         // end Login()
Exemple #13
0
        /// <summary>
        /// Handler event for this plugin.
        /// </summary>
        /// <param name="b">A currently active RestBot</param>
        /// <param name="Parameters">A dictionary containing the message </param>
        /// <returns></returns>
        public override string Process(RestBot b, Dictionary <string, string> Parameters)
        {
            string      message;
            string      subject;
            UUID        groupUUID      = UUID.Zero;
            UUID        attachmentUUID = UUID.Zero;
            GroupNotice notice;

            try
            {
                if (Parameters.ContainsKey("subject"))
                {
                    subject = Parameters["subject"].ToString().Replace("%20", " ").Replace("+", " ");
                }
                else
                {
                    return("<error>No notice subject</error>");
                }

                if (Parameters.ContainsKey("message"))
                {
                    message = Parameters["message"].ToString().Replace("%20", " ").Replace("+", " ");
                }
                else
                {
                    return("<error>No notice message</error>");
                }

                if (Parameters.ContainsKey("group"))
                {
                    if (!UUID.TryParse(Parameters["group"].ToString().Replace("_", " "), out groupUUID))
                    {
                        return("<error>parsekey group</error>");
                    }
                }
                else
                {
                    return("<error>arguments: no group key</error>");
                }

                if (Parameters.ContainsKey("attachment"))
                {
                    if (!UUID.TryParse(Parameters["attachment"].ToString().Replace("_", " "), out attachmentUUID))
                    {
                        return("<error>parsekey attachment</error>");
                    }
                }
                else
                {
                    // just a warning, attachment can be empty
                    DebugUtilities.WriteWarning(session + " " + MethodName + " Notice has no attachment (no problem)");
                }

                DebugUtilities.WriteDebug(session + " " + MethodName + " Attempting to create a notice");

                /* This doesn't work as it should!
                 * if (!b.Client.Inventory.Store.Contains(attachmentUUID))
                 * {
                 *      DebugUtilities.WriteWarning(session + " " + MethodName + " Item UUID " + attachmentUUID.ToString() + " not found on inventory (are you using an Asset UUID by mistake?)");
                 *      attachmentUUID = UUID.Zero;
                 * }
                 */

                notice = new GroupNotice();

                notice.Subject      = subject;
                notice.Message      = message;
                notice.AttachmentID = attachmentUUID;                 // this is the inventory UUID, not the asset UUID
                notice.OwnerID      = b.Client.Self.AgentID;

                b.Client.Groups.SendGroupNotice(groupUUID, notice);

                DebugUtilities.WriteDebug($"{session} {MethodName} Sent Notice from avatar {notice.OwnerID.ToString()} to group: {groupUUID.ToString()} subject: '{notice.Subject.ToString()}' message: '{notice.Message.ToString()}' Optional attachment: {notice.AttachmentID.ToString()} Serialisation: {Utils.BytesToString(notice.SerializeAttachment())}");

                return("<notice>sent</notice>");
            }
            catch (Exception e)
            {
                DebugUtilities.WriteError(e.Message);
                return("<error>loads of errors</error>");
            }
        }
Exemple #14
0
        Process(RestBot b, Dictionary <string, string> Parameters)
        {
            UUID   avatarKey       = UUID.Zero;
            bool   check           = true;
            string message         = String.Empty;
            string avatarFirstName = String.Empty;
            string avatarLastName  = String.Empty;

            if (Parameters.ContainsKey("key"))
            {
                check =
                    UUID
                    .TryParse(Parameters["key"].ToString().Replace("_", " "),
                              out avatarKey);
            }
            else
            {
                if (Parameters.ContainsKey("last"))
                {
                    avatarLastName = Parameters["last"];
                }

                if (Parameters.ContainsKey("first"))
                {
                    avatarFirstName = Parameters["first"];
                    check           = true;
                }
                else
                {
                    check = false;
                }

                // Massage the data we got; we can get away with an empty string for the last name (gwyneth 20220122).
                if (avatarLastName == String.Empty)
                {
                    avatarLastName = "Resident";
                    check          = true;
                }

                // InstantMessage *always* needs an avatar UUID!
                // We need to look it up; fortunately, there are plenty of options available to get those.
                String avatarFullName =
                    avatarFirstName.ToString() + " " + avatarLastName.ToString();

                avatarKey = Utilities.getKeySimple(b, avatarFullName);                 // handles conversion to lowercase.

                if (avatarKey == UUID.Zero)
                {
                    DebugUtilities.WriteWarning($"Key not found for unknown avatar '{avatarFullName}'");
                    check = false;
                }
                else
                {
                    DebugUtilities.WriteDebug($"Avatar '{avatarFullName}' has key '{avatarKey}'");
                }
            }

            if (Parameters.ContainsKey("message"))
            {
                message = Parameters["message"];
                if (message == String.Empty)
                {
                    check = false;
                }
            }
            else
            {
                check = false;
            }

            if (!check)
            {
                return($"<error>{MethodName}: wrong parameters passed; IM not sent</error>");
            }

            // make sure message is not too big (gwyneth 20220212)
            message = message.TrimEnd();
            if (message.Length > 1023)                  /// <see href="https://wiki.secondlife.com/wiki/LlInstantMessage" />
            {
                message = message.Remove(1023);
            }
            b.Client.Self.InstantMessage(avatarKey, message);
            return($"<{MethodName}><key>{avatarKey.ToString()}</key><message>{message}</message></{MethodName}>");
        } // end Process
Exemple #15
0
		Process(RestBot b, Dictionary<string, string> Parameters)
		{
			bool check = false;

			/// <summary>temporary, so we don't need to lock the name while accessing it</summary>
			string tempName = String.Empty;

			/// <summary>temporary, so we don't need to lock the key while the UUID is being parsed</summary>
			UUID tempKey = UUID.Zero;

			// Name can be either an avatar name, or an object name, we can listen to both (gwyneth 20220213)
			// Note that either one is optional, but if *both* are empty/invalid, then this will listen to anything!
			if (Parameters.ContainsKey("name"))
			{
				tempName = Parameters["name"].ToString().Replace("+", " ");

				if (tempName == String.Empty)
				{
					check = false;
				}
				else
				{
					lock (listenAgentName)	// make it thread-safe!
					{
						listenAgentName = tempName;
					}
					check = true;
				}

				// very likely, this will fail if the name isn't an avatar, but an object, but we'll see,
				// it's possible that we can still do something useful upstream (gwyneth 20220213)
				tempKey = Utilities.getKeySimple(b, tempName);

				if (tempKey == UUID.Zero)
				{
					check = false;
				}
				else
				{
					lock (listenAgentKey)
					{
						listenAgentKey = tempKey.ToString();
					}
				}

				check = true;
			}
			else if (Parameters.ContainsKey("key"))
			{
				check =	UUID.TryParse(Parameters["key"].ToString().Replace("_", " "), out tempKey);

				if (tempKey == UUID.Zero) {
					check = false;
				}
				else
				{
					lock (listenAgentKey)
					{
						listenAgentKey = tempKey.ToString();
					}
					check = true;
				}
			}
			else check = false;	// neither contains name, nor key; we listen to everything instead!

			if (check == false)
			{
				DebugUtilities.WriteWarning("Listen called without valid name/UUID, we're listening to anything");
			}
			else
			{
				DebugUtilities.WriteDebug($"Listen called, after parsing we're going to check for '{tempName}' and/or '{tempKey}'");
			}

			// add the callback, and start listening
			b.Client.Self.ChatFromSimulator += Self_ChatFromSimulator;

			// start listening! Note that this should not be blocking on a timer, but for the sake
			// of expediency, we're pretending that all of this is synchronised and single-threaded... (gwyneth 20220213)
			ListenEvent.WaitOne(30000, false);

			/// <summary>Eventually, someone will type something, and that means our <c>chatMetaData</c> will be non-null.</summary>
			/// <remarks>(gwyneth 20220214)</remarks>
			if (chatMetaData != null)
			{
				if (chatMetaData != (ChatEventArgs) EventArgs.Empty)
				{
					string ret = $@"<{MethodName}>
		<simulator>{chatMetaData.Simulator.ToString()}</simulator>
		<message>{chatMetaData.Message}</message>
		<audiblelevel>{chatMetaData.AudibleLevel.ToString()}</audiblelevel>
		<chattype>{chatMetaData.Type.ToString()}</chattype>
		<sourcetype>{chatMetaData.SourceType.ToString()}</sourcetype>
		<fromname>{chatMetaData.FromName}</fromname>
		<sourceid>{chatMetaData.SourceID.ToString()}</sourceid>
		<ownerid>{chatMetaData.OwnerID.ToString()}</ownerid>
		<position>{chatMetaData.Position.ToString()}</position>
	</{MethodName}>";

					DebugUtilities.WriteDebug($"Captured message XML: {ret}");
					return ret;
				}
				else
				{
					DebugUtilities.WriteError($"Error retrieving chatMetaData, got: '{chatMetaData.ToString()}'");
					return "<error>Listen returned error or empty message</error>";
				}
			}
			else
			{
				// shouldn't happen...
				DebugUtilities.WriteError("Error retrieving chatMetaData, was null");
				return "<error>null</error>";
			}
		}