OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
MoapModule.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Collections.Specialized;
32 using System.Reflection;
33 using System.IO;
34 using System.Web;
35 using System.Xml;
36 using log4net;
37 using Mono.Addins;
38 using Nini.Config;
39 using OpenMetaverse;
40 using OpenMetaverse.Messages.Linden;
41 using OpenMetaverse.StructuredData;
42 using OpenSim.Framework;
43 using OpenSim.Framework.Capabilities;
44 using OpenSim.Framework.Servers;
45 using OpenSim.Framework.Servers.HttpServer;
46 using OpenSim.Region.Framework.Interfaces;
47 using OpenSim.Region.Framework.Scenes;
48 using OpenSim.Services.Interfaces;
52 
53 namespace OpenSim.Region.CoreModules.World.Media.Moap
54 {
55  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MoapModule")]
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 
66  protected bool m_isEnabled = true;
67 
71  protected Scene m_scene;
72 
76  protected Dictionary<string, UUID> m_omCapUsers = new Dictionary<string, UUID>();
77 
81  protected Dictionary<UUID, string> m_omCapUrls = new Dictionary<UUID, string>();
82 
86  protected Dictionary<string, UUID> m_omuCapUsers = new Dictionary<string, UUID>();
87 
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",
149  new RestStreamHandler(
150  "POST", omCapUrl, HandleObjectMediaMessage, "ObjectMedia", agentID.ToString()));
151  }
152 
153  string omuCapUrl = "/CAPS/" + UUID.Random();
154 
155  lock (m_omuCapUsers)
156  {
157  m_omuCapUsers[omuCapUrl] = agentID;
158  m_omuCapUrls[agentID] = omuCapUrl;
159 
160  // Even though we're registering for POST we're going to get GETS and UPDATES too
161  caps.RegisterHandler(
162  "ObjectMediaNavigate",
163  new RestStreamHandler(
164  "POST", omuCapUrl, HandleObjectMediaNavigateMessage, "ObjectMediaNavigate", agentID.ToString()));
165  }
166  }
167 
168  public void OnDeregisterCaps(UUID agentID, Caps caps)
169  {
170  lock (m_omCapUsers)
171  {
172  string path = m_omCapUrls[agentID];
173  m_omCapUrls.Remove(agentID);
174  m_omCapUsers.Remove(path);
175  }
176 
177  lock (m_omuCapUsers)
178  {
179  string path = m_omuCapUrls[agentID];
180  m_omuCapUrls.Remove(agentID);
181  m_omuCapUsers.Remove(path);
182  }
183  }
184 
185  protected void OnSceneObjectPartCopy(SceneObjectPart copy, SceneObjectPart original, bool userExposed)
186  {
187  if (original.Shape.Media != null)
188  {
190  lock (original.Shape.Media)
191  {
192  foreach (MediaEntry me in original.Shape.Media)
193  {
194  if (me != null)
195  dupeMedia.Add(MediaEntry.FromOSD(me.GetOSD()));
196  else
197  dupeMedia.Add(null);
198  }
199  }
200 
201  copy.Shape.Media = dupeMedia;
202  }
203  }
204 
205  public MediaEntry GetMediaEntry(SceneObjectPart part, int face)
206  {
207  MediaEntry me = null;
208 
209  CheckFaceParam(part, face);
210 
211  List<MediaEntry> media = part.Shape.Media;
212 
213  if (null == media)
214  {
215  me = null;
216  }
217  else
218  {
219  lock (media)
220  me = media[face];
221 
222  // TODO: Really need a proper copy constructor down in libopenmetaverse
223  if (me != null)
224  me = MediaEntry.FromOSD(me.GetOSD());
225  }
226 
227 // m_log.DebugFormat("[MOAP]: GetMediaEntry for {0} face {1} found {2}", part.Name, face, me);
228 
229  return me;
230  }
231 
238  public void SetMediaEntry(SceneObjectPart part, int face, MediaEntry me)
239  {
240 // m_log.DebugFormat("[MOAP]: SetMediaEntry for {0}, face {1}", part.Name, face);
241 
242  CheckFaceParam(part, face);
243 
244  if (null == part.Shape.Media)
245  {
246  if (me == null)
247  return;
248  else
249  part.Shape.Media = new PrimitiveBaseShape.MediaList(new MediaEntry[part.GetNumberOfSides()]);
250  }
251 
252  lock (part.Shape.Media)
253  part.Shape.Media[face] = me;
254 
255  UpdateMediaUrl(part, UUID.Zero);
256 
257  SetPartMediaFlags(part, face, me != null);
258 
259  part.ScheduleFullUpdate();
260  part.TriggerScriptChangedEvent(Changed.MEDIA);
261  }
262 
268  public void ClearMediaEntry(SceneObjectPart part, int face)
269  {
270  SetMediaEntry(part, face, null);
271  }
272 
282  protected void SetPartMediaFlags(SceneObjectPart part, int face, bool flag)
283  {
284  Primitive.TextureEntry te = part.Shape.Textures;
285  Primitive.TextureEntryFace teFace = te.CreateFace((uint)face);
286  teFace.MediaFlags = flag;
287  part.Shape.Textures = te;
288  }
289 
299  protected string HandleObjectMediaMessage(
300  string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
301  {
302 // m_log.DebugFormat("[MOAP]: Got ObjectMedia path [{0}], raw request [{1}]", path, request);
303 
304  OSDMap osd = (OSDMap)OSDParser.DeserializeLLSDXml(request);
305  ObjectMediaMessage omm = new ObjectMediaMessage();
306  omm.Deserialize(osd);
307 
308  if (omm.Request is ObjectMediaRequest)
309  return HandleObjectMediaRequest(omm.Request as ObjectMediaRequest);
310  else if (omm.Request is ObjectMediaUpdate)
311  return HandleObjectMediaUpdate(path, omm.Request as ObjectMediaUpdate);
312 
313  throw new Exception(
314  string.Format(
315  "[MOAP]: ObjectMediaMessage has unrecognized ObjectMediaBlock of {0}",
316  omm.Request.GetType()));
317  }
318 
324  protected string HandleObjectMediaRequest(ObjectMediaRequest omr)
325  {
326  UUID primId = omr.PrimID;
327 
328  SceneObjectPart part = m_scene.GetSceneObjectPart(primId);
329 
330  if (null == part)
331  {
332  m_log.WarnFormat(
333  "[MOAP]: Received a GET ObjectMediaRequest for prim {0} but this doesn't exist in region {1}",
334  primId, m_scene.RegionInfo.RegionName);
335  return string.Empty;
336  }
337 
338  if (null == part.Shape.Media)
339  return string.Empty;
340 
341  ObjectMediaResponse resp = new ObjectMediaResponse();
342 
343  resp.PrimID = primId;
344 
345  lock (part.Shape.Media)
346  resp.FaceMedia = part.Shape.Media.ToArray();
347 
348  resp.Version = part.MediaUrl;
349 
350  string rawResp = OSDParser.SerializeLLSDXmlString(resp.Serialize());
351 
352 // m_log.DebugFormat("[MOAP]: Got HandleObjectMediaRequestGet raw response is [{0}]", rawResp);
353 
354  return rawResp;
355  }
356 
363  protected string HandleObjectMediaUpdate(string path, ObjectMediaUpdate omu)
364  {
365  UUID primId = omu.PrimID;
366 
367  SceneObjectPart part = m_scene.GetSceneObjectPart(primId);
368 
369  if (null == part)
370  {
371  m_log.WarnFormat(
372  "[MOAP]: Received an UPDATE ObjectMediaRequest for prim {0} but this doesn't exist in region {1}",
373  primId, m_scene.RegionInfo.RegionName);
374  return string.Empty;
375  }
376 
377 // m_log.DebugFormat("[MOAP]: Received {0} media entries for prim {1}", omu.FaceMedia.Length, primId);
378 //
379 // for (int i = 0; i < omu.FaceMedia.Length; i++)
380 // {
381 // MediaEntry me = omu.FaceMedia[i];
382 // string v = (null == me ? "null": OSDParser.SerializeLLSDXmlString(me.GetOSD()));
383 // m_log.DebugFormat("[MOAP]: Face {0} [{1}]", i, v);
384 // }
385 
386  if (omu.FaceMedia.Length > part.GetNumberOfSides())
387  {
388  m_log.WarnFormat(
389  "[MOAP]: Received {0} media entries from client for prim {1} {2} but this prim has only {3} faces. Dropping request.",
390  omu.FaceMedia.Length, part.Name, part.UUID, part.GetNumberOfSides());
391  return string.Empty;
392  }
393 
394  UUID agentId = default(UUID);
395 
396  lock (m_omCapUsers)
397  agentId = m_omCapUsers[path];
398 
399  List<MediaEntry> media = part.Shape.Media;
400 
401  if (null == media)
402  {
403 // m_log.DebugFormat("[MOAP]: Setting all new media list for {0}", part.Name);
404  part.Shape.Media = new PrimitiveBaseShape.MediaList(omu.FaceMedia);
405 
406  for (int i = 0; i < omu.FaceMedia.Length; i++)
407  {
408  if (omu.FaceMedia[i] != null)
409  {
410  // FIXME: Race condition here since some other texture entry manipulator may overwrite/get
411  // overwritten. Unfortunately, PrimitiveBaseShape does not allow us to change texture entry
412  // directly.
413  SetPartMediaFlags(part, i, true);
414 // m_log.DebugFormat(
415 // "[MOAP]: Media flags for face {0} is {1}",
416 // i, part.Shape.Textures.FaceTextures[i].MediaFlags);
417  }
418  }
419  }
420  else
421  {
422 // m_log.DebugFormat("[MOAP]: Setting existing media list for {0}", part.Name);
423 
424  // We need to go through the media textures one at a time to make sure that we have permission
425  // to change them
426 
427  // FIXME: Race condition here since some other texture entry manipulator may overwrite/get
428  // overwritten. Unfortunately, PrimitiveBaseShape does not allow us to change texture entry
429  // directly.
430  Primitive.TextureEntry te = part.Shape.Textures;
431 
432  lock (media)
433  {
434  for (int i = 0; i < media.Count; i++)
435  {
436  if (m_scene.Permissions.CanControlPrimMedia(agentId, part.UUID, i))
437  {
438  media[i] = omu.FaceMedia[i];
439 
440  // When a face is cleared this is done by setting the MediaFlags in the TextureEntry via a normal
441  // texture update, so we don't need to worry about clearing MediaFlags here.
442  if (null == media[i])
443  continue;
444 
445  SetPartMediaFlags(part, i, true);
446 
447  // m_log.DebugFormat(
448  // "[MOAP]: Media flags for face {0} is {1}",
449  // i, face.MediaFlags);
450  // m_log.DebugFormat("[MOAP]: Set media entry for face {0} on {1}", i, part.Name);
451  }
452  }
453  }
454 
455  part.Shape.Textures = te;
456 
457 // for (int i2 = 0; i2 < part.Shape.Textures.FaceTextures.Length; i2++)
458 // m_log.DebugFormat("[MOAP]: FaceTexture[{0}] is {1}", i2, part.Shape.Textures.FaceTextures[i2]);
459  }
460 
461  UpdateMediaUrl(part, agentId);
462 
463  // Arguably, we could avoid sending a full update to the avatar that just changed the texture.
464  part.ScheduleFullUpdate();
465 
466  part.TriggerScriptChangedEvent(Changed.MEDIA);
467 
468  return string.Empty;
469  }
470 
481  string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
482  {
483 // m_log.DebugFormat("[MOAP]: Got ObjectMediaNavigate request [{0}]", request);
484 
485  OSDMap osd = (OSDMap)OSDParser.DeserializeLLSDXml(request);
486  ObjectMediaNavigateMessage omn = new ObjectMediaNavigateMessage();
487  omn.Deserialize(osd);
488 
489  UUID primId = omn.PrimID;
490 
491  SceneObjectPart part = m_scene.GetSceneObjectPart(primId);
492 
493  if (null == part)
494  {
495  m_log.WarnFormat(
496  "[MOAP]: Received an ObjectMediaNavigateMessage for prim {0} but this doesn't exist in region {1}",
497  primId, m_scene.RegionInfo.RegionName);
498  return string.Empty;
499  }
500 
501  UUID agentId = default(UUID);
502 
503  lock (m_omuCapUsers)
504  agentId = m_omuCapUsers[path];
505 
506  if (!m_scene.Permissions.CanInteractWithPrimMedia(agentId, part.UUID, omn.Face))
507  return string.Empty;
508 
509 // m_log.DebugFormat(
510 // "[MOAP]: Received request to update media entry for face {0} on prim {1} {2} to {3}",
511 // omn.Face, part.Name, part.UUID, omn.URL);
512 
513  // If media has never been set for this prim, then just return.
514  if (null == part.Shape.Media)
515  return string.Empty;
516 
517  MediaEntry me = null;
518 
519  lock (part.Shape.Media)
520  me = part.Shape.Media[omn.Face];
521 
522  // Do the same if media has not been set up for a specific face
523  if (null == me)
524  return string.Empty;
525 
526  if (me.EnableWhiteList)
527  {
528  if (!CheckUrlAgainstWhitelist(omn.URL, me.WhiteList))
529  {
530 // m_log.DebugFormat(
531 // "[MOAP]: Blocking change of face {0} on prim {1} {2} to {3} since it's not on the enabled whitelist",
532 // omn.Face, part.Name, part.UUID, omn.URL);
533 
534  return string.Empty;
535  }
536  }
537 
538  me.CurrentURL = omn.URL;
539 
540  UpdateMediaUrl(part, agentId);
541 
542  part.ScheduleFullUpdate();
543 
544  part.TriggerScriptChangedEvent(Changed.MEDIA);
545 
546  return OSDParser.SerializeLLSDXmlString(new OSD());
547  }
548 
554  protected void CheckFaceParam(SceneObjectPart part, int face)
555  {
556  if (face < 0)
557  throw new ArgumentException("Face cannot be less than zero");
558 
559  int maxFaces = part.GetNumberOfSides() - 1;
560  if (face > maxFaces)
561  throw new ArgumentException(
562  string.Format("Face argument was {0} but max is {1}", face, maxFaces));
563  }
564 
573  protected void UpdateMediaUrl(SceneObjectPart part, UUID updateId)
574  {
575  if (null == part.MediaUrl)
576  {
577  // TODO: We can't set the last changer until we start tracking which cap we give to which agent id
578  part.MediaUrl = "x-mv:0000000000/" + updateId;
579  }
580  else
581  {
582  string rawVersion = part.MediaUrl.Substring(5, 10);
583  int version = int.Parse(rawVersion);
584  part.MediaUrl = string.Format("x-mv:{0:D10}/{1}", ++version, updateId);
585  }
586 
587 // m_log.DebugFormat("[MOAP]: Storing media url [{0}] in prim {1} {2}", part.MediaUrl, part.Name, part.UUID);
588  }
589 
596  protected bool CheckUrlAgainstWhitelist(string rawUrl, string[] whitelist)
597  {
598  if (whitelist == null)
599  return false;
600 
601  Uri url = new Uri(rawUrl);
602 
603  foreach (string origWlUrl in whitelist)
604  {
605  string wlUrl = origWlUrl;
606 
607  // Deal with a line-ending wildcard
608  if (wlUrl.EndsWith("*"))
609  wlUrl = wlUrl.Remove(wlUrl.Length - 1);
610 
611 // m_log.DebugFormat("[MOAP]: Checking whitelist URL pattern {0}", origWlUrl);
612 
613  // Handle a line starting wildcard slightly differently since this can only match the domain, not the path
614  if (wlUrl.StartsWith("*"))
615  {
616  wlUrl = wlUrl.Substring(1);
617 
618  if (url.Host.Contains(wlUrl))
619  {
620 // m_log.DebugFormat("[MOAP]: Whitelist URL {0} matches {1}", origWlUrl, rawUrl);
621  return true;
622  }
623  }
624  else
625  {
626  string urlToMatch = url.Authority + url.AbsolutePath;
627 
628  if (urlToMatch.StartsWith(wlUrl))
629  {
630 // m_log.DebugFormat("[MOAP]: Whitelist URL {0} matches {1}", origWlUrl, rawUrl);
631  return true;
632  }
633  }
634  }
635 
636  return false;
637  }
638  }
639 }
int GetNumberOfSides()
Get the number of sides that this part has.
void ClearMediaEntry(SceneObjectPart part, int face)
Clear the media entry from the face of the given part.
Definition: MoapModule.cs:268
string HandleObjectMediaNavigateMessage(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
Received from the viewer if a user has changed the url of a media texture.
Definition: MoapModule.cs:480
void Initialise(IConfigSource configSource)
This is called to initialize the region module. For shared modules, this is called exactly once...
Definition: MoapModule.cs:93
OpenMetaverse.StructuredData.OSDMap OSDMap
Definition: MoapModule.cs:51
MediaList Media
Entries to store media textures on each face
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
Definition: MoapModule.cs:124
Encapsulates a list of media entries.
string MediaUrl
Used for media on a prim.
Scene m_scene
The scene to which this module is attached
Definition: MoapModule.cs:71
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
Definition: MoapModule.cs:103
string HandleObjectMediaRequest(ObjectMediaRequest omr)
Handle a fetch request for media textures
Definition: MoapModule.cs:324
MediaEntry GetMediaEntry(SceneObjectPart part, int face)
Get the media entry for a given prim face.
Definition: MoapModule.cs:205
void SetMediaEntry(SceneObjectPart part, int face, MediaEntry me)
Set the media entry on the face of the given part.
Definition: MoapModule.cs:238
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
Definition: MoapModule.cs:114
string HandleObjectMediaUpdate(string path, ObjectMediaUpdate omu)
Handle an update of media textures.
Definition: MoapModule.cs:363
OpenMetaverse.StructuredData.OSD OSD
OpenMetaverse.StructuredData.OSDArray OSDArray
Definition: MoapModule.cs:50
void OnSceneObjectPartCopy(SceneObjectPart copy, SceneObjectPart original, bool userExposed)
Definition: MoapModule.cs:185
void CheckFaceParam(SceneObjectPart part, int face)
Check that the face number is valid for the given prim.
Definition: MoapModule.cs:554
Interactive OpenSim region server
Definition: OpenSim.cs:55
void UpdateMediaUrl(SceneObjectPart part, UUID updateId)
Update the media url of the given part
Definition: MoapModule.cs:573
OpenSim.Framework.Capabilities.Caps Caps
Definition: MoapModule.cs:49
void SetPartMediaFlags(SceneObjectPart part, int face, bool flag)
Set the media flags on the texture face of the given part.
Definition: MoapModule.cs:282
string HandleObjectMediaMessage(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
Sets or gets per face media textures.
Definition: MoapModule.cs:299
Provides methods from manipulating media-on-a-prim
Definition: IMoapModule.cs:37
bool CheckUrlAgainstWhitelist(string rawUrl, string[] whitelist)
Check the given url against the given whitelist.
Definition: MoapModule.cs:596
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
Definition: MoapModule.cs:112