aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Services/HypergridService/HGInstantMessageService.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Services/HypergridService/HGInstantMessageService.cs')
-rw-r--r--OpenSim/Services/HypergridService/HGInstantMessageService.cs376
1 files changed, 376 insertions, 0 deletions
diff --git a/OpenSim/Services/HypergridService/HGInstantMessageService.cs b/OpenSim/Services/HypergridService/HGInstantMessageService.cs
new file mode 100644
index 0000000..32ca09a
--- /dev/null
+++ b/OpenSim/Services/HypergridService/HGInstantMessageService.cs
@@ -0,0 +1,376 @@
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.Generic;
30using System.Net;
31using System.Reflection;
32
33using OpenSim.Framework;
34using OpenSim.Services.Connectors.Friends;
35using OpenSim.Services.Connectors.Hypergrid;
36using OpenSim.Services.Interfaces;
37using OpenSim.Services.Connectors.InstantMessage;
38using GridRegion = OpenSim.Services.Interfaces.GridRegion;
39using OpenSim.Server.Base;
40using FriendInfo = OpenSim.Services.Interfaces.FriendInfo;
41
42using OpenMetaverse;
43using log4net;
44using Nini.Config;
45
46namespace OpenSim.Services.HypergridService
47{
48 /// <summary>
49 /// Inter-grid IM
50 /// </summary>
51 public class HGInstantMessageService : IInstantMessage
52 {
53 private static readonly ILog m_log =
54 LogManager.GetLogger(
55 MethodBase.GetCurrentMethod().DeclaringType);
56
57 private const double CACHE_EXPIRATION_SECONDS = 120000.0; // 33 hours
58
59 static bool m_Initialized = false;
60
61 protected static IGridService m_GridService;
62 protected static IPresenceService m_PresenceService;
63 protected static IUserAgentService m_UserAgentService;
64 protected static IOfflineIMService m_OfflineIMService;
65
66 protected static IInstantMessageSimConnector m_IMSimConnector;
67
68 protected static Dictionary<UUID, object> m_UserLocationMap = new Dictionary<UUID, object>();
69 private static ExpiringCache<UUID, GridRegion> m_RegionCache;
70
71 private static bool m_ForwardOfflineGroupMessages;
72 private static bool m_InGatekeeper;
73
74 public HGInstantMessageService(IConfigSource config)
75 : this(config, null)
76 {
77 }
78
79 public HGInstantMessageService(IConfigSource config, IInstantMessageSimConnector imConnector)
80 {
81 if (imConnector != null)
82 m_IMSimConnector = imConnector;
83
84 if (!m_Initialized)
85 {
86 m_Initialized = true;
87
88 IConfig serverConfig = config.Configs["HGInstantMessageService"];
89 if (serverConfig == null)
90 throw new Exception(String.Format("No section HGInstantMessageService in config file"));
91
92 string gridService = serverConfig.GetString("GridService", String.Empty);
93 string presenceService = serverConfig.GetString("PresenceService", String.Empty);
94 string userAgentService = serverConfig.GetString("UserAgentService", String.Empty);
95 m_InGatekeeper = serverConfig.GetBoolean("InGatekeeper", false);
96 m_log.DebugFormat("[HG IM SERVICE]: Starting... InRobust? {0}", m_InGatekeeper);
97
98 if (gridService == string.Empty || presenceService == string.Empty)
99 throw new Exception(String.Format("Incomplete specifications, InstantMessage Service cannot function."));
100
101 Object[] args = new Object[] { config };
102 m_GridService = ServerUtils.LoadPlugin<IGridService>(gridService, args);
103 m_PresenceService = ServerUtils.LoadPlugin<IPresenceService>(presenceService, args);
104 try
105 {
106 m_UserAgentService = ServerUtils.LoadPlugin<IUserAgentService>(userAgentService, args);
107 }
108 catch
109 {
110 m_log.WarnFormat("[HG IM SERVICE]: Unable to create User Agent Service. Missing config var in [HGInstantMessageService]?");
111 }
112
113 m_RegionCache = new ExpiringCache<UUID, GridRegion>();
114
115 IConfig cnf = config.Configs["Messaging"];
116 if (cnf == null)
117 {
118 return;
119 }
120
121 m_ForwardOfflineGroupMessages = cnf.GetBoolean("ForwardOfflineGroupMessages", false);
122
123 if (m_InGatekeeper)
124 {
125 string offlineIMService = cnf.GetString("OfflineIMService", string.Empty);
126 if (offlineIMService != string.Empty)
127 m_OfflineIMService = ServerUtils.LoadPlugin<IOfflineIMService>(offlineIMService, args);
128 }
129 }
130 }
131
132 public bool IncomingInstantMessage(GridInstantMessage im)
133 {
134// m_log.DebugFormat("[HG IM SERVICE]: Received message from {0} to {1}", im.fromAgentID, im.toAgentID);
135// UUID toAgentID = new UUID(im.toAgentID);
136
137 bool success = false;
138 if (m_IMSimConnector != null)
139 {
140 //m_log.DebugFormat("[XXX] SendIMToRegion local im connector");
141 success = m_IMSimConnector.SendInstantMessage(im);
142 }
143 else
144 {
145 success = TrySendInstantMessage(im, "", true, false);
146 }
147
148 if (!success && m_InGatekeeper) // we do this only in the Gatekeeper IM service
149 UndeliveredMessage(im);
150
151 return success;
152 }
153
154 public bool OutgoingInstantMessage(GridInstantMessage im, string url, bool foreigner)
155 {
156// m_log.DebugFormat("[HG IM SERVICE]: Sending message from {0} to {1}@{2}", im.fromAgentID, im.toAgentID, url);
157 if (url != string.Empty)
158 return TrySendInstantMessage(im, url, true, foreigner);
159 else
160 {
161 PresenceInfo upd = new PresenceInfo();
162 upd.RegionID = UUID.Zero;
163 return TrySendInstantMessage(im, upd, true, foreigner);
164 }
165
166 }
167
168 protected bool TrySendInstantMessage(GridInstantMessage im, object previousLocation, bool firstTime, bool foreigner)
169 {
170 UUID toAgentID = new UUID(im.toAgentID);
171
172 PresenceInfo upd = null;
173 string url = string.Empty;
174
175 bool lookupAgent = false;
176
177 lock (m_UserLocationMap)
178 {
179 if (m_UserLocationMap.ContainsKey(toAgentID))
180 {
181 object o = m_UserLocationMap[toAgentID];
182 if (o is PresenceInfo)
183 upd = (PresenceInfo)o;
184 else if (o is string)
185 url = (string)o;
186
187 // We need to compare the current location with the previous
188 // or the recursive loop will never end because it will never try to lookup the agent again
189 if (!firstTime)
190 {
191 lookupAgent = true;
192 upd = null;
193 }
194 }
195 else
196 {
197 lookupAgent = true;
198 }
199 }
200
201 //m_log.DebugFormat("[XXX] Neeed lookup ? {0}", (lookupAgent ? "yes" : "no"));
202
203 // Are we needing to look-up an agent?
204 if (lookupAgent)
205 {
206 // Non-cached user agent lookup.
207 PresenceInfo[] presences = m_PresenceService.GetAgents(new string[] { toAgentID.ToString() });
208 if (presences != null && presences.Length > 0)
209 {
210 foreach (PresenceInfo p in presences)
211 {
212 if (p.RegionID != UUID.Zero)
213 {
214 //m_log.DebugFormat("[XXX]: Found presence in {0}", p.RegionID);
215 upd = p;
216 break;
217 }
218 }
219 }
220
221 if (upd == null && !foreigner)
222 {
223 // Let's check with the UAS if the user is elsewhere
224 m_log.DebugFormat("[HG IM SERVICE]: User is not present. Checking location with User Agent service");
225 try
226 {
227 url = m_UserAgentService.LocateUser(toAgentID);
228 }
229 catch (Exception e)
230 {
231 m_log.Warn("[HG IM SERVICE]: LocateUser call failed ", e);
232 url = string.Empty;
233 }
234 }
235
236 // check if we've tried this before..
237 // This is one way to end the recursive loop
238 //
239 if (!firstTime && ((previousLocation is PresenceInfo && upd != null && upd.RegionID == ((PresenceInfo)previousLocation).RegionID) ||
240 (previousLocation is string && upd == null && previousLocation.Equals(url))))
241 {
242 // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message");
243 m_log.DebugFormat("[HG IM SERVICE]: Fail 2 {0} {1}", previousLocation, url);
244
245 return false;
246 }
247 }
248
249 if (upd != null)
250 {
251 // ok, the user is around somewhere. Let's send back the reply with "success"
252 // even though the IM may still fail. Just don't keep the caller waiting for
253 // the entire time we're trying to deliver the IM
254 return SendIMToRegion(upd, im, toAgentID, foreigner);
255 }
256 else if (url != string.Empty)
257 {
258 // ok, the user is around somewhere. Let's send back the reply with "success"
259 // even though the IM may still fail. Just don't keep the caller waiting for
260 // the entire time we're trying to deliver the IM
261 return ForwardIMToGrid(url, im, toAgentID, foreigner);
262 }
263 else if (firstTime && previousLocation is string && (string)previousLocation != string.Empty)
264 {
265 return ForwardIMToGrid((string)previousLocation, im, toAgentID, foreigner);
266 }
267 else
268 m_log.DebugFormat("[HG IM SERVICE]: Unable to locate user {0}", toAgentID);
269 return false;
270 }
271
272 bool SendIMToRegion(PresenceInfo upd, GridInstantMessage im, UUID toAgentID, bool foreigner)
273 {
274 bool imresult = false;
275 GridRegion reginfo = null;
276 if (!m_RegionCache.TryGetValue(upd.RegionID, out reginfo))
277 {
278 reginfo = m_GridService.GetRegionByUUID(UUID.Zero /*!!!*/, upd.RegionID);
279 if (reginfo != null)
280 m_RegionCache.AddOrUpdate(upd.RegionID, reginfo, CACHE_EXPIRATION_SECONDS);
281 }
282
283 if (reginfo != null)
284 {
285 imresult = InstantMessageServiceConnector.SendInstantMessage(reginfo.ServerURI, im);
286 }
287 else
288 {
289 m_log.DebugFormat("[HG IM SERVICE]: Failed to deliver message to {0}", reginfo.ServerURI);
290 return false;
291 }
292
293 if (imresult)
294 {
295 // IM delivery successful, so store the Agent's location in our local cache.
296 lock (m_UserLocationMap)
297 {
298 if (m_UserLocationMap.ContainsKey(toAgentID))
299 {
300 m_UserLocationMap[toAgentID] = upd;
301 }
302 else
303 {
304 m_UserLocationMap.Add(toAgentID, upd);
305 }
306 }
307 return true;
308 }
309 else
310 {
311 // try again, but lookup user this time.
312 // Warning, this must call the Async version
313 // of this method or we'll be making thousands of threads
314 // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync
315 // The version that spawns the thread is SendGridInstantMessageViaXMLRPC
316
317 // This is recursive!!!!!
318 return TrySendInstantMessage(im, upd, false, foreigner);
319 }
320 }
321
322 bool ForwardIMToGrid(string url, GridInstantMessage im, UUID toAgentID, bool foreigner)
323 {
324 if (InstantMessageServiceConnector.SendInstantMessage(url, im))
325 {
326 // IM delivery successful, so store the Agent's location in our local cache.
327 lock (m_UserLocationMap)
328 {
329 if (m_UserLocationMap.ContainsKey(toAgentID))
330 {
331 m_UserLocationMap[toAgentID] = url;
332 }
333 else
334 {
335 m_UserLocationMap.Add(toAgentID, url);
336 }
337 }
338
339 return true;
340 }
341 else
342 {
343 // try again, but lookup user this time.
344
345 // This is recursive!!!!!
346 return TrySendInstantMessage(im, url, false, foreigner);
347 }
348 }
349
350 private bool UndeliveredMessage(GridInstantMessage im)
351 {
352 if (m_OfflineIMService == null)
353 return false;
354
355 if (im.dialog != (byte)InstantMessageDialog.MessageFromObject &&
356 im.dialog != (byte)InstantMessageDialog.MessageFromAgent &&
357 im.dialog != (byte)InstantMessageDialog.GroupNotice &&
358 im.dialog != (byte)InstantMessageDialog.GroupInvitation &&
359 im.dialog != (byte)InstantMessageDialog.InventoryOffered)
360 {
361 return false;
362 }
363
364 if (!m_ForwardOfflineGroupMessages)
365 {
366 if (im.dialog == (byte)InstantMessageDialog.GroupNotice ||
367 im.dialog == (byte)InstantMessageDialog.GroupInvitation)
368 return false;
369 }
370
371// m_log.DebugFormat("[HG IM SERVICE]: Message saved");
372 string reason = string.Empty;
373 return m_OfflineIMService.StoreMessage(im, out reason);
374 }
375 }
376} \ No newline at end of file