Esempio n. 1
0
        /// <summary>
        /// Loop through all (pending) replies for avatar properties
        /// and do something as yet unknown to me (gwyneth 20220120)
        /// </summary>
        /// <param name="sender">parameter ignored</param>
        /// <param name="e">List of avatar properties</param>
        /// <returns>void</returns>
        /// <remarks>updated for new callbacks</remarks>
        public void AvatarPropertiesReply(
            object?sender,
            AvatarPropertiesReplyEventArgs e
            )
        {
            /*
             * AvatarPropertiesReplyPacket reply = (AvatarPropertiesReplyPacket)packet;
             * bool status = false;
             * if ( (reply.PropertiesData.Flags & 1 << 4 ) != 0 ) {
             *      status = true;
             * } else {
             *      status = false;
             * }
             */
            Avatar.AvatarProperties Properties = new Avatar.AvatarProperties();
            Properties = e.Properties;

            DebugUtilities
            .WriteInfo($"{session} {MethodName} Processing AvatarPropertiesReply for {e.AvatarID.ToString()} is {Properties.Online.ToString()}");
            lock (avatarOnline)
            {
                avatarOnline[e.AvatarID] = Properties.Online;
            }
            if (OnlineLookupEvents.ContainsKey(e.AvatarID))
            {
                OnlineLookupEvents[e.AvatarID].Set();
            }
        }
Esempio n. 2
0
 /// <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>obsolete syntax changed</remarks>
 private static void Avatars_OnAvatarNames(
     object?sender,
     UUIDNameReplyEventArgs e
     )
 {
     DebugUtilities
     .WriteInfo("Avatars_OnAvatarNames(): Processing " +
                e.Names.Count.ToString() +
                " AvatarNames replies");
     foreach (KeyValuePair <UUID, string> kvp in e.Names)
     {
         if (!avatarNames.ContainsKey(kvp.Key) || avatarNames[kvp.Key] == null)
         {
             DebugUtilities
             .WriteInfo("Avatars_OnAvatarNames(): Reply Name: " +
                        kvp.Value +
                        " Key : " +
                        kvp.Key.ToString());
             lock (avatarNames)
             {
                 // avatarNames[kvp.Key] = new Avatar(); // why all this trouble?
                 // FIXME: Change this to .name when we move inside libsecondlife
                 // avatarNames[kvp.Key].Name = kvp.Value; // protected
                 avatarNames[kvp.Key] = kvp.Value;
             }
             if (NameLookupEvents.ContainsKey(kvp.Key))
             {
                 NameLookupEvents[kvp.Key].Set();
             }
         }
     }
 }         // end Avatars_OnAvatarNames()
Esempio n. 3
0
 /// <summary>
 /// Called once the timer for the login reconnect event finishes.
 /// </summary>
 /// <param name="sender">Sender object</param>
 /// <param name="e">Arguments for the elapsed event</param>
 void ReloginTimer_Elapsed(object?sender, ElapsedEventArgs e)
 {
     ReloginTimer.Stop();
     DebugUtilities.WriteInfo(sessionid.ToString() + " relogging...");
     Login();
     //This is where we can handle relogin failures, too.
 }
Esempio n. 4
0
        /// <summary>
        /// Internal method to send a message to a group the 'bot is in.
        /// </summary>
        /// <param name="b">A currently active RestBot</param>
        /// <param name="groupUUID">Group UUID</param>
        /// <param name="message">Message to send to the group IM</param>
        /// <returns>String with "message sent", if successful; "timeout" otherwise</returns>
        private string sendIMGroup(RestBot b, UUID groupUUID, string message)
        {
            DebugUtilities.WriteInfo(session.ToString() + " " + MethodName + " Sending message '" + message + "' to group UUID " + groupUUID.ToString());
            b.Client.Self.GroupChatJoined += Self_GroupChatJoined;

            if (!b.Client.Self.GroupChatSessions.ContainsKey(groupUUID))
            {
                WaitForSessionStart.Reset();
                b.Client.Self.RequestJoinGroupChat(groupUUID);
            }
            else
            {
                WaitForSessionStart.Set();
            }

            if (WaitForSessionStart.WaitOne(20000, false))
            {
                b.Client.Self.InstantMessageGroup(groupUUID, message);
            }
            else
            {
                DebugUtilities.WriteInfo(session.ToString() + " " + MethodName + " Timeout waiting for group session start");
                return("timeout");
            }

            b.Client.Self.GroupChatJoined -= Self_GroupChatJoined;
            DebugUtilities.WriteInfo(session.ToString() + " " + MethodName + " Instant Messaged group " + groupUUID.ToString() + " with message: " + message);

            return("message sent");
        }
