clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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  
28 using System;
29 using System.Collections.Generic;
30 using System.Net;
31 using System.Reflection;
32  
33 using OpenSim.Framework;
34 using OpenSim.Services.Connectors.Friends;
35 using OpenSim.Services.Connectors.Hypergrid;
36 using OpenSim.Services.Interfaces;
37 using OpenSim.Services.Connectors.InstantMessage;
38 using GridRegion = OpenSim.Services.Interfaces.GridRegion;
39 using OpenSim.Server.Base;
40 using FriendInfo = OpenSim.Services.Interfaces.FriendInfo;
41  
42 using OpenMetaverse;
43 using log4net;
44 using Nini.Config;
45  
46 namespace 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 m_UserAgentService = ServerUtils.LoadPlugin<IUserAgentService>(userAgentService, args);
105  
106 m_RegionCache = new ExpiringCache<UUID, GridRegion>();
107  
108 IConfig cnf = config.Configs["Messaging"];
109 if (cnf == null)
110 {
111 return;
112 }
113  
114 m_ForwardOfflineGroupMessages = cnf.GetBoolean("ForwardOfflineGroupMessages", false);
115  
116 if (m_InGatekeeper)
117 {
118 string offlineIMService = cnf.GetString("OfflineIMService", string.Empty);
119 if (offlineIMService != string.Empty)
120 m_OfflineIMService = ServerUtils.LoadPlugin<IOfflineIMService>(offlineIMService, args);
121 }
122 }
123 }
124  
125 public bool IncomingInstantMessage(GridInstantMessage im)
126 {
127 // m_log.DebugFormat("[HG IM SERVICE]: Received message from {0} to {1}", im.fromAgentID, im.toAgentID);
128 // UUID toAgentID = new UUID(im.toAgentID);
129  
130 bool success = false;
131 if (m_IMSimConnector != null)
132 {
133 //m_log.DebugFormat("[XXX] SendIMToRegion local im connector");
134 success = m_IMSimConnector.SendInstantMessage(im);
135 }
136 else
137 {
138 success = TrySendInstantMessage(im, "", true, false);
139 }
140  
141 if (!success && m_InGatekeeper) // we do this only in the Gatekeeper IM service
142 UndeliveredMessage(im);
143  
144 return success;
145 }
146  
147 public bool OutgoingInstantMessage(GridInstantMessage im, string url, bool foreigner)
148 {
149 // m_log.DebugFormat("[HG IM SERVICE]: Sending message from {0} to {1}@{2}", im.fromAgentID, im.toAgentID, url);
150 if (url != string.Empty)
151 return TrySendInstantMessage(im, url, true, foreigner);
152 else
153 {
154 PresenceInfo upd = new PresenceInfo();
155 upd.RegionID = UUID.Zero;
156 return TrySendInstantMessage(im, upd, true, foreigner);
157 }
158  
159 }
160  
161 protected bool TrySendInstantMessage(GridInstantMessage im, object previousLocation, bool firstTime, bool foreigner)
162 {
163 UUID toAgentID = new UUID(im.toAgentID);
164  
165 PresenceInfo upd = null;
166 string url = string.Empty;
167  
168 bool lookupAgent = false;
169  
170 lock (m_UserLocationMap)
171 {
172 if (m_UserLocationMap.ContainsKey(toAgentID))
173 {
174 object o = m_UserLocationMap[toAgentID];
175 if (o is PresenceInfo)
176 upd = (PresenceInfo)o;
177 else if (o is string)
178 url = (string)o;
179  
180 // We need to compare the current location with the previous
181 // or the recursive loop will never end because it will never try to lookup the agent again
182 if (!firstTime)
183 {
184 lookupAgent = true;
185 upd = null;
186 }
187 }
188 else
189 {
190 lookupAgent = true;
191 }
192 }
193  
194 //m_log.DebugFormat("[XXX] Neeed lookup ? {0}", (lookupAgent ? "yes" : "no"));
195  
196 // Are we needing to look-up an agent?
197 if (lookupAgent)
198 {
199 // Non-cached user agent lookup.
200 PresenceInfo[] presences = m_PresenceService.GetAgents(new string[] { toAgentID.ToString() });
201 if (presences != null && presences.Length > 0)
202 {
203 foreach (PresenceInfo p in presences)
204 {
205 if (p.RegionID != UUID.Zero)
206 {
207 //m_log.DebugFormat("[XXX]: Found presence in {0}", p.RegionID);
208 upd = p;
209 break;
210 }
211 }
212 }
213  
214 if (upd == null && !foreigner)
215 {
216 // Let's check with the UAS if the user is elsewhere
217 m_log.DebugFormat("[HG IM SERVICE]: User is not present. Checking location with User Agent service");
218 try
219 {
220 url = m_UserAgentService.LocateUser(toAgentID);
221 }
222 catch (Exception e)
223 {
224 m_log.Warn("[HG IM SERVICE]: LocateUser call failed ", e);
225 url = string.Empty;
226 }
227 }
228  
229 // check if we've tried this before..
230 // This is one way to end the recursive loop
231 //
232 if (!firstTime && ((previousLocation is PresenceInfo && upd != null && upd.RegionID == ((PresenceInfo)previousLocation).RegionID) ||
233 (previousLocation is string && upd == null && previousLocation.Equals(url))))
234 {
235 // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message");
236 m_log.DebugFormat("[HG IM SERVICE]: Fail 2 {0} {1}", previousLocation, url);
237  
238 return false;
239 }
240 }
241  
242 if (upd != null)
243 {
244 // ok, the user is around somewhere. Let's send back the reply with "success"
245 // even though the IM may still fail. Just don't keep the caller waiting for
246 // the entire time we're trying to deliver the IM
247 return SendIMToRegion(upd, im, toAgentID, foreigner);
248 }
249 else if (url != string.Empty)
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 ForwardIMToGrid(url, im, toAgentID, foreigner);
255 }
256 else if (firstTime && previousLocation is string && (string)previousLocation != string.Empty)
257 {
258 return ForwardIMToGrid((string)previousLocation, im, toAgentID, foreigner);
259 }
260 else
261 m_log.DebugFormat("[HG IM SERVICE]: Unable to locate user {0}", toAgentID);
262 return false;
263 }
264  
265 bool SendIMToRegion(PresenceInfo upd, GridInstantMessage im, UUID toAgentID, bool foreigner)
266 {
267 bool imresult = false;
268 GridRegion reginfo = null;
269 if (!m_RegionCache.TryGetValue(upd.RegionID, out reginfo))
270 {
271 reginfo = m_GridService.GetRegionByUUID(UUID.Zero /*!!!*/, upd.RegionID);
272 if (reginfo != null)
273 m_RegionCache.AddOrUpdate(upd.RegionID, reginfo, CACHE_EXPIRATION_SECONDS);
274 }
275  
276 if (reginfo != null)
277 {
278 imresult = InstantMessageServiceConnector.SendInstantMessage(reginfo.ServerURI, im);
279 }
280 else
281 {
282 m_log.DebugFormat("[HG IM SERVICE]: Failed to deliver message to {0}", reginfo.ServerURI);
283 return false;
284 }
285  
286 if (imresult)
287 {
288 // IM delivery successful, so store the Agent's location in our local cache.
289 lock (m_UserLocationMap)
290 {
291 if (m_UserLocationMap.ContainsKey(toAgentID))
292 {
293 m_UserLocationMap[toAgentID] = upd;
294 }
295 else
296 {
297 m_UserLocationMap.Add(toAgentID, upd);
298 }
299 }
300 return true;
301 }
302 else
303 {
304 // try again, but lookup user this time.
305 // Warning, this must call the Async version
306 // of this method or we'll be making thousands of threads
307 // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync
308 // The version that spawns the thread is SendGridInstantMessageViaXMLRPC
309  
310 // This is recursive!!!!!
311 return TrySendInstantMessage(im, upd, false, foreigner);
312 }
313 }
314  
315 bool ForwardIMToGrid(string url, GridInstantMessage im, UUID toAgentID, bool foreigner)
316 {
317 if (InstantMessageServiceConnector.SendInstantMessage(url, im))
318 {
319 // IM delivery successful, so store the Agent's location in our local cache.
320 lock (m_UserLocationMap)
321 {
322 if (m_UserLocationMap.ContainsKey(toAgentID))
323 {
324 m_UserLocationMap[toAgentID] = url;
325 }
326 else
327 {
328 m_UserLocationMap.Add(toAgentID, url);
329 }
330 }
331  
332 return true;
333 }
334 else
335 {
336 // try again, but lookup user this time.
337  
338 // This is recursive!!!!!
339 return TrySendInstantMessage(im, url, false, foreigner);
340 }
341 }
342  
343 private bool UndeliveredMessage(GridInstantMessage im)
344 {
345 if (m_OfflineIMService == null)
346 return false;
347  
348 if (im.dialog != (byte)InstantMessageDialog.MessageFromObject &&
349 im.dialog != (byte)InstantMessageDialog.MessageFromAgent &&
350 im.dialog != (byte)InstantMessageDialog.GroupNotice &&
351 im.dialog != (byte)InstantMessageDialog.GroupInvitation &&
352 im.dialog != (byte)InstantMessageDialog.InventoryOffered)
353 {
354 return false;
355 }
356  
357 if (!m_ForwardOfflineGroupMessages)
358 {
359 if (im.dialog == (byte)InstantMessageDialog.GroupNotice ||
360 im.dialog == (byte)InstantMessageDialog.GroupInvitation)
361 return false;
362 }
363  
364 // m_log.DebugFormat("[HG IM SERVICE]: Message saved");
365 string reason = string.Empty;
366 return m_OfflineIMService.StoreMessage(im, out reason);
367 }
368 }
369 }