aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/World/Media
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2010-08-06 18:28:53 +0100
committerJustin Clark-Casey (justincc)2010-08-06 18:29:30 +0100
commit1270727c969db07831866947b611d49873031739 (patch)
tree8fd490eaf5c49d69be948fd9e58c0be1f7e38331 /OpenSim/Region/CoreModules/World/Media
parentChange XEngine to use the new constant (diff)
parentfix mysql/mssql prim serialization problem (diff)
downloadopensim-SC_OLD-1270727c969db07831866947b611d49873031739.zip
opensim-SC_OLD-1270727c969db07831866947b611d49873031739.tar.gz
opensim-SC_OLD-1270727c969db07831866947b611d49873031739.tar.bz2
opensim-SC_OLD-1270727c969db07831866947b611d49873031739.tar.xz
Merge branch 'moap'
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Media')
-rw-r--r--OpenSim/Region/CoreModules/World/Media/Moap/MoapModule.cs596
1 files changed, 596 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/World/Media/Moap/MoapModule.cs b/OpenSim/Region/CoreModules/World/Media/Moap/MoapModule.cs
new file mode 100644
index 0000000..82ad109
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Media/Moap/MoapModule.cs
@@ -0,0 +1,596 @@
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;
30using System.Collections.Generic;
31using System.Collections.Specialized;
32using System.Reflection;
33using System.IO;
34using System.Web;
35using System.Xml;
36using log4net;
37using Mono.Addins;
38using Nini.Config;
39using OpenMetaverse;
40using OpenMetaverse.Messages.Linden;
41using OpenMetaverse.StructuredData;
42using OpenSim.Framework;
43using OpenSim.Framework.Capabilities;
44using OpenSim.Framework.Servers;
45using OpenSim.Framework.Servers.HttpServer;
46using OpenSim.Region.Framework.Interfaces;
47using OpenSim.Region.Framework.Scenes;
48using OpenSim.Services.Interfaces;
49using Caps = OpenSim.Framework.Capabilities.Caps;
50using OSDArray = OpenMetaverse.StructuredData.OSDArray;
51using OSDMap = OpenMetaverse.StructuredData.OSDMap;
52
53namespace OpenSim.Region.CoreModules.Media.Moap
54{
55 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MoapModule")]
56 public class MoapModule : INonSharedRegionModule, IMoapModule
57 {
58 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
59
60 public string Name { get { return "MoapModule"; } }
61 public Type ReplaceableInterface { get { return null; } }
62
63 /// <summary>
64 /// Is this module enabled?
65 /// </summary>
66 protected bool m_isEnabled = true;
67
68 /// <summary>
69 /// The scene to which this module is attached
70 /// </summary>
71 protected Scene m_scene;
72
73 /// <summary>
74 /// Track the ObjectMedia capabilities given to users keyed by path
75 /// </summary>
76 protected Dictionary<string, UUID> m_omCapUsers = new Dictionary<string, UUID>();
77
78 /// <summary>
79 /// Track the ObjectMedia capabilities given to users keyed by agent. Lock m_omCapUsers to manipulate.
80 /// </summary>
81 protected Dictionary<UUID, string> m_omCapUrls = new Dictionary<UUID, string>();
82
83 /// <summary>
84 /// Track the ObjectMediaUpdate capabilities given to users keyed by path
85 /// </summary>
86 protected Dictionary<string, UUID> m_omuCapUsers = new Dictionary<string, UUID>();
87
88 /// <summary>
89 /// Track the ObjectMediaUpdate capabilities given to users keyed by agent. Lock m_omuCapUsers to manipulate
90 /// </summary>
91 protected Dictionary<UUID, string> m_omuCapUrls = new Dictionary<UUID, string>();
92
93 public void Initialise(IConfigSource configSource)
94 {
95 IConfig config = configSource.Configs["MediaOnAPrim"];
96
97 if (config != null && !config.GetBoolean("Enabled", false))
98 m_isEnabled = false;
99// else
100// m_log.Debug("[MOAP]: Initialised module.")l
101 }
102
103 public void AddRegion(Scene scene)
104 {
105 if (!m_isEnabled)
106 return;
107
108 m_scene = scene;
109 m_scene.RegisterModuleInterface<IMoapModule>(this);
110 }
111
112 public void RemoveRegion(Scene scene) {}
113
114 public void RegionLoaded(Scene scene)
115 {
116 if (!m_isEnabled)
117 return;
118
119 m_scene.EventManager.OnRegisterCaps += OnRegisterCaps;
120 m_scene.EventManager.OnDeregisterCaps += OnDeregisterCaps;
121 m_scene.EventManager.OnSceneObjectPartCopy += OnSceneObjectPartCopy;
122 }
123
124 public void Close()
125 {
126 if (!m_isEnabled)
127 return;
128
129 m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps;
130 m_scene.EventManager.OnDeregisterCaps -= OnDeregisterCaps;
131 m_scene.EventManager.OnSceneObjectPartCopy -= OnSceneObjectPartCopy;
132 }
133
134 public void OnRegisterCaps(UUID agentID, Caps caps)
135 {
136// m_log.DebugFormat(
137// "[MOAP]: Registering ObjectMedia and ObjectMediaNavigate capabilities for agent {0}", agentID);
138
139 string omCapUrl = "/CAPS/" + UUID.Random();
140
141 lock (m_omCapUsers)
142 {
143 m_omCapUsers[omCapUrl] = agentID;
144 m_omCapUrls[agentID] = omCapUrl;
145
146 // Even though we're registering for POST we're going to get GETS and UPDATES too
147 caps.RegisterHandler(
148 "ObjectMedia", new RestStreamHandler("POST", omCapUrl, HandleObjectMediaMessage));
149 }
150
151 string omuCapUrl = "/CAPS/" + UUID.Random();
152
153 lock (m_omuCapUsers)
154 {
155 m_omuCapUsers[omuCapUrl] = agentID;
156 m_omuCapUrls[agentID] = omuCapUrl;
157
158 // Even though we're registering for POST we're going to get GETS and UPDATES too
159 caps.RegisterHandler(
160 "ObjectMediaNavigate", new RestStreamHandler("POST", omuCapUrl, HandleObjectMediaNavigateMessage));
161 }
162 }
163
164 public void OnDeregisterCaps(UUID agentID, Caps caps)
165 {
166 lock (m_omCapUsers)
167 {
168 string path = m_omCapUrls[agentID];
169 m_omCapUrls.Remove(agentID);
170 m_omCapUsers.Remove(path);
171 }
172
173 lock (m_omuCapUsers)
174 {
175 string path = m_omuCapUrls[agentID];
176 m_omuCapUrls.Remove(agentID);
177 m_omuCapUsers.Remove(path);
178 }
179 }
180
181 protected void OnSceneObjectPartCopy(SceneObjectPart copy, SceneObjectPart original, bool userExposed)
182 {
183 if (original.Shape.Media != null)
184 {
185 PrimitiveBaseShape.MediaList dupeMedia = new PrimitiveBaseShape.MediaList();
186 lock (original.Shape.Media)
187 {
188 foreach (MediaEntry me in original.Shape.Media)
189 {
190 if (me != null)
191 dupeMedia.Add(MediaEntry.FromOSD(me.GetOSD()));
192 else
193 dupeMedia.Add(null);
194 }
195 }
196
197 copy.Shape.Media = dupeMedia;
198 }
199 }
200
201 public MediaEntry GetMediaEntry(SceneObjectPart part, int face)
202 {
203 MediaEntry me = null;
204
205 CheckFaceParam(part, face);
206
207 List<MediaEntry> media = part.Shape.Media;
208
209 if (null == media)
210 {
211 me = null;
212 }
213 else
214 {
215 lock (media)
216 me = media[face];
217
218 // TODO: Really need a proper copy constructor down in libopenmetaverse
219 if (me != null)
220 me = MediaEntry.FromOSD(me.GetOSD());
221 }
222
223// m_log.DebugFormat("[MOAP]: GetMediaEntry for {0} face {1} found {2}", part.Name, face, me);
224
225 return me;
226 }
227
228 public void SetMediaEntry(SceneObjectPart part, int face, MediaEntry me)
229 {
230 CheckFaceParam(part, face);
231
232 if (null == part.Shape.Media)
233 part.Shape.Media = new PrimitiveBaseShape.MediaList(new MediaEntry[part.GetNumberOfSides()]);
234
235 lock (part.Shape.Media)
236 part.Shape.Media[face] = me;
237
238 UpdateMediaUrl(part, UUID.Zero);
239 part.ScheduleFullUpdate();
240 part.TriggerScriptChangedEvent(Changed.MEDIA);
241 }
242
243 public void ClearMediaEntry(SceneObjectPart part, int face)
244 {
245 SetMediaEntry(part, face, null);
246 }
247
248 /// <summary>
249 /// Sets or gets per face media textures.
250 /// </summary>
251 /// <param name="request"></param>
252 /// <param name="path"></param>
253 /// <param name="param"></param>
254 /// <param name="httpRequest"></param>
255 /// <param name="httpResponse"></param>
256 /// <returns></returns>
257 protected string HandleObjectMediaMessage(
258 string request, string path, string param, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
259 {
260// m_log.DebugFormat("[MOAP]: Got ObjectMedia path [{0}], raw request [{1}]", path, request);
261
262 OSDMap osd = (OSDMap)OSDParser.DeserializeLLSDXml(request);
263 ObjectMediaMessage omm = new ObjectMediaMessage();
264 omm.Deserialize(osd);
265
266 if (omm.Request is ObjectMediaRequest)
267 return HandleObjectMediaRequest(omm.Request as ObjectMediaRequest);
268 else if (omm.Request is ObjectMediaUpdate)
269 return HandleObjectMediaUpdate(path, omm.Request as ObjectMediaUpdate);
270
271 throw new Exception(
272 string.Format(
273 "[MOAP]: ObjectMediaMessage has unrecognized ObjectMediaBlock of {0}",
274 omm.Request.GetType()));
275 }
276
277 /// <summary>
278 /// Handle a fetch request for media textures
279 /// </summary>
280 /// <param name="omr"></param>
281 /// <returns></returns>
282 protected string HandleObjectMediaRequest(ObjectMediaRequest omr)
283 {
284 UUID primId = omr.PrimID;
285
286 SceneObjectPart part = m_scene.GetSceneObjectPart(primId);
287
288 if (null == part)
289 {
290 m_log.WarnFormat(
291 "[MOAP]: Received a GET ObjectMediaRequest for prim {0} but this doesn't exist in region {1}",
292 primId, m_scene.RegionInfo.RegionName);
293 return string.Empty;
294 }
295
296 if (null == part.Shape.Media)
297 return string.Empty;
298
299 ObjectMediaResponse resp = new ObjectMediaResponse();
300
301 resp.PrimID = primId;
302
303 lock (part.Shape.Media)
304 resp.FaceMedia = part.Shape.Media.ToArray();
305
306 resp.Version = part.MediaUrl;
307
308 string rawResp = OSDParser.SerializeLLSDXmlString(resp.Serialize());
309
310// m_log.DebugFormat("[MOAP]: Got HandleObjectMediaRequestGet raw response is [{0}]", rawResp);
311
312 return rawResp;
313 }
314
315 /// <summary>
316 /// Handle an update of media textures.
317 /// </summary>
318 /// <param name="path">Path on which this request was made</param>
319 /// <param name="omu">/param>
320 /// <returns></returns>
321 protected string HandleObjectMediaUpdate(string path, ObjectMediaUpdate omu)
322 {
323 UUID primId = omu.PrimID;
324
325 SceneObjectPart part = m_scene.GetSceneObjectPart(primId);
326
327 if (null == part)
328 {
329 m_log.WarnFormat(
330 "[MOAP]: Received an UPDATE ObjectMediaRequest for prim {0} but this doesn't exist in region {1}",
331 primId, m_scene.RegionInfo.RegionName);
332 return string.Empty;
333 }
334
335// m_log.DebugFormat("[MOAP]: Received {0} media entries for prim {1}", omu.FaceMedia.Length, primId);
336
337// for (int i = 0; i < omu.FaceMedia.Length; i++)
338// {
339// MediaEntry me = omu.FaceMedia[i];
340// string v = (null == me ? "null": OSDParser.SerializeLLSDXmlString(me.GetOSD()));
341// m_log.DebugFormat("[MOAP]: Face {0} [{1}]", i, v);
342// }
343
344 if (omu.FaceMedia.Length > part.GetNumberOfSides())
345 {
346 m_log.WarnFormat(
347 "[MOAP]: Received {0} media entries from client for prim {1} {2} but this prim has only {3} faces. Dropping request.",
348 omu.FaceMedia.Length, part.Name, part.UUID, part.GetNumberOfSides());
349 return string.Empty;
350 }
351
352 UUID agentId = default(UUID);
353
354 lock (m_omCapUsers)
355 agentId = m_omCapUsers[path];
356
357 List<MediaEntry> media = part.Shape.Media;
358
359 if (null == media)
360 {
361// m_log.DebugFormat("[MOAP]: Setting all new media list for {0}", part.Name);
362 part.Shape.Media = new PrimitiveBaseShape.MediaList(omu.FaceMedia);
363
364 for (int i = 0; i < omu.FaceMedia.Length; i++)
365 {
366 if (omu.FaceMedia[i] != null)
367 {
368 // FIXME: Race condition here since some other texture entry manipulator may overwrite/get
369 // overwritten. Unfortunately, PrimitiveBaseShape does not allow us to change texture entry
370 // directly.
371 Primitive.TextureEntry te = part.Shape.Textures;
372 Primitive.TextureEntryFace face = te.CreateFace((uint)i);
373 face.MediaFlags = true;
374 part.Shape.Textures = te;
375// m_log.DebugFormat(
376// "[MOAP]: Media flags for face {0} is {1}",
377// i, part.Shape.Textures.FaceTextures[i].MediaFlags);
378 }
379 }
380 }
381 else
382 {
383 // We need to go through the media textures one at a time to make sure that we have permission
384 // to change them
385
386 // FIXME: Race condition here since some other texture entry manipulator may overwrite/get
387 // overwritten. Unfortunately, PrimitiveBaseShape does not allow us to change texture entry
388 // directly.
389 Primitive.TextureEntry te = part.Shape.Textures;
390
391 lock (media)
392 {
393 for (int i = 0; i < media.Count; i++)
394 {
395 if (m_scene.Permissions.CanControlPrimMedia(agentId, part.UUID, i))
396 {
397 media[i] = omu.FaceMedia[i];
398
399 // When a face is cleared this is done by setting the MediaFlags in the TextureEntry via a normal
400 // texture update, so we don't need to worry about clearing MediaFlags here.
401 if (null == media[i])
402 continue;
403
404 Primitive.TextureEntryFace face = te.CreateFace((uint)i);
405 face.MediaFlags = true;
406
407 // m_log.DebugFormat(
408 // "[MOAP]: Media flags for face {0} is {1}",
409 // i, face.MediaFlags);
410 // m_log.DebugFormat("[MOAP]: Set media entry for face {0} on {1}", i, part.Name);
411 }
412 }
413 }
414
415 part.Shape.Textures = te;
416
417// for (int i2 = 0; i2 < part.Shape.Textures.FaceTextures.Length; i2++)
418// m_log.DebugFormat("[MOAP]: FaceTexture[{0}] is {1}", i2, part.Shape.Textures.FaceTextures[i2]);
419 }
420
421 UpdateMediaUrl(part, agentId);
422
423 // Arguably, we could avoid sending a full update to the avatar that just changed the texture.
424 part.ScheduleFullUpdate();
425
426 part.TriggerScriptChangedEvent(Changed.MEDIA);
427
428 return string.Empty;
429 }
430
431 /// <summary>
432 /// Received from the viewer if a user has changed the url of a media texture.
433 /// </summary>
434 /// <param name="request"></param>
435 /// <param name="path"></param>
436 /// <param name="param"></param>
437 /// <param name="httpRequest">/param>
438 /// <param name="httpResponse">/param>
439 /// <returns></returns>
440 protected string HandleObjectMediaNavigateMessage(
441 string request, string path, string param, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
442 {
443// m_log.DebugFormat("[MOAP]: Got ObjectMediaNavigate request [{0}]", request);
444
445 OSDMap osd = (OSDMap)OSDParser.DeserializeLLSDXml(request);
446 ObjectMediaNavigateMessage omn = new ObjectMediaNavigateMessage();
447 omn.Deserialize(osd);
448
449 UUID primId = omn.PrimID;
450
451 SceneObjectPart part = m_scene.GetSceneObjectPart(primId);
452
453 if (null == part)
454 {
455 m_log.WarnFormat(
456 "[MOAP]: Received an ObjectMediaNavigateMessage for prim {0} but this doesn't exist in region {1}",
457 primId, m_scene.RegionInfo.RegionName);
458 return string.Empty;
459 }
460
461 UUID agentId = default(UUID);
462
463 lock (m_omuCapUsers)
464 agentId = m_omuCapUsers[path];
465
466 if (!m_scene.Permissions.CanInteractWithPrimMedia(agentId, part.UUID, omn.Face))
467 return string.Empty;
468
469// m_log.DebugFormat(
470// "[MOAP]: Received request to update media entry for face {0} on prim {1} {2} to {3}",
471// omn.Face, part.Name, part.UUID, omn.URL);
472
473 // If media has never been set for this prim, then just return.
474 if (null == part.Shape.Media)
475 return string.Empty;
476
477 MediaEntry me = null;
478
479 lock (part.Shape.Media)
480 me = part.Shape.Media[omn.Face];
481
482 // Do the same if media has not been set up for a specific face
483 if (null == me)
484 return string.Empty;
485
486 if (me.EnableWhiteList)
487 {
488 if (!CheckUrlAgainstWhitelist(omn.URL, me.WhiteList))
489 {
490// m_log.DebugFormat(
491// "[MOAP]: Blocking change of face {0} on prim {1} {2} to {3} since it's not on the enabled whitelist",
492// omn.Face, part.Name, part.UUID, omn.URL);
493
494 return string.Empty;
495 }
496 }
497
498 me.CurrentURL = omn.URL;
499
500 UpdateMediaUrl(part, agentId);
501
502 part.ScheduleFullUpdate();
503
504 part.TriggerScriptChangedEvent(Changed.MEDIA);
505
506 return OSDParser.SerializeLLSDXmlString(new OSD());
507 }
508
509 /// <summary>
510 /// Check that the face number is valid for the given prim.
511 /// </summary>
512 /// <param name="part"></param>
513 /// <param name="face"></param>
514 protected void CheckFaceParam(SceneObjectPart part, int face)
515 {
516 if (face < 0)
517 throw new ArgumentException("Face cannot be less than zero");
518
519 int maxFaces = part.GetNumberOfSides() - 1;
520 if (face > maxFaces)
521 throw new ArgumentException(
522 string.Format("Face argument was {0} but max is {1}", face, maxFaces));
523 }
524
525 /// <summary>
526 /// Update the media url of the given part
527 /// </summary>
528 /// <param name="part"></param>
529 /// <param name="updateId">
530 /// The id to attach to this update. Normally, this is the user that changed the
531 /// texture
532 /// </param>
533 protected void UpdateMediaUrl(SceneObjectPart part, UUID updateId)
534 {
535 if (null == part.MediaUrl)
536 {
537 // TODO: We can't set the last changer until we start tracking which cap we give to which agent id
538 part.MediaUrl = "x-mv:0000000000/" + updateId;
539 }
540 else
541 {
542 string rawVersion = part.MediaUrl.Substring(5, 10);
543 int version = int.Parse(rawVersion);
544 part.MediaUrl = string.Format("x-mv:{0:D10}/{1}", ++version, updateId);
545 }
546
547// m_log.DebugFormat("[MOAP]: Storing media url [{0}] in prim {1} {2}", part.MediaUrl, part.Name, part.UUID);
548 }
549
550 /// <summary>
551 /// Check the given url against the given whitelist.
552 /// </summary>
553 /// <param name="rawUrl"></param>
554 /// <param name="whitelist"></param>
555 /// <returns>true if the url matches an entry on the whitelist, false otherwise</returns>
556 protected bool CheckUrlAgainstWhitelist(string rawUrl, string[] whitelist)
557 {
558 Uri url = new Uri(rawUrl);
559
560 foreach (string origWlUrl in whitelist)
561 {
562 string wlUrl = origWlUrl;
563
564 // Deal with a line-ending wildcard
565 if (wlUrl.EndsWith("*"))
566 wlUrl = wlUrl.Remove(wlUrl.Length - 1);
567
568// m_log.DebugFormat("[MOAP]: Checking whitelist URL pattern {0}", origWlUrl);
569
570 // Handle a line starting wildcard slightly differently since this can only match the domain, not the path
571 if (wlUrl.StartsWith("*"))
572 {
573 wlUrl = wlUrl.Substring(1);
574
575 if (url.Host.Contains(wlUrl))
576 {
577// m_log.DebugFormat("[MOAP]: Whitelist URL {0} matches {1}", origWlUrl, rawUrl);
578 return true;
579 }
580 }
581 else
582 {
583 string urlToMatch = url.Authority + url.AbsolutePath;
584
585 if (urlToMatch.StartsWith(wlUrl))
586 {
587// m_log.DebugFormat("[MOAP]: Whitelist URL {0} matches {1}", origWlUrl, rawUrl);
588 return true;
589 }
590 }
591 }
592
593 return false;
594 }
595 }
596} \ No newline at end of file