Esempio n. 5
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()
Esempio n. 6
0
        /// <summary>
        /// Calls the C# garbage collector
        ///
        /// We have major memory leaks, this is an attempt to keep them under control. (gwyneth 20220411)
        /// </summary>
        /// <param />
        /// <returns>void</returns>
        private static void CollectGarbage()
        {
            DateTime t = DateTime.Now;

            DebugUtilities.WriteInfo($"{t.ToString("yyyy-MM-dd HH:mm:ss")} - Memory in use before GC.Collect: {(GC.GetTotalMemory(false)):N0} bytes");
            GC.Collect();             // collect garbage (gwyneth 20220207) and wait for GC to finish.
            t = DateTime.Now;
            DebugUtilities.WriteInfo($"{t.ToString("yyyy-MM-dd HH:mm:ss")} - Memory in use after  GC.Collect: {(GC.GetTotalMemory(true)):N0} bytes");
        }
Esempio n. 7
0
        /// <summary>
        /// Look up the profile for a UUID
        /// </summary>
        /// <param name="b">RESTbot object</param>
        /// <param name="key">UUID of avatar to retrieve profile</param>
        /// <returns>Full profile as a string, or null if profile is empty/does not exist</returns>
        /// <remarks>C# 8+ is stricter when returning nulls, thus the <c>string?</c> method type.</remarks>
        public string?getProfile(RestBot b, UUID key)
        {
            DebugUtilities
            .WriteInfo(session +
                       " " +
                       MethodName +
                       " Looking up profile for " +
                       key.ToString());

            lock (ProfileLookupEvents)
            {
                ProfileLookupEvents.Add(key, new AutoResetEvent(false));
            }
            b.Client.Avatars.RequestAvatarProperties(key);

            ProfileLookupEvents[key].WaitOne(15000, true);

            lock (ProfileLookupEvents)
            {
                ProfileLookupEvents.Remove(key);
            }
            if (avatarProfile.ContainsKey(key))
            {
                Avatar.AvatarProperties p = avatarProfile[key];
                string response           = $@"
	<profile>
		<publish>{p.AllowPublish.ToString()}</publish>
		<firstlife>
			<text>
				{p.FirstLifeText.Replace(">", "%3C").Replace("<", "%3E")}
			</text>
			<image>{p.FirstLifeImage.ToString()}</image>
		</firstlife>
		<partner>{p.Partner.ToString()}</partner>
		<born>{p.BornOn}</born>
		<about>
			{p.AboutText.Replace(">", "%3C").Replace("<", "%3E")}
		</about>
		<charter>{p.CharterMember}</charter>
		<profileimage>{p.ProfileImage.ToString()}</profileimage>
		<mature>{p.MaturePublish.ToString()}</mature>
		<identified>{p.Identified.ToString()}</identified>
		<transacted>{p.Transacted.ToString()}</transacted>
		<url>{p.ProfileURL}</url>
	</profile>"    ;
                lock (avatarProfile)
                {
                    avatarProfile.Remove(key);
                }
                return(response);
            }
            else
            {
                return(null);
            }
        }
Esempio n. 8
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()
Esempio n. 9
0
 void Self_GroupChatJoined(object?sender, GroupChatJoinedEventArgs e)
 {
     if (e.Success)
     {
         DebugUtilities.WriteInfo(session.ToString() + " " + MethodName + "Joined {0} Group Chat Success!");
         WaitForSessionStart.Set();
     }
     else
     {
         DebugUtilities.WriteInfo(session.ToString() + " " + MethodName + "Join Group Chat failed :(");
     }
 }
