OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
HGInstantMessageService.cs
Go to the documentation of this file.
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;
39 using OpenSim.Server.Base;
41 
42 using OpenMetaverse;
43 using log4net;
44 using Nini.Config;
45 
46 namespace OpenSim.Services.HypergridService
47 {
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;
65 
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 
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  {
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 }
bool OutgoingInstantMessage(GridInstantMessage im, string url, bool foreigner)
delegate void UndeliveredMessage(GridInstantMessage im)
static bool SendInstantMessage(string url, GridInstantMessage im)
This actually does the XMLRPC Request
HGInstantMessageService(IConfigSource config, IInstantMessageSimConnector imConnector)
OpenSim.Services.Interfaces.GridRegion GridRegion
bool TrySendInstantMessage(GridInstantMessage im, object previousLocation, bool firstTime, bool foreigner)
OpenSim.Services.Interfaces.FriendInfo FriendInfo
OpenSim.Services.Interfaces.PresenceInfo PresenceInfo