opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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 url = m_UserAgentService.LocateUser(toAgentID);
219 }
220  
221 // check if we've tried this before..
222 // This is one way to end the recursive loop
223 //
224 if (!firstTime && ((previousLocation is PresenceInfo && upd != null && upd.RegionID == ((PresenceInfo)previousLocation).RegionID) ||
225 (previousLocation is string && upd == null && previousLocation.Equals(url))))
226 {
227 // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message");
228 m_log.DebugFormat("[HG IM SERVICE]: Fail 2 {0} {1}", previousLocation, url);
229  
230 return false;
231 }
232 }
233  
234 if (upd != null)
235 {
236 // ok, the user is around somewhere. Let's send back the reply with "success"
237 // even though the IM may still fail. Just don't keep the caller waiting for
238 // the entire time we're trying to deliver the IM
239 return SendIMToRegion(upd, im, toAgentID, foreigner);
240 }
241 else if (url != string.Empty)
242 {
243 // ok, the user is around somewhere. Let's send back the reply with "success"
244 // even though the IM may still fail. Just don't keep the caller waiting for
245 // the entire time we're trying to deliver the IM
246 return ForwardIMToGrid(url, im, toAgentID, foreigner);
247 }
248 else if (firstTime && previousLocation is string && (string)previousLocation != string.Empty)
249 {
250 return ForwardIMToGrid((string)previousLocation, im, toAgentID, foreigner);
251 }
252 else
253 m_log.DebugFormat("[HG IM SERVICE]: Unable to locate user {0}", toAgentID);
254 return false;
255 }
256  
257 bool SendIMToRegion(PresenceInfo upd, GridInstantMessage im, UUID toAgentID, bool foreigner)
258 {
259 bool imresult = false;
260 GridRegion reginfo = null;
261 if (!m_RegionCache.TryGetValue(upd.RegionID, out reginfo))
262 {
263 reginfo = m_GridService.GetRegionByUUID(UUID.Zero /*!!!*/, upd.RegionID);
264 if (reginfo != null)
265 m_RegionCache.AddOrUpdate(upd.RegionID, reginfo, CACHE_EXPIRATION_SECONDS);
266 }
267  
268 if (reginfo != null)
269 {
270 imresult = InstantMessageServiceConnector.SendInstantMessage(reginfo.ServerURI, im);
271 }
272 else
273 {
274 m_log.DebugFormat("[HG IM SERVICE]: Failed to deliver message to {0}", reginfo.ServerURI);
275 return false;
276 }
277  
278 if (imresult)
279 {
280 // IM delivery successful, so store the Agent's location in our local cache.
281 lock (m_UserLocationMap)
282 {
283 if (m_UserLocationMap.ContainsKey(toAgentID))
284 {
285 m_UserLocationMap[toAgentID] = upd;
286 }
287 else
288 {
289 m_UserLocationMap.Add(toAgentID, upd);
290 }
291 }
292 return true;
293 }
294 else
295 {
296 // try again, but lookup user this time.
297 // Warning, this must call the Async version
298 // of this method or we'll be making thousands of threads
299 // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync
300 // The version that spawns the thread is SendGridInstantMessageViaXMLRPC
301  
302 // This is recursive!!!!!
303 return TrySendInstantMessage(im, upd, false, foreigner);
304 }
305 }
306  
307 bool ForwardIMToGrid(string url, GridInstantMessage im, UUID toAgentID, bool foreigner)
308 {
309 if (InstantMessageServiceConnector.SendInstantMessage(url, im))
310 {
311 // IM delivery successful, so store the Agent's location in our local cache.
312 lock (m_UserLocationMap)
313 {
314 if (m_UserLocationMap.ContainsKey(toAgentID))
315 {
316 m_UserLocationMap[toAgentID] = url;
317 }
318 else
319 {
320 m_UserLocationMap.Add(toAgentID, url);
321 }
322 }
323  
324 return true;
325 }
326 else
327 {
328 // try again, but lookup user this time.
329  
330 // This is recursive!!!!!
331 return TrySendInstantMessage(im, url, false, foreigner);
332 }
333 }
334  
335 private bool UndeliveredMessage(GridInstantMessage im)
336 {
337 if (m_OfflineIMService == null)
338 return false;
339  
340 if (im.dialog != (byte)InstantMessageDialog.MessageFromObject &&
341 im.dialog != (byte)InstantMessageDialog.MessageFromAgent &&
342 im.dialog != (byte)InstantMessageDialog.GroupNotice &&
343 im.dialog != (byte)InstantMessageDialog.GroupInvitation &&
344 im.dialog != (byte)InstantMessageDialog.InventoryOffered)
345 {
346 return false;
347 }
348  
349 if (!m_ForwardOfflineGroupMessages)
350 {
351 if (im.dialog == (byte)InstantMessageDialog.GroupNotice ||
352 im.dialog == (byte)InstantMessageDialog.GroupInvitation)
353 return false;
354 }
355  
356 // m_log.DebugFormat("[HG IM SERVICE]: Message saved");
357 string reason = string.Empty;
358 return m_OfflineIMService.StoreMessage(im, out reason);
359 }
360 }
361 }