Esempio n. 10
0
        /// <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 UUID getKey(RestBot b, String name)
        {
            DebugUtilities
            .WriteInfo($"{session} {MethodName} Looking up key for {name}");
            name = name.ToLower();
            DebugUtilities.WriteDebug($"Looking up: {name}");
            DebugUtilities
            .WriteDebug("Key not in cache, requesting directory lookup");
            lock (KeyLookupEvents)
            {
                KeyLookupEvents.Add(name, new AutoResetEvent(false));
            }
            DebugUtilities
            .WriteDebug($"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($"Packet sent - KLE has {KeyLookupEvents.Count.ToString()} entries... now waiting");
            KeyLookupEvents[name].WaitOne(15000, true);
            DebugUtilities.WriteDebug("Waiting done!");
            lock (KeyLookupEvents)
            {
                KeyLookupEvents.Remove(name);
            }
            DebugUtilities
            .WriteDebug($"Done with KLE, now has {KeyLookupEvents.Count.ToString()} entries");
            UUID response = new UUID();

            if (avatarKeys.ContainsKey(name))
            {
                response = avatarKeys[name];
                lock (avatarKeys)
                {
                    avatarKeys.Remove(name);
                }
            }
            return(response);
        }
Esempio n. 11
0
        /*
         * // 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!");
                        }
                    }
                }
            }
        }
Esempio n. 12
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()
Esempio n. 13
0
        /// <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);
        }
Esempio n. 14
0
        /// <summary>
        /// Get online status of an avatar
        /// </summary>
        /// <param name="b">RESTbot object</param>
        /// <param name="key">UUID of avatar to check</param>
        /// <returns>true or false, if the avatar is or isn't online</returns>
        public bool getOnline(RestBot b, UUID key)
        {
            DebugUtilities
            .WriteInfo($"{session} {MethodName} Looking up online status for {key.ToString()}");

            lock (OnlineLookupEvents)
            {
                OnlineLookupEvents.Add(key, new AutoResetEvent(false));
            }

            // obsolete

            /*
             * AvatarPropertiesRequestPacket p = new AvatarPropertiesRequestPacket();
             * p.AgentData.AgentID = b.Client.Network.AgentID;
             * p.AgentData.SessionID = b.Client.Network.SessionID;
             * p.AgentData.AvatarID = key;
             *
             * b.Client.Network.SendPacket( (Packet) p);
             */
            b.Client.Avatars.RequestAvatarProperties(key);

            OnlineLookupEvents[key].WaitOne(15000, true);

            lock (OnlineLookupEvents)
            {
                OnlineLookupEvents.Remove(key);
            }
            bool response = avatarOnline[key];

            lock (avatarOnline)
            {
                avatarOnline.Remove(key);
            }
            return(response);
        }
Esempio n. 15
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)
        }
Esempio n. 16
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);
        }
Esempio n. 17
0
        /// <summary>
        /// Returns all groups that an avatar is in, or null if none
        /// </summary>
        /// <param name="b">RESTbot object</param>
        /// <param name="key">UUID of avatar to check</param>
        /// <returns>List of groups as a XML-formatted string, or null if the avatar does not belong to any group</returns>
        /// <remarks>
        ///   <para>C# 8+ is stricter when returning nulls, thus the <c>string?</c> method type.</para>
        ///		<para>Note(gwyneth): Instead of returning null, it would make more sense to return an empty XML!</para>
        /// </remarks>
        public string?getGroups(RestBot b, UUID key)
        {
            DebugUtilities
            .WriteInfo($"{session} {MethodName} Looking up groups for {key.ToString()}");

            lock (GroupsLookupEvents)
            {
                GroupsLookupEvents.Add(key, new AutoResetEvent(false));
            }
            b.Client.Avatars.RequestAvatarProperties(key);

            GroupsLookupEvents[key].WaitOne(15000, true);

            lock (GroupsLookupEvents)
            {
                GroupsLookupEvents.Remove(key);
            }

            if (null == avatarGroups)
            {
                DebugUtilities
                .WriteInfo($"{session} {MethodName} Groups cache failed.");
                return(null);
            }
            if (0 == avatarGroups.Count)
            {
                DebugUtilities.WriteInfo($"{session} {MethodName} No groups");
                return(null);
            }
            if (avatarGroups.ContainsKey(key))
            {
                string response = "<groups>";

                foreach (AvatarGroup g in avatarGroups[key])
                {
                    response += $@"
	<group>
		<name>
			{g.GroupName.Replace(">", "%3C").Replace("<", "%3E")}
		</name>
		<key>{g.GroupID.ToString()}</key>
		<title>
			{g.GroupTitle.Replace(">", "%3C").Replace("<", "%3E")}
		</title>
		<notices>{g.AcceptNotices.ToString()}</notices>
		<powers>{g.GroupPowers.ToString()}</powers>
		<insignia>{g.GroupInsigniaID.ToString()}</insignia>
	</group>"    ;
                }
                response += "</groups>";

                lock (avatarGroups)
                {
                    avatarGroups.Remove(key);
                }
                return(response);
            }
            else
            {
                return(null);
            }
        }
