aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Services/Connectors/SimianGrid
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Services/Connectors/SimianGrid')
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs410
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs201
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianAvatarServiceConnector.cs262
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs232
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianGrid.cs47
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs421
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianInventoryServiceConnector.cs895
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs511
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs435
-rw-r--r--OpenSim/Services/Connectors/SimianGrid/SimianUserAccountServiceConnector.cs311
10 files changed, 3725 insertions, 0 deletions
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs
new file mode 100644
index 0000000..1c22a72
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs
@@ -0,0 +1,410 @@
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.IO;
31using System.Net;
32using System.Reflection;
33using log4net;
34using Mono.Addins;
35using Nini.Config;
36using OpenSim.Framework;
37using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Framework.Scenes;
39using OpenSim.Services.Interfaces;
40using OpenMetaverse;
41using OpenMetaverse.StructuredData;
42
43namespace OpenSim.Services.Connectors.SimianGrid
44{
45 /// <summary>
46 /// Connects to the SimianGrid asset service
47 /// </summary>
48 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
49 public class SimianAssetServiceConnector : IAssetService, ISharedRegionModule
50 {
51 private static readonly ILog m_log =
52 LogManager.GetLogger(
53 MethodBase.GetCurrentMethod().DeclaringType);
54 private static string ZeroID = UUID.Zero.ToString();
55
56 private string m_serverUrl = String.Empty;
57 private IImprovedAssetCache m_cache;
58
59 #region ISharedRegionModule
60
61 public Type ReplaceableInterface { get { return null; } }
62 public void RegionLoaded(Scene scene)
63 {
64 if (m_cache == null)
65 {
66 IImprovedAssetCache cache = scene.RequestModuleInterface<IImprovedAssetCache>();
67 if (cache is ISharedRegionModule)
68 m_cache = cache;
69 }
70 }
71 public void PostInitialise() { }
72 public void Close() { }
73
74 public SimianAssetServiceConnector() { }
75 public string Name { get { return "SimianAssetServiceConnector"; } }
76 public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface<IAssetService>(this); } }
77 public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface<IAssetService>(this); } }
78
79 #endregion ISharedRegionModule
80
81 public SimianAssetServiceConnector(IConfigSource source)
82 {
83 Initialise(source);
84 }
85
86 public void Initialise(IConfigSource source)
87 {
88 if (Simian.IsSimianEnabled(source, "AssetServices"))
89 {
90 IConfig gridConfig = source.Configs["AssetService"];
91 if (gridConfig == null)
92 {
93 m_log.Error("[ASSET CONNECTOR]: AssetService missing from OpenSim.ini");
94 throw new Exception("Asset connector init error");
95 }
96
97 string serviceUrl = gridConfig.GetString("AssetServerURI");
98 if (String.IsNullOrEmpty(serviceUrl))
99 {
100 m_log.Error("[ASSET CONNECTOR]: No AssetServerURI in section AssetService");
101 throw new Exception("Asset connector init error");
102 }
103
104 if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("="))
105 serviceUrl = serviceUrl + '/';
106
107 m_serverUrl = serviceUrl;
108 }
109 }
110
111 #region IAssetService
112
113 public AssetBase Get(string id)
114 {
115 AssetBase asset = null;
116
117 // Cache fetch
118 if (m_cache != null)
119 {
120 asset = m_cache.Get(id);
121 if (asset != null)
122 return asset;
123 }
124
125 Uri url;
126
127 // Determine if id is an absolute URL or a grid-relative UUID
128 if (!Uri.TryCreate(id, UriKind.Absolute, out url))
129 url = new Uri(m_serverUrl + id);
130
131 try
132 {
133 HttpWebRequest request = UntrustedHttpWebRequest.Create(url);
134
135 using (WebResponse response = request.GetResponse())
136 {
137 using (Stream responseStream = response.GetResponseStream())
138 {
139 string creatorID = response.Headers.GetOne("X-Asset-Creator-Id") ?? String.Empty;
140
141 // Create the asset object
142 asset = new AssetBase(id, String.Empty, SLUtil.ContentTypeToSLAssetType(response.ContentType), creatorID);
143
144 UUID assetID;
145 if (UUID.TryParse(id, out assetID))
146 asset.FullID = assetID;
147
148 // Grab the asset data from the response stream
149 using (MemoryStream stream = new MemoryStream())
150 {
151 responseStream.CopyTo(stream, Int32.MaxValue);
152 asset.Data = stream.ToArray();
153 }
154 }
155 }
156
157 // Cache store
158 if (m_cache != null && asset != null)
159 m_cache.Cache(asset);
160
161 return asset;
162 }
163 catch (Exception ex)
164 {
165 m_log.Warn("[ASSET CONNECTOR]: Asset GET from " + url + " failed: " + ex.Message);
166 return null;
167 }
168 }
169
170 /// <summary>
171 /// Get an asset's metadata
172 /// </summary>
173 /// <param name="id"></param>
174 /// <returns></returns>
175 public AssetMetadata GetMetadata(string id)
176 {
177 AssetMetadata metadata = null;
178
179 // Cache fetch
180 if (m_cache != null)
181 {
182 AssetBase asset = m_cache.Get(id);
183 if (asset != null)
184 return asset.Metadata;
185 }
186
187 Uri url;
188
189 // Determine if id is an absolute URL or a grid-relative UUID
190 if (!Uri.TryCreate(id, UriKind.Absolute, out url))
191 url = new Uri(m_serverUrl + id);
192
193 try
194 {
195 HttpWebRequest request = UntrustedHttpWebRequest.Create(url);
196 request.Method = "HEAD";
197
198 using (WebResponse response = request.GetResponse())
199 {
200 using (Stream responseStream = response.GetResponseStream())
201 {
202 // Create the metadata object
203 metadata = new AssetMetadata();
204 metadata.ContentType = response.ContentType;
205 metadata.ID = id;
206
207 UUID uuid;
208 if (UUID.TryParse(id, out uuid))
209 metadata.FullID = uuid;
210
211 string lastModifiedStr = response.Headers.Get("Last-Modified");
212 if (!String.IsNullOrEmpty(lastModifiedStr))
213 {
214 DateTime lastModified;
215 if (DateTime.TryParse(lastModifiedStr, out lastModified))
216 metadata.CreationDate = lastModified;
217 }
218 }
219 }
220 }
221 catch (Exception ex)
222 {
223 m_log.Warn("[ASSET CONNECTOR]: Asset GET from " + url + " failed: " + ex.Message);
224 }
225
226 return metadata;
227 }
228
229 public byte[] GetData(string id)
230 {
231 AssetBase asset = Get(id);
232
233 if (asset != null)
234 return asset.Data;
235
236 return null;
237 }
238
239 /// <summary>
240 /// Get an asset asynchronously
241 /// </summary>
242 /// <param name="id">The asset id</param>
243 /// <param name="sender">Represents the requester. Passed back via the handler</param>
244 /// <param name="handler">The handler to call back once the asset has been retrieved</param>
245 /// <returns>True if the id was parseable, false otherwise</returns>
246 public bool Get(string id, Object sender, AssetRetrieved handler)
247 {
248 Util.FireAndForget(
249 delegate(object o)
250 {
251 AssetBase asset = Get(id);
252 handler(id, sender, asset);
253 }
254 );
255
256 return true;
257 }
258
259 /// <summary>
260 /// Creates a new asset
261 /// </summary>
262 /// Returns a random ID if none is passed into it
263 /// <param name="asset"></param>
264 /// <returns></returns>
265 public string Store(AssetBase asset)
266 {
267 bool storedInCache = false;
268 string errorMessage = null;
269
270 // AssetID handling
271 if (String.IsNullOrEmpty(asset.ID) || asset.ID == ZeroID)
272 {
273 asset.FullID = UUID.Random();
274 asset.ID = asset.FullID.ToString();
275 }
276
277 // Cache handling
278 if (m_cache != null)
279 {
280 m_cache.Cache(asset);
281 storedInCache = true;
282 }
283
284 // Local asset handling
285 if (asset.Local)
286 {
287 if (!storedInCache)
288 {
289 m_log.Error("Cannot store local " + asset.Metadata.ContentType + " asset without an asset cache");
290 asset.ID = null;
291 asset.FullID = UUID.Zero;
292 }
293
294 return asset.ID;
295 }
296
297 // Distinguish public and private assets
298 bool isPublic = true;
299 switch ((AssetType)asset.Type)
300 {
301 case AssetType.CallingCard:
302 case AssetType.Gesture:
303 case AssetType.LSLBytecode:
304 case AssetType.LSLText:
305 isPublic = false;
306 break;
307 }
308
309 // Make sure ContentType is set
310 if (String.IsNullOrEmpty(asset.Metadata.ContentType))
311 asset.Metadata.ContentType = SLUtil.SLAssetTypeToContentType(asset.Type);
312
313 // Build the remote storage request
314 List<MultipartForm.Element> postParameters = new List<MultipartForm.Element>()
315 {
316 new MultipartForm.Parameter("AssetID", asset.FullID.ToString()),
317 new MultipartForm.Parameter("CreatorID", asset.Metadata.CreatorID),
318 new MultipartForm.Parameter("Temporary", asset.Temporary ? "1" : "0"),
319 new MultipartForm.Parameter("Public", isPublic ? "1" : "0"),
320 new MultipartForm.File("Asset", asset.Name, asset.Metadata.ContentType, asset.Data)
321 };
322
323 // Make the remote storage request
324 try
325 {
326 HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(m_serverUrl);
327
328 HttpWebResponse response = MultipartForm.Post(request, postParameters);
329 using (Stream responseStream = response.GetResponseStream())
330 {
331 try
332 {
333 string responseStr = responseStream.GetStreamString();
334 OSD responseOSD = OSDParser.Deserialize(responseStr);
335 if (responseOSD.Type == OSDType.Map)
336 {
337 OSDMap responseMap = (OSDMap)responseOSD;
338 if (responseMap["Success"].AsBoolean())
339 return asset.ID;
340 else
341 errorMessage = "Upload failed: " + responseMap["Message"].AsString();
342 }
343 else
344 {
345 errorMessage = "Response format was invalid.";
346 }
347 }
348 catch
349 {
350 errorMessage = "Failed to parse the response.";
351 }
352 }
353 }
354 catch (WebException ex)
355 {
356 errorMessage = ex.Message;
357 }
358
359 m_log.WarnFormat("[ASSET CONNECTOR]: Failed to store asset \"{0}\" ({1}, {2}): {3}",
360 asset.Name, asset.ID, asset.Metadata.ContentType, errorMessage);
361 return null;
362 }
363
364 /// <summary>
365 /// Update an asset's content
366 /// </summary>
367 /// Attachments and bare scripts need this!!
368 /// <param name="id"> </param>
369 /// <param name="data"></param>
370 /// <returns></returns>
371 public bool UpdateContent(string id, byte[] data)
372 {
373 AssetBase asset = Get(id);
374
375 if (asset == null)
376 {
377 m_log.Warn("[ASSET CONNECTOR]: Failed to fetch asset " + id + " for updating");
378 return false;
379 }
380
381 asset.Data = data;
382
383 string result = Store(asset);
384 return !String.IsNullOrEmpty(result);
385 }
386
387 /// <summary>
388 /// Delete an asset
389 /// </summary>
390 /// <param name="id"></param>
391 /// <returns></returns>
392 public bool Delete(string id)
393 {
394 if (m_cache != null)
395 m_cache.Expire(id);
396
397 string url = m_serverUrl + id;
398
399 OSDMap response = WebUtil.ServiceRequest(url, "DELETE");
400 if (response["Success"].AsBoolean())
401 return true;
402 else
403 m_log.Warn("[ASSET CONNECTOR]: Failed to delete asset " + id + " from the asset service");
404
405 return false;
406 }
407
408 #endregion IAssetService
409 }
410}
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs
new file mode 100644
index 0000000..6317b87
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs
@@ -0,0 +1,201 @@
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.Specialized;
30using System.Reflection;
31using log4net;
32using Mono.Addins;
33using Nini.Config;
34using OpenMetaverse;
35using OpenMetaverse.StructuredData;
36using OpenSim.Framework;
37using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Framework.Scenes;
39using OpenSim.Services.Interfaces;
40
41namespace OpenSim.Services.Connectors.SimianGrid
42{
43 /// <summary>
44 /// Connects authentication/authorization to the SimianGrid backend
45 /// </summary>
46 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
47 public class SimianAuthenticationServiceConnector : IAuthenticationService, ISharedRegionModule
48 {
49 private static readonly ILog m_log =
50 LogManager.GetLogger(
51 MethodBase.GetCurrentMethod().DeclaringType);
52
53 private string m_serverUrl = String.Empty;
54
55 #region ISharedRegionModule
56
57 public Type ReplaceableInterface { get { return null; } }
58 public void RegionLoaded(Scene scene) { }
59 public void PostInitialise() { }
60 public void Close() { }
61
62 public SimianAuthenticationServiceConnector() { }
63 public string Name { get { return "SimianAuthenticationServiceConnector"; } }
64 public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface<IAuthenticationService>(this); } }
65 public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface<IAuthenticationService>(this); } }
66
67 #endregion ISharedRegionModule
68
69 public SimianAuthenticationServiceConnector(IConfigSource source)
70 {
71 Initialise(source);
72 }
73
74 public void Initialise(IConfigSource source)
75 {
76 if (Simian.IsSimianEnabled(source, "AuthenticationServices"))
77 {
78 IConfig assetConfig = source.Configs["AuthenticationService"];
79 if (assetConfig == null)
80 {
81 m_log.Error("[AUTH CONNECTOR]: AuthenticationService missing from OpenSim.ini");
82 throw new Exception("Authentication connector init error");
83 }
84
85 string serviceURI = assetConfig.GetString("AuthenticationServerURI");
86 if (String.IsNullOrEmpty(serviceURI))
87 {
88 m_log.Error("[AUTH CONNECTOR]: No Server URI named in section AuthenticationService");
89 throw new Exception("Authentication connector init error");
90 }
91
92 m_serverUrl = serviceURI;
93 }
94 }
95
96 public string Authenticate(UUID principalID, string password, int lifetime)
97 {
98 NameValueCollection requestArgs = new NameValueCollection
99 {
100 { "RequestMethod", "GetIdentities" },
101 { "UserID", principalID.ToString() }
102 };
103
104 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
105 if (response["Success"].AsBoolean() && response["Identities"] is OSDArray)
106 {
107 OSDArray identities = (OSDArray)response["Identities"];
108 for (int i = 0; i < identities.Count; i++)
109 {
110 OSDMap identity = identities[i] as OSDMap;
111 if (identity != null)
112 {
113 if (identity["Type"].AsString() == "md5hash")
114 {
115 string credential = identity["Credential"].AsString();
116
117 if (password == credential || Utils.MD5String(password) == credential)
118 return Authorize(principalID);
119 }
120 }
121 }
122
123 m_log.Warn("[AUTH CONNECTOR]: Authentication failed for " + principalID);
124 }
125 else
126 {
127 m_log.Warn("[AUTH CONNECTOR]: Failed to retrieve identities for " + principalID + ": " +
128 response["Message"].AsString());
129 }
130
131 return String.Empty;
132 }
133
134 public bool Verify(UUID principalID, string token, int lifetime)
135 {
136 NameValueCollection requestArgs = new NameValueCollection
137 {
138 { "RequestMethod", "GetSession" },
139 { "SessionID", token }
140 };
141
142 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
143 if (response["Success"].AsBoolean())
144 {
145 return true;
146 }
147 else
148 {
149 m_log.Warn("[AUTH CONNECTOR]: Could not verify session for " + principalID + ": " +
150 response["Message"].AsString());
151 }
152
153 return false;
154 }
155
156 public bool Release(UUID principalID, string token)
157 {
158 NameValueCollection requestArgs = new NameValueCollection
159 {
160 { "RequestMethod", "RemoveSession" },
161 { "UserID", principalID.ToString() }
162 };
163
164 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
165 if (response["Success"].AsBoolean())
166 {
167 return true;
168 }
169 else
170 {
171 m_log.Warn("[AUTH CONNECTOR]: Failed to remove session for " + principalID + ": " +
172 response["Message"].AsString());
173 }
174
175 return false;
176 }
177
178 public bool SetPassword(UUID principalID, string passwd)
179 {
180 // TODO: Use GetIdentities to find the md5hash identity for principalID
181 // and then update it with AddIdentity
182 m_log.Error("[AUTH CONNECTOR]: Changing passwords is not implemented yet");
183 return false;
184 }
185
186 private string Authorize(UUID userID)
187 {
188 NameValueCollection requestArgs = new NameValueCollection
189 {
190 { "RequestMethod", "AddSession" },
191 { "UserID", userID.ToString() }
192 };
193
194 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
195 if (response["Success"].AsBoolean())
196 return response["SessionID"].AsUUID().ToString();
197 else
198 return String.Empty;
199 }
200 }
201}
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAvatarServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAvatarServiceConnector.cs
new file mode 100644
index 0000000..a18cb22
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianAvatarServiceConnector.cs
@@ -0,0 +1,262 @@
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.Collections.Specialized;
31using System.IO;
32using System.Net;
33using System.Reflection;
34using log4net;
35using Mono.Addins;
36using Nini.Config;
37using OpenSim.Framework;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Server.Base;
41using OpenSim.Services.Interfaces;
42using OpenMetaverse;
43using OpenMetaverse.StructuredData;
44
45namespace OpenSim.Services.Connectors.SimianGrid
46{
47 /// <summary>
48 /// Connects avatar appearance data to the SimianGrid backend
49 /// </summary>
50 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
51 public class SimianAvatarServiceConnector : IAvatarService, ISharedRegionModule
52 {
53 private static readonly ILog m_log =
54 LogManager.GetLogger(
55 MethodBase.GetCurrentMethod().DeclaringType);
56 private static string ZeroID = UUID.Zero.ToString();
57
58 private string m_serverUrl = String.Empty;
59
60 #region ISharedRegionModule
61
62 public Type ReplaceableInterface { get { return null; } }
63 public void RegionLoaded(Scene scene) { }
64 public void PostInitialise() { }
65 public void Close() { }
66
67 public SimianAvatarServiceConnector() { }
68 public string Name { get { return "SimianAvatarServiceConnector"; } }
69 public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface<IAvatarService>(this); } }
70 public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface<IAvatarService>(this); } }
71
72 #endregion ISharedRegionModule
73
74 public SimianAvatarServiceConnector(IConfigSource source)
75 {
76 Initialise(source);
77 }
78
79 public void Initialise(IConfigSource source)
80 {
81 if (Simian.IsSimianEnabled(source, "AvatarServices"))
82 {
83 IConfig gridConfig = source.Configs["AvatarService"];
84 if (gridConfig == null)
85 {
86 m_log.Error("[AVATAR CONNECTOR]: AvatarService missing from OpenSim.ini");
87 throw new Exception("Avatar connector init error");
88 }
89
90 string serviceUrl = gridConfig.GetString("AvatarServerURI");
91 if (String.IsNullOrEmpty(serviceUrl))
92 {
93 m_log.Error("[AVATAR CONNECTOR]: No AvatarServerURI in section AvatarService");
94 throw new Exception("Avatar connector init error");
95 }
96
97 if (!serviceUrl.EndsWith("/"))
98 serviceUrl = serviceUrl + '/';
99
100 m_serverUrl = serviceUrl;
101 }
102 }
103
104 #region IAvatarService
105
106 public AvatarData GetAvatar(UUID userID)
107 {
108 NameValueCollection requestArgs = new NameValueCollection
109 {
110 { "RequestMethod", "GetUser" },
111 { "UserID", userID.ToString() }
112 };
113
114 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
115 if (response["Success"].AsBoolean())
116 {
117 OSDMap map = null;
118 try { map = OSDParser.DeserializeJson(response["LLAppearance"].AsString()) as OSDMap; }
119 catch { }
120
121 if (map != null)
122 {
123 AvatarWearable[] wearables = new AvatarWearable[13];
124 wearables[0] = new AvatarWearable(map["ShapeItem"].AsUUID(), map["ShapeAsset"].AsUUID());
125 wearables[1] = new AvatarWearable(map["SkinItem"].AsUUID(), map["SkinAsset"].AsUUID());
126 wearables[2] = new AvatarWearable(map["HairItem"].AsUUID(), map["HairAsset"].AsUUID());
127 wearables[3] = new AvatarWearable(map["EyesItem"].AsUUID(), map["EyesAsset"].AsUUID());
128 wearables[4] = new AvatarWearable(map["ShirtItem"].AsUUID(), map["ShirtAsset"].AsUUID());
129 wearables[5] = new AvatarWearable(map["PantsItem"].AsUUID(), map["PantsAsset"].AsUUID());
130 wearables[6] = new AvatarWearable(map["ShoesItem"].AsUUID(), map["ShoesAsset"].AsUUID());
131 wearables[7] = new AvatarWearable(map["SocksItem"].AsUUID(), map["SocksAsset"].AsUUID());
132 wearables[8] = new AvatarWearable(map["JacketItem"].AsUUID(), map["JacketAsset"].AsUUID());
133 wearables[9] = new AvatarWearable(map["GlovesItem"].AsUUID(), map["GlovesAsset"].AsUUID());
134 wearables[10] = new AvatarWearable(map["UndershirtItem"].AsUUID(), map["UndershirtAsset"].AsUUID());
135 wearables[11] = new AvatarWearable(map["UnderpantsItem"].AsUUID(), map["UnderpantsAsset"].AsUUID());
136 wearables[12] = new AvatarWearable(map["SkirtItem"].AsUUID(), map["SkirtAsset"].AsUUID());
137
138 AvatarAppearance appearance = new AvatarAppearance(userID);
139 appearance.Wearables = wearables;
140 appearance.AvatarHeight = (float)map["Height"].AsReal();
141
142 AvatarData avatar = new AvatarData(appearance);
143
144 // Get attachments
145 map = null;
146 try { map = OSDParser.DeserializeJson(response["LLAttachments"].AsString()) as OSDMap; }
147 catch { }
148
149 if (map != null)
150 {
151 foreach (KeyValuePair<string, OSD> kvp in map)
152 avatar.Data[kvp.Key] = kvp.Value.AsString();
153 }
154
155 return avatar;
156 }
157 else
158 {
159 m_log.Warn("[AVATAR CONNECTOR]: Failed to get user appearance for " + userID +
160 ", LLAppearance is missing or invalid");
161 return null;
162 }
163 }
164 else
165 {
166 m_log.Warn("[AVATAR CONNECTOR]: Failed to get user appearance for " + userID + ": " +
167 response["Message"].AsString());
168 }
169
170 return null;
171 }
172
173 public bool SetAvatar(UUID userID, AvatarData avatar)
174 {
175 m_log.Debug("[AVATAR CONNECTOR]: SetAvatar called for " + userID);
176
177 if (avatar.AvatarType == 1) // LLAvatar
178 {
179 AvatarAppearance appearance = avatar.ToAvatarAppearance(userID);
180
181 OSDMap map = new OSDMap();
182
183 map["Height"] = OSD.FromReal(appearance.AvatarHeight);
184
185 map["ShapeItem"] = OSD.FromUUID(appearance.BodyItem);
186 map["ShapeAsset"] = OSD.FromUUID(appearance.BodyAsset);
187 map["SkinItem"] = OSD.FromUUID(appearance.SkinItem);
188 map["SkinAsset"] = OSD.FromUUID(appearance.SkinAsset);
189 map["HairItem"] = OSD.FromUUID(appearance.HairItem);
190 map["HairAsset"] = OSD.FromUUID(appearance.HairAsset);
191 map["EyesItem"] = OSD.FromUUID(appearance.EyesItem);
192 map["EyesAsset"] = OSD.FromUUID(appearance.EyesAsset);
193 map["ShirtItem"] = OSD.FromUUID(appearance.ShirtItem);
194 map["ShirtAsset"] = OSD.FromUUID(appearance.ShirtAsset);
195 map["PantsItem"] = OSD.FromUUID(appearance.PantsItem);
196 map["PantsAsset"] = OSD.FromUUID(appearance.PantsAsset);
197 map["ShoesItem"] = OSD.FromUUID(appearance.ShoesItem);
198 map["ShoesAsset"] = OSD.FromUUID(appearance.ShoesAsset);
199 map["SocksItem"] = OSD.FromUUID(appearance.SocksItem);
200 map["SocksAsset"] = OSD.FromUUID(appearance.SocksAsset);
201 map["JacketItem"] = OSD.FromUUID(appearance.JacketItem);
202 map["JacketAsset"] = OSD.FromUUID(appearance.JacketAsset);
203 map["GlovesItem"] = OSD.FromUUID(appearance.GlovesItem);
204 map["GlovesAsset"] = OSD.FromUUID(appearance.GlovesAsset);
205 map["UndershirtItem"] = OSD.FromUUID(appearance.UnderShirtItem);
206 map["UndershirtAsset"] = OSD.FromUUID(appearance.UnderShirtAsset);
207 map["UnderpantsItem"] = OSD.FromUUID(appearance.UnderPantsItem);
208 map["UnderpantsAsset"] = OSD.FromUUID(appearance.UnderPantsAsset);
209 map["SkirtItem"] = OSD.FromUUID(appearance.SkirtItem);
210 map["SkirtAsset"] = OSD.FromUUID(appearance.SkirtAsset);
211
212 OSDMap items = new OSDMap();
213 foreach (KeyValuePair<string, string> kvp in avatar.Data)
214 {
215 if (kvp.Key.StartsWith("_ap_"))
216 items.Add(kvp.Key, OSD.FromString(kvp.Value));
217 }
218
219 NameValueCollection requestArgs = new NameValueCollection
220 {
221 { "RequestMethod", "AddUserData" },
222 { "UserID", userID.ToString() },
223 { "LLAppearance", OSDParser.SerializeJsonString(map) },
224 { "LLAttachments", OSDParser.SerializeJsonString(items) }
225 };
226
227 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
228 bool success = response["Success"].AsBoolean();
229
230 if (!success)
231 m_log.Warn("[AVATAR CONNECTOR]: Failed saving appearance for " + userID + ": " + response["Message"].AsString());
232
233 return success;
234 }
235 else
236 {
237 m_log.Error("[AVATAR CONNECTOR]: Can't save appearance for " + userID + ". Unhandled avatar type " + avatar.AvatarType);
238 return false;
239 }
240 }
241
242 public bool ResetAvatar(UUID userID)
243 {
244 m_log.Error("[AVATAR CONNECTOR]: ResetAvatar called for " + userID + ", implement this");
245 return false;
246 }
247
248 public bool SetItems(UUID userID, string[] names, string[] values)
249 {
250 m_log.Error("[AVATAR CONNECTOR]: SetItems called for " + userID + " with " + names.Length + " names and " + values.Length + " values, implement this");
251 return false;
252 }
253
254 public bool RemoveItems(UUID userID, string[] names)
255 {
256 m_log.Error("[AVATAR CONNECTOR]: RemoveItems called for " + userID + " with " + names.Length + " names, implement this");
257 return false;
258 }
259
260 #endregion IAvatarService
261 }
262}
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs
new file mode 100644
index 0000000..b3ecc7e
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs
@@ -0,0 +1,232 @@
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.Collections.Specialized;
31using System.Reflection;
32using log4net;
33using Mono.Addins;
34using Nini.Config;
35using OpenMetaverse;
36using OpenMetaverse.StructuredData;
37using OpenSim.Framework;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Services.Interfaces;
41
42using FriendInfo = OpenSim.Services.Interfaces.FriendInfo;
43
44namespace OpenSim.Services.Connectors.SimianGrid
45{
46 /// <summary>
47 /// Stores and retrieves friend lists from the SimianGrid backend
48 /// </summary>
49 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
50 public class SimianFriendsServiceConnector : IFriendsService, ISharedRegionModule
51 {
52 private static readonly ILog m_log =
53 LogManager.GetLogger(
54 MethodBase.GetCurrentMethod().DeclaringType);
55
56 private string m_serverUrl = String.Empty;
57
58 #region ISharedRegionModule
59
60 public Type ReplaceableInterface { get { return null; } }
61 public void RegionLoaded(Scene scene) { }
62 public void PostInitialise() { }
63 public void Close() { }
64
65 public SimianFriendsServiceConnector() { }
66 public string Name { get { return "SimianFriendsServiceConnector"; } }
67 public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface<IFriendsService>(this); } }
68 public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface<IFriendsService>(this); } }
69
70 #endregion ISharedRegionModule
71
72 public SimianFriendsServiceConnector(IConfigSource source)
73 {
74 Initialise(source);
75 }
76
77 public void Initialise(IConfigSource source)
78 {
79 if (Simian.IsSimianEnabled(source, "FriendsServices"))
80 {
81 IConfig assetConfig = source.Configs["FriendsService"];
82 if (assetConfig == null)
83 {
84 m_log.Error("[FRIENDS CONNECTOR]: FriendsService missing from OpenSim.ini");
85 throw new Exception("Friends connector init error");
86 }
87
88 string serviceURI = assetConfig.GetString("FriendsServerURI");
89 if (String.IsNullOrEmpty(serviceURI))
90 {
91 m_log.Error("[FRIENDS CONNECTOR]: No Server URI named in section FriendsService");
92 throw new Exception("Friends connector init error");
93 }
94
95 m_serverUrl = serviceURI;
96 }
97 }
98
99 #region IFriendsService
100
101 public FriendInfo[] GetFriends(UUID principalID)
102 {
103 Dictionary<UUID, FriendInfo> friends = new Dictionary<UUID, FriendInfo>();
104
105 OSDArray friendsArray = GetFriended(principalID);
106 OSDArray friendedMeArray = GetFriendedBy(principalID);
107
108 // Load the list of friends and their granted permissions
109 for (int i = 0; i < friendsArray.Count; i++)
110 {
111 OSDMap friendEntry = friendsArray[i] as OSDMap;
112 if (friendEntry != null)
113 {
114 UUID friendID = friendEntry["Key"].AsUUID();
115
116 FriendInfo friend = new FriendInfo();
117 friend.PrincipalID = principalID;
118 friend.Friend = friendID.ToString();
119 friend.MyFlags = friendEntry["Value"].AsInteger();
120 friend.TheirFlags = -1;
121
122 friends[friendID] = friend;
123 }
124 }
125
126 // Load the permissions those friends have granted to this user
127 for (int i = 0; i < friendedMeArray.Count; i++)
128 {
129 OSDMap friendedMeEntry = friendedMeArray[i] as OSDMap;
130 if (friendedMeEntry != null)
131 {
132 UUID friendID = friendedMeEntry["OwnerID"].AsUUID();
133
134 FriendInfo friend;
135 if (friends.TryGetValue(friendID, out friend))
136 friend.TheirFlags = friendedMeEntry["Value"].AsInteger();
137 }
138 }
139
140 // Convert the dictionary of friends to an array and return it
141 FriendInfo[] array = new FriendInfo[friends.Count];
142 int j = 0;
143 foreach (FriendInfo friend in friends.Values)
144 array[j++] = friend;
145
146 return array;
147 }
148
149 public bool StoreFriend(UUID principalID, string friend, int flags)
150 {
151 NameValueCollection requestArgs = new NameValueCollection
152 {
153 { "RequestMethod", "AddGeneric" },
154 { "OwnerID", principalID.ToString() },
155 { "Type", "Friend" },
156 { "Key", friend },
157 { "Value", flags.ToString() }
158 };
159
160 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
161 bool success = response["Success"].AsBoolean();
162
163 if (!success)
164 m_log.Error("[FRIENDS CONNECTOR]: Failed to store friend " + friend + " for user " + principalID + ": " + response["Message"].AsString());
165
166 return success;
167 }
168
169 public bool Delete(UUID principalID, string friend)
170 {
171 NameValueCollection requestArgs = new NameValueCollection
172 {
173 { "RequestMethod", "RemoveGeneric" },
174 { "OwnerID", principalID.ToString() },
175 { "Type", "Friend" },
176 { "Key", friend }
177 };
178
179 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
180 bool success = response["Success"].AsBoolean();
181
182 if (!success)
183 m_log.Error("[FRIENDS CONNECTOR]: Failed to remove friend " + friend + " for user " + principalID + ": " + response["Message"].AsString());
184
185 return success;
186 }
187
188 #endregion IFriendsService
189
190 private OSDArray GetFriended(UUID ownerID)
191 {
192 NameValueCollection requestArgs = new NameValueCollection
193 {
194 { "RequestMethod", "GetGenerics" },
195 { "OwnerID", ownerID.ToString() },
196 { "Type", "Friend" }
197 };
198
199 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
200 if (response["Success"].AsBoolean() && response["Entries"] is OSDArray)
201 {
202 return (OSDArray)response["Entries"];
203 }
204 else
205 {
206 m_log.Warn("[FRIENDS CONNECTOR]: Failed to retrieve friends for user " + ownerID + ": " + response["Message"].AsString());
207 return new OSDArray(0);
208 }
209 }
210
211 private OSDArray GetFriendedBy(UUID ownerID)
212 {
213 NameValueCollection requestArgs = new NameValueCollection
214 {
215 { "RequestMethod", "GetGenerics" },
216 { "Key", ownerID.ToString() },
217 { "Type", "Friend" }
218 };
219
220 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
221 if (response["Success"].AsBoolean() && response["Entries"] is OSDArray)
222 {
223 return (OSDArray)response["Entries"];
224 }
225 else
226 {
227 m_log.Warn("[FRIENDS CONNECTOR]: Failed to retrieve reverse friends for user " + ownerID + ": " + response["Message"].AsString());
228 return new OSDArray(0);
229 }
230 }
231 }
232}
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianGrid.cs b/OpenSim/Services/Connectors/SimianGrid/SimianGrid.cs
new file mode 100644
index 0000000..c3de98e
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianGrid.cs
@@ -0,0 +1,47 @@
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 Mono.Addins;
30using Nini.Config;
31
32[assembly: Addin("SimianGrid", "1.0")]
33[assembly: AddinDependency("OpenSim", "0.5")]
34
35public static class Simian
36{
37 public static bool IsSimianEnabled(IConfigSource config, string moduleName)
38 {
39 if (config.Configs["Modules"] != null)
40 {
41 string module = config.Configs["Modules"].GetString("AuthenticationServices");
42 return !String.IsNullOrEmpty(module) && module.Contains("Simian");
43 }
44
45 return false;
46 }
47} \ No newline at end of file
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs
new file mode 100644
index 0000000..eebdf14
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs
@@ -0,0 +1,421 @@
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.Collections.Specialized;
31using System.Net;
32using System.Reflection;
33using log4net;
34using Mono.Addins;
35using Nini.Config;
36using OpenSim.Framework;
37using OpenSim.Framework.Servers.HttpServer;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Services.Interfaces;
41using OpenSim.Server.Base;
42using OpenMetaverse;
43using OpenMetaverse.StructuredData;
44
45using GridRegion = OpenSim.Services.Interfaces.GridRegion;
46
47namespace OpenSim.Services.Connectors.SimianGrid
48{
49 /// <summary>
50 /// Connects region registration and neighbor lookups to the SimianGrid
51 /// backend
52 /// </summary>
53 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
54 public class SimianGridServiceConnector : IGridService, ISharedRegionModule
55 {
56 private static readonly ILog m_log =
57 LogManager.GetLogger(
58 MethodBase.GetCurrentMethod().DeclaringType);
59
60 private string m_serverUrl = String.Empty;
61
62 #region ISharedRegionModule
63
64 public Type ReplaceableInterface { get { return null; } }
65 public void RegionLoaded(Scene scene) { }
66 public void PostInitialise() { }
67 public void Close() { }
68
69 public SimianGridServiceConnector() { }
70 public string Name { get { return "SimianGridServiceConnector"; } }
71 public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface<IGridService>(this); } }
72 public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface<IGridService>(this); } }
73
74 #endregion ISharedRegionModule
75
76 public SimianGridServiceConnector(IConfigSource source)
77 {
78 Initialise(source);
79 }
80
81 public void Initialise(IConfigSource source)
82 {
83 if (Simian.IsSimianEnabled(source, "GridServices"))
84 {
85 IConfig gridConfig = source.Configs["GridService"];
86 if (gridConfig == null)
87 {
88 m_log.Error("[GRID CONNECTOR]: GridService missing from OpenSim.ini");
89 throw new Exception("Grid connector init error");
90 }
91
92 string serviceUrl = gridConfig.GetString("GridServerURI");
93 if (String.IsNullOrEmpty(serviceUrl))
94 {
95 m_log.Error("[GRID CONNECTOR]: No Server URI named in section GridService");
96 throw new Exception("Grid connector init error");
97 }
98
99 m_serverUrl = serviceUrl;
100 }
101 }
102
103 #region IGridService
104
105 public string RegisterRegion(UUID scopeID, GridRegion regionInfo)
106 {
107 Vector3d minPosition = new Vector3d(regionInfo.RegionLocX, regionInfo.RegionLocY, 0.0);
108 Vector3d maxPosition = minPosition + new Vector3d(Constants.RegionSize, Constants.RegionSize, 4096.0);
109
110 string httpAddress = "http://" + regionInfo.ExternalHostName + ":" + regionInfo.HttpPort + "/";
111
112 OSDMap extraData = new OSDMap
113 {
114 { "ServerURI", OSD.FromString(regionInfo.ServerURI) },
115 { "InternalAddress", OSD.FromString(regionInfo.InternalEndPoint.Address.ToString()) },
116 { "InternalPort", OSD.FromInteger(regionInfo.InternalEndPoint.Port) },
117 { "ExternalAddress", OSD.FromString(regionInfo.ExternalEndPoint.Address.ToString()) },
118 { "ExternalPort", OSD.FromInteger(regionInfo.ExternalEndPoint.Port) },
119 { "MapTexture", OSD.FromUUID(regionInfo.TerrainImage) },
120 { "Access", OSD.FromInteger(regionInfo.Access) },
121 { "RegionSecret", OSD.FromString(regionInfo.RegionSecret) },
122 { "EstateOwner", OSD.FromUUID(regionInfo.EstateOwner) },
123 { "Token", OSD.FromString(regionInfo.Token) }
124 };
125
126 NameValueCollection requestArgs = new NameValueCollection
127 {
128 { "RequestMethod", "AddScene" },
129 { "SceneID", regionInfo.RegionID.ToString() },
130 { "Name", regionInfo.RegionName },
131 { "MinPosition", minPosition.ToString() },
132 { "MaxPosition", maxPosition.ToString() },
133 { "Address", httpAddress },
134 { "Enabled", "1" },
135 { "ExtraData", OSDParser.SerializeJsonString(extraData) }
136 };
137
138 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
139 if (response["Success"].AsBoolean())
140 return String.Empty;
141 else
142 return "Region registration for " + regionInfo.RegionName + " failed: " + response["Message"].AsString();
143 }
144
145 public bool DeregisterRegion(UUID regionID)
146 {
147 NameValueCollection requestArgs = new NameValueCollection
148 {
149 { "RequestMethod", "AddScene" },
150 { "SceneID", regionID.ToString() },
151 { "Enabled", "0" }
152 };
153
154 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
155 bool success = response["Success"].AsBoolean();
156
157 if (!success)
158 m_log.Warn("[GRID CONNECTOR]: Region deregistration for " + regionID + " failed: " + response["Message"].AsString());
159
160 return success;
161 }
162
163 public List<GridRegion> GetNeighbours(UUID scopeID, UUID regionID)
164 {
165 const int NEIGHBOR_RADIUS = 128;
166
167 GridRegion region = GetRegionByUUID(scopeID, regionID);
168
169 if (region != null)
170 {
171 List<GridRegion> regions = GetRegionRange(scopeID,
172 region.RegionLocX - NEIGHBOR_RADIUS, region.RegionLocX + (int)Constants.RegionSize + NEIGHBOR_RADIUS,
173 region.RegionLocY - NEIGHBOR_RADIUS, region.RegionLocY + (int)Constants.RegionSize + NEIGHBOR_RADIUS);
174
175 for (int i = 0; i < regions.Count; i++)
176 {
177 if (regions[i].RegionID == regionID)
178 {
179 regions.RemoveAt(i);
180 break;
181 }
182 }
183
184 m_log.Debug("[GRID CONNECTOR]: Found " + regions.Count + " neighbors for region " + regionID);
185 return regions;
186 }
187
188 return new List<GridRegion>(0);
189 }
190
191 public GridRegion GetRegionByUUID(UUID scopeID, UUID regionID)
192 {
193 NameValueCollection requestArgs = new NameValueCollection
194 {
195 { "RequestMethod", "GetScene" },
196 { "SceneID", regionID.ToString() }
197 };
198
199 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
200 if (response["Success"].AsBoolean())
201 {
202 return ResponseToGridRegion(response);
203 }
204 else
205 {
206 m_log.Warn("[GRID CONNECTOR]: Grid service did not find a match for region " + regionID);
207 return null;
208 }
209 }
210
211 public GridRegion GetRegionByPosition(UUID scopeID, int x, int y)
212 {
213 // Go one meter in from the requested x/y coords to avoid requesting a position
214 // that falls on the border of two sims
215 Vector3d position = new Vector3d(x + 1, y + 1, 0.0);
216
217 NameValueCollection requestArgs = new NameValueCollection
218 {
219 { "RequestMethod", "GetScene" },
220 { "Position", position.ToString() },
221 { "Enabled", "1" }
222 };
223
224 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
225 if (response["Success"].AsBoolean())
226 {
227 return ResponseToGridRegion(response);
228 }
229 else
230 {
231 //m_log.InfoFormat("[GRID CONNECTOR]: Grid service did not find a match for region at {0},{1}",
232 // x / Constants.RegionSize, y / Constants.RegionSize);
233 return null;
234 }
235 }
236
237 public GridRegion GetRegionByName(UUID scopeID, string regionName)
238 {
239 List<GridRegion> regions = GetRegionsByName(scopeID, regionName, 1);
240
241 m_log.Debug("[GRID CONNECTOR]: Got " + regions.Count + " matches for region name " + regionName);
242
243 if (regions.Count > 0)
244 return regions[0];
245
246 return null;
247 }
248
249 public List<GridRegion> GetRegionsByName(UUID scopeID, string name, int maxNumber)
250 {
251 List<GridRegion> foundRegions = new List<GridRegion>();
252
253 NameValueCollection requestArgs = new NameValueCollection
254 {
255 { "RequestMethod", "GetScenes" },
256 { "NameQuery", name },
257 { "Enabled", "1" }
258 };
259 if (maxNumber > 0)
260 requestArgs["MaxNumber"] = maxNumber.ToString();
261
262 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
263 if (response["Success"].AsBoolean())
264 {
265 OSDArray array = response["Scenes"] as OSDArray;
266 if (array != null)
267 {
268 for (int i = 0; i < array.Count; i++)
269 {
270 GridRegion region = ResponseToGridRegion(array[i] as OSDMap);
271 if (region != null)
272 foundRegions.Add(region);
273 }
274 }
275 }
276
277 return foundRegions;
278 }
279
280 public List<GridRegion> GetRegionRange(UUID scopeID, int xmin, int xmax, int ymin, int ymax)
281 {
282 List<GridRegion> foundRegions = new List<GridRegion>();
283
284 Vector3d minPosition = new Vector3d(xmin, ymin, 0.0);
285 Vector3d maxPosition = new Vector3d(xmax, ymax, 4096.0);
286
287 NameValueCollection requestArgs = new NameValueCollection
288 {
289 { "RequestMethod", "GetScenes" },
290 { "MinPosition", minPosition.ToString() },
291 { "MaxPosition", maxPosition.ToString() },
292 { "Enabled", "1" }
293 };
294
295 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
296 if (response["Success"].AsBoolean())
297 {
298 OSDArray array = response["Scenes"] as OSDArray;
299 if (array != null)
300 {
301 for (int i = 0; i < array.Count; i++)
302 {
303 GridRegion region = ResponseToGridRegion(array[i] as OSDMap);
304 if (region != null)
305 foundRegions.Add(region);
306 }
307 }
308 }
309
310 return foundRegions;
311 }
312
313 public List<GridRegion> GetDefaultRegions(UUID scopeID)
314 {
315 // TODO: Allow specifying the default grid location
316 const int DEFAULT_X = 1000 * 256;
317 const int DEFAULT_Y = 1000 * 256;
318
319 GridRegion defRegion = GetNearestRegion(new Vector3d(DEFAULT_X, DEFAULT_Y, 0.0), true);
320 if (defRegion != null)
321 return new List<GridRegion>(1) { defRegion };
322 else
323 return new List<GridRegion>(0);
324 }
325
326 public List<GridRegion> GetFallbackRegions(UUID scopeID, int x, int y)
327 {
328 GridRegion defRegion = GetNearestRegion(new Vector3d(x, y, 0.0), true);
329 if (defRegion != null)
330 return new List<GridRegion>(1) { defRegion };
331 else
332 return new List<GridRegion>(0);
333 }
334
335 public int GetRegionFlags(UUID scopeID, UUID regionID)
336 {
337 const int REGION_ONLINE = 4;
338
339 NameValueCollection requestArgs = new NameValueCollection
340 {
341 { "RequestMethod", "GetScene" },
342 { "SceneID", regionID.ToString() }
343 };
344
345 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
346 if (response["Success"].AsBoolean())
347 {
348 return response["Enabled"].AsBoolean() ? REGION_ONLINE : 0;
349 }
350 else
351 {
352 m_log.Warn("[GRID CONNECTOR]: Grid service did not find a match for region " + regionID + " during region flags check");
353 return -1;
354 }
355 }
356
357 #endregion IGridService
358
359 private GridRegion GetNearestRegion(Vector3d position, bool onlyEnabled)
360 {
361 NameValueCollection requestArgs = new NameValueCollection
362 {
363 { "RequestMethod", "GetScene" },
364 { "Position", position.ToString() },
365 { "FindClosest", "1" }
366 };
367 if (onlyEnabled)
368 requestArgs["Enabled"] = "1";
369
370 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
371 if (response["Success"].AsBoolean())
372 {
373 return ResponseToGridRegion(response);
374 }
375 else
376 {
377 m_log.Warn("[GRID CONNECTOR]: Grid service did not find a match for region at " + position);
378 return null;
379 }
380 }
381
382 private GridRegion ResponseToGridRegion(OSDMap response)
383 {
384 if (response == null)
385 return null;
386
387 OSDMap extraData = response["ExtraData"] as OSDMap;
388 if (extraData == null)
389 return null;
390
391 GridRegion region = new GridRegion();
392
393 region.RegionID = response["SceneID"].AsUUID();
394 region.RegionName = response["Name"].AsString();
395
396 Vector3d minPosition = response["MinPosition"].AsVector3d();
397 region.RegionLocX = (int)minPosition.X;
398 region.RegionLocY = (int)minPosition.Y;
399
400 Uri httpAddress = response["Address"].AsUri();
401 region.ExternalHostName = httpAddress.Host;
402 region.HttpPort = (uint)httpAddress.Port;
403
404 region.ServerURI = extraData["ServerURI"].AsString();
405
406 IPAddress internalAddress;
407 IPAddress.TryParse(extraData["InternalAddress"].AsString(), out internalAddress);
408 if (internalAddress == null)
409 internalAddress = IPAddress.Any;
410
411 region.InternalEndPoint = new IPEndPoint(internalAddress, extraData["InternalPort"].AsInteger());
412 region.TerrainImage = extraData["MapTexture"].AsUUID();
413 region.Access = (byte)extraData["Access"].AsInteger();
414 region.RegionSecret = extraData["RegionSecret"].AsString();
415 region.EstateOwner = extraData["EstateOwner"].AsUUID();
416 region.Token = extraData["Token"].AsString();
417
418 return region;
419 }
420 }
421}
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianInventoryServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianInventoryServiceConnector.cs
new file mode 100644
index 0000000..891782f
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianInventoryServiceConnector.cs
@@ -0,0 +1,895 @@
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.Collections.Specialized;
31using System.Reflection;
32using log4net;
33using Mono.Addins;
34using Nini.Config;
35using OpenMetaverse;
36using OpenMetaverse.StructuredData;
37using OpenSim.Framework;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Server.Base;
41using OpenSim.Services.Interfaces;
42
43namespace OpenSim.Services.Connectors.SimianGrid
44{
45 /// <summary>
46 /// Permissions bitflags
47 /// </summary>
48 [Flags]
49 public enum PermissionMask : uint
50 {
51 None = 0,
52 Transfer = 1 << 13,
53 Modify = 1 << 14,
54 Copy = 1 << 15,
55 Move = 1 << 19,
56 Damage = 1 << 20,
57 All = 0x7FFFFFFF
58 }
59
60 /// <summary>
61 /// Connects avatar inventories to the SimianGrid backend
62 /// </summary>
63 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
64 public class SimianInventoryServiceConnector : IInventoryService, ISharedRegionModule
65 {
66 private static readonly ILog m_log =
67 LogManager.GetLogger(
68 MethodBase.GetCurrentMethod().DeclaringType);
69
70 private string m_serverUrl = String.Empty;
71 private string m_userServerUrl = String.Empty;
72 private object m_gestureSyncRoot = new object();
73
74 #region ISharedRegionModule
75
76 public Type ReplaceableInterface { get { return null; } }
77 public void RegionLoaded(Scene scene) { }
78 public void PostInitialise() { }
79 public void Close() { }
80
81 public SimianInventoryServiceConnector() { }
82 public string Name { get { return "SimianInventoryServiceConnector"; } }
83 public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface<IInventoryService>(this); } }
84 public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface<IInventoryService>(this); } }
85
86 #endregion ISharedRegionModule
87
88 public SimianInventoryServiceConnector(IConfigSource source)
89 {
90 Initialise(source);
91 }
92
93 public void Initialise(IConfigSource source)
94 {
95 if (Simian.IsSimianEnabled(source, "InventoryServices"))
96 {
97 IConfig gridConfig = source.Configs["InventoryService"];
98 if (gridConfig == null)
99 {
100 m_log.Error("[INVENTORY CONNECTOR]: InventoryService missing from OpenSim.ini");
101 throw new Exception("Inventory connector init error");
102 }
103
104 string serviceUrl = gridConfig.GetString("InventoryServerURI");
105 if (String.IsNullOrEmpty(serviceUrl))
106 {
107 m_log.Error("[INVENTORY CONNECTOR]: No Server URI named in section InventoryService");
108 throw new Exception("Inventory connector init error");
109 }
110
111 m_serverUrl = serviceUrl;
112
113 gridConfig = source.Configs["UserAccountService"];
114 if (gridConfig != null)
115 {
116 serviceUrl = gridConfig.GetString("UserAccountServerURI");
117 if (!String.IsNullOrEmpty(serviceUrl))
118 m_userServerUrl = serviceUrl;
119 else
120 m_log.Info("[INVENTORY CONNECTOR]: No Server URI named in section UserAccountService");
121 }
122 else
123 {
124 m_log.Warn("[INVENTORY CONNECTOR]: UserAccountService missing from OpenSim.ini");
125 }
126 }
127 }
128
129 /// <summary>
130 /// Create the entire inventory for a given user
131 /// </summary>
132 /// <param name="user"></param>
133 /// <returns></returns>
134 public bool CreateUserInventory(UUID userID)
135 {
136 NameValueCollection requestArgs = new NameValueCollection
137 {
138 { "RequestMethod", "AddInventory" },
139 { "OwnerID", userID.ToString() }
140 };
141
142 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
143 bool success = response["Success"].AsBoolean();
144
145 if (!success)
146 m_log.Warn("[INVENTORY CONNECTOR]: Inventory creation for " + userID + " failed: " + response["Message"].AsString());
147
148 return success;
149 }
150
151 /// <summary>
152 /// Gets the skeleton of the inventory -- folders only
153 /// </summary>
154 /// <param name="userID"></param>
155 /// <returns></returns>
156 public List<InventoryFolderBase> GetInventorySkeleton(UUID userID)
157 {
158 NameValueCollection requestArgs = new NameValueCollection
159 {
160 { "RequestMethod", "GetInventoryNode" },
161 { "ItemID", userID.ToString() },
162 { "OwnerID", userID.ToString() },
163 { "IncludeFolders", "1" },
164 { "IncludeItems", "0" },
165 { "ChildrenOnly", "0" }
166 };
167
168 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
169 if (response["Success"].AsBoolean() && response["Items"] is OSDArray)
170 {
171 OSDArray items = (OSDArray)response["Items"];
172 return GetFoldersFromResponse(items, userID, true);
173 }
174 else
175 {
176 m_log.Warn("[INVENTORY CONNECTOR]: Failed to retrieve inventory skeleton for " + userID + ": " +
177 response["Message"].AsString());
178 return new List<InventoryFolderBase>(0);
179 }
180 }
181
182 /// <summary>
183 /// Synchronous inventory fetch.
184 /// </summary>
185 /// <param name="userID"></param>
186 /// <returns></returns>
187 [Obsolete]
188 public InventoryCollection GetUserInventory(UUID userID)
189 {
190 m_log.Error("[INVENTORY CONNECTOR]: Obsolete GetUserInventory called for " + userID);
191
192 InventoryCollection inventory = new InventoryCollection();
193 inventory.UserID = userID;
194 inventory.Folders = new List<InventoryFolderBase>();
195 inventory.Items = new List<InventoryItemBase>();
196
197 return inventory;
198 }
199
200 /// <summary>
201 /// Request the inventory for a user. This is an asynchronous operation that will call the callback when the
202 /// inventory has been received
203 /// </summary>
204 /// <param name="userID"></param>
205 /// <param name="callback"></param>
206 [Obsolete]
207 public void GetUserInventory(UUID userID, InventoryReceiptCallback callback)
208 {
209 m_log.Error("[INVENTORY CONNECTOR]: Obsolete GetUserInventory called for " + userID);
210 callback(new List<InventoryFolderImpl>(0), new List<InventoryItemBase>(0));
211 }
212
213 /// <summary>
214 /// Retrieve the root inventory folder for the given user.
215 /// </summary>
216 /// <param name="userID"></param>
217 /// <returns>null if no root folder was found</returns>
218 public InventoryFolderBase GetRootFolder(UUID userID)
219 {
220 NameValueCollection requestArgs = new NameValueCollection
221 {
222 { "RequestMethod", "GetInventoryNode" },
223 { "ItemID", userID.ToString() },
224 { "OwnerID", userID.ToString() },
225 { "IncludeFolders", "1" },
226 { "IncludeItems", "0" },
227 { "ChildrenOnly", "1" }
228 };
229
230 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
231 if (response["Success"].AsBoolean() && response["Items"] is OSDArray)
232 {
233 OSDArray items = (OSDArray)response["Items"];
234 List<InventoryFolderBase> folders = GetFoldersFromResponse(items, userID, true);
235
236 if (folders.Count > 0)
237 return folders[0];
238 }
239
240 return null;
241 }
242
243 /// <summary>
244 /// Gets the user folder for the given folder-type
245 /// </summary>
246 /// <param name="userID"></param>
247 /// <param name="type"></param>
248 /// <returns></returns>
249 public InventoryFolderBase GetFolderForType(UUID userID, AssetType type)
250 {
251 string contentType = SLUtil.SLAssetTypeToContentType((int)type);
252
253 NameValueCollection requestArgs = new NameValueCollection
254 {
255 { "RequestMethod", "GetFolderForType" },
256 { "ContentType", contentType },
257 { "OwnerID", userID.ToString() }
258 };
259
260 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
261 if (response["Success"].AsBoolean() && response["Folder"] is OSDMap)
262 {
263 OSDMap folder = (OSDMap)response["Folder"];
264
265 return new InventoryFolderBase(
266 folder["ID"].AsUUID(),
267 folder["Name"].AsString(),
268 folder["OwnerID"].AsUUID(),
269 (short)SLUtil.ContentTypeToSLAssetType(folder["ContentType"].AsString()),
270 folder["ParentID"].AsUUID(),
271 (ushort)folder["Version"].AsInteger()
272 );
273 }
274 else
275 {
276 m_log.Warn("[INVENTORY CONNECTOR]: Default folder not found for content type " + contentType);
277 return GetRootFolder(userID);
278 }
279 }
280
281 /// <summary>
282 /// Get an item, given by its UUID
283 /// </summary>
284 /// <param name="item"></param>
285 /// <returns></returns>
286 public InventoryItemBase GetItem(InventoryItemBase item)
287 {
288 NameValueCollection requestArgs = new NameValueCollection
289 {
290 { "RequestMethod", "GetInventoryNode" },
291 { "ItemID", item.ID.ToString() },
292 { "OwnerID", item.Owner.ToString() },
293 { "IncludeFolders", "1" },
294 { "IncludeItems", "1" },
295 { "ChildrenOnly", "1" }
296 };
297
298 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
299 if (response["Success"].AsBoolean() && response["Items"] is OSDArray)
300 {
301 List<InventoryItemBase> items = GetItemsFromResponse((OSDArray)response["Items"]);
302 if (items.Count > 0)
303 {
304 // The requested item should be the first in this list, but loop through
305 // and sanity check just in case
306 for (int i = 0; i < items.Count; i++)
307 {
308 if (items[i].ID == item.ID)
309 return items[i];
310 }
311 }
312 }
313
314 m_log.Warn("[INVENTORY CONNECTOR]: Item " + item.ID + " owned by " + item.Owner + " not found");
315 return null;
316 }
317
318 /// <summary>
319 /// Get a folder, given by its UUID
320 /// </summary>
321 /// <param name="folder"></param>
322 /// <returns></returns>
323 public InventoryFolderBase GetFolder(InventoryFolderBase folder)
324 {
325 NameValueCollection requestArgs = new NameValueCollection
326 {
327 { "RequestMethod", "GetInventoryNode" },
328 { "ItemID", folder.ID.ToString() },
329 { "OwnerID", folder.Owner.ToString() },
330 { "IncludeFolders", "1" },
331 { "IncludeItems", "0" },
332 { "ChildrenOnly", "1" }
333 };
334
335 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
336 if (response["Success"].AsBoolean() && response["Items"] is OSDArray)
337 {
338 OSDArray items = (OSDArray)response["Items"];
339 List<InventoryFolderBase> folders = GetFoldersFromResponse(items, folder.ID, true);
340
341 if (folders.Count > 0)
342 return folders[0];
343 }
344
345 return null;
346 }
347
348 /// <summary>
349 /// Gets everything (folders and items) inside a folder
350 /// </summary>
351 /// <param name="userID"></param>
352 /// <param name="folderID"></param>
353 /// <returns></returns>
354 public InventoryCollection GetFolderContent(UUID userID, UUID folderID)
355 {
356 InventoryCollection inventory = new InventoryCollection();
357 inventory.UserID = userID;
358
359 NameValueCollection requestArgs = new NameValueCollection
360 {
361 { "RequestMethod", "GetInventoryNode" },
362 { "ItemID", folderID.ToString() },
363 { "OwnerID", userID.ToString() },
364 { "IncludeFolders", "1" },
365 { "IncludeItems", "1" },
366 { "ChildrenOnly", "1" }
367 };
368
369 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
370 if (response["Success"].AsBoolean() && response["Items"] is OSDArray)
371 {
372 OSDArray items = (OSDArray)response["Items"];
373
374 inventory.Folders = GetFoldersFromResponse(items, folderID, false);
375 inventory.Items = GetItemsFromResponse(items);
376 }
377 else
378 {
379 m_log.Warn("[INVENTORY CONNECTOR]: Error fetching folder " + folderID + " content for " + userID + ": " +
380 response["Message"].AsString());
381 inventory.Folders = new List<InventoryFolderBase>(0);
382 inventory.Items = new List<InventoryItemBase>(0);
383 }
384
385 return inventory;
386 }
387
388 /// <summary>
389 /// Gets the items inside a folder
390 /// </summary>
391 /// <param name="userID"></param>
392 /// <param name="folderID"></param>
393 /// <returns></returns>
394 public List<InventoryItemBase> GetFolderItems(UUID userID, UUID folderID)
395 {
396 InventoryCollection inventory = new InventoryCollection();
397 inventory.UserID = userID;
398
399 NameValueCollection requestArgs = new NameValueCollection
400 {
401 { "RequestMethod", "GetInventoryNode" },
402 { "ItemID", folderID.ToString() },
403 { "OwnerID", userID.ToString() },
404 { "IncludeFolders", "0" },
405 { "IncludeItems", "1" },
406 { "ChildrenOnly", "1" }
407 };
408
409 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
410 if (response["Success"].AsBoolean() && response["Items"] is OSDArray)
411 {
412 OSDArray items = (OSDArray)response["Items"];
413 return GetItemsFromResponse(items);
414 }
415 else
416 {
417 m_log.Warn("[INVENTORY CONNECTOR]: Error fetching folder " + folderID + " for " + userID + ": " +
418 response["Message"].AsString());
419 return new List<InventoryItemBase>(0);
420 }
421 }
422
423 /// <summary>
424 /// Add a new folder to the user's inventory
425 /// </summary>
426 /// <param name="folder"></param>
427 /// <returns>true if the folder was successfully added</returns>
428 public bool AddFolder(InventoryFolderBase folder)
429 {
430 NameValueCollection requestArgs = new NameValueCollection
431 {
432 { "RequestMethod", "AddInventoryFolder" },
433 { "FolderID", folder.ID.ToString() },
434 { "ParentID", folder.ParentID.ToString() },
435 { "OwnerID", folder.Owner.ToString() },
436 { "Name", folder.Name },
437 { "ContentType", SLUtil.SLAssetTypeToContentType(folder.Type) }
438 };
439
440 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
441 bool success = response["Success"].AsBoolean();
442
443 if (!success)
444 {
445 m_log.Warn("[INVENTORY CONNECTOR]: Error creating folder " + folder.Name + " for " + folder.Owner + ": " +
446 response["Message"].AsString());
447 }
448
449 return success;
450 }
451
452 /// <summary>
453 /// Update a folder in the user's inventory
454 /// </summary>
455 /// <param name="folder"></param>
456 /// <returns>true if the folder was successfully updated</returns>
457 public bool UpdateFolder(InventoryFolderBase folder)
458 {
459 return AddFolder(folder);
460 }
461
462 /// <summary>
463 /// Move an inventory folder to a new location
464 /// </summary>
465 /// <param name="folder">A folder containing the details of the new location</param>
466 /// <returns>true if the folder was successfully moved</returns>
467 public bool MoveFolder(InventoryFolderBase folder)
468 {
469 return AddFolder(folder);
470 }
471
472 /// <summary>
473 /// Delete an item from the user's inventory
474 /// </summary>
475 /// <param name="item"></param>
476 /// <returns>true if the item was successfully deleted</returns>
477 //bool DeleteItem(InventoryItemBase item);
478 public bool DeleteFolders(UUID userID, List<UUID> folderIDs)
479 {
480 return DeleteItems(userID, folderIDs);
481 }
482
483 /// <summary>
484 /// Delete an item from the user's inventory
485 /// </summary>
486 /// <param name="item"></param>
487 /// <returns>true if the item was successfully deleted</returns>
488 public bool DeleteItems(UUID userID, List<UUID> itemIDs)
489 {
490 // TODO: RemoveInventoryNode should be replaced with RemoveInventoryNodes
491 bool allSuccess = true;
492
493 for (int i = 0; i < itemIDs.Count; i++)
494 {
495 UUID itemID = itemIDs[i];
496
497 NameValueCollection requestArgs = new NameValueCollection
498 {
499 { "RequestMethod", "RemoveInventoryNode" },
500 { "OwnerID", userID.ToString() },
501 { "ItemID", itemID.ToString() }
502 };
503
504 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
505 bool success = response["Success"].AsBoolean();
506
507 if (!success)
508 {
509 m_log.Warn("[INVENTORY CONNECTOR]: Error removing item " + itemID + " for " + userID + ": " +
510 response["Message"].AsString());
511 allSuccess = false;
512 }
513 }
514
515 return allSuccess;
516 }
517
518 /// <summary>
519 /// Purge an inventory folder of all its items and subfolders.
520 /// </summary>
521 /// <param name="folder"></param>
522 /// <returns>true if the folder was successfully purged</returns>
523 public bool PurgeFolder(InventoryFolderBase folder)
524 {
525 NameValueCollection requestArgs = new NameValueCollection
526 {
527 { "RequestMethod", "PurgeInventoryFolder" },
528 { "OwnerID", folder.Owner.ToString() },
529 { "FolderID", folder.ID.ToString() }
530 };
531
532 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
533 bool success = response["Success"].AsBoolean();
534
535 if (!success)
536 {
537 m_log.Warn("[INVENTORY CONNECTOR]: Error purging folder " + folder.ID + " for " + folder.Owner + ": " +
538 response["Message"].AsString());
539 }
540
541 return success;
542 }
543
544 /// <summary>
545 /// Add a new item to the user's inventory
546 /// </summary>
547 /// <param name="item"></param>
548 /// <returns>true if the item was successfully added</returns>
549 public bool AddItem(InventoryItemBase item)
550 {
551 // A folder of UUID.Zero means we need to find the most appropriate home for this item
552 if (item.Folder == UUID.Zero)
553 {
554 InventoryFolderBase folder = GetFolderForType(item.Owner, (AssetType)item.AssetType);
555 if (folder != null && folder.ID != UUID.Zero)
556 item.Folder = folder.ID;
557 else
558 item.Folder = item.Owner; // Root folder
559 }
560
561 if ((AssetType)item.AssetType == AssetType.Gesture)
562 UpdateGesture(item.Owner, item.ID, item.Flags == 1);
563
564 if (item.BasePermissions == 0)
565 m_log.WarnFormat("[INVENTORY CONNECTOR]: Adding inventory item {0} ({1}) with no base permissions", item.Name, item.ID);
566
567 OSDMap permissions = new OSDMap
568 {
569 { "BaseMask", OSD.FromInteger(item.BasePermissions) },
570 { "EveryoneMask", OSD.FromInteger(item.EveryOnePermissions) },
571 { "GroupMask", OSD.FromInteger(item.GroupPermissions) },
572 { "NextOwnerMask", OSD.FromInteger(item.NextPermissions) },
573 { "OwnerMask", OSD.FromInteger(item.CurrentPermissions) }
574 };
575
576 OSDMap extraData = new OSDMap()
577 {
578 { "Flags", OSD.FromInteger(item.Flags) },
579 { "GroupID", OSD.FromUUID(item.GroupID) },
580 { "GroupOwned", OSD.FromBoolean(item.GroupOwned) },
581 { "SalePrice", OSD.FromInteger(item.SalePrice) },
582 { "SaleType", OSD.FromInteger(item.SaleType) },
583 { "Permissions", permissions }
584 };
585
586 NameValueCollection requestArgs = new NameValueCollection
587 {
588 { "RequestMethod", "AddInventoryItem" },
589 { "ItemID", item.ID.ToString() },
590 { "AssetID", item.AssetID.ToString() },
591 { "ParentID", item.Folder.ToString() },
592 { "OwnerID", item.Owner.ToString() },
593 { "Name", item.Name },
594 { "Description", item.Description },
595 { "CreatorID", item.CreatorId },
596 { "ExtraData", OSDParser.SerializeJsonString(extraData) }
597 };
598
599 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
600 bool success = response["Success"].AsBoolean();
601
602 if (!success)
603 {
604 m_log.Warn("[INVENTORY CONNECTOR]: Error creating item " + item.Name + " for " + item.Owner + ": " +
605 response["Message"].AsString());
606 }
607
608 return success;
609 }
610
611 /// <summary>
612 /// Update an item in the user's inventory
613 /// </summary>
614 /// <param name="item"></param>
615 /// <returns>true if the item was successfully updated</returns>
616 public bool UpdateItem(InventoryItemBase item)
617 {
618 if (item.AssetID != UUID.Zero)
619 {
620 return AddItem(item);
621 }
622 else
623 {
624 // This is actually a folder update
625 InventoryFolderBase folder = new InventoryFolderBase(item.ID, item.Name, item.Owner, (short)item.AssetType, item.Folder, 0);
626 return UpdateFolder(folder);
627 }
628 }
629
630 public bool MoveItems(UUID ownerID, List<InventoryItemBase> items)
631 {
632 bool success = true;
633
634 while (items.Count > 0)
635 {
636 List<InventoryItemBase> currentItems = new List<InventoryItemBase>();
637 UUID destFolderID = items[0].Folder;
638
639 // Find all of the items being moved to the current destination folder
640 for (int i = 0; i < items.Count; i++)
641 {
642 InventoryItemBase item = items[i];
643 if (item.Folder == destFolderID)
644 currentItems.Add(item);
645 }
646
647 // Do the inventory move for the current items
648 success &= MoveItems(ownerID, items, destFolderID);
649
650 // Remove the processed items from the list
651 for (int i = 0; i < currentItems.Count; i++)
652 items.Remove(currentItems[i]);
653 }
654
655 return success;
656 }
657
658 /// <summary>
659 /// Does the given user have an inventory structure?
660 /// </summary>
661 /// <param name="userID"></param>
662 /// <returns></returns>
663 public bool HasInventoryForUser(UUID userID)
664 {
665 return GetRootFolder(userID) != null;
666 }
667
668 /// <summary>
669 /// Get the active gestures of the agent.
670 /// </summary>
671 /// <param name="userID"></param>
672 /// <returns></returns>
673 public List<InventoryItemBase> GetActiveGestures(UUID userID)
674 {
675 OSDArray items = FetchGestures(userID);
676
677 string[] itemIDs = new string[items.Count];
678 for (int i = 0; i < items.Count; i++)
679 itemIDs[i] = items[i].AsUUID().ToString();
680
681 NameValueCollection requestArgs = new NameValueCollection
682 {
683 { "RequestMethod", "GetInventoryNodes" },
684 { "OwnerID", userID.ToString() },
685 { "Items", String.Join(",", itemIDs) }
686 };
687
688 // FIXME: Implement this in SimianGrid
689 return new List<InventoryItemBase>(0);
690 }
691
692 /// <summary>
693 /// Get the union of permissions of all inventory items
694 /// that hold the given assetID.
695 /// </summary>
696 /// <param name="userID"></param>
697 /// <param name="assetID"></param>
698 /// <returns>The permissions or 0 if no such asset is found in
699 /// the user's inventory</returns>
700 public int GetAssetPermissions(UUID userID, UUID assetID)
701 {
702 NameValueCollection requestArgs = new NameValueCollection
703 {
704 { "RequestMethod", "GetInventoryNodes" },
705 { "OwnerID", userID.ToString() },
706 { "AssetID", assetID.ToString() }
707 };
708
709 // FIXME: Implement this in SimianGrid
710 return (int)PermissionMask.All;
711 }
712
713 private List<InventoryFolderBase> GetFoldersFromResponse(OSDArray items, UUID baseFolder, bool includeBaseFolder)
714 {
715 List<InventoryFolderBase> invFolders = new List<InventoryFolderBase>(items.Count);
716
717 for (int i = 0; i < items.Count; i++)
718 {
719 OSDMap item = items[i] as OSDMap;
720
721 if (item != null && item["Type"].AsString() == "Folder")
722 {
723 UUID folderID = item["ID"].AsUUID();
724
725 if (folderID == baseFolder && !includeBaseFolder)
726 continue;
727
728 invFolders.Add(new InventoryFolderBase(
729 folderID,
730 item["Name"].AsString(),
731 item["OwnerID"].AsUUID(),
732 (short)SLUtil.ContentTypeToSLAssetType(item["ContentType"].AsString()),
733 item["ParentID"].AsUUID(),
734 (ushort)item["Version"].AsInteger()
735 ));
736 }
737 }
738
739 return invFolders;
740 }
741
742 private List<InventoryItemBase> GetItemsFromResponse(OSDArray items)
743 {
744 List<InventoryItemBase> invItems = new List<InventoryItemBase>(items.Count);
745
746 for (int i = 0; i < items.Count; i++)
747 {
748 OSDMap item = items[i] as OSDMap;
749
750 if (item != null && item["Type"].AsString() == "Item")
751 {
752 InventoryItemBase invItem = new InventoryItemBase();
753
754 invItem.AssetID = item["AssetID"].AsUUID();
755 invItem.AssetType = SLUtil.ContentTypeToSLAssetType(item["ContentType"].AsString());
756 invItem.CreationDate = item["CreationDate"].AsInteger();
757 invItem.CreatorId = item["CreatorID"].AsString();
758 invItem.CreatorIdAsUuid = item["CreatorID"].AsUUID();
759 invItem.Description = item["Description"].AsString();
760 invItem.Folder = item["ParentID"].AsUUID();
761 invItem.ID = item["ID"].AsUUID();
762 invItem.InvType = SLUtil.ContentTypeToSLInvType(item["ContentType"].AsString());
763 invItem.Name = item["Name"].AsString();
764 invItem.Owner = item["OwnerID"].AsUUID();
765
766 OSDMap extraData = item["ExtraData"] as OSDMap;
767 if (extraData != null && extraData.Count > 0)
768 {
769 invItem.Flags = extraData["Flags"].AsUInteger();
770 invItem.GroupID = extraData["GroupID"].AsUUID();
771 invItem.GroupOwned = extraData["GroupOwned"].AsBoolean();
772 invItem.SalePrice = extraData["SalePrice"].AsInteger();
773 invItem.SaleType = (byte)extraData["SaleType"].AsInteger();
774
775 OSDMap perms = extraData["Permissions"] as OSDMap;
776 if (perms != null)
777 {
778 invItem.BasePermissions = perms["BaseMask"].AsUInteger();
779 invItem.CurrentPermissions = perms["OwnerMask"].AsUInteger();
780 invItem.EveryOnePermissions = perms["EveryoneMask"].AsUInteger();
781 invItem.GroupPermissions = perms["GroupMask"].AsUInteger();
782 invItem.NextPermissions = perms["NextOwnerMask"].AsUInteger();
783 }
784 }
785
786 if (invItem.BasePermissions == 0)
787 {
788 m_log.InfoFormat("[INVENTORY CONNECTOR]: Forcing item permissions to full for item {0} ({1})",
789 invItem.Name, invItem.ID);
790 invItem.BasePermissions = (uint)PermissionMask.All;
791 invItem.CurrentPermissions = (uint)PermissionMask.All;
792 invItem.EveryOnePermissions = (uint)PermissionMask.All;
793 invItem.GroupPermissions = (uint)PermissionMask.All;
794 invItem.NextPermissions = (uint)PermissionMask.All;
795 }
796
797 invItems.Add(invItem);
798 }
799 }
800
801 return invItems;
802 }
803
804 private bool MoveItems(UUID ownerID, List<InventoryItemBase> items, UUID destFolderID)
805 {
806 string[] itemIDs = new string[items.Count];
807 for (int i = 0; i < items.Count; i++)
808 itemIDs[i] = items[i].ID.ToString();
809
810 NameValueCollection requestArgs = new NameValueCollection
811 {
812 { "RequestMethod", "MoveInventoryNodes" },
813 { "OwnerID", ownerID.ToString() },
814 { "FolderID", destFolderID.ToString() },
815 { "Items", String.Join(",", itemIDs) }
816 };
817
818 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
819 bool success = response["Success"].AsBoolean();
820
821 if (!success)
822 {
823 m_log.Warn("[INVENTORY CONNECTOR]: Failed to move " + items.Count + " items to " +
824 destFolderID + ": " + response["Message"].AsString());
825 }
826
827 return success;
828 }
829
830 private void UpdateGesture(UUID userID, UUID itemID, bool enabled)
831 {
832 OSDArray gestures = FetchGestures(userID);
833 OSDArray newGestures = new OSDArray();
834
835 for (int i = 0; i < gestures.Count; i++)
836 {
837 UUID gesture = gestures[i].AsUUID();
838 if (gesture != itemID)
839 newGestures.Add(OSD.FromUUID(gesture));
840 }
841
842 if (enabled)
843 newGestures.Add(OSD.FromUUID(itemID));
844
845 SaveGestures(userID, newGestures);
846 }
847
848 private OSDArray FetchGestures(UUID userID)
849 {
850 NameValueCollection requestArgs = new NameValueCollection
851 {
852 { "RequestMethod", "GetUser" },
853 { "UserID", userID.ToString() }
854 };
855
856 OSDMap response = WebUtil.PostToService(m_userServerUrl, requestArgs);
857 if (response["Success"].AsBoolean())
858 {
859 OSDMap user = response["User"] as OSDMap;
860 if (user != null && response.ContainsKey("Gestures"))
861 {
862 OSD gestures = OSDParser.DeserializeJson(response["Gestures"].AsString());
863 if (gestures != null && gestures is OSDArray)
864 return (OSDArray)gestures;
865 else
866 m_log.Error("[INVENTORY CONNECTOR]: Unrecognized active gestures data for " + userID);
867 }
868 }
869 else
870 {
871 m_log.Warn("[INVENTORY CONNECTOR]: Failed to fetch active gestures for " + userID + ": " +
872 response["Message"].AsString());
873 }
874
875 return new OSDArray();
876 }
877
878 private void SaveGestures(UUID userID, OSDArray gestures)
879 {
880 NameValueCollection requestArgs = new NameValueCollection
881 {
882 { "RequestMethod", "AddUserData" },
883 { "UserID", userID.ToString() },
884 { "Gestures", OSDParser.SerializeJsonString(gestures) }
885 };
886
887 OSDMap response = WebUtil.PostToService(m_userServerUrl, requestArgs);
888 if (!response["Success"].AsBoolean())
889 {
890 m_log.Warn("[INVENTORY CONNECTOR]: Failed to save active gestures for " + userID + ": " +
891 response["Message"].AsString());
892 }
893 }
894 }
895}
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs
new file mode 100644
index 0000000..1b5edf4
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs
@@ -0,0 +1,511 @@
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.Collections.Specialized;
31using System.Net;
32using System.Reflection;
33using log4net;
34using Mono.Addins;
35using Nini.Config;
36using OpenSim.Framework;
37using OpenSim.Framework.Servers.HttpServer;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Services.Interfaces;
41using OpenSim.Server.Base;
42using OpenMetaverse;
43using OpenMetaverse.StructuredData;
44
45using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
46
47namespace OpenSim.Services.Connectors.SimianGrid
48{
49 /// <summary>
50 /// Connects avatar presence information (for tracking current location and
51 /// message routing) to the SimianGrid backend
52 /// </summary>
53 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
54 public class SimianPresenceServiceConnector : IPresenceService, ISharedRegionModule
55 {
56 private static readonly ILog m_log =
57 LogManager.GetLogger(
58 MethodBase.GetCurrentMethod().DeclaringType);
59
60 private string m_serverUrl = String.Empty;
61
62 #region ISharedRegionModule
63
64 public Type ReplaceableInterface { get { return null; } }
65 public void RegionLoaded(Scene scene) { }
66 public void PostInitialise() { }
67 public void Close() { }
68
69 public SimianPresenceServiceConnector() { }
70 public string Name { get { return "SimianPresenceServiceConnector"; } }
71 public void AddRegion(Scene scene)
72 {
73 if (!String.IsNullOrEmpty(m_serverUrl))
74 {
75 scene.RegisterModuleInterface<IPresenceService>(this);
76
77 scene.EventManager.OnMakeRootAgent += MakeRootAgentHandler;
78 scene.EventManager.OnNewClient += NewClientHandler;
79 scene.EventManager.OnSignificantClientMovement += SignificantClientMovementHandler;
80
81 LogoutRegionAgents(scene.RegionInfo.RegionID);
82 }
83 }
84 public void RemoveRegion(Scene scene)
85 {
86 if (!String.IsNullOrEmpty(m_serverUrl))
87 {
88 scene.UnregisterModuleInterface<IPresenceService>(this);
89
90 scene.EventManager.OnMakeRootAgent -= MakeRootAgentHandler;
91 scene.EventManager.OnNewClient -= NewClientHandler;
92 scene.EventManager.OnSignificantClientMovement -= SignificantClientMovementHandler;
93
94 LogoutRegionAgents(scene.RegionInfo.RegionID);
95 }
96 }
97
98 #endregion ISharedRegionModule
99
100 public SimianPresenceServiceConnector(IConfigSource source)
101 {
102 Initialise(source);
103 }
104
105 public void Initialise(IConfigSource source)
106 {
107 if (Simian.IsSimianEnabled(source, "PresenceServices"))
108 {
109 IConfig gridConfig = source.Configs["PresenceService"];
110 if (gridConfig == null)
111 {
112 m_log.Error("[PRESENCE CONNECTOR]: PresenceService missing from OpenSim.ini");
113 throw new Exception("Presence connector init error");
114 }
115
116 string serviceUrl = gridConfig.GetString("PresenceServerURI");
117 if (String.IsNullOrEmpty(serviceUrl))
118 {
119 m_log.Error("[PRESENCE CONNECTOR]: No PresenceServerURI in section PresenceService");
120 throw new Exception("Presence connector init error");
121 }
122
123 m_serverUrl = serviceUrl;
124 }
125 }
126
127 #region IPresenceService
128
129 public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID)
130 {
131 m_log.ErrorFormat("[PRESENCE CONNECTOR]: Login requested, UserID={0}, SessionID={1}, SecureSessionID={2}",
132 userID, sessionID, secureSessionID);
133
134 NameValueCollection requestArgs = new NameValueCollection
135 {
136 { "RequestMethod", "AddSession" },
137 { "UserID", userID.ToString() }
138 };
139 if (sessionID != UUID.Zero)
140 {
141 requestArgs["SessionID"] = sessionID.ToString();
142 requestArgs["SecureSessionID"] = secureSessionID.ToString();
143 }
144
145 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
146 bool success = response["Success"].AsBoolean();
147
148 if (!success)
149 m_log.Warn("[PRESENCE CONNECTOR]: Failed to login agent " + userID + ": " + response["Message"].AsString());
150
151 return success;
152 }
153
154 public bool LogoutAgent(UUID sessionID, Vector3 position, Vector3 lookAt)
155 {
156 m_log.InfoFormat("[PRESENCE CONNECTOR]: Logout requested for agent with sessionID " + sessionID);
157
158 NameValueCollection requestArgs = new NameValueCollection
159 {
160 { "RequestMethod", "RemoveSession" },
161 { "SessionID", sessionID.ToString() }
162 };
163
164 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
165 bool success = response["Success"].AsBoolean();
166
167 if (!success)
168 m_log.Warn("[PRESENCE CONNECTOR]: Failed to logout agent with sessionID " + sessionID + ": " + response["Message"].AsString());
169
170 return success;
171 }
172
173 public bool LogoutRegionAgents(UUID regionID)
174 {
175 m_log.InfoFormat("[PRESENCE CONNECTOR]: Logout requested for all agents in region " + regionID);
176
177 NameValueCollection requestArgs = new NameValueCollection
178 {
179 { "RequestMethod", "RemoveSessions" },
180 { "SceneID", regionID.ToString() }
181 };
182
183 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
184 bool success = response["Success"].AsBoolean();
185
186 if (!success)
187 m_log.Warn("[PRESENCE CONNECTOR]: Failed to logout agents from region " + regionID + ": " + response["Message"].AsString());
188
189 return success;
190 }
191
192 public bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt)
193 {
194 //m_log.DebugFormat("[PRESENCE CONNECTOR]: Updating session data for agent with sessionID " + sessionID);
195
196 NameValueCollection requestArgs = new NameValueCollection
197 {
198 { "RequestMethod", "UpdateSession" },
199 { "SessionID", sessionID.ToString() },
200 { "SceneID", regionID.ToString() },
201 { "ScenePosition", position.ToString() },
202 { "SceneLookAt", lookAt.ToString() }
203 };
204
205 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
206 bool success = response["Success"].AsBoolean();
207
208 if (!success)
209 m_log.Warn("[PRESENCE CONNECTOR]: Failed to update agent session " + sessionID + ": " + response["Message"].AsString());
210
211 return success;
212 }
213
214 public PresenceInfo GetAgent(UUID sessionID)
215 {
216 m_log.DebugFormat("[PRESENCE CONNECTOR]: Requesting session data for agent with sessionID " + sessionID);
217
218 NameValueCollection requestArgs = new NameValueCollection
219 {
220 { "RequestMethod", "GetSession" },
221 { "SessionID", sessionID.ToString() }
222 };
223
224 OSDMap sessionResponse = WebUtil.PostToService(m_serverUrl, requestArgs);
225 if (sessionResponse["Success"].AsBoolean())
226 {
227 UUID userID = sessionResponse["UserID"].AsUUID();
228 m_log.DebugFormat("[PRESENCE CONNECTOR]: Requesting user data for " + userID);
229
230 requestArgs = new NameValueCollection
231 {
232 { "RequestMethod", "GetUser" },
233 { "UserID", userID.ToString() }
234 };
235
236 OSDMap userResponse = WebUtil.PostToService(m_serverUrl, requestArgs);
237 if (userResponse["Success"].AsBoolean())
238 return ResponseToPresenceInfo(sessionResponse, userResponse);
239 else
240 m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + userResponse["Message"].AsString());
241 }
242 else
243 {
244 m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve session " + sessionID + ": " + sessionResponse["Message"].AsString());
245 }
246
247 return null;
248 }
249
250 public PresenceInfo[] GetAgents(string[] userIDs)
251 {
252 List<PresenceInfo> presences = new List<PresenceInfo>(userIDs.Length);
253
254 for (int i = 0; i < userIDs.Length; i++)
255 {
256 UUID userID;
257 if (UUID.TryParse(userIDs[i], out userID) && userID != UUID.Zero)
258 presences.AddRange(GetSessions(userID));
259 }
260
261 return presences.ToArray();
262 }
263
264 public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt)
265 {
266 m_log.DebugFormat("[PRESENCE CONNECTOR]: Setting home location for user " + userID);
267
268 NameValueCollection requestArgs = new NameValueCollection
269 {
270 { "RequestMethod", "AddUserData" },
271 { "UserID", userID.ToString() },
272 { "HomeLocation", SerializeLocation(regionID, position, lookAt) }
273 };
274
275 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
276 bool success = response["Success"].AsBoolean();
277
278 if (!success)
279 m_log.Warn("[PRESENCE CONNECTOR]: Failed to set home location for " + userID + ": " + response["Message"].AsString());
280
281 return success;
282 }
283
284 #endregion IPresenceService
285
286 #region Presence Detection
287
288 private void MakeRootAgentHandler(ScenePresence sp)
289 {
290 m_log.DebugFormat("[PRESENCE DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName);
291
292 ReportAgent(sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
293 SetLastLocation(sp.UUID, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
294 }
295
296 private void NewClientHandler(IClientAPI client)
297 {
298 client.OnConnectionClosed += LogoutHandler;
299 }
300
301 private void SignificantClientMovementHandler(IClientAPI client)
302 {
303 ScenePresence sp;
304 if (client.Scene is Scene && ((Scene)client.Scene).TryGetAvatar(client.AgentId, out sp))
305 ReportAgent(sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
306 }
307
308 private void LogoutHandler(IClientAPI client)
309 {
310 if (client.IsLoggingOut)
311 {
312 client.OnConnectionClosed -= LogoutHandler;
313
314 object obj;
315 if (client.Scene.TryGetAvatar(client.AgentId, out obj) && obj is ScenePresence)
316 {
317 // The avatar is still in the scene, we can get the exact logout position
318 ScenePresence sp = (ScenePresence)obj;
319 SetLastLocation(client.AgentId, client.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat);
320 }
321 else
322 {
323 // The avatar was already removed from the scene, store LastLocation using the most recent session data
324 m_log.Warn("[PRESENCE]: " + client.Name + " has already been removed from the scene, storing approximate LastLocation");
325 SetLastLocation(client.SessionId);
326 }
327
328 LogoutAgent(client.SessionId, Vector3.Zero, Vector3.UnitX);
329 }
330 }
331
332 #endregion Presence Detection
333
334 #region Helpers
335
336 private OSDMap GetUserData(UUID userID)
337 {
338 m_log.DebugFormat("[PRESENCE CONNECTOR]: Requesting user data for " + userID);
339
340 NameValueCollection requestArgs = new NameValueCollection
341 {
342 { "RequestMethod", "GetUser" },
343 { "UserID", userID.ToString() }
344 };
345
346 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
347 if (response["Success"].AsBoolean() && response["User"] is OSDMap)
348 return response;
349 else
350 m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + response["Message"].AsString());
351
352 return null;
353 }
354
355 private OSDMap GetSessionData(UUID sessionID)
356 {
357 m_log.DebugFormat("[PRESENCE CONNECTOR]: Requesting session data for session " + sessionID);
358
359 NameValueCollection requestArgs = new NameValueCollection
360 {
361 { "RequestMethod", "GetSession" },
362 { "SessionID", sessionID.ToString() }
363 };
364
365 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
366 if (response["Success"].AsBoolean())
367 return response;
368 else
369 m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve session data for session " + sessionID);
370
371 return null;
372 }
373
374 private List<PresenceInfo> GetSessions(UUID userID)
375 {
376 List<PresenceInfo> presences = new List<PresenceInfo>(1);
377
378 OSDMap userResponse = GetUserData(userID);
379 if (userResponse != null)
380 {
381 m_log.DebugFormat("[PRESENCE CONNECTOR]: Requesting sessions for " + userID);
382
383 NameValueCollection requestArgs = new NameValueCollection
384 {
385 { "RequestMethod", "GetSession" },
386 { "UserID", userID.ToString() }
387 };
388
389 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
390 if (response["Success"].AsBoolean())
391 {
392 PresenceInfo presence = ResponseToPresenceInfo(response, userResponse);
393 if (presence != null)
394 presences.Add(presence);
395 }
396 else
397 {
398 m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve sessions for " + userID + ": " + response["Message"].AsString());
399 }
400 }
401
402 return presences;
403 }
404
405 /// <summary>
406 /// Fetch the last known avatar location with GetSession and persist it
407 /// as user data with AddUserData
408 /// </summary>
409 private bool SetLastLocation(UUID sessionID)
410 {
411 NameValueCollection requestArgs = new NameValueCollection
412 {
413 { "RequestMethod", "GetSession" },
414 { "SessionID", sessionID.ToString() }
415 };
416
417 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
418 bool success = response["Success"].AsBoolean();
419
420 if (success)
421 {
422 UUID userID = response["UserID"].AsUUID();
423 UUID sceneID = response["SceneID"].AsUUID();
424 Vector3 position = response["ScenePosition"].AsVector3();
425 Vector3 lookAt = response["SceneLookAt"].AsVector3();
426
427 return SetLastLocation(userID, sceneID, position, lookAt);
428 }
429 else
430 {
431 m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve presence information for session " + sessionID +
432 " while saving last location: " + response["Message"].AsString());
433 }
434
435 return success;
436 }
437
438 private bool SetLastLocation(UUID userID, UUID sceneID, Vector3 position, Vector3 lookAt)
439 {
440 NameValueCollection requestArgs = new NameValueCollection
441 {
442 { "RequestMethod", "AddUserData" },
443 { "UserID", userID.ToString() },
444 { "LastLocation", SerializeLocation(sceneID, position, lookAt) }
445 };
446
447 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
448 bool success = response["Success"].AsBoolean();
449
450 if (!success)
451 m_log.Warn("[PRESENCE CONNECTOR]: Failed to set last location for " + userID + ": " + response["Message"].AsString());
452
453 return success;
454 }
455
456 private PresenceInfo ResponseToPresenceInfo(OSDMap sessionResponse, OSDMap userResponse)
457 {
458 if (sessionResponse == null)
459 return null;
460
461 PresenceInfo info = new PresenceInfo();
462
463 info.Online = true;
464 info.UserID = sessionResponse["UserID"].AsUUID().ToString();
465 info.RegionID = sessionResponse["SceneID"].AsUUID();
466 info.Position = sessionResponse["ScenePosition"].AsVector3();
467 info.LookAt = sessionResponse["SceneLookAt"].AsVector3();
468
469 if (userResponse != null && userResponse["User"] is OSDMap)
470 {
471 OSDMap user = (OSDMap)userResponse["User"];
472
473 info.Login = user["LastLoginDate"].AsDate();
474 info.Logout = user["LastLogoutDate"].AsDate();
475 DeserializeLocation(user["HomeLocation"].AsString(), out info.HomeRegionID, out info.HomePosition, out info.HomeLookAt);
476 }
477
478 return info;
479 }
480
481 private string SerializeLocation(UUID regionID, Vector3 position, Vector3 lookAt)
482 {
483 return "{" + String.Format("\"SceneID\":\"{0}\",\"Position\":\"{1}\",\"LookAt\":\"{2}\"", regionID, position, lookAt) + "}";
484 }
485
486 private bool DeserializeLocation(string location, out UUID regionID, out Vector3 position, out Vector3 lookAt)
487 {
488 OSDMap map = null;
489
490 try { map = OSDParser.DeserializeJson(location) as OSDMap; }
491 catch { }
492
493 if (map != null)
494 {
495 regionID = map["SceneID"].AsUUID();
496 if (Vector3.TryParse(map["Position"].AsString(), out position) &&
497 Vector3.TryParse(map["LookAt"].AsString(), out lookAt))
498 {
499 return true;
500 }
501 }
502
503 regionID = UUID.Zero;
504 position = Vector3.Zero;
505 lookAt = Vector3.Zero;
506 return false;
507 }
508
509 #endregion Helpers
510 }
511}
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs b/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs
new file mode 100644
index 0000000..9c226fb
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs
@@ -0,0 +1,435 @@
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.Collections.Specialized;
31using System.Reflection;
32using log4net;
33using Mono.Addins;
34using Nini.Config;
35using OpenMetaverse;
36using OpenMetaverse.StructuredData;
37using OpenSim.Framework;
38using OpenSim.Framework.Client;
39using OpenSim.Region.Framework.Interfaces;
40using OpenSim.Region.Framework.Scenes;
41using OpenSim.Services.Interfaces;
42
43namespace OpenSim.Services.Connectors.SimianGrid
44{
45 /// <summary>
46 /// Avatar profile flags
47 /// </summary>
48 [Flags]
49 public enum ProfileFlags : uint
50 {
51 AllowPublish = 1,
52 MaturePublish = 2,
53 Identified = 4,
54 Transacted = 8,
55 Online = 16
56 }
57
58 /// <summary>
59 /// Connects avatar profile and classified queries to the SimianGrid
60 /// backend
61 /// </summary>
62 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
63 public class SimianProfiles : INonSharedRegionModule
64 {
65 private static readonly ILog m_log =
66 LogManager.GetLogger(
67 MethodBase.GetCurrentMethod().DeclaringType);
68
69 private string m_serverUrl = String.Empty;
70
71 #region INonSharedRegionModule
72
73 public Type ReplaceableInterface { get { return null; } }
74 public void RegionLoaded(Scene scene) { }
75 public void Close() { }
76
77 public SimianProfiles() { }
78 public string Name { get { return "SimianProfiles"; } }
79 public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { CheckEstateManager(scene); scene.EventManager.OnClientConnect += ClientConnectHandler; } }
80 public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.EventManager.OnClientConnect -= ClientConnectHandler; } }
81
82 #endregion INonSharedRegionModule
83
84 public SimianProfiles(IConfigSource source)
85 {
86 Initialise(source);
87 }
88
89 public void Initialise(IConfigSource source)
90 {
91 if (Simian.IsSimianEnabled(source, "UserAccountServices"))
92 {
93 IConfig gridConfig = source.Configs["UserAccountService"];
94 if (gridConfig == null)
95 {
96 m_log.Error("[PROFILES]: UserAccountService missing from OpenSim.ini");
97 throw new Exception("Profiles init error");
98 }
99
100 string serviceUrl = gridConfig.GetString("UserAccountServerURI");
101 if (String.IsNullOrEmpty(serviceUrl))
102 {
103 m_log.Error("[PROFILES]: No UserAccountServerURI in section UserAccountService");
104 throw new Exception("Profiles init error");
105 }
106
107 if (!serviceUrl.EndsWith("/"))
108 serviceUrl = serviceUrl + '/';
109
110 m_serverUrl = serviceUrl;
111 }
112 }
113
114 private void ClientConnectHandler(IClientCore clientCore)
115 {
116 if (clientCore is IClientAPI)
117 {
118 IClientAPI client = (IClientAPI)clientCore;
119
120 // Classifieds
121 client.AddGenericPacketHandler("avatarclassifiedsrequest", AvatarClassifiedsRequestHandler);
122 client.OnClassifiedInfoRequest += ClassifiedInfoRequestHandler;
123 client.OnClassifiedInfoUpdate += ClassifiedInfoUpdateHandler;
124 client.OnClassifiedDelete += ClassifiedDeleteHandler;
125
126 // Picks
127 client.AddGenericPacketHandler("avatarpicksrequest", HandleAvatarPicksRequest);
128 client.AddGenericPacketHandler("pickinforequest", HandlePickInfoRequest);
129 client.OnPickInfoUpdate += PickInfoUpdateHandler;
130 client.OnPickDelete += PickDeleteHandler;
131
132 // Notes
133 client.AddGenericPacketHandler("avatarnotesrequest", HandleAvatarNotesRequest);
134 client.OnAvatarNotesUpdate += AvatarNotesUpdateHandler;
135
136 // Profiles
137 client.OnRequestAvatarProperties += RequestAvatarPropertiesHandler;
138 client.OnUpdateAvatarProperties += UpdateAvatarPropertiesHandler;
139 client.OnAvatarInterestUpdate += AvatarInterestUpdateHandler;
140 client.OnUserInfoRequest += UserInfoRequestHandler;
141 client.OnUpdateUserInfo += UpdateUserInfoHandler;
142 }
143 }
144
145 #region Classifieds
146
147 private void AvatarClassifiedsRequestHandler(Object sender, string method, List<String> args)
148 {
149 if (!(sender is IClientAPI))
150 return;
151 IClientAPI client = (IClientAPI)sender;
152
153 UUID targetAvatarID;
154 if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID))
155 {
156 m_log.Error("[PROFILES]: Unrecognized arguments for " + method);
157 return;
158 }
159
160 // FIXME: Query the generic key/value store for classifieds
161 client.SendAvatarClassifiedReply(targetAvatarID, new Dictionary<UUID, string>(0));
162 }
163
164 private void ClassifiedInfoRequestHandler(UUID classifiedID, IClientAPI client)
165 {
166 // FIXME: Fetch this info
167 client.SendClassifiedInfoReply(classifiedID, UUID.Zero, 0, Utils.DateTimeToUnixTime(DateTime.UtcNow + TimeSpan.FromDays(1)),
168 0, String.Empty, String.Empty, UUID.Zero, 0, UUID.Zero, String.Empty, Vector3.Zero, String.Empty, 0, 0);
169 }
170
171 private void ClassifiedInfoUpdateHandler(UUID classifiedID, uint category, string name, string description,
172 UUID parcelID, uint parentEstate, UUID snapshotID, Vector3 globalPos, byte classifiedFlags, int price,
173 IClientAPI client)
174 {
175 // FIXME: Save this info
176 }
177
178 private void ClassifiedDeleteHandler(UUID classifiedID, IClientAPI client)
179 {
180 // FIXME: Delete the specified classified ad
181 }
182
183 #endregion Classifieds
184
185 #region Picks
186
187 private void HandleAvatarPicksRequest(Object sender, string method, List<String> args)
188 {
189 if (!(sender is IClientAPI))
190 return;
191 IClientAPI client = (IClientAPI)sender;
192
193 UUID targetAvatarID;
194 if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID))
195 {
196 m_log.Error("[PROFILES]: Unrecognized arguments for " + method);
197 return;
198 }
199
200 // FIXME: Fetch these
201 client.SendAvatarPicksReply(targetAvatarID, new Dictionary<UUID, string>(0));
202 }
203
204 private void HandlePickInfoRequest(Object sender, string method, List<String> args)
205 {
206 if (!(sender is IClientAPI))
207 return;
208 IClientAPI client = (IClientAPI)sender;
209
210 UUID avatarID;
211 UUID pickID;
212 if (args.Count < 2 || !UUID.TryParse(args[0], out avatarID) || !UUID.TryParse(args[1], out pickID))
213 {
214 m_log.Error("[PROFILES]: Unrecognized arguments for " + method);
215 return;
216 }
217
218 // FIXME: Fetch this
219 client.SendPickInfoReply(pickID, avatarID, false, UUID.Zero, String.Empty, String.Empty, UUID.Zero, String.Empty,
220 String.Empty, String.Empty, Vector3.Zero, 0, false);
221 }
222
223 private void PickInfoUpdateHandler(IClientAPI client, UUID pickID, UUID creatorID, bool topPick, string name,
224 string desc, UUID snapshotID, int sortOrder, bool enabled)
225 {
226 // FIXME: Save this
227 }
228
229 private void PickDeleteHandler(IClientAPI client, UUID pickID)
230 {
231 // FIXME: Delete
232 }
233
234 #endregion Picks
235
236 #region Notes
237
238 private void HandleAvatarNotesRequest(Object sender, string method, List<String> args)
239 {
240 if (!(sender is IClientAPI))
241 return;
242 IClientAPI client = (IClientAPI)sender;
243
244 UUID targetAvatarID;
245 if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID))
246 {
247 m_log.Error("[PROFILES]: Unrecognized arguments for " + method);
248 return;
249 }
250
251 // FIXME: Fetch this
252 client.SendAvatarNotesReply(targetAvatarID, String.Empty);
253 }
254
255 private void AvatarNotesUpdateHandler(IClientAPI client, UUID targetID, string notes)
256 {
257 // FIXME: Save this
258 }
259
260 #endregion Notes
261
262 #region Profiles
263
264 private void RequestAvatarPropertiesHandler(IClientAPI client, UUID avatarID)
265 {
266 OSDMap user = FetchUserData(avatarID);
267
268 ProfileFlags flags = ProfileFlags.AllowPublish | ProfileFlags.MaturePublish;
269
270 if (user != null)
271 {
272 OSDMap about = null;
273 if (user.ContainsKey("LLAbout"))
274 {
275 try { about = OSDParser.DeserializeJson(user["LLAbout"].AsString()) as OSDMap; }
276 catch { }
277 }
278
279 if (about == null)
280 about = new OSDMap(0);
281
282 // Check if this user is a grid operator
283 byte[] charterMember;
284 if (user["AccessLevel"].AsInteger() >= 200)
285 charterMember = Utils.StringToBytes("Operator");
286 else
287 charterMember = Utils.EmptyBytes;
288
289 // Check if the user is online
290 if (client.Scene is Scene)
291 {
292 OpenSim.Services.Interfaces.PresenceInfo[] presences = ((Scene)client.Scene).PresenceService.GetAgents(new string[] { avatarID.ToString() });
293 if (presences != null && presences.Length > 0)
294 flags |= ProfileFlags.Online;
295 }
296
297 // Check if the user is identified
298 if (user["Identified"].AsBoolean())
299 flags |= ProfileFlags.Identified;
300
301 client.SendAvatarProperties(avatarID, about["About"].AsString(), user["CreationDate"].AsDate().ToString("M/d/yyyy",
302 System.Globalization.CultureInfo.InvariantCulture), charterMember, about["FLAbout"].AsString(), (uint)flags,
303 about["FLImage"].AsUUID(), about["Image"].AsUUID(), about["URL"].AsString(), user["Partner"].AsUUID());
304
305 }
306 else
307 {
308 m_log.Warn("[PROFILES]: Failed to fetch profile information for " + client.Name + ", returning default values");
309 client.SendAvatarProperties(avatarID, String.Empty, "1/1/1970", Utils.EmptyBytes,
310 String.Empty, (uint)flags, UUID.Zero, UUID.Zero, String.Empty, UUID.Zero);
311 }
312 }
313
314 private void UpdateAvatarPropertiesHandler(IClientAPI client, UserProfileData profileData)
315 {
316 OSDMap map = new OSDMap
317 {
318 { "About", OSD.FromString(profileData.AboutText) },
319 { "Image", OSD.FromUUID(profileData.Image) },
320 { "FLAbout", OSD.FromString(profileData.FirstLifeAboutText) },
321 { "FLImage", OSD.FromUUID(profileData.FirstLifeImage) },
322 { "URL", OSD.FromString(profileData.ProfileUrl) }
323 };
324
325 AddUserData(client.AgentId, "LLAbout", map);
326 }
327
328 private void AvatarInterestUpdateHandler(IClientAPI client, uint wantmask, string wanttext, uint skillsmask,
329 string skillstext, string languages)
330 {
331 OSDMap map = new OSDMap
332 {
333 { "WantMask", OSD.FromInteger(wantmask) },
334 { "WantText", OSD.FromString(wanttext) },
335 { "SkillsMask", OSD.FromInteger(skillsmask) },
336 { "SkillsText", OSD.FromString(skillstext) },
337 { "Languages", OSD.FromString(languages) }
338 };
339
340 AddUserData(client.AgentId, "LLInterests", map);
341 }
342
343 private void UserInfoRequestHandler(IClientAPI client)
344 {
345 m_log.Error("[PROFILES]: UserInfoRequestHandler");
346
347 // Fetch this user's e-mail address
348 NameValueCollection requestArgs = new NameValueCollection
349 {
350 { "RequestMethod", "GetUser" },
351 { "UserID", client.AgentId.ToString() }
352 };
353
354 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
355 string email = response["Email"].AsString();
356
357 if (!response["Success"].AsBoolean())
358 m_log.Warn("[PROFILES]: GetUser failed during a user info request for " + client.Name);
359
360 client.SendUserInfoReply(false, true, email);
361 }
362
363 private void UpdateUserInfoHandler(bool imViaEmail, bool visible, IClientAPI client)
364 {
365 m_log.Info("[PROFILES]: Ignoring user info update from " + client.Name);
366 }
367
368 #endregion Profiles
369
370 /// <summary>
371 /// Sanity checks regions for a valid estate owner at startup
372 /// </summary>
373 private void CheckEstateManager(Scene scene)
374 {
375 EstateSettings estate = scene.RegionInfo.EstateSettings;
376
377 if (estate.EstateOwner == UUID.Zero)
378 {
379 // Attempt to lookup the grid admin
380 UserAccount admin = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, UUID.Zero);
381 if (admin != null)
382 {
383 m_log.InfoFormat("[PROFILES]: Setting estate {0} (ID: {1}) owner to {2}", estate.EstateName,
384 estate.EstateID, admin.Name);
385
386 estate.EstateOwner = admin.PrincipalID;
387 estate.Save();
388 }
389 else
390 {
391 m_log.WarnFormat("[PROFILES]: Estate {0} (ID: {1}) does not have an owner", estate.EstateName, estate.EstateID);
392 }
393 }
394 }
395
396 private bool AddUserData(UUID userID, string key, OSDMap value)
397 {
398 NameValueCollection requestArgs = new NameValueCollection
399 {
400 { "RequestMethod", "AddUserData" },
401 { "UserID", userID.ToString() },
402 { key, OSDParser.SerializeJsonString(value) }
403 };
404
405 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
406 bool success = response["Success"].AsBoolean();
407
408 if (!success)
409 m_log.WarnFormat("[PROFILES]: Failed to add user data with key {0} for {1}: {2}", key, userID, response["Message"].AsString());
410
411 return success;
412 }
413
414 private OSDMap FetchUserData(UUID userID)
415 {
416 NameValueCollection requestArgs = new NameValueCollection
417 {
418 { "RequestMethod", "GetUser" },
419 { "UserID", userID.ToString() }
420 };
421
422 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
423 if (response["Success"].AsBoolean() && response["User"] is OSDMap)
424 {
425 return (OSDMap)response["User"];
426 }
427 else
428 {
429 m_log.Error("[PROFILES]: Failed to fetch user data for " + userID + ": " + response["Message"].AsString());
430 }
431
432 return null;
433 }
434 }
435}
diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianUserAccountServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianUserAccountServiceConnector.cs
new file mode 100644
index 0000000..bb0ac57
--- /dev/null
+++ b/OpenSim/Services/Connectors/SimianGrid/SimianUserAccountServiceConnector.cs
@@ -0,0 +1,311 @@
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.Collections.Specialized;
31using System.IO;
32using System.Reflection;
33using OpenSim.Framework;
34using OpenSim.Region.Framework.Interfaces;
35using OpenSim.Region.Framework.Scenes;
36using OpenSim.Services.Interfaces;
37using log4net;
38using Mono.Addins;
39using Nini.Config;
40using OpenMetaverse;
41using OpenMetaverse.StructuredData;
42
43namespace OpenSim.Services.Connectors.SimianGrid
44{
45 /// <summary>
46 /// Connects user account data (creating new users, looking up existing
47 /// users) to the SimianGrid backend
48 /// </summary>
49 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
50 public class SimianUserAccountServiceConnector : IUserAccountService, ISharedRegionModule
51 {
52 private static readonly ILog m_log =
53 LogManager.GetLogger(
54 MethodBase.GetCurrentMethod().DeclaringType);
55
56 private string m_serverUrl = String.Empty;
57 private ExpiringCache<UUID, UserAccount> m_accountCache;
58
59 #region ISharedRegionModule
60
61 public Type ReplaceableInterface { get { return null; } }
62 public void RegionLoaded(Scene scene) { }
63 public void PostInitialise() { }
64 public void Close() { }
65
66 public SimianUserAccountServiceConnector() { }
67 public string Name { get { return "SimianUserAccountServiceConnector"; } }
68 public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface<IUserAccountService>(this); } }
69 public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface<IUserAccountService>(this); } }
70
71 #endregion ISharedRegionModule
72
73 public SimianUserAccountServiceConnector(IConfigSource source)
74 {
75 Initialise(source);
76 }
77
78 public void Initialise(IConfigSource source)
79 {
80 if (Simian.IsSimianEnabled(source, "UserAccountServices"))
81 {
82 IConfig assetConfig = source.Configs["UserAccountService"];
83 if (assetConfig == null)
84 {
85 m_log.Error("[ACCOUNT CONNECTOR]: UserAccountService missing from OpenSim.ini");
86 throw new Exception("User account connector init error");
87 }
88
89 string serviceURI = assetConfig.GetString("UserAccountServerURI");
90 if (String.IsNullOrEmpty(serviceURI))
91 {
92 m_log.Error("[ACCOUNT CONNECTOR]: No UserAccountServerURI in section UserAccountService, skipping SimianUserAccountServiceConnector");
93 throw new Exception("User account connector init error");
94 }
95
96 m_accountCache = new ExpiringCache<UUID, UserAccount>();
97 m_serverUrl = serviceURI;
98 }
99 }
100
101 public UserAccount GetUserAccount(UUID scopeID, string firstName, string lastName)
102 {
103 NameValueCollection requestArgs = new NameValueCollection
104 {
105 { "RequestMethod", "GetUser" },
106 { "Name", firstName + ' ' + lastName }
107 };
108
109 return GetUser(requestArgs);
110 }
111
112 public UserAccount GetUserAccount(UUID scopeID, string email)
113 {
114 NameValueCollection requestArgs = new NameValueCollection
115 {
116 { "RequestMethod", "GetUser" },
117 { "Email", email }
118 };
119
120 return GetUser(requestArgs);
121 }
122
123 public UserAccount GetUserAccount(UUID scopeID, UUID userID)
124 {
125 // Cache check
126 UserAccount account;
127 if (m_accountCache.TryGetValue(userID, out account))
128 return account;
129
130 NameValueCollection requestArgs = new NameValueCollection
131 {
132 { "RequestMethod", "GetUser" },
133 { "UserID", userID.ToString() }
134 };
135
136 return GetUser(requestArgs);
137 }
138
139 public List<UserAccount> GetUserAccounts(UUID scopeID, string query)
140 {
141 List<UserAccount> accounts = new List<UserAccount>();
142
143 m_log.DebugFormat("[ACCOUNT CONNECTOR]: Searching for user accounts with name query " + query);
144
145 NameValueCollection requestArgs = new NameValueCollection
146 {
147 { "RequestMethod", "GetUsers" },
148 { "NameQuery", query }
149 };
150
151 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
152 if (response["Success"].AsBoolean())
153 {
154 OSDArray array = response["Users"] as OSDArray;
155 if (array != null && array.Count > 0)
156 {
157 for (int i = 0; i < array.Count; i++)
158 {
159 UserAccount account = ResponseToUserAccount(array[i] as OSDMap);
160 if (account != null)
161 accounts.Add(account);
162 }
163 }
164 else
165 {
166 m_log.Warn("[ACCOUNT CONNECTOR]: Account search failed, response data was in an invalid format");
167 }
168 }
169 else
170 {
171 m_log.Warn("[ACCOUNT CONNECTOR]: Failed to search for account data by name " + query);
172 }
173
174 return accounts;
175 }
176
177 public bool StoreUserAccount(UserAccount data)
178 {
179 m_log.InfoFormat("[ACCOUNT CONNECTOR]: Storing user account for " + data.Name);
180
181 NameValueCollection requestArgs = new NameValueCollection
182 {
183 { "RequestMethod", "AddUser" },
184 { "UserID", data.PrincipalID.ToString() },
185 { "Name", data.Name },
186 { "Email", data.Email },
187 { "AccessLevel", data.UserLevel.ToString() }
188 };
189
190 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
191
192 if (response["Success"].AsBoolean())
193 {
194 m_log.InfoFormat("[ACCOUNT CONNECTOR]: Storing user account data for " + data.Name);
195
196 requestArgs = new NameValueCollection
197 {
198 { "RequestMethod", "AddUserData" },
199 { "UserID", data.PrincipalID.ToString() },
200 { "CreationDate", data.Created.ToString() },
201 { "UserFlags", data.UserFlags.ToString() },
202 { "UserTitle", data.UserTitle }
203 };
204
205 response = WebUtil.PostToService(m_serverUrl, requestArgs);
206 bool success = response["Success"].AsBoolean();
207
208 if (success)
209 {
210 // Cache the user account info
211 m_accountCache.AddOrUpdate(data.PrincipalID, data, DateTime.Now + TimeSpan.FromMinutes(2.0d));
212 }
213 else
214 {
215 m_log.Warn("[ACCOUNT CONNECTOR]: Failed to store user account data for " + data.Name + ": " + response["Message"].AsString());
216 }
217
218 return success;
219 }
220 else
221 {
222 m_log.Warn("[ACCOUNT CONNECTOR]: Failed to store user account for " + data.Name + ": " + response["Message"].AsString());
223 }
224
225 return false;
226 }
227
228 /// <summary>
229 /// Helper method for the various ways of retrieving a user account
230 /// </summary>
231 /// <param name="requestArgs">Service query parameters</param>
232 /// <returns>A UserAccount object on success, null on failure</returns>
233 private UserAccount GetUser(NameValueCollection requestArgs)
234 {
235 string lookupValue = (requestArgs.Count > 1) ? requestArgs[1] : "(Unknown)";
236 m_log.DebugFormat("[ACCOUNT CONNECTOR]: Looking up user account with query: " + lookupValue);
237
238 OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs);
239 if (response["Success"].AsBoolean())
240 {
241 OSDMap user = response["User"] as OSDMap;
242 if (user != null)
243 return ResponseToUserAccount(user);
244 else
245 m_log.Warn("[ACCOUNT CONNECTOR]: Account search failed, response data was in an invalid format");
246 }
247 else
248 {
249 m_log.Warn("[ACCOUNT CONNECTOR]: Failed to lookup user account with query: " + lookupValue);
250 }
251
252 return null;
253 }
254
255 /// <summary>
256 /// Convert a User object in LLSD format to a UserAccount
257 /// </summary>
258 /// <param name="response">LLSD containing user account data</param>
259 /// <returns>A UserAccount object on success, null on failure</returns>
260 private UserAccount ResponseToUserAccount(OSDMap response)
261 {
262 if (response == null)
263 return null;
264
265 UserAccount account = new UserAccount();
266 account.PrincipalID = response["UserID"].AsUUID();
267 account.Created = response["CreationDate"].AsInteger();
268 account.Email = response["Email"].AsString();
269 account.ServiceURLs = new Dictionary<string, object>(0);
270 account.UserFlags = response["UserFlags"].AsInteger();
271 account.UserLevel = response["AccessLevel"].AsInteger();
272 account.UserTitle = response["UserTitle"].AsString();
273 GetFirstLastName(response["Name"].AsString(), out account.FirstName, out account.LastName);
274
275 // Cache the user account info
276 m_accountCache.AddOrUpdate(account.PrincipalID, account, DateTime.Now + TimeSpan.FromMinutes(2.0d));
277
278 return account;
279 }
280
281 /// <summary>
282 /// Convert a name with a single space in it to a first and last name
283 /// </summary>
284 /// <param name="name">A full name such as "John Doe"</param>
285 /// <param name="firstName">First name</param>
286 /// <param name="lastName">Last name (surname)</param>
287 private static void GetFirstLastName(string name, out string firstName, out string lastName)
288 {
289 if (String.IsNullOrEmpty(name))
290 {
291 firstName = String.Empty;
292 lastName = String.Empty;
293 }
294 else
295 {
296 string[] names = name.Split(' ');
297
298 if (names.Length == 2)
299 {
300 firstName = names[0];
301 lastName = names[1];
302 }
303 else
304 {
305 firstName = String.Empty;
306 lastName = name;
307 }
308 }
309 }
310 }
311}