aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Avatar/InstantMessage
diff options
context:
space:
mode:
authorUbitUmarov2015-09-01 11:43:07 +0100
committerUbitUmarov2015-09-01 11:43:07 +0100
commitfb78b182520fc9bb0f971afd0322029c70278ea6 (patch)
treeb4e30d383938fdeef8c92d1d1c2f44bb61d329bd /OpenSim/Region/CoreModules/Avatar/InstantMessage
parentlixo (diff)
parentMantis #7713: fixed bug introduced by 1st MOSES patch. (diff)
downloadopensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.zip
opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.gz
opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.bz2
opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.xz
Merge remote-tracking branch 'os/master'
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/InstantMessage')
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs332
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs206
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs629
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs146
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs249
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs156
6 files changed, 1718 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs
new file mode 100644
index 0000000..a1b918a
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs
@@ -0,0 +1,332 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Net;
32using System.Reflection;
33using log4net;
34using Nini.Config;
35using Nwc.XmlRpc;
36using Mono.Addins;
37using OpenMetaverse;
38using OpenSim.Framework;
39using OpenSim.Framework.Servers;
40using OpenSim.Region.Framework.Interfaces;
41using OpenSim.Region.Framework.Scenes;
42using GridRegion = OpenSim.Services.Interfaces.GridRegion;
43using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
44using OpenSim.Services.Interfaces;
45using OpenSim.Services.Connectors.InstantMessage;
46using OpenSim.Services.Connectors.Hypergrid;
47using OpenSim.Server.Handlers.Hypergrid;
48
49namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
50{
51 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "HGMessageTransferModule")]
52 public class HGMessageTransferModule : ISharedRegionModule, IMessageTransferModule, IInstantMessageSimConnector
53 {
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55
56 protected bool m_Enabled = false;
57 protected List<Scene> m_Scenes = new List<Scene>();
58
59 protected IInstantMessage m_IMService;
60 protected Dictionary<UUID, object> m_UserLocationMap = new Dictionary<UUID, object>();
61
62 public event UndeliveredMessage OnUndeliveredMessage;
63
64 IUserManagement m_uMan;
65 IUserManagement UserManagementModule
66 {
67 get
68 {
69 if (m_uMan == null)
70 m_uMan = m_Scenes[0].RequestModuleInterface<IUserManagement>();
71 return m_uMan;
72 }
73 }
74
75 public virtual void Initialise(IConfigSource config)
76 {
77 IConfig cnf = config.Configs["Messaging"];
78 if (cnf != null && cnf.GetString(
79 "MessageTransferModule", "MessageTransferModule") != Name)
80 {
81 m_log.Debug("[HG MESSAGE TRANSFER]: Disabled by configuration");
82 return;
83 }
84
85 InstantMessageServerConnector imServer = new InstantMessageServerConnector(config, MainServer.Instance, this);
86 m_IMService = imServer.GetService();
87 m_Enabled = true;
88 }
89
90 public virtual void AddRegion(Scene scene)
91 {
92 if (!m_Enabled)
93 return;
94
95 lock (m_Scenes)
96 {
97 m_log.DebugFormat("[HG MESSAGE TRANSFER]: Message transfer module {0} active", Name);
98 scene.RegisterModuleInterface<IMessageTransferModule>(this);
99 m_Scenes.Add(scene);
100 }
101 }
102
103 public virtual void PostInitialise()
104 {
105 if (!m_Enabled)
106 return;
107
108 }
109
110 public virtual void RegionLoaded(Scene scene)
111 {
112 }
113
114 public virtual void RemoveRegion(Scene scene)
115 {
116 if (!m_Enabled)
117 return;
118
119 lock (m_Scenes)
120 {
121 m_Scenes.Remove(scene);
122 }
123 }
124
125 public virtual void Close()
126 {
127 }
128
129 public virtual string Name
130 {
131 get { return "HGMessageTransferModule"; }
132 }
133
134 public virtual Type ReplaceableInterface
135 {
136 get { return null; }
137 }
138
139 public void SendInstantMessage(GridInstantMessage im, MessageResultNotification result)
140 {
141 UUID toAgentID = new UUID(im.toAgentID);
142
143 // Try root avatar only first
144 foreach (Scene scene in m_Scenes)
145 {
146// m_log.DebugFormat(
147// "[HG INSTANT MESSAGE]: Looking for root agent {0} in {1}",
148// toAgentID.ToString(), scene.RegionInfo.RegionName);
149 ScenePresence sp = scene.GetScenePresence(toAgentID);
150 if (sp != null && !sp.IsChildAgent)
151 {
152 // Local message
153// m_log.DebugFormat("[HG INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", user.Name, toAgentID);
154 sp.ControllingClient.SendInstantMessage(im);
155
156 // Message sent
157 result(true);
158 return;
159 }
160 }
161
162 // try child avatar second
163 foreach (Scene scene in m_Scenes)
164 {
165// m_log.DebugFormat(
166// "[HG INSTANT MESSAGE]: Looking for child of {0} in {1}",
167// toAgentID, scene.RegionInfo.RegionName);
168 ScenePresence sp = scene.GetScenePresence(toAgentID);
169 if (sp != null)
170 {
171 // Local message
172// m_log.DebugFormat("[HG INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", user.Name, toAgentID);
173 sp.ControllingClient.SendInstantMessage(im);
174
175 // Message sent
176 result(true);
177 return;
178 }
179 }
180
181// m_log.DebugFormat("[HG INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID);
182 // Is the user a local user?
183 string url = string.Empty;
184 bool foreigner = false;
185 if (UserManagementModule != null && !UserManagementModule.IsLocalGridUser(toAgentID)) // foreign user
186 {
187 url = UserManagementModule.GetUserServerURL(toAgentID, "IMServerURI");
188 foreigner = true;
189 }
190
191 Util.FireAndForget(delegate
192 {
193 bool success = false;
194 if (foreigner && url == string.Empty) // we don't know about this user
195 {
196 string recipientUUI = TryGetRecipientUUI(new UUID(im.fromAgentID), toAgentID);
197 m_log.DebugFormat("[HG MESSAGE TRANSFER]: Got UUI {0}", recipientUUI);
198 if (recipientUUI != string.Empty)
199 {
200 UUID id; string u = string.Empty, first = string.Empty, last = string.Empty, secret = string.Empty;
201 if (Util.ParseUniversalUserIdentifier(recipientUUI, out id, out u, out first, out last, out secret))
202 {
203 success = m_IMService.OutgoingInstantMessage(im, u, true);
204 if (success)
205 UserManagementModule.AddUser(toAgentID, u + ";" + first + " " + last);
206 }
207 }
208 }
209 else
210 success = m_IMService.OutgoingInstantMessage(im, url, foreigner);
211
212 if (!success && !foreigner)
213 HandleUndeliverableMessage(im, result);
214 else
215 result(success);
216 }, null, "HGMessageTransferModule.SendInstantMessage");
217
218 return;
219 }
220
221 protected bool SendIMToScene(GridInstantMessage gim, UUID toAgentID)
222 {
223 bool successful = false;
224 foreach (Scene scene in m_Scenes)
225 {
226 ScenePresence sp = scene.GetScenePresence(toAgentID);
227 if(sp != null && !sp.IsChildAgent)
228 {
229 scene.EventManager.TriggerIncomingInstantMessage(gim);
230 successful = true;
231 }
232 }
233
234 if (!successful)
235 {
236 // If the message can't be delivered to an agent, it
237 // is likely to be a group IM. On a group IM, the
238 // imSessionID = toAgentID = group id. Raise the
239 // unhandled IM event to give the groups module
240 // a chance to pick it up. We raise that in a random
241 // scene, since the groups module is shared.
242 //
243 m_Scenes[0].EventManager.TriggerUnhandledInstantMessage(gim);
244 }
245
246 return successful;
247 }
248
249 public void HandleUndeliverableMessage(GridInstantMessage im, MessageResultNotification result)
250 {
251 UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage;
252
253 // If this event has handlers, then an IM from an agent will be
254 // considered delivered. This will suppress the error message.
255 //
256 if (handlerUndeliveredMessage != null)
257 {
258 handlerUndeliveredMessage(im);
259 if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent)
260 result(true);
261 else
262 result(false);
263 return;
264 }
265
266 //m_log.DebugFormat("[INSTANT MESSAGE]: Undeliverable");
267 result(false);
268 }
269
270 private string TryGetRecipientUUI(UUID fromAgent, UUID toAgent)
271 {
272 // Let's call back the fromAgent's user agent service
273 // Maybe that service knows about the toAgent
274 IClientAPI client = LocateClientObject(fromAgent);
275 if (client != null)
276 {
277 AgentCircuitData circuit = m_Scenes[0].AuthenticateHandler.GetAgentCircuitData(client.AgentId);
278 if (circuit != null)
279 {
280 if (circuit.ServiceURLs.ContainsKey("HomeURI"))
281 {
282 string uasURL = circuit.ServiceURLs["HomeURI"].ToString();
283 m_log.DebugFormat("[HG MESSAGE TRANSFER]: getting UUI of user {0} from {1}", toAgent, uasURL);
284 UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uasURL);
285
286 string agentUUI = string.Empty;
287 try
288 {
289 agentUUI = uasConn.GetUUI(fromAgent, toAgent);
290 }
291 catch (Exception e) {
292 m_log.Debug("[HG MESSAGE TRANSFER]: GetUUI call failed ", e);
293 }
294
295 return agentUUI;
296 }
297 }
298 }
299
300 return string.Empty;
301 }
302
303 /// <summary>
304 /// Find the root client for a ID
305 /// </summary>
306 public IClientAPI LocateClientObject(UUID agentID)
307 {
308 lock (m_Scenes)
309 {
310 foreach (Scene scene in m_Scenes)
311 {
312 ScenePresence presence = scene.GetScenePresence(agentID);
313 if (presence != null && !presence.IsChildAgent)
314 return presence.ControllingClient;
315 }
316 }
317
318 return null;
319 }
320
321 #region IInstantMessageSimConnector
322 public bool SendInstantMessage(GridInstantMessage im)
323 {
324 //m_log.DebugFormat("[XXX] Hook SendInstantMessage {0}", im.message);
325 UUID agentID = new UUID(im.toAgentID);
326 return SendIMToScene(im, agentID);
327 }
328 #endregion
329
330
331 }
332}
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs
new file mode 100644
index 0000000..c33a296
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs
@@ -0,0 +1,206 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Reflection;
30using log4net;
31using Mono.Addins;
32using Nini.Config;
33using OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Framework.Client;
36using OpenSim.Region.Framework.Interfaces;
37using OpenSim.Region.Framework.Scenes;
38
39namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
40{
41 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "InstantMessageModule")]
42 public class InstantMessageModule : ISharedRegionModule
43 {
44 private static readonly ILog m_log = LogManager.GetLogger(
45 MethodBase.GetCurrentMethod().DeclaringType);
46
47 /// <value>
48 /// Is this module enabled?
49 /// </value>
50 private bool m_enabled = false;
51
52 private readonly List<Scene> m_scenes = new List<Scene>();
53
54 #region Region Module interface
55
56 private IMessageTransferModule m_TransferModule = null;
57
58 public void Initialise(IConfigSource config)
59 {
60 if (config.Configs["Messaging"] != null)
61 {
62 if (config.Configs["Messaging"].GetString(
63 "InstantMessageModule", "InstantMessageModule") !=
64 "InstantMessageModule")
65 return;
66 }
67
68 m_enabled = true;
69 }
70
71 public void AddRegion(Scene scene)
72 {
73 if (!m_enabled)
74 return;
75
76 lock (m_scenes)
77 {
78 if (!m_scenes.Contains(scene))
79 {
80 m_scenes.Add(scene);
81 scene.EventManager.OnClientConnect += OnClientConnect;
82 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
83 }
84 }
85 }
86
87 public void RegionLoaded(Scene scene)
88 {
89 if (!m_enabled)
90 return;
91
92 if (m_TransferModule == null)
93 {
94 m_TransferModule =
95 scene.RequestModuleInterface<IMessageTransferModule>();
96
97 if (m_TransferModule == null)
98 {
99 m_log.Error("[INSTANT MESSAGE]: No message transfer module, IM will not work!");
100 scene.EventManager.OnClientConnect -= OnClientConnect;
101 scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
102
103 m_scenes.Clear();
104 m_enabled = false;
105 }
106 }
107 }
108
109 public void RemoveRegion(Scene scene)
110 {
111 if (!m_enabled)
112 return;
113
114 lock (m_scenes)
115 {
116 m_scenes.Remove(scene);
117 }
118 }
119
120 void OnClientConnect(IClientCore client)
121 {
122 IClientIM clientIM;
123 if (client.TryGet(out clientIM))
124 {
125 clientIM.OnInstantMessage += OnInstantMessage;
126 }
127 }
128
129 public void PostInitialise()
130 {
131 }
132
133 public void Close()
134 {
135 }
136
137 public string Name
138 {
139 get { return "InstantMessageModule"; }
140 }
141
142 public Type ReplaceableInterface
143 {
144 get { return null; }
145 }
146
147 #endregion
148
149 public void OnInstantMessage(IClientAPI client, GridInstantMessage im)
150 {
151 byte dialog = im.dialog;
152
153 if (dialog != (byte)InstantMessageDialog.MessageFromAgent
154 && dialog != (byte)InstantMessageDialog.StartTyping
155 && dialog != (byte)InstantMessageDialog.StopTyping
156 && dialog != (byte)InstantMessageDialog.BusyAutoResponse
157 && dialog != (byte)InstantMessageDialog.MessageFromObject)
158 {
159 return;
160 }
161
162 if (m_TransferModule != null)
163 {
164 if (client != null)
165 im.fromAgentName = client.FirstName + " " + client.LastName;
166 m_TransferModule.SendInstantMessage(im,
167 delegate(bool success)
168 {
169 if (dialog == (uint)InstantMessageDialog.StartTyping ||
170 dialog == (uint)InstantMessageDialog.StopTyping ||
171 dialog == (uint)InstantMessageDialog.MessageFromObject)
172 {
173 return;
174 }
175
176 if ((client != null) && !success)
177 {
178 client.SendInstantMessage(
179 new GridInstantMessage(
180 null, new UUID(im.fromAgentID), "System",
181 new UUID(im.toAgentID),
182 (byte)InstantMessageDialog.BusyAutoResponse,
183 "Unable to send instant message. "+
184 "User is not logged in.", false,
185 new Vector3()));
186 }
187 }
188 );
189 }
190 }
191
192 /// <summary>
193 ///
194 /// </summary>
195 /// <param name="msg"></param>
196 private void OnGridInstantMessage(GridInstantMessage msg)
197 {
198 // Just call the Text IM handler above
199 // This event won't be raised unless we have that agent,
200 // so we can depend on the above not trying to send
201 // via grid again
202 //
203 OnInstantMessage(null, msg);
204 }
205 }
206}
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
new file mode 100644
index 0000000..2462ff8
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
@@ -0,0 +1,629 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections;
29using System.Collections.Generic;
30using System.Net;
31using System.Reflection;
32using log4net;
33using Mono.Addins;
34using Nini.Config;
35using Nwc.XmlRpc;
36using OpenMetaverse;
37using OpenSim.Framework;
38using OpenSim.Framework.Servers;
39using OpenSim.Region.Framework.Interfaces;
40using OpenSim.Region.Framework.Scenes;
41using GridRegion = OpenSim.Services.Interfaces.GridRegion;
42using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
43using OpenSim.Services.Interfaces;
44
45namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
46{
47 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MessageTransferModule")]
48 public class MessageTransferModule : ISharedRegionModule, IMessageTransferModule
49 {
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51
52 private bool m_Enabled = false;
53 protected List<Scene> m_Scenes = new List<Scene>();
54 protected Dictionary<UUID, UUID> m_UserRegionMap = new Dictionary<UUID, UUID>();
55
56 public event UndeliveredMessage OnUndeliveredMessage;
57
58 private IPresenceService m_PresenceService;
59 protected IPresenceService PresenceService
60 {
61 get
62 {
63 if (m_PresenceService == null)
64 m_PresenceService = m_Scenes[0].RequestModuleInterface<IPresenceService>();
65 return m_PresenceService;
66 }
67 }
68
69 public virtual void Initialise(IConfigSource config)
70 {
71 IConfig cnf = config.Configs["Messaging"];
72 if (cnf != null && cnf.GetString(
73 "MessageTransferModule", "MessageTransferModule") !=
74 "MessageTransferModule")
75 {
76 m_log.Debug("[MESSAGE TRANSFER]: Disabled by configuration");
77 return;
78 }
79
80 m_Enabled = true;
81 }
82
83 public virtual void AddRegion(Scene scene)
84 {
85 if (!m_Enabled)
86 return;
87
88 lock (m_Scenes)
89 {
90 m_log.Debug("[MESSAGE TRANSFER]: Message transfer module active");
91 scene.RegisterModuleInterface<IMessageTransferModule>(this);
92 m_Scenes.Add(scene);
93 }
94 }
95
96 public virtual void PostInitialise()
97 {
98 if (!m_Enabled)
99 return;
100
101 MainServer.Instance.AddXmlRPCHandler(
102 "grid_instant_message", processXMLRPCGridInstantMessage);
103 }
104
105 public virtual void RegionLoaded(Scene scene)
106 {
107 }
108
109 public virtual void RemoveRegion(Scene scene)
110 {
111 if (!m_Enabled)
112 return;
113
114 lock (m_Scenes)
115 {
116 m_Scenes.Remove(scene);
117 }
118 }
119
120 public virtual void Close()
121 {
122 }
123
124 public virtual string Name
125 {
126 get { return "MessageTransferModule"; }
127 }
128
129 public virtual Type ReplaceableInterface
130 {
131 get { return null; }
132 }
133
134 public virtual void SendInstantMessage(GridInstantMessage im, MessageResultNotification result)
135 {
136 UUID toAgentID = new UUID(im.toAgentID);
137
138 // Try root avatar only first
139 foreach (Scene scene in m_Scenes)
140 {
141// m_log.DebugFormat(
142// "[INSTANT MESSAGE]: Looking for root agent {0} in {1}",
143// toAgentID.ToString(), scene.RegionInfo.RegionName);
144
145 ScenePresence sp = scene.GetScenePresence(toAgentID);
146 if (sp != null && !sp.IsChildAgent)
147 {
148 // Local message
149// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID);
150
151 sp.ControllingClient.SendInstantMessage(im);
152
153 // Message sent
154 result(true);
155 return;
156 }
157 }
158
159 // try child avatar second
160 foreach (Scene scene in m_Scenes)
161 {
162// m_log.DebugFormat(
163// "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName);
164
165 ScenePresence sp = scene.GetScenePresence(toAgentID);
166 if (sp != null)
167 {
168 // Local message
169// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", sp.Name, toAgentID);
170
171 sp.ControllingClient.SendInstantMessage(im);
172
173 // Message sent
174 result(true);
175 return;
176 }
177 }
178
179// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID);
180
181 SendGridInstantMessageViaXMLRPC(im, result);
182 }
183
184 public void HandleUndeliverableMessage(GridInstantMessage im, MessageResultNotification result)
185 {
186 UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage;
187
188 // If this event has handlers, then an IM from an agent will be
189 // considered delivered. This will suppress the error message.
190 //
191 if (handlerUndeliveredMessage != null)
192 {
193 handlerUndeliveredMessage(im);
194 if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent)
195 result(true);
196 else
197 result(false);
198 return;
199 }
200
201 //m_log.DebugFormat("[INSTANT MESSAGE]: Undeliverable");
202 result(false);
203 }
204
205 /// <summary>
206 /// Process a XMLRPC Grid Instant Message
207 /// </summary>
208 /// <param name="request">XMLRPC parameters
209 /// </param>
210 /// <returns>Nothing much</returns>
211 protected virtual XmlRpcResponse processXMLRPCGridInstantMessage(XmlRpcRequest request, IPEndPoint remoteClient)
212 {
213 bool successful = false;
214
215 // TODO: For now, as IMs seem to be a bit unreliable on OSGrid, catch all exception that
216 // happen here and aren't caught and log them.
217 try
218 {
219 // various rational defaults
220 UUID fromAgentID = UUID.Zero;
221 UUID toAgentID = UUID.Zero;
222 UUID imSessionID = UUID.Zero;
223 uint timestamp = 0;
224 string fromAgentName = "";
225 string message = "";
226 byte dialog = (byte)0;
227 bool fromGroup = false;
228 byte offline = (byte)0;
229 uint ParentEstateID=0;
230 Vector3 Position = Vector3.Zero;
231 UUID RegionID = UUID.Zero ;
232 byte[] binaryBucket = new byte[0];
233
234 float pos_x = 0;
235 float pos_y = 0;
236 float pos_z = 0;
237 //m_log.Info("Processing IM");
238
239
240 Hashtable requestData = (Hashtable)request.Params[0];
241 // Check if it's got all the data
242 if (requestData.ContainsKey("from_agent_id")
243 && requestData.ContainsKey("to_agent_id") && requestData.ContainsKey("im_session_id")
244 && requestData.ContainsKey("timestamp") && requestData.ContainsKey("from_agent_name")
245 && requestData.ContainsKey("message") && requestData.ContainsKey("dialog")
246 && requestData.ContainsKey("from_group")
247 && requestData.ContainsKey("offline") && requestData.ContainsKey("parent_estate_id")
248 && requestData.ContainsKey("position_x") && requestData.ContainsKey("position_y")
249 && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id")
250 && requestData.ContainsKey("binary_bucket"))
251 {
252 // Do the easy way of validating the UUIDs
253 UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID);
254 UUID.TryParse((string)requestData["to_agent_id"], out toAgentID);
255 UUID.TryParse((string)requestData["im_session_id"], out imSessionID);
256 UUID.TryParse((string)requestData["region_id"], out RegionID);
257
258 try
259 {
260 timestamp = (uint)Convert.ToInt32((string)requestData["timestamp"]);
261 }
262 catch (ArgumentException)
263 {
264 }
265 catch (FormatException)
266 {
267 }
268 catch (OverflowException)
269 {
270 }
271
272 fromAgentName = (string)requestData["from_agent_name"];
273 message = (string)requestData["message"];
274 if (message == null)
275 message = string.Empty;
276
277 // Bytes don't transfer well over XMLRPC, so, we Base64 Encode them.
278 string requestData1 = (string)requestData["dialog"];
279 if (string.IsNullOrEmpty(requestData1))
280 {
281 dialog = 0;
282 }
283 else
284 {
285 byte[] dialogdata = Convert.FromBase64String(requestData1);
286 dialog = dialogdata[0];
287 }
288
289 if ((string)requestData["from_group"] == "TRUE")
290 fromGroup = true;
291
292 string requestData2 = (string)requestData["offline"];
293 if (String.IsNullOrEmpty(requestData2))
294 {
295 offline = 0;
296 }
297 else
298 {
299 byte[] offlinedata = Convert.FromBase64String(requestData2);
300 offline = offlinedata[0];
301 }
302
303 try
304 {
305 ParentEstateID = (uint)Convert.ToInt32((string)requestData["parent_estate_id"]);
306 }
307 catch (ArgumentException)
308 {
309 }
310 catch (FormatException)
311 {
312 }
313 catch (OverflowException)
314 {
315 }
316
317 try
318 {
319 pos_x = (uint)Convert.ToInt32((string)requestData["position_x"]);
320 }
321 catch (ArgumentException)
322 {
323 }
324 catch (FormatException)
325 {
326 }
327 catch (OverflowException)
328 {
329 }
330 try
331 {
332 pos_y = (uint)Convert.ToInt32((string)requestData["position_y"]);
333 }
334 catch (ArgumentException)
335 {
336 }
337 catch (FormatException)
338 {
339 }
340 catch (OverflowException)
341 {
342 }
343 try
344 {
345 pos_z = (uint)Convert.ToInt32((string)requestData["position_z"]);
346 }
347 catch (ArgumentException)
348 {
349 }
350 catch (FormatException)
351 {
352 }
353 catch (OverflowException)
354 {
355 }
356
357 Position = new Vector3(pos_x, pos_y, pos_z);
358
359 string requestData3 = (string)requestData["binary_bucket"];
360 if (string.IsNullOrEmpty(requestData3))
361 {
362 binaryBucket = new byte[0];
363 }
364 else
365 {
366 binaryBucket = Convert.FromBase64String(requestData3);
367 }
368
369 // Create a New GridInstantMessageObject the the data
370 GridInstantMessage gim = new GridInstantMessage();
371 gim.fromAgentID = fromAgentID.Guid;
372 gim.fromAgentName = fromAgentName;
373 gim.fromGroup = fromGroup;
374 gim.imSessionID = imSessionID.Guid;
375 gim.RegionID = RegionID.Guid;
376 gim.timestamp = timestamp;
377 gim.toAgentID = toAgentID.Guid;
378 gim.message = message;
379 gim.dialog = dialog;
380 gim.offline = offline;
381 gim.ParentEstateID = ParentEstateID;
382 gim.Position = Position;
383 gim.binaryBucket = binaryBucket;
384
385
386 // Trigger the Instant message in the scene.
387 foreach (Scene scene in m_Scenes)
388 {
389 ScenePresence sp = scene.GetScenePresence(toAgentID);
390 if (sp != null && !sp.IsChildAgent)
391 {
392 scene.EventManager.TriggerIncomingInstantMessage(gim);
393 successful = true;
394 }
395 }
396 if (!successful)
397 {
398 // If the message can't be delivered to an agent, it
399 // is likely to be a group IM. On a group IM, the
400 // imSessionID = toAgentID = group id. Raise the
401 // unhandled IM event to give the groups module
402 // a chance to pick it up. We raise that in a random
403 // scene, since the groups module is shared.
404 //
405 m_Scenes[0].EventManager.TriggerUnhandledInstantMessage(gim);
406 }
407 }
408 }
409 catch (Exception e)
410 {
411 m_log.Error("[INSTANT MESSAGE]: Caught unexpected exception:", e);
412 successful = false;
413 }
414
415 //Send response back to region calling if it was successful
416 // calling region uses this to know when to look up a user's location again.
417 XmlRpcResponse resp = new XmlRpcResponse();
418 Hashtable respdata = new Hashtable();
419 if (successful)
420 respdata["success"] = "TRUE";
421 else
422 respdata["success"] = "FALSE";
423 resp.Value = respdata;
424
425 return resp;
426 }
427
428 /// <summary>
429 /// delegate for sending a grid instant message asynchronously
430 /// </summary>
431 public delegate void GridInstantMessageDelegate(GridInstantMessage im, MessageResultNotification result);
432
433 protected virtual void GridInstantMessageCompleted(IAsyncResult iar)
434 {
435 GridInstantMessageDelegate icon =
436 (GridInstantMessageDelegate)iar.AsyncState;
437 icon.EndInvoke(iar);
438 }
439
440
441 protected virtual void SendGridInstantMessageViaXMLRPC(GridInstantMessage im, MessageResultNotification result)
442 {
443 GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsync;
444
445 d.BeginInvoke(im, result, GridInstantMessageCompleted, d);
446 }
447
448 /// <summary>
449 /// Internal SendGridInstantMessage over XMLRPC method.
450 /// </summary>
451 /// <remarks>
452 /// This is called from within a dedicated thread.
453 /// </remarks>
454 private void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result)
455 {
456 UUID toAgentID = new UUID(im.toAgentID);
457 UUID regionID;
458 bool needToLookupAgent;
459
460 lock (m_UserRegionMap)
461 needToLookupAgent = !m_UserRegionMap.TryGetValue(toAgentID, out regionID);
462
463 while (true)
464 {
465 if (needToLookupAgent)
466 {
467 PresenceInfo[] presences = PresenceService.GetAgents(new string[] { toAgentID.ToString() });
468
469 UUID foundRegionID = UUID.Zero;
470
471 if (presences != null)
472 {
473 foreach (PresenceInfo p in presences)
474 {
475 if (p.RegionID != UUID.Zero)
476 {
477 foundRegionID = p.RegionID;
478 break;
479 }
480 }
481 }
482
483 // If not found or the found region is the same as the last lookup, then message is undeliverable
484 if (foundRegionID == UUID.Zero || foundRegionID == regionID)
485 break;
486 else
487 regionID = foundRegionID;
488 }
489
490 GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, regionID);
491 if (reginfo == null)
492 {
493 m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", regionID);
494 break;
495 }
496
497 // Try to send the message to the agent via the retrieved region.
498 Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im);
499 msgdata["region_handle"] = 0;
500 bool imresult = doIMSending(reginfo, msgdata);
501
502 // If the message delivery was successful, then cache the entry.
503 if (imresult)
504 {
505 lock (m_UserRegionMap)
506 {
507 m_UserRegionMap[toAgentID] = regionID;
508 }
509 result(true);
510 return;
511 }
512
513 // If we reach this point in the first iteration of the while, then we may have unsuccessfully tried
514 // to use a locally cached region ID. All subsequent attempts need to lookup agent details from
515 // the presence service.
516 needToLookupAgent = true;
517 }
518
519 // If we reached this point then the message was not deliverable. Remove the bad cache entry and
520 // signal the delivery failure.
521 lock (m_UserRegionMap)
522 m_UserRegionMap.Remove(toAgentID);
523
524 // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message");
525 HandleUndeliverableMessage(im, result);
526 }
527
528 /// <summary>
529 /// This actually does the XMLRPC Request
530 /// </summary>
531 /// <param name="reginfo">RegionInfo we pull the data out of to send the request to</param>
532 /// <param name="xmlrpcdata">The Instant Message data Hashtable</param>
533 /// <returns>Bool if the message was successfully delivered at the other side.</returns>
534 protected virtual bool doIMSending(GridRegion reginfo, Hashtable xmlrpcdata)
535 {
536 ArrayList SendParams = new ArrayList();
537 SendParams.Add(xmlrpcdata);
538 XmlRpcRequest GridReq = new XmlRpcRequest("grid_instant_message", SendParams);
539 try
540 {
541
542 XmlRpcResponse GridResp = GridReq.Send(reginfo.ServerURI, 3000);
543
544 Hashtable responseData = (Hashtable)GridResp.Value;
545
546 if (responseData.ContainsKey("success"))
547 {
548 if ((string)responseData["success"] == "TRUE")
549 {
550 return true;
551 }
552 else
553 {
554 return false;
555 }
556 }
557 else
558 {
559 return false;
560 }
561 }
562 catch (WebException e)
563 {
564 m_log.ErrorFormat("[GRID INSTANT MESSAGE]: Error sending message to {0} the host didn't respond " + e.ToString(), reginfo.ServerURI.ToString());
565 }
566
567 return false;
568 }
569
570 /// <summary>
571 /// Get ulong region handle for region by it's Region UUID.
572 /// We use region handles over grid comms because there's all sorts of free and cool caching.
573 /// </summary>
574 /// <param name="regionID">UUID of region to get the region handle for</param>
575 /// <returns></returns>
576// private virtual ulong getLocalRegionHandleFromUUID(UUID regionID)
577// {
578// ulong returnhandle = 0;
579//
580// lock (m_Scenes)
581// {
582// foreach (Scene sn in m_Scenes)
583// {
584// if (sn.RegionInfo.RegionID == regionID)
585// {
586// returnhandle = sn.RegionInfo.RegionHandle;
587// break;
588// }
589// }
590// }
591// return returnhandle;
592// }
593
594 /// <summary>
595 /// Takes a GridInstantMessage and converts it into a Hashtable for XMLRPC
596 /// </summary>
597 /// <param name="msg">The GridInstantMessage object</param>
598 /// <returns>Hashtable containing the XMLRPC request</returns>
599 protected virtual Hashtable ConvertGridInstantMessageToXMLRPC(GridInstantMessage msg)
600 {
601 Hashtable gim = new Hashtable();
602 gim["from_agent_id"] = msg.fromAgentID.ToString();
603 // Kept for compatibility
604 gim["from_agent_session"] = UUID.Zero.ToString();
605 gim["to_agent_id"] = msg.toAgentID.ToString();
606 gim["im_session_id"] = msg.imSessionID.ToString();
607 gim["timestamp"] = msg.timestamp.ToString();
608 gim["from_agent_name"] = msg.fromAgentName;
609 gim["message"] = msg.message;
610 byte[] dialogdata = new byte[1];dialogdata[0] = msg.dialog;
611 gim["dialog"] = Convert.ToBase64String(dialogdata,Base64FormattingOptions.None);
612
613 if (msg.fromGroup)
614 gim["from_group"] = "TRUE";
615 else
616 gim["from_group"] = "FALSE";
617 byte[] offlinedata = new byte[1]; offlinedata[0] = msg.offline;
618 gim["offline"] = Convert.ToBase64String(offlinedata, Base64FormattingOptions.None);
619 gim["parent_estate_id"] = msg.ParentEstateID.ToString();
620 gim["position_x"] = msg.Position.X.ToString();
621 gim["position_y"] = msg.Position.Y.ToString();
622 gim["position_z"] = msg.Position.Z.ToString();
623 gim["region_id"] = new UUID(msg.RegionID).ToString();
624 gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None);
625 return gim;
626 }
627
628 }
629}
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs
new file mode 100644
index 0000000..7ce2813
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs
@@ -0,0 +1,146 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Reflection;
30using log4net;
31using Nini.Config;
32using Mono.Addins;
33using OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Framework.Communications;
36using OpenSim.Framework.Servers;
37using OpenSim.Framework.Client;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40
41namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
42{
43 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MuteListModule")]
44 public class MuteListModule : ISharedRegionModule
45 {
46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47
48 private bool enabled = true;
49 private List<Scene> m_SceneList = new List<Scene>();
50 private string m_RestURL = String.Empty;
51
52 public void Initialise(IConfigSource config)
53 {
54 IConfig cnf = config.Configs["Messaging"];
55 if (cnf == null)
56 {
57 enabled = false;
58 return;
59 }
60
61 if (cnf != null && cnf.GetString("MuteListModule", "None") !=
62 "MuteListModule")
63 {
64 enabled = false;
65 return;
66 }
67
68 m_RestURL = cnf.GetString("MuteListURL", "");
69 if (m_RestURL == "")
70 {
71 m_log.Error("[MUTE LIST] Module was enabled, but no URL is given, disabling");
72 enabled = false;
73 return;
74 }
75 }
76
77 public void AddRegion(Scene scene)
78 {
79 if (!enabled)
80 return;
81
82 lock (m_SceneList)
83 {
84 m_SceneList.Add(scene);
85
86 scene.EventManager.OnNewClient += OnNewClient;
87 }
88 }
89
90 public void RegionLoaded(Scene scene)
91 {
92 }
93
94 public void RemoveRegion(Scene scene)
95 {
96 if (!enabled)
97 return;
98
99 lock (m_SceneList)
100 {
101 m_SceneList.Remove(scene);
102 }
103 }
104
105 public void PostInitialise()
106 {
107 if (!enabled)
108 return;
109
110 m_log.Debug("[MUTE LIST] Mute list enabled");
111 }
112
113 public string Name
114 {
115 get { return "MuteListModule"; }
116 }
117
118 public Type ReplaceableInterface
119 {
120 get { return null; }
121 }
122
123 public void Close()
124 {
125 }
126
127 private void OnNewClient(IClientAPI client)
128 {
129 client.OnMuteListRequest += OnMuteListRequest;
130 }
131
132 private void OnMuteListRequest(IClientAPI client, uint crc)
133 {
134 m_log.DebugFormat("[MUTE LIST] Got mute list request for crc {0}", crc);
135 string filename = "mutes"+client.AgentId.ToString();
136
137 IXfer xfer = client.Scene.RequestModuleInterface<IXfer>();
138 if (xfer != null)
139 {
140 xfer.AddNewFile(filename, new Byte[0]);
141 client.SendMuteListUpdate(filename);
142 }
143 }
144 }
145}
146
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
new file mode 100644
index 0000000..c75920d
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
@@ -0,0 +1,249 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Reflection;
30using log4net;
31using Mono.Addins;
32using Nini.Config;
33using OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Framework.Communications;
36using OpenSim.Framework.Servers;
37using OpenSim.Framework.Client;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40
41namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
42{
43 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "OfflineMessageModule")]
44 public class OfflineMessageModule : ISharedRegionModule
45 {
46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47
48 private bool enabled = true;
49 private List<Scene> m_SceneList = new List<Scene>();
50 private string m_RestURL = String.Empty;
51 IMessageTransferModule m_TransferModule = null;
52 private bool m_ForwardOfflineGroupMessages = true;
53
54 public void Initialise(IConfigSource config)
55 {
56 IConfig cnf = config.Configs["Messaging"];
57 if (cnf == null)
58 {
59 enabled = false;
60 return;
61 }
62 if (cnf != null && cnf.GetString("OfflineMessageModule", "None") !=
63 "OfflineMessageModule")
64 {
65 enabled = false;
66 return;
67 }
68
69 m_RestURL = cnf.GetString("OfflineMessageURL", "");
70 if (m_RestURL == "")
71 {
72 m_log.Error("[OFFLINE MESSAGING] Module was enabled, but no URL is given, disabling");
73 enabled = false;
74 return;
75 }
76
77 m_ForwardOfflineGroupMessages = cnf.GetBoolean("ForwardOfflineGroupMessages", m_ForwardOfflineGroupMessages);
78 }
79
80 public void AddRegion(Scene scene)
81 {
82 if (!enabled)
83 return;
84
85 lock (m_SceneList)
86 {
87 m_SceneList.Add(scene);
88
89 scene.EventManager.OnNewClient += OnNewClient;
90 }
91 }
92
93 public void RegionLoaded(Scene scene)
94 {
95 if (!enabled)
96 return;
97
98 if (m_TransferModule == null)
99 {
100 m_TransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
101 if (m_TransferModule == null)
102 {
103 scene.EventManager.OnNewClient -= OnNewClient;
104
105 enabled = false;
106 m_SceneList.Clear();
107
108 m_log.Error("[OFFLINE MESSAGING] No message transfer module is enabled. Diabling offline messages");
109 }
110 m_TransferModule.OnUndeliveredMessage += UndeliveredMessage;
111 }
112 }
113
114 public void RemoveRegion(Scene scene)
115 {
116 if (!enabled)
117 return;
118
119 lock (m_SceneList)
120 {
121 m_SceneList.Remove(scene);
122 }
123 }
124
125 public void PostInitialise()
126 {
127 if (!enabled)
128 return;
129
130 m_log.Debug("[OFFLINE MESSAGING] Offline messages enabled");
131 }
132
133 public string Name
134 {
135 get { return "OfflineMessageModule"; }
136 }
137
138 public Type ReplaceableInterface
139 {
140 get { return null; }
141 }
142
143 public void Close()
144 {
145 }
146
147 private Scene FindScene(UUID agentID)
148 {
149 foreach (Scene s in m_SceneList)
150 {
151 ScenePresence presence = s.GetScenePresence(agentID);
152 if (presence != null && !presence.IsChildAgent)
153 return s;
154 }
155 return null;
156 }
157
158 private IClientAPI FindClient(UUID agentID)
159 {
160 foreach (Scene s in m_SceneList)
161 {
162 ScenePresence presence = s.GetScenePresence(agentID);
163 if (presence != null && !presence.IsChildAgent)
164 return presence.ControllingClient;
165 }
166 return null;
167 }
168
169 private void OnNewClient(IClientAPI client)
170 {
171 client.OnRetrieveInstantMessages += RetrieveInstantMessages;
172 }
173
174 private void RetrieveInstantMessages(IClientAPI client)
175 {
176 if (m_RestURL != "")
177 {
178 m_log.DebugFormat("[OFFLINE MESSAGING]: Retrieving stored messages for {0}", client.AgentId);
179
180 List<GridInstantMessage> msglist
181 = SynchronousRestObjectRequester.MakeRequest<UUID, List<GridInstantMessage>>(
182 "POST", m_RestURL + "/RetrieveMessages/", client.AgentId);
183
184 if (msglist == null)
185 {
186 m_log.WarnFormat("[OFFLINE MESSAGING]: WARNING null message list.");
187 return;
188 }
189
190 foreach (GridInstantMessage im in msglist)
191 {
192 if (im.dialog == (byte)InstantMessageDialog.InventoryOffered)
193 // send it directly or else the item will be given twice
194 client.SendInstantMessage(im);
195 else
196 {
197 // Send through scene event manager so all modules get a chance
198 // to look at this message before it gets delivered.
199 //
200 // Needed for proper state management for stored group
201 // invitations
202 //
203 Scene s = FindScene(client.AgentId);
204 if (s != null)
205 s.EventManager.TriggerIncomingInstantMessage(im);
206 }
207 }
208 }
209 }
210
211 private void UndeliveredMessage(GridInstantMessage im)
212 {
213 if (im.dialog != (byte)InstantMessageDialog.MessageFromObject &&
214 im.dialog != (byte)InstantMessageDialog.MessageFromAgent &&
215 im.dialog != (byte)InstantMessageDialog.GroupNotice &&
216 im.dialog != (byte)InstantMessageDialog.GroupInvitation &&
217 im.dialog != (byte)InstantMessageDialog.InventoryOffered)
218 {
219 return;
220 }
221
222 if (!m_ForwardOfflineGroupMessages)
223 {
224 if (im.dialog == (byte)InstantMessageDialog.GroupNotice ||
225 im.dialog == (byte)InstantMessageDialog.GroupInvitation)
226 return;
227 }
228
229 bool success = SynchronousRestObjectRequester.MakeRequest<GridInstantMessage, bool>(
230 "POST", m_RestURL+"/SaveMessage/", im, 10000);
231
232 if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent)
233 {
234 IClientAPI client = FindClient(new UUID(im.fromAgentID));
235 if (client == null)
236 return;
237
238 client.SendInstantMessage(new GridInstantMessage(
239 null, new UUID(im.toAgentID),
240 "System", new UUID(im.fromAgentID),
241 (byte)InstantMessageDialog.MessageFromAgent,
242 "User is not logged in. "+
243 (success ? "Message saved." : "Message not saved"),
244 false, new Vector3()));
245 }
246 }
247 }
248}
249
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs
new file mode 100644
index 0000000..c2440d8
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs
@@ -0,0 +1,156 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections;
29using System.Collections.Generic;
30using System.Net;
31using System.Reflection;
32using log4net;
33using Nini.Config;
34using Nwc.XmlRpc;
35using OpenMetaverse;
36using Mono.Addins;
37using OpenSim.Framework;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Services.Interfaces;
41using GridRegion = OpenSim.Services.Interfaces.GridRegion;
42using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
43
44namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
45{
46 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "PresenceModule")]
47 public class PresenceModule : ISharedRegionModule, IPresenceModule
48 {
49 private static readonly ILog m_log = LogManager.GetLogger(
50 MethodBase.GetCurrentMethod().DeclaringType);
51
52#pragma warning disable 0067
53 public event PresenceChange OnPresenceChange;
54 public event BulkPresenceData OnBulkPresenceData;
55#pragma warning restore 0067
56
57 protected List<Scene> m_Scenes = new List<Scene>();
58
59 protected IPresenceService m_PresenceService = null;
60
61 protected IPresenceService PresenceService
62 {
63 get
64 {
65 if (m_PresenceService == null)
66 {
67 if (m_Scenes.Count > 0)
68 m_PresenceService = m_Scenes[0].RequestModuleInterface<IPresenceService>();
69 }
70
71 return m_PresenceService;
72 }
73 }
74
75 public void Initialise(IConfigSource config)
76 {
77 }
78
79 public void AddRegion(Scene scene)
80 {
81 m_Scenes.Add(scene);
82
83 scene.EventManager.OnNewClient += OnNewClient;
84
85 scene.RegisterModuleInterface<IPresenceModule>(this);
86 }
87
88 public void RegionLoaded(Scene scene)
89 {
90 }
91
92 public void RemoveRegion(Scene scene)
93 {
94 m_Scenes.Remove(scene);
95 }
96
97 public void PostInitialise()
98 {
99 }
100
101 public void Close()
102 {
103 }
104
105 public string Name
106 {
107 get { return "PresenceModule"; }
108 }
109
110 public Type ReplaceableInterface
111 {
112 get { return null; }
113 }
114
115 public void RequestBulkPresenceData(UUID[] users)
116 {
117 }
118
119 public void OnNewClient(IClientAPI client)
120 {
121 client.AddGenericPacketHandler("requestonlinenotification", OnRequestOnlineNotification);
122 }
123
124 public void OnRequestOnlineNotification(Object sender, string method, List<String> args)
125 {
126 if (!(sender is IClientAPI))
127 return;
128
129 IClientAPI client = (IClientAPI)sender;
130 m_log.DebugFormat("[PRESENCE MODULE]: OnlineNotification requested by {0}", client.Name);
131
132 PresenceInfo[] status = PresenceService.GetAgents(args.ToArray());
133
134 List<UUID> online = new List<UUID>();
135 List<UUID> offline = new List<UUID>();
136
137 foreach (PresenceInfo pi in status)
138 {
139 UUID uuid = new UUID(pi.UserID);
140 if (!online.Contains(uuid))
141 online.Add(uuid);
142 }
143 foreach (string s in args)
144 {
145 UUID uuid = new UUID(s);
146 if (!online.Contains(uuid) && !offline.Contains(uuid))
147 offline.Add(uuid);
148 }
149
150 if (online.Count > 0)
151 client.SendAgentOnline(online.ToArray());
152 if (offline.Count > 0)
153 client.SendAgentOffline(offline.ToArray());
154 }
155 }
156}