Esempio n. 18
0
        } = "0.0.0.0";                                                                                  // will be filled in later by Main() (gwyneth 20220425)

        /// <summary>
        /// Bootstrap method.
        /// </summary>
        /// <param name="args">Arguments passed to the application</param>
        /// <remarks>The arguments seem to get promptly ignored! (gwyneth 20220109)</remarks>
        static void Main(string[] args)
        {
            // LogManager.GetLogger(typeof(RestBot));

            // new function to parse some useful arguments and do interesting things (gwyneth 20220425)
            ParseArguments(args);

            // see if we can get the version string
            try
            {
                // Note: we ought to also extract the Assembly name, we presume it's the default (gwyneth 20220426)
#if Windows
                var fileVersionInfo = FileVersionInfo.GetVersionInfo("@RESTbot.dll");
#else
                var fileVersionInfo = FileVersionInfo.GetVersionInfo("@RESTbot");
#endif
                Version = fileVersionInfo.FileVersion + "-file";
            }
            catch (Exception e1)
            {
                // nope, this doesn't work under macOS
                DebugUtilities.WriteDebug($"Cannot retrieve file version, exception caught: {e1.Message}");
                // let's try to get the assembly name instead
                try
                {
                    var assembly = Assembly.GetExecutingAssembly();
                    Version = assembly.GetName().Version + "-assembly";
                }
                catch (Exception e2)
                {
                    // nope, that didn't work either
                    DebugUtilities.WriteDebug($"Cannot retrieve assembly version either, exception caught: {e2.Message}");
                    // finally, our last choice is trying the Informational Version
                    try
                    {
                        var assembly = Assembly.GetEntryAssembly();
                        if (assembly != null)
                        {
                            var customAttribute = assembly.GetCustomAttribute <AssemblyInformationalVersionAttribute>();
                            if (customAttribute != null)
                            {
                                var infoVersion = customAttribute.InformationalVersion;

                                if (infoVersion != null)
                                {
                                    Version = infoVersion;
                                }
                            }
                        }
                    }
                    catch (Exception e3)
                    {
                        // we're out of luck today, we cannot even get the Informational Versoin
                        DebugUtilities.WriteDebug($"Also cannot retrieve informational version, exception caught: {e3.Message}");
                        // we'll have to stick with the hard-coded default Version instead...
                    }
                }
            }
            if (Version == null)
            {
                Version = "0.0.0.0";
            }
            DebugUtilities.WriteInfo($"RESTbot file version: {Version}");

            DebugUtilities.WriteInfo($"Reading config file '{configFile}'...");
            config = XMLConfig.Configuration.LoadConfiguration(configFile);
            if (config == null)
            {
                // configuration is mandatory! (gwyneth 20220213)
                DebugUtilities.WriteError($"Unable to open configuration file '{configFile}'! Aborting...");
                Environment.Exit(1);
                return;
            }

            DebugUtilities.WriteInfo("RESTbot startup");
            // Sessions should never be null (?) (gwyneth 20220214)
            if (Sessions == null)
            {
                // Trouble expects us later on, when adding and removing sessions...
                DebugUtilities.WriteError("Error initialising Sessions directory; it was set to null!");
            }

            /// <summary>Get the file version of LibreMetaverse.</summary>
            /// <remarks><see href="https://stackoverflow.com/a/14612480/1035977"/> (gwyneth 20220414)</remarks>
            FileVersionInfo myFileVersionInfo = FileVersionInfo.GetVersionInfo(@"LibreMetaverse.dll");

            // Print the file name and version number.
            DebugUtilities.WriteInfo($"LibreMetaverse DLL version: {myFileVersionInfo.FileDescription} {myFileVersionInfo.FileVersion}");

            DebugUtilities.WriteInfo("Loading plugins");
            RegisterAllCommands(Assembly.GetExecutingAssembly());
            DebugUtilities.WriteDebug("Loading stateful plugins");
            RegisterAllStatefulPlugins(Assembly.GetExecutingAssembly());

            DebugUtilities.WriteInfo($"Listening on port {config.networking.port.ToString()}");

            // Set up the listener / router
            Listener = new RESTBot.Server.Router(IPAddress.Parse(config.networking.ip), config.networking.port);

            StillRunning = true;
            DebugUtilities.WriteInfo("Startup complete");
            uptime = DateTime.Now;

            // let's see if we can figure out how much memory is being wasted
            int stupidCounter = 0;
            while (StillRunning)
            {
                System.Threading.Thread.Sleep(1);
                //TODO: Replace above with a manualresetevent

                if (stupidCounter % 3600000 == 0)                       // stop every hour and check available memory (gwyneth 20220210)
                {
                    CollectGarbage();
                }
                stupidCounter++;
            }

            Listener.StillRunning = false;
        }
Esempio n. 19
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