diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Scripting')
7 files changed, 3253 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs new file mode 100644 index 0000000..e6a12a4 --- /dev/null +++ b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs | |||
@@ -0,0 +1,332 @@ | |||
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 OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Drawing; | ||
31 | using System.Drawing.Imaging; | ||
32 | using OpenMetaverse; | ||
33 | using OpenMetaverse.Imaging; | ||
34 | using Nini.Config; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Region.Framework.Interfaces; | ||
37 | using OpenSim.Region.Framework.Scenes; | ||
38 | |||
39 | namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture | ||
40 | { | ||
41 | public class DynamicTextureModule : IRegionModule, IDynamicTextureManager | ||
42 | { | ||
43 | private Dictionary<UUID, Scene> RegisteredScenes = new Dictionary<UUID, Scene>(); | ||
44 | |||
45 | private Dictionary<string, IDynamicTextureRender> RenderPlugins = | ||
46 | new Dictionary<string, IDynamicTextureRender>(); | ||
47 | |||
48 | private Dictionary<UUID, DynamicTextureUpdater> Updaters = new Dictionary<UUID, DynamicTextureUpdater>(); | ||
49 | |||
50 | #region IDynamicTextureManager Members | ||
51 | |||
52 | public void RegisterRender(string handleType, IDynamicTextureRender render) | ||
53 | { | ||
54 | if (!RenderPlugins.ContainsKey(handleType)) | ||
55 | { | ||
56 | RenderPlugins.Add(handleType, render); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | /// <summary> | ||
61 | /// Called by code which actually renders the dynamic texture to supply texture data. | ||
62 | /// </summary> | ||
63 | /// <param name="id"></param> | ||
64 | /// <param name="data"></param> | ||
65 | public void ReturnData(UUID id, byte[] data) | ||
66 | { | ||
67 | if (Updaters.ContainsKey(id)) | ||
68 | { | ||
69 | DynamicTextureUpdater updater = Updaters[id]; | ||
70 | if (RegisteredScenes.ContainsKey(updater.SimUUID)) | ||
71 | { | ||
72 | Scene scene = RegisteredScenes[updater.SimUUID]; | ||
73 | updater.DataReceived(data, scene); | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | public UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url, | ||
79 | string extraParams, int updateTimer) | ||
80 | { | ||
81 | return AddDynamicTextureURL(simID, primID, contentType, url, extraParams, updateTimer, false, 255); | ||
82 | } | ||
83 | |||
84 | public UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url, | ||
85 | string extraParams, int updateTimer, bool SetBlending, byte AlphaValue) | ||
86 | { | ||
87 | if (RenderPlugins.ContainsKey(contentType)) | ||
88 | { | ||
89 | //Console.WriteLine("dynamic texture being created: " + url + " of type " + contentType); | ||
90 | |||
91 | DynamicTextureUpdater updater = new DynamicTextureUpdater(); | ||
92 | updater.SimUUID = simID; | ||
93 | updater.PrimID = primID; | ||
94 | updater.ContentType = contentType; | ||
95 | updater.Url = url; | ||
96 | updater.UpdateTimer = updateTimer; | ||
97 | updater.UpdaterID = UUID.Random(); | ||
98 | updater.Params = extraParams; | ||
99 | updater.BlendWithOldTexture = SetBlending; | ||
100 | updater.FrontAlpha = AlphaValue; | ||
101 | |||
102 | if (!Updaters.ContainsKey(updater.UpdaterID)) | ||
103 | { | ||
104 | Updaters.Add(updater.UpdaterID, updater); | ||
105 | } | ||
106 | |||
107 | RenderPlugins[contentType].AsyncConvertUrl(updater.UpdaterID, url, extraParams); | ||
108 | return updater.UpdaterID; | ||
109 | } | ||
110 | return UUID.Zero; | ||
111 | } | ||
112 | |||
113 | public UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data, | ||
114 | string extraParams, int updateTimer) | ||
115 | { | ||
116 | return AddDynamicTextureData(simID, primID, contentType, data, extraParams, updateTimer, false, 255); | ||
117 | } | ||
118 | |||
119 | public UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data, | ||
120 | string extraParams, int updateTimer, bool SetBlending, byte AlphaValue) | ||
121 | { | ||
122 | if (RenderPlugins.ContainsKey(contentType)) | ||
123 | { | ||
124 | DynamicTextureUpdater updater = new DynamicTextureUpdater(); | ||
125 | updater.SimUUID = simID; | ||
126 | updater.PrimID = primID; | ||
127 | updater.ContentType = contentType; | ||
128 | updater.BodyData = data; | ||
129 | updater.UpdateTimer = updateTimer; | ||
130 | updater.UpdaterID = UUID.Random(); | ||
131 | updater.Params = extraParams; | ||
132 | updater.BlendWithOldTexture = SetBlending; | ||
133 | updater.FrontAlpha = AlphaValue; | ||
134 | |||
135 | if (!Updaters.ContainsKey(updater.UpdaterID)) | ||
136 | { | ||
137 | Updaters.Add(updater.UpdaterID, updater); | ||
138 | } | ||
139 | |||
140 | RenderPlugins[contentType].AsyncConvertData(updater.UpdaterID, data, extraParams); | ||
141 | return updater.UpdaterID; | ||
142 | } | ||
143 | return UUID.Zero; | ||
144 | } | ||
145 | |||
146 | #endregion | ||
147 | |||
148 | #region IRegionModule Members | ||
149 | |||
150 | public void Initialise(Scene scene, IConfigSource config) | ||
151 | { | ||
152 | if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID)) | ||
153 | { | ||
154 | RegisteredScenes.Add(scene.RegionInfo.RegionID, scene); | ||
155 | scene.RegisterModuleInterface<IDynamicTextureManager>(this); | ||
156 | } | ||
157 | } | ||
158 | |||
159 | public void PostInitialise() | ||
160 | { | ||
161 | } | ||
162 | |||
163 | public void Close() | ||
164 | { | ||
165 | } | ||
166 | |||
167 | public string Name | ||
168 | { | ||
169 | get { return "DynamicTextureModule"; } | ||
170 | } | ||
171 | |||
172 | public bool IsSharedModule | ||
173 | { | ||
174 | get { return true; } | ||
175 | } | ||
176 | |||
177 | #endregion | ||
178 | |||
179 | #region Nested type: DynamicTextureUpdater | ||
180 | |||
181 | public class DynamicTextureUpdater | ||
182 | { | ||
183 | public bool BlendWithOldTexture = false; | ||
184 | public string BodyData; | ||
185 | public string ContentType; | ||
186 | public byte FrontAlpha = 255; | ||
187 | public UUID LastAssetID; | ||
188 | public string Params; | ||
189 | public UUID PrimID; | ||
190 | public bool SetNewFrontAlpha = false; | ||
191 | public UUID SimUUID; | ||
192 | public UUID UpdaterID; | ||
193 | public int UpdateTimer; | ||
194 | public string Url; | ||
195 | |||
196 | public DynamicTextureUpdater() | ||
197 | { | ||
198 | LastAssetID = UUID.Zero; | ||
199 | UpdateTimer = 0; | ||
200 | BodyData = null; | ||
201 | } | ||
202 | |||
203 | /// <summary> | ||
204 | /// Called once new texture data has been received for this updater. | ||
205 | /// </summary> | ||
206 | public void DataReceived(byte[] data, Scene scene) | ||
207 | { | ||
208 | SceneObjectPart part = scene.GetSceneObjectPart(PrimID); | ||
209 | byte[] assetData; | ||
210 | AssetBase oldAsset = null; | ||
211 | |||
212 | if (BlendWithOldTexture) | ||
213 | { | ||
214 | UUID lastTextureID = part.Shape.Textures.DefaultTexture.TextureID; | ||
215 | oldAsset = scene.AssetCache.GetAsset(lastTextureID, true); | ||
216 | if (oldAsset != null) | ||
217 | { | ||
218 | assetData = BlendTextures(data, oldAsset.Data, SetNewFrontAlpha, FrontAlpha); | ||
219 | } | ||
220 | else | ||
221 | { | ||
222 | assetData = new byte[data.Length]; | ||
223 | Array.Copy(data, assetData, data.Length); | ||
224 | } | ||
225 | } | ||
226 | else | ||
227 | { | ||
228 | assetData = new byte[data.Length]; | ||
229 | Array.Copy(data, assetData, data.Length); | ||
230 | } | ||
231 | |||
232 | // Create a new asset for user | ||
233 | AssetBase asset = new AssetBase(); | ||
234 | asset.Metadata.FullID = UUID.Random(); | ||
235 | asset.Data = assetData; | ||
236 | asset.Metadata.Name = "DynamicImage" + Util.RandomClass.Next(1, 10000); | ||
237 | asset.Metadata.Type = 0; | ||
238 | asset.Metadata.Description = "dynamic image"; | ||
239 | asset.Metadata.Local = false; | ||
240 | asset.Metadata.Temporary = true; | ||
241 | scene.AssetCache.AddAsset(asset); | ||
242 | |||
243 | LastAssetID = asset.Metadata.FullID; | ||
244 | |||
245 | IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>(); | ||
246 | if (cacheLayerDecode != null) | ||
247 | { | ||
248 | cacheLayerDecode.syncdecode(asset.Metadata.FullID, asset.Data); | ||
249 | } | ||
250 | cacheLayerDecode = null; | ||
251 | |||
252 | // mostly keep the values from before | ||
253 | Primitive.TextureEntry tmptex = part.Shape.Textures; | ||
254 | |||
255 | // remove the old asset from the cache | ||
256 | UUID oldID = tmptex.DefaultTexture.TextureID; | ||
257 | scene.AssetCache.ExpireAsset(oldID); | ||
258 | |||
259 | tmptex.DefaultTexture.TextureID = asset.Metadata.FullID; | ||
260 | // I'm pretty sure we always want to force this to true | ||
261 | tmptex.DefaultTexture.Fullbright = true; | ||
262 | |||
263 | part.Shape.Textures = tmptex; | ||
264 | part.ScheduleFullUpdate(); | ||
265 | } | ||
266 | |||
267 | private byte[] BlendTextures(byte[] frontImage, byte[] backImage, bool setNewAlpha, byte newAlpha) | ||
268 | { | ||
269 | ManagedImage managedImage; | ||
270 | Image image; | ||
271 | |||
272 | if (OpenJPEG.DecodeToImage(frontImage, out managedImage, out image)) | ||
273 | { | ||
274 | Bitmap image1 = new Bitmap(image); | ||
275 | |||
276 | if (OpenJPEG.DecodeToImage(backImage, out managedImage, out image)) | ||
277 | { | ||
278 | Bitmap image2 = new Bitmap(image); | ||
279 | |||
280 | if (setNewAlpha) | ||
281 | SetAlpha(ref image1, newAlpha); | ||
282 | |||
283 | Bitmap joint = MergeBitMaps(image1, image2); | ||
284 | |||
285 | byte[] result = new byte[0]; | ||
286 | |||
287 | try | ||
288 | { | ||
289 | result = OpenJPEG.EncodeFromImage(joint, true); | ||
290 | } | ||
291 | catch (Exception) | ||
292 | { | ||
293 | Console.WriteLine( | ||
294 | "[DYNAMICTEXTUREMODULE]: OpenJpeg Encode Failed. Empty byte data returned!"); | ||
295 | } | ||
296 | |||
297 | return result; | ||
298 | } | ||
299 | } | ||
300 | |||
301 | return null; | ||
302 | } | ||
303 | |||
304 | public Bitmap MergeBitMaps(Bitmap front, Bitmap back) | ||
305 | { | ||
306 | Bitmap joint; | ||
307 | Graphics jG; | ||
308 | |||
309 | joint = new Bitmap(back.Width, back.Height, PixelFormat.Format32bppArgb); | ||
310 | jG = Graphics.FromImage(joint); | ||
311 | |||
312 | jG.DrawImage(back, 0, 0, back.Width, back.Height); | ||
313 | jG.DrawImage(front, 0, 0, back.Width, back.Height); | ||
314 | |||
315 | return joint; | ||
316 | } | ||
317 | |||
318 | private void SetAlpha(ref Bitmap b, byte alpha) | ||
319 | { | ||
320 | for (int w = 0; w < b.Width; w++) | ||
321 | { | ||
322 | for (int h = 0; h < b.Height; h++) | ||
323 | { | ||
324 | b.SetPixel(w, h, Color.FromArgb(alpha, b.GetPixel(w, h))); | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | } | ||
329 | |||
330 | #endregion | ||
331 | } | ||
332 | } | ||
diff --git a/OpenSim/Region/CoreModules/Scripting/EMailModules/EmailModule.cs b/OpenSim/Region/CoreModules/Scripting/EMailModules/EmailModule.cs new file mode 100644 index 0000000..c23ff1e --- /dev/null +++ b/OpenSim/Region/CoreModules/Scripting/EMailModules/EmailModule.cs | |||
@@ -0,0 +1,288 @@ | |||
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 OpenSim 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.Reflection; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Text.RegularExpressions; | ||
32 | using OpenMetaverse; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Region.Framework.Interfaces; | ||
35 | using OpenSim.Region.Framework.Scenes; | ||
36 | using log4net; | ||
37 | using Nini.Config; | ||
38 | using DotNetOpenMail; | ||
39 | using DotNetOpenMail.SmtpAuth; | ||
40 | |||
41 | namespace OpenSim.Region.CoreModules.Scripting.EmailModules | ||
42 | { | ||
43 | public class EmailModule : IEmailModule | ||
44 | { | ||
45 | // | ||
46 | // Log | ||
47 | // | ||
48 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
49 | |||
50 | // | ||
51 | // Module vars | ||
52 | // | ||
53 | private IConfigSource m_Config; | ||
54 | private string m_HostName = string.Empty; | ||
55 | //private string m_RegionName = string.Empty; | ||
56 | private string SMTP_SERVER_HOSTNAME = string.Empty; | ||
57 | private int SMTP_SERVER_PORT = 25; | ||
58 | private string SMTP_SERVER_LOGIN = string.Empty; | ||
59 | private string SMTP_SERVER_PASSWORD = string.Empty; | ||
60 | |||
61 | // Scenes by Region Handle | ||
62 | private Dictionary<ulong, Scene> m_Scenes = | ||
63 | new Dictionary<ulong, Scene>(); | ||
64 | |||
65 | private bool m_Enabled = false; | ||
66 | |||
67 | public void Initialise(Scene scene, IConfigSource config) | ||
68 | { | ||
69 | m_Config = config; | ||
70 | IConfig SMTPConfig; | ||
71 | |||
72 | //FIXME: RegionName is correct?? | ||
73 | //m_RegionName = scene.RegionInfo.RegionName; | ||
74 | |||
75 | IConfig startupConfig = m_Config.Configs["Startup"]; | ||
76 | |||
77 | m_Enabled = (startupConfig.GetString("emailmodule", "DefaultEmailModule") == "DefaultEmailModule"); | ||
78 | |||
79 | //Load SMTP SERVER config | ||
80 | try | ||
81 | { | ||
82 | if ((SMTPConfig = m_Config.Configs["SMTP"]) == null) | ||
83 | { | ||
84 | m_log.InfoFormat("[SMTP] SMTP server not configured"); | ||
85 | m_Enabled = false; | ||
86 | return; | ||
87 | } | ||
88 | |||
89 | if (!SMTPConfig.GetBoolean("enabled", false)) | ||
90 | { | ||
91 | m_log.InfoFormat("[SMTP] module disabled in configuration"); | ||
92 | m_Enabled = false; | ||
93 | return; | ||
94 | } | ||
95 | |||
96 | m_HostName = SMTPConfig.GetString("host_domain_header_from", m_HostName); | ||
97 | SMTP_SERVER_HOSTNAME = SMTPConfig.GetString("SMTP_SERVER_HOSTNAME",SMTP_SERVER_HOSTNAME); | ||
98 | SMTP_SERVER_PORT = SMTPConfig.GetInt("SMTP_SERVER_PORT", SMTP_SERVER_PORT); | ||
99 | SMTP_SERVER_LOGIN = SMTPConfig.GetString("SMTP_SERVER_LOGIN", SMTP_SERVER_LOGIN); | ||
100 | SMTP_SERVER_PASSWORD = SMTPConfig.GetString("SMTP_SERVER_PASSWORD", SMTP_SERVER_PASSWORD); | ||
101 | } | ||
102 | catch (Exception e) | ||
103 | { | ||
104 | m_log.Error("[EMAIL] DefaultEmailModule not configured: "+ e.Message); | ||
105 | m_Enabled = false; | ||
106 | return; | ||
107 | } | ||
108 | |||
109 | // It's a go! | ||
110 | if (m_Enabled) | ||
111 | { | ||
112 | lock (m_Scenes) | ||
113 | { | ||
114 | // Claim the interface slot | ||
115 | scene.RegisterModuleInterface<IEmailModule>(this); | ||
116 | |||
117 | // Add to scene list | ||
118 | if (m_Scenes.ContainsKey(scene.RegionInfo.RegionHandle)) | ||
119 | { | ||
120 | m_Scenes[scene.RegionInfo.RegionHandle] = scene; | ||
121 | } | ||
122 | else | ||
123 | { | ||
124 | m_Scenes.Add(scene.RegionInfo.RegionHandle, scene); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | m_log.Info("[EMAIL] Activated DefaultEmailModule"); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | public void PostInitialise() | ||
133 | { | ||
134 | } | ||
135 | |||
136 | public void Close() | ||
137 | { | ||
138 | } | ||
139 | |||
140 | public string Name | ||
141 | { | ||
142 | get { return "DefaultEmailModule"; } | ||
143 | } | ||
144 | |||
145 | public bool IsSharedModule | ||
146 | { | ||
147 | get { return true; } | ||
148 | } | ||
149 | |||
150 | /// <summary> | ||
151 | /// | ||
152 | /// </summary> | ||
153 | /// <param name="seconds"></param> | ||
154 | private void DelayInSeconds(int seconds) | ||
155 | { | ||
156 | TimeSpan DiffDelay = new TimeSpan(0, 0, seconds); | ||
157 | DateTime EndDelay = DateTime.Now.Add(DiffDelay); | ||
158 | while (DateTime.Now < EndDelay) | ||
159 | { | ||
160 | ;//Do nothing!! | ||
161 | } | ||
162 | } | ||
163 | |||
164 | private SceneObjectPart findPrim(UUID objectID, out string ObjectRegionName) | ||
165 | { | ||
166 | lock (m_Scenes) | ||
167 | { | ||
168 | foreach (Scene s in m_Scenes.Values) | ||
169 | { | ||
170 | SceneObjectPart part = s.GetSceneObjectPart(objectID); | ||
171 | if (part != null) | ||
172 | { | ||
173 | ObjectRegionName = s.RegionInfo.RegionName; | ||
174 | return part; | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | ObjectRegionName = string.Empty; | ||
179 | return null; | ||
180 | } | ||
181 | |||
182 | private void resolveNamePositionRegionName(UUID objectID, out string ObjectName, out string ObjectAbsolutePosition, out string ObjectRegionName) | ||
183 | { | ||
184 | string m_ObjectRegionName; | ||
185 | SceneObjectPart part = findPrim(objectID, out m_ObjectRegionName); | ||
186 | if (part != null) | ||
187 | { | ||
188 | ObjectAbsolutePosition = part.AbsolutePosition.ToString(); | ||
189 | ObjectName = part.Name; | ||
190 | ObjectRegionName = m_ObjectRegionName; | ||
191 | return; | ||
192 | } | ||
193 | ObjectAbsolutePosition = part.AbsolutePosition.ToString(); | ||
194 | ObjectName = part.Name; | ||
195 | ObjectRegionName = m_ObjectRegionName; | ||
196 | return; | ||
197 | } | ||
198 | |||
199 | /// <summary> | ||
200 | /// SendMail function utilized by llEMail | ||
201 | /// </summary> | ||
202 | /// <param name="objectID"></param> | ||
203 | /// <param name="address"></param> | ||
204 | /// <param name="subject"></param> | ||
205 | /// <param name="body"></param> | ||
206 | public void SendEmail(UUID objectID, string address, string subject, string body) | ||
207 | { | ||
208 | //Check if address is empty | ||
209 | if (address == string.Empty) | ||
210 | return; | ||
211 | |||
212 | //FIXED:Check the email is correct form in REGEX | ||
213 | string EMailpatternStrict = @"^(([^<>()[\]\\.,;:\s@\""]+" | ||
214 | + @"(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\""))@" | ||
215 | + @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" | ||
216 | + @"\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+" | ||
217 | + @"[a-zA-Z]{2,}))$"; | ||
218 | Regex EMailreStrict = new Regex(EMailpatternStrict); | ||
219 | bool isEMailStrictMatch = EMailreStrict.IsMatch(address); | ||
220 | if (!isEMailStrictMatch) | ||
221 | { | ||
222 | m_log.Error("[EMAIL] REGEX Problem in EMail Address: "+address); | ||
223 | return; | ||
224 | } | ||
225 | //FIXME:Check if subject + body = 4096 Byte | ||
226 | if ((subject.Length + body.Length) > 1024) | ||
227 | { | ||
228 | m_log.Error("[EMAIL] subject + body > 1024 Byte"); | ||
229 | return; | ||
230 | } | ||
231 | |||
232 | try | ||
233 | { | ||
234 | string LastObjectName = string.Empty; | ||
235 | string LastObjectPosition = string.Empty; | ||
236 | string LastObjectRegionName = string.Empty; | ||
237 | //DONE: Message as Second Life style | ||
238 | //20 second delay - AntiSpam System - for now only 10 seconds | ||
239 | DelayInSeconds(10); | ||
240 | //Creation EmailMessage | ||
241 | EmailMessage emailMessage = new EmailMessage(); | ||
242 | //From | ||
243 | emailMessage.FromAddress = new EmailAddress(objectID.ToString()+"@"+m_HostName); | ||
244 | //To - Only One | ||
245 | emailMessage.AddToAddress(new EmailAddress(address)); | ||
246 | //Subject | ||
247 | emailMessage.Subject = subject; | ||
248 | //TEXT Body | ||
249 | resolveNamePositionRegionName(objectID, out LastObjectName, out LastObjectPosition, out LastObjectRegionName); | ||
250 | emailMessage.TextPart = new TextAttachment("Object-Name: " + LastObjectName + | ||
251 | "\r\nRegion: " + LastObjectRegionName + "\r\nLocal-Position: " + | ||
252 | LastObjectPosition+"\r\n\r\n\r\n" + body); | ||
253 | //HTML Body | ||
254 | emailMessage.HtmlPart = new HtmlAttachment("<html><body><p>" + | ||
255 | "<BR>Object-Name: " + LastObjectName + | ||
256 | "<BR>Region: " + LastObjectRegionName + | ||
257 | "<BR>Local-Position: " + LastObjectPosition + "<BR><BR><BR>" | ||
258 | +body+"\r\n</p></body><html>"); | ||
259 | |||
260 | //Set SMTP SERVER config | ||
261 | SmtpServer smtpServer=new SmtpServer(SMTP_SERVER_HOSTNAME,SMTP_SERVER_PORT); | ||
262 | //Authentication | ||
263 | smtpServer.SmtpAuthToken=new SmtpAuthToken(SMTP_SERVER_LOGIN, SMTP_SERVER_PASSWORD); | ||
264 | //Send Email Message | ||
265 | emailMessage.Send(smtpServer); | ||
266 | //Log | ||
267 | m_log.Info("[EMAIL] EMail sent to: " + address + " from object: " + objectID.ToString()); | ||
268 | } | ||
269 | catch (Exception e) | ||
270 | { | ||
271 | m_log.Error("[EMAIL] DefaultEmailModule Exception: "+e.Message); | ||
272 | return; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | /// <summary> | ||
277 | /// | ||
278 | /// </summary> | ||
279 | /// <param name="objectID"></param> | ||
280 | /// <param name="sender"></param> | ||
281 | /// <param name="subject"></param> | ||
282 | /// <returns></returns> | ||
283 | public Email GetNextEmail(UUID objectID, string sender, string subject) | ||
284 | { | ||
285 | return null; | ||
286 | } | ||
287 | } | ||
288 | } | ||
diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs new file mode 100644 index 0000000..9f3bd09 --- /dev/null +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs | |||
@@ -0,0 +1,437 @@ | |||
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 OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Net; | ||
32 | using System.Text; | ||
33 | using System.Threading; | ||
34 | using OpenMetaverse; | ||
35 | using Nini.Config; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Framework.Servers; | ||
38 | using OpenSim.Region.Framework.Interfaces; | ||
39 | using OpenSim.Region.Framework.Scenes; | ||
40 | using System.Collections; | ||
41 | |||
42 | /***************************************************** | ||
43 | * | ||
44 | * ScriptsHttpRequests | ||
45 | * | ||
46 | * Implements the llHttpRequest and http_response | ||
47 | * callback. | ||
48 | * | ||
49 | * Some stuff was already in LSLLongCmdHandler, and then | ||
50 | * there was this file with a stub class in it. So, | ||
51 | * I am moving some of the objects and functions out of | ||
52 | * LSLLongCmdHandler, such as the HttpRequestClass, the | ||
53 | * start and stop methods, and setting up pending and | ||
54 | * completed queues. These are processed in the | ||
55 | * LSLLongCmdHandler polling loop. Similiar to the | ||
56 | * XMLRPCModule, since that seems to work. | ||
57 | * | ||
58 | * //TODO | ||
59 | * | ||
60 | * This probably needs some throttling mechanism but | ||
61 | * it's wide open right now. This applies to both | ||
62 | * number of requests and data volume. | ||
63 | * | ||
64 | * Linden puts all kinds of header fields in the requests. | ||
65 | * Not doing any of that: | ||
66 | * User-Agent | ||
67 | * X-SecondLife-Shard | ||
68 | * X-SecondLife-Object-Name | ||
69 | * X-SecondLife-Object-Key | ||
70 | * X-SecondLife-Region | ||
71 | * X-SecondLife-Local-Position | ||
72 | * X-SecondLife-Local-Velocity | ||
73 | * X-SecondLife-Local-Rotation | ||
74 | * X-SecondLife-Owner-Name | ||
75 | * X-SecondLife-Owner-Key | ||
76 | * | ||
77 | * HTTPS support | ||
78 | * | ||
79 | * Configurable timeout? | ||
80 | * Configurable max response size? | ||
81 | * Configurable | ||
82 | * | ||
83 | * **************************************************/ | ||
84 | |||
85 | namespace OpenSim.Region.CoreModules.Scripting.HttpRequest | ||
86 | { | ||
87 | public class HttpRequestModule : IRegionModule, IHttpRequestModule | ||
88 | { | ||
89 | private object HttpListLock = new object(); | ||
90 | private int httpTimeout = 30000; | ||
91 | private string m_name = "HttpScriptRequests"; | ||
92 | |||
93 | private string m_proxyurl = ""; | ||
94 | private string m_proxyexcepts = ""; | ||
95 | |||
96 | // <request id, HttpRequestClass> | ||
97 | private Dictionary<UUID, HttpRequestClass> m_pendingRequests; | ||
98 | private Scene m_scene; | ||
99 | // private Queue<HttpRequestClass> rpcQueue = new Queue<HttpRequestClass>(); | ||
100 | |||
101 | public HttpRequestModule() | ||
102 | { | ||
103 | } | ||
104 | |||
105 | #region IHttpRequestModule Members | ||
106 | |||
107 | public UUID MakeHttpRequest(string url, string parameters, string body) | ||
108 | { | ||
109 | return UUID.Zero; | ||
110 | } | ||
111 | |||
112 | public UUID StartHttpRequest(uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body) | ||
113 | { | ||
114 | UUID reqID = UUID.Random(); | ||
115 | HttpRequestClass htc = new HttpRequestClass(); | ||
116 | |||
117 | // Partial implementation: support for parameter flags needed | ||
118 | // see http://wiki.secondlife.com/wiki/LlHTTPRequest | ||
119 | // | ||
120 | // Parameters are expected in {key, value, ... , key, value} | ||
121 | if (parameters != null) | ||
122 | { | ||
123 | string[] parms = parameters.ToArray(); | ||
124 | for (int i = 0; i < parms.Length; i += 2) | ||
125 | { | ||
126 | switch (Int32.Parse(parms[i])) | ||
127 | { | ||
128 | case (int)HttpRequestConstants.HTTP_METHOD: | ||
129 | |||
130 | htc.HttpMethod = parms[i + 1]; | ||
131 | break; | ||
132 | |||
133 | case (int)HttpRequestConstants.HTTP_MIMETYPE: | ||
134 | |||
135 | htc.HttpMIMEType = parms[i + 1]; | ||
136 | break; | ||
137 | |||
138 | case (int)HttpRequestConstants.HTTP_BODY_MAXLENGTH: | ||
139 | |||
140 | // TODO implement me | ||
141 | break; | ||
142 | |||
143 | case (int)HttpRequestConstants.HTTP_VERIFY_CERT: | ||
144 | |||
145 | // TODO implement me | ||
146 | break; | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | |||
151 | htc.LocalID = localID; | ||
152 | htc.ItemID = itemID; | ||
153 | htc.Url = url; | ||
154 | htc.ReqID = reqID; | ||
155 | htc.HttpTimeout = httpTimeout; | ||
156 | htc.OutboundBody = body; | ||
157 | htc.ResponseHeaders = headers; | ||
158 | htc.proxyurl = m_proxyurl; | ||
159 | htc.proxyexcepts = m_proxyexcepts; | ||
160 | |||
161 | lock (HttpListLock) | ||
162 | { | ||
163 | m_pendingRequests.Add(reqID, htc); | ||
164 | } | ||
165 | |||
166 | htc.Process(); | ||
167 | |||
168 | return reqID; | ||
169 | } | ||
170 | |||
171 | public void StopHttpRequest(uint m_localID, UUID m_itemID) | ||
172 | { | ||
173 | if (m_pendingRequests != null) | ||
174 | { | ||
175 | lock (HttpListLock) | ||
176 | { | ||
177 | HttpRequestClass tmpReq; | ||
178 | if (m_pendingRequests.TryGetValue(m_itemID, out tmpReq)) | ||
179 | { | ||
180 | tmpReq.Stop(); | ||
181 | m_pendingRequests.Remove(m_itemID); | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * TODO | ||
189 | * Not sure how important ordering is is here - the next first | ||
190 | * one completed in the list is returned, based soley on its list | ||
191 | * position, not the order in which the request was started or | ||
192 | * finsihed. I thought about setting up a queue for this, but | ||
193 | * it will need some refactoring and this works 'enough' right now | ||
194 | */ | ||
195 | |||
196 | public IServiceRequest GetNextCompletedRequest() | ||
197 | { | ||
198 | lock (HttpListLock) | ||
199 | { | ||
200 | foreach (UUID luid in m_pendingRequests.Keys) | ||
201 | { | ||
202 | HttpRequestClass tmpReq; | ||
203 | |||
204 | if (m_pendingRequests.TryGetValue(luid, out tmpReq)) | ||
205 | { | ||
206 | if (tmpReq.Finished) | ||
207 | { | ||
208 | return tmpReq; | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | return null; | ||
214 | } | ||
215 | |||
216 | public void RemoveCompletedRequest(UUID id) | ||
217 | { | ||
218 | lock (HttpListLock) | ||
219 | { | ||
220 | HttpRequestClass tmpReq; | ||
221 | if (m_pendingRequests.TryGetValue(id, out tmpReq)) | ||
222 | { | ||
223 | tmpReq.Stop(); | ||
224 | tmpReq = null; | ||
225 | m_pendingRequests.Remove(id); | ||
226 | } | ||
227 | } | ||
228 | } | ||
229 | |||
230 | #endregion | ||
231 | |||
232 | #region IRegionModule Members | ||
233 | |||
234 | public void Initialise(Scene scene, IConfigSource config) | ||
235 | { | ||
236 | m_scene = scene; | ||
237 | |||
238 | m_scene.RegisterModuleInterface<IHttpRequestModule>(this); | ||
239 | |||
240 | m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); | ||
241 | m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); | ||
242 | |||
243 | m_pendingRequests = new Dictionary<UUID, HttpRequestClass>(); | ||
244 | } | ||
245 | |||
246 | public void PostInitialise() | ||
247 | { | ||
248 | } | ||
249 | |||
250 | public void Close() | ||
251 | { | ||
252 | } | ||
253 | |||
254 | public string Name | ||
255 | { | ||
256 | get { return m_name; } | ||
257 | } | ||
258 | |||
259 | public bool IsSharedModule | ||
260 | { | ||
261 | get { return true; } | ||
262 | } | ||
263 | |||
264 | #endregion | ||
265 | } | ||
266 | |||
267 | public class HttpRequestClass: IServiceRequest | ||
268 | { | ||
269 | // Constants for parameters | ||
270 | // public const int HTTP_BODY_MAXLENGTH = 2; | ||
271 | // public const int HTTP_METHOD = 0; | ||
272 | // public const int HTTP_MIMETYPE = 1; | ||
273 | // public const int HTTP_VERIFY_CERT = 3; | ||
274 | private bool _finished; | ||
275 | public bool Finished | ||
276 | { | ||
277 | get { return _finished; } | ||
278 | } | ||
279 | // public int HttpBodyMaxLen = 2048; // not implemented | ||
280 | |||
281 | // Parameter members and default values | ||
282 | public string HttpMethod = "GET"; | ||
283 | public string HttpMIMEType = "text/plain;charset=utf-8"; | ||
284 | public int HttpTimeout; | ||
285 | // public bool HttpVerifyCert = true; // not implemented | ||
286 | private Thread httpThread; | ||
287 | |||
288 | // Request info | ||
289 | private UUID _itemID; | ||
290 | public UUID ItemID | ||
291 | { | ||
292 | get { return _itemID; } | ||
293 | set { _itemID = value; } | ||
294 | } | ||
295 | private uint _localID; | ||
296 | public uint LocalID | ||
297 | { | ||
298 | get { return _localID; } | ||
299 | set { _localID = value; } | ||
300 | } | ||
301 | public DateTime Next; | ||
302 | public string proxyurl; | ||
303 | public string proxyexcepts; | ||
304 | public string OutboundBody; | ||
305 | private UUID _reqID; | ||
306 | public UUID ReqID | ||
307 | { | ||
308 | get { return _reqID; } | ||
309 | set { _reqID = value; } | ||
310 | } | ||
311 | public HttpWebRequest Request; | ||
312 | public string ResponseBody; | ||
313 | public List<string> ResponseMetadata; | ||
314 | public Dictionary<string, string> ResponseHeaders; | ||
315 | public int Status; | ||
316 | public string Url; | ||
317 | |||
318 | public void Process() | ||
319 | { | ||
320 | httpThread = new Thread(SendRequest); | ||
321 | httpThread.Name = "HttpRequestThread"; | ||
322 | httpThread.Priority = ThreadPriority.BelowNormal; | ||
323 | httpThread.IsBackground = true; | ||
324 | _finished = false; | ||
325 | httpThread.Start(); | ||
326 | ThreadTracker.Add(httpThread); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * TODO: More work on the response codes. Right now | ||
331 | * returning 200 for success or 499 for exception | ||
332 | */ | ||
333 | |||
334 | public void SendRequest() | ||
335 | { | ||
336 | HttpWebResponse response = null; | ||
337 | StringBuilder sb = new StringBuilder(); | ||
338 | byte[] buf = new byte[8192]; | ||
339 | string tempString = null; | ||
340 | int count = 0; | ||
341 | |||
342 | try | ||
343 | { | ||
344 | Request = (HttpWebRequest) WebRequest.Create(Url); | ||
345 | Request.Method = HttpMethod; | ||
346 | Request.ContentType = HttpMIMEType; | ||
347 | |||
348 | if (proxyurl != null && proxyurl.Length > 0) | ||
349 | { | ||
350 | if (proxyexcepts != null && proxyexcepts.Length > 0) | ||
351 | { | ||
352 | string[] elist = proxyexcepts.Split(';'); | ||
353 | Request.Proxy = new WebProxy(proxyurl, true, elist); | ||
354 | } | ||
355 | else | ||
356 | { | ||
357 | Request.Proxy = new WebProxy(proxyurl, true); | ||
358 | } | ||
359 | } | ||
360 | |||
361 | foreach (KeyValuePair<string, string> entry in ResponseHeaders) | ||
362 | Request.Headers[entry.Key] = entry.Value; | ||
363 | |||
364 | // Encode outbound data | ||
365 | if (OutboundBody.Length > 0) | ||
366 | { | ||
367 | byte[] data = Encoding.UTF8.GetBytes(OutboundBody); | ||
368 | |||
369 | Request.ContentLength = data.Length; | ||
370 | Stream bstream = Request.GetRequestStream(); | ||
371 | bstream.Write(data, 0, data.Length); | ||
372 | bstream.Close(); | ||
373 | } | ||
374 | |||
375 | Request.Timeout = HttpTimeout; | ||
376 | // execute the request | ||
377 | response = (HttpWebResponse) Request.GetResponse(); | ||
378 | |||
379 | Stream resStream = response.GetResponseStream(); | ||
380 | |||
381 | do | ||
382 | { | ||
383 | // fill the buffer with data | ||
384 | count = resStream.Read(buf, 0, buf.Length); | ||
385 | |||
386 | // make sure we read some data | ||
387 | if (count != 0) | ||
388 | { | ||
389 | // translate from bytes to ASCII text | ||
390 | tempString = Encoding.UTF8.GetString(buf, 0, count); | ||
391 | |||
392 | // continue building the string | ||
393 | sb.Append(tempString); | ||
394 | } | ||
395 | } while (count > 0); // any more data to read? | ||
396 | |||
397 | ResponseBody = sb.ToString(); | ||
398 | } | ||
399 | catch (Exception e) | ||
400 | { | ||
401 | if (e is WebException && ((WebException)e).Status == WebExceptionStatus.ProtocolError) | ||
402 | { | ||
403 | HttpWebResponse webRsp = (HttpWebResponse)((WebException)e).Response; | ||
404 | Status = (int)webRsp.StatusCode; | ||
405 | ResponseBody = webRsp.StatusDescription; | ||
406 | } | ||
407 | else | ||
408 | { | ||
409 | Status = (int)OSHttpStatusCode.ClientErrorJoker; | ||
410 | ResponseBody = e.Message; | ||
411 | } | ||
412 | |||
413 | _finished = true; | ||
414 | return; | ||
415 | } | ||
416 | finally | ||
417 | { | ||
418 | if (response != null) | ||
419 | response.Close(); | ||
420 | } | ||
421 | |||
422 | Status = (int)OSHttpStatusCode.SuccessOk; | ||
423 | _finished = true; | ||
424 | } | ||
425 | |||
426 | public void Stop() | ||
427 | { | ||
428 | try | ||
429 | { | ||
430 | httpThread.Abort(); | ||
431 | } | ||
432 | catch (Exception) | ||
433 | { | ||
434 | } | ||
435 | } | ||
436 | } | ||
437 | } | ||
diff --git a/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs new file mode 100644 index 0000000..afcaff1 --- /dev/null +++ b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs | |||
@@ -0,0 +1,229 @@ | |||
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 OpenSim 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.Drawing; | ||
30 | using System.IO; | ||
31 | using System.Net; | ||
32 | using OpenMetaverse; | ||
33 | using OpenMetaverse.Imaging; | ||
34 | using Nini.Config; | ||
35 | using OpenSim.Region.Framework.Interfaces; | ||
36 | using OpenSim.Region.Framework.Scenes; | ||
37 | |||
38 | namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL | ||
39 | { | ||
40 | public class LoadImageURLModule : IRegionModule, IDynamicTextureRender | ||
41 | { | ||
42 | private string m_name = "LoadImageURL"; | ||
43 | private Scene m_scene; | ||
44 | private IDynamicTextureManager m_textureManager; | ||
45 | |||
46 | private string m_proxyurl = ""; | ||
47 | private string m_proxyexcepts = ""; | ||
48 | |||
49 | #region IDynamicTextureRender Members | ||
50 | |||
51 | public string GetName() | ||
52 | { | ||
53 | return m_name; | ||
54 | } | ||
55 | |||
56 | public string GetContentType() | ||
57 | { | ||
58 | return ("image"); | ||
59 | } | ||
60 | |||
61 | public bool SupportsAsynchronous() | ||
62 | { | ||
63 | return true; | ||
64 | } | ||
65 | |||
66 | public byte[] ConvertUrl(string url, string extraParams) | ||
67 | { | ||
68 | return null; | ||
69 | } | ||
70 | |||
71 | public byte[] ConvertStream(Stream data, string extraParams) | ||
72 | { | ||
73 | return null; | ||
74 | } | ||
75 | |||
76 | public bool AsyncConvertUrl(UUID id, string url, string extraParams) | ||
77 | { | ||
78 | MakeHttpRequest(url, id); | ||
79 | return true; | ||
80 | } | ||
81 | |||
82 | public bool AsyncConvertData(UUID id, string bodyData, string extraParams) | ||
83 | { | ||
84 | return false; | ||
85 | } | ||
86 | |||
87 | #endregion | ||
88 | |||
89 | #region IRegionModule Members | ||
90 | |||
91 | public void Initialise(Scene scene, IConfigSource config) | ||
92 | { | ||
93 | if (m_scene == null) | ||
94 | { | ||
95 | m_scene = scene; | ||
96 | } | ||
97 | |||
98 | m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); | ||
99 | m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); | ||
100 | } | ||
101 | |||
102 | public void PostInitialise() | ||
103 | { | ||
104 | m_textureManager = m_scene.RequestModuleInterface<IDynamicTextureManager>(); | ||
105 | if (m_textureManager != null) | ||
106 | { | ||
107 | m_textureManager.RegisterRender(GetContentType(), this); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | public void Close() | ||
112 | { | ||
113 | } | ||
114 | |||
115 | public string Name | ||
116 | { | ||
117 | get { return m_name; } | ||
118 | } | ||
119 | |||
120 | public bool IsSharedModule | ||
121 | { | ||
122 | get { return true; } | ||
123 | } | ||
124 | |||
125 | #endregion | ||
126 | |||
127 | private void MakeHttpRequest(string url, UUID requestID) | ||
128 | { | ||
129 | WebRequest request = HttpWebRequest.Create(url); | ||
130 | |||
131 | if (m_proxyurl != null && m_proxyurl.Length > 0) | ||
132 | { | ||
133 | if (m_proxyexcepts != null && m_proxyexcepts.Length > 0) | ||
134 | { | ||
135 | string[] elist = m_proxyexcepts.Split(';'); | ||
136 | request.Proxy = new WebProxy(m_proxyurl, true, elist); | ||
137 | } | ||
138 | else | ||
139 | { | ||
140 | request.Proxy = new WebProxy(m_proxyurl, true); | ||
141 | } | ||
142 | } | ||
143 | |||
144 | RequestState state = new RequestState((HttpWebRequest) request, requestID); | ||
145 | // IAsyncResult result = request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state); | ||
146 | request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state); | ||
147 | |||
148 | TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1)); | ||
149 | state.TimeOfRequest = (int) t.TotalSeconds; | ||
150 | } | ||
151 | |||
152 | private void HttpRequestReturn(IAsyncResult result) | ||
153 | { | ||
154 | RequestState state = (RequestState) result.AsyncState; | ||
155 | WebRequest request = (WebRequest) state.Request; | ||
156 | try | ||
157 | { | ||
158 | HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result); | ||
159 | if (response.StatusCode == HttpStatusCode.OK) | ||
160 | { | ||
161 | Bitmap image = new Bitmap(response.GetResponseStream()); | ||
162 | Size newsize; | ||
163 | |||
164 | // TODO: make this a bit less hard coded | ||
165 | if ((image.Height < 64) && (image.Width < 64)) | ||
166 | { | ||
167 | newsize = new Size(32, 32); | ||
168 | } | ||
169 | else if ((image.Height < 128) && (image.Width < 128)) | ||
170 | { | ||
171 | newsize = new Size(64, 64); | ||
172 | } | ||
173 | else if ((image.Height < 256) && (image.Width < 256)) | ||
174 | { | ||
175 | newsize = new Size(128, 128); | ||
176 | } | ||
177 | else if ((image.Height < 512 && image.Width < 512)) | ||
178 | { | ||
179 | newsize = new Size(256, 256); | ||
180 | } | ||
181 | else if ((image.Height < 1024 && image.Width < 1024)) | ||
182 | { | ||
183 | newsize = new Size(512, 512); | ||
184 | } | ||
185 | else | ||
186 | { | ||
187 | newsize = new Size(1024, 1024); | ||
188 | } | ||
189 | |||
190 | Bitmap resize = new Bitmap(image, newsize); | ||
191 | byte[] imageJ2000 = new byte[0]; | ||
192 | |||
193 | try | ||
194 | { | ||
195 | imageJ2000 = OpenJPEG.EncodeFromImage(resize, true); | ||
196 | } | ||
197 | catch (Exception) | ||
198 | { | ||
199 | Console.WriteLine( | ||
200 | "[LOADIMAGEURLMODULE]: OpenJpeg Encode Failed. Empty byte data returned!"); | ||
201 | } | ||
202 | |||
203 | m_textureManager.ReturnData(state.RequestID, imageJ2000); | ||
204 | } | ||
205 | } | ||
206 | catch (WebException) | ||
207 | { | ||
208 | |||
209 | } | ||
210 | } | ||
211 | |||
212 | #region Nested type: RequestState | ||
213 | |||
214 | public class RequestState | ||
215 | { | ||
216 | public HttpWebRequest Request = null; | ||
217 | public UUID RequestID = UUID.Zero; | ||
218 | public int TimeOfRequest = 0; | ||
219 | |||
220 | public RequestState(HttpWebRequest request, UUID requestID) | ||
221 | { | ||
222 | Request = request; | ||
223 | RequestID = requestID; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | #endregion | ||
228 | } | ||
229 | } | ||
diff --git a/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs new file mode 100644 index 0000000..0c709b5 --- /dev/null +++ b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs | |||
@@ -0,0 +1,515 @@ | |||
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 OpenSim 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.Drawing; | ||
30 | using System.Drawing.Imaging; | ||
31 | using System.Globalization; | ||
32 | using System.IO; | ||
33 | using System.Net; | ||
34 | using OpenMetaverse; | ||
35 | using OpenMetaverse.Imaging; | ||
36 | using Nini.Config; | ||
37 | using OpenSim.Region.Framework.Interfaces; | ||
38 | using OpenSim.Region.Framework.Scenes; | ||
39 | |||
40 | //using Cairo; | ||
41 | |||
42 | namespace OpenSim.Region.CoreModules.Scripting.VectorRender | ||
43 | { | ||
44 | public class VectorRenderModule : IRegionModule, IDynamicTextureRender | ||
45 | { | ||
46 | private string m_name = "VectorRenderModule"; | ||
47 | private Scene m_scene; | ||
48 | private IDynamicTextureManager m_textureManager; | ||
49 | |||
50 | public VectorRenderModule() | ||
51 | { | ||
52 | } | ||
53 | |||
54 | #region IDynamicTextureRender Members | ||
55 | |||
56 | public string GetContentType() | ||
57 | { | ||
58 | return ("vector"); | ||
59 | } | ||
60 | |||
61 | public string GetName() | ||
62 | { | ||
63 | return m_name; | ||
64 | } | ||
65 | |||
66 | public bool SupportsAsynchronous() | ||
67 | { | ||
68 | return true; | ||
69 | } | ||
70 | |||
71 | public byte[] ConvertUrl(string url, string extraParams) | ||
72 | { | ||
73 | return null; | ||
74 | } | ||
75 | |||
76 | public byte[] ConvertStream(Stream data, string extraParams) | ||
77 | { | ||
78 | return null; | ||
79 | } | ||
80 | |||
81 | public bool AsyncConvertUrl(UUID id, string url, string extraParams) | ||
82 | { | ||
83 | return false; | ||
84 | } | ||
85 | |||
86 | public bool AsyncConvertData(UUID id, string bodyData, string extraParams) | ||
87 | { | ||
88 | Draw(bodyData, id, extraParams); | ||
89 | return true; | ||
90 | } | ||
91 | |||
92 | #endregion | ||
93 | |||
94 | #region IRegionModule Members | ||
95 | |||
96 | public void Initialise(Scene scene, IConfigSource config) | ||
97 | { | ||
98 | if (m_scene == null) | ||
99 | { | ||
100 | m_scene = scene; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | public void PostInitialise() | ||
105 | { | ||
106 | m_textureManager = m_scene.RequestModuleInterface<IDynamicTextureManager>(); | ||
107 | if (m_textureManager != null) | ||
108 | { | ||
109 | m_textureManager.RegisterRender(GetContentType(), this); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | public void Close() | ||
114 | { | ||
115 | } | ||
116 | |||
117 | public string Name | ||
118 | { | ||
119 | get { return m_name; } | ||
120 | } | ||
121 | |||
122 | public bool IsSharedModule | ||
123 | { | ||
124 | get { return true; } | ||
125 | } | ||
126 | |||
127 | #endregion | ||
128 | |||
129 | private void Draw(string data, UUID id, string extraParams) | ||
130 | { | ||
131 | // We need to cater for old scripts that didnt use extraParams neatly, they use either an integer size which represents both width and height, or setalpha | ||
132 | // we will now support multiple comma seperated params in the form width:256,height:512,alpha:255 | ||
133 | int width = 256; | ||
134 | int height = 256; | ||
135 | int alpha = 255; // 0 is transparent | ||
136 | |||
137 | char[] paramDelimiter = { ',' }; | ||
138 | char[] nvpDelimiter = { ':' }; | ||
139 | |||
140 | extraParams = extraParams.Trim(); | ||
141 | extraParams = extraParams.ToLower(); | ||
142 | |||
143 | string[] nvps = extraParams.Split(paramDelimiter); | ||
144 | |||
145 | int temp = -1; | ||
146 | foreach (string pair in nvps) | ||
147 | { | ||
148 | string[] nvp = pair.Split(nvpDelimiter); | ||
149 | string name = ""; | ||
150 | string value = ""; | ||
151 | |||
152 | if (nvp[0] != null) | ||
153 | { | ||
154 | name = nvp[0].Trim(); | ||
155 | } | ||
156 | |||
157 | if (nvp.Length == 2) | ||
158 | { | ||
159 | value = nvp[1].Trim(); | ||
160 | } | ||
161 | |||
162 | switch (name) | ||
163 | { | ||
164 | case "width": | ||
165 | temp = parseIntParam(value); | ||
166 | if (temp != -1) | ||
167 | { | ||
168 | if (temp < 1) | ||
169 | { | ||
170 | width = 1; | ||
171 | } | ||
172 | else if (temp > 2048) | ||
173 | { | ||
174 | width = 2048; | ||
175 | } | ||
176 | else | ||
177 | { | ||
178 | width = temp; | ||
179 | } | ||
180 | } | ||
181 | break; | ||
182 | case "height": | ||
183 | temp = parseIntParam(value); | ||
184 | if (temp != -1) | ||
185 | { | ||
186 | if (temp < 1) | ||
187 | { | ||
188 | height = 1; | ||
189 | } | ||
190 | else if (temp > 2048) | ||
191 | { | ||
192 | height = 2048; | ||
193 | } | ||
194 | else | ||
195 | { | ||
196 | height = temp; | ||
197 | } | ||
198 | } | ||
199 | break; | ||
200 | case "alpha": | ||
201 | temp = parseIntParam(value); | ||
202 | if (temp != -1) | ||
203 | { | ||
204 | if (temp < 0) | ||
205 | { | ||
206 | alpha = 0; | ||
207 | } | ||
208 | else if (temp > 255) | ||
209 | { | ||
210 | alpha = 255; | ||
211 | } | ||
212 | else | ||
213 | { | ||
214 | alpha = temp; | ||
215 | } | ||
216 | } | ||
217 | break; | ||
218 | case "": | ||
219 | // blank string has been passed do nothing just use defaults | ||
220 | break; | ||
221 | default: // this is all for backwards compat, all a bit ugly hopfully can be removed in future | ||
222 | // could be either set alpha or just an int | ||
223 | if (name == "setalpha") | ||
224 | { | ||
225 | alpha = 0; // set the texture to have transparent background (maintains backwards compat) | ||
226 | } | ||
227 | else | ||
228 | { | ||
229 | // this function used to accept an int on its own that represented both | ||
230 | // width and height, this is to maintain backwards compat, could be removed | ||
231 | // but would break existing scripts | ||
232 | temp = parseIntParam(name); | ||
233 | if (temp != -1) | ||
234 | { | ||
235 | if (temp > 1024) | ||
236 | temp = 1024; | ||
237 | |||
238 | if (temp < 128) | ||
239 | temp = 128; | ||
240 | |||
241 | width = temp; | ||
242 | height = temp; | ||
243 | } | ||
244 | } | ||
245 | break; | ||
246 | } | ||
247 | |||
248 | } | ||
249 | |||
250 | Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); | ||
251 | |||
252 | Graphics graph = Graphics.FromImage(bitmap); | ||
253 | |||
254 | // this is really just to save people filling the | ||
255 | // background white in their scripts, only do when fully opaque | ||
256 | if (alpha == 255) | ||
257 | { | ||
258 | graph.FillRectangle(new SolidBrush(Color.White), 0, 0, width, height); | ||
259 | } | ||
260 | |||
261 | for (int w = 0; w < bitmap.Width; w++) | ||
262 | { | ||
263 | for (int h = 0; h < bitmap.Height; h++) | ||
264 | { | ||
265 | bitmap.SetPixel(w, h, Color.FromArgb(alpha, bitmap.GetPixel(w, h))); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | |||
270 | GDIDraw(data, graph); | ||
271 | |||
272 | byte[] imageJ2000 = new byte[0]; | ||
273 | |||
274 | try | ||
275 | { | ||
276 | imageJ2000 = OpenJPEG.EncodeFromImage(bitmap, true); | ||
277 | } | ||
278 | catch (Exception) | ||
279 | { | ||
280 | Console.WriteLine( | ||
281 | "[VECTORRENDERMODULE]: OpenJpeg Encode Failed. Empty byte data returned!"); | ||
282 | } | ||
283 | m_textureManager.ReturnData(id, imageJ2000); | ||
284 | } | ||
285 | |||
286 | private int parseIntParam(string strInt) | ||
287 | { | ||
288 | int parsed; | ||
289 | try | ||
290 | { | ||
291 | parsed = Convert.ToInt32(strInt); | ||
292 | } | ||
293 | catch (Exception) | ||
294 | { | ||
295 | //Ckrinke: Add a WriteLine to remove the warning about 'e' defined but not used | ||
296 | // Console.WriteLine("Problem with Draw. Please verify parameters." + e.ToString()); | ||
297 | parsed = -1; | ||
298 | } | ||
299 | |||
300 | return parsed; | ||
301 | |||
302 | } | ||
303 | |||
304 | |||
305 | /* | ||
306 | private void CairoDraw(string data, System.Drawing.Graphics graph) | ||
307 | { | ||
308 | using (Win32Surface draw = new Win32Surface(graph.GetHdc())) | ||
309 | { | ||
310 | Context contex = new Context(draw); | ||
311 | |||
312 | contex.Antialias = Antialias.None; //fastest method but low quality | ||
313 | contex.LineWidth = 7; | ||
314 | char[] lineDelimiter = { ';' }; | ||
315 | char[] partsDelimiter = { ',' }; | ||
316 | string[] lines = data.Split(lineDelimiter); | ||
317 | |||
318 | foreach (string line in lines) | ||
319 | { | ||
320 | string nextLine = line.Trim(); | ||
321 | |||
322 | if (nextLine.StartsWith("MoveTO")) | ||
323 | { | ||
324 | float x = 0; | ||
325 | float y = 0; | ||
326 | GetParams(partsDelimiter, ref nextLine, ref x, ref y); | ||
327 | contex.MoveTo(x, y); | ||
328 | } | ||
329 | else if (nextLine.StartsWith("LineTo")) | ||
330 | { | ||
331 | float x = 0; | ||
332 | float y = 0; | ||
333 | GetParams(partsDelimiter, ref nextLine, ref x, ref y); | ||
334 | contex.LineTo(x, y); | ||
335 | contex.Stroke(); | ||
336 | } | ||
337 | } | ||
338 | } | ||
339 | graph.ReleaseHdc(); | ||
340 | } | ||
341 | */ | ||
342 | |||
343 | private void GDIDraw(string data, Graphics graph) | ||
344 | { | ||
345 | Point startPoint = new Point(0, 0); | ||
346 | Point endPoint = new Point(0, 0); | ||
347 | Pen drawPen = new Pen(Color.Black, 7); | ||
348 | string fontName = "Arial"; | ||
349 | float fontSize = 14; | ||
350 | Font myFont = new Font(fontName, fontSize); | ||
351 | SolidBrush myBrush = new SolidBrush(Color.Black); | ||
352 | char[] lineDelimiter = {';'}; | ||
353 | char[] partsDelimiter = {','}; | ||
354 | string[] lines = data.Split(lineDelimiter); | ||
355 | |||
356 | foreach (string line in lines) | ||
357 | { | ||
358 | string nextLine = line.Trim(); | ||
359 | //replace with switch, or even better, do some proper parsing | ||
360 | if (nextLine.StartsWith("MoveTo")) | ||
361 | { | ||
362 | float x = 0; | ||
363 | float y = 0; | ||
364 | GetParams(partsDelimiter, ref nextLine, 6, ref x, ref y); | ||
365 | startPoint.X = (int) x; | ||
366 | startPoint.Y = (int) y; | ||
367 | } | ||
368 | else if (nextLine.StartsWith("LineTo")) | ||
369 | { | ||
370 | float x = 0; | ||
371 | float y = 0; | ||
372 | GetParams(partsDelimiter, ref nextLine, 6, ref x, ref y); | ||
373 | endPoint.X = (int) x; | ||
374 | endPoint.Y = (int) y; | ||
375 | graph.DrawLine(drawPen, startPoint, endPoint); | ||
376 | startPoint.X = endPoint.X; | ||
377 | startPoint.Y = endPoint.Y; | ||
378 | } | ||
379 | else if (nextLine.StartsWith("Text")) | ||
380 | { | ||
381 | nextLine = nextLine.Remove(0, 4); | ||
382 | nextLine = nextLine.Trim(); | ||
383 | graph.DrawString(nextLine, myFont, myBrush, startPoint); | ||
384 | } | ||
385 | else if (nextLine.StartsWith("Image")) | ||
386 | { | ||
387 | float x = 0; | ||
388 | float y = 0; | ||
389 | GetParams(partsDelimiter, ref nextLine, 5, ref x, ref y); | ||
390 | endPoint.X = (int) x; | ||
391 | endPoint.Y = (int) y; | ||
392 | Image image = ImageHttpRequest(nextLine); | ||
393 | graph.DrawImage(image, (float) startPoint.X, (float) startPoint.Y, x, y); | ||
394 | startPoint.X += endPoint.X; | ||
395 | startPoint.Y += endPoint.Y; | ||
396 | } | ||
397 | else if (nextLine.StartsWith("Rectangle")) | ||
398 | { | ||
399 | float x = 0; | ||
400 | float y = 0; | ||
401 | GetParams(partsDelimiter, ref nextLine, 9, ref x, ref y); | ||
402 | endPoint.X = (int) x; | ||
403 | endPoint.Y = (int) y; | ||
404 | graph.DrawRectangle(drawPen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y); | ||
405 | startPoint.X += endPoint.X; | ||
406 | startPoint.Y += endPoint.Y; | ||
407 | } | ||
408 | else if (nextLine.StartsWith("FillRectangle")) | ||
409 | { | ||
410 | float x = 0; | ||
411 | float y = 0; | ||
412 | GetParams(partsDelimiter, ref nextLine, 13, ref x, ref y); | ||
413 | endPoint.X = (int) x; | ||
414 | endPoint.Y = (int) y; | ||
415 | graph.FillRectangle(myBrush, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y); | ||
416 | startPoint.X += endPoint.X; | ||
417 | startPoint.Y += endPoint.Y; | ||
418 | } | ||
419 | else if (nextLine.StartsWith("Ellipse")) | ||
420 | { | ||
421 | float x = 0; | ||
422 | float y = 0; | ||
423 | GetParams(partsDelimiter, ref nextLine, 7, ref x, ref y); | ||
424 | endPoint.X = (int) x; | ||
425 | endPoint.Y = (int) y; | ||
426 | graph.DrawEllipse(drawPen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y); | ||
427 | startPoint.X += endPoint.X; | ||
428 | startPoint.Y += endPoint.Y; | ||
429 | } | ||
430 | else if (nextLine.StartsWith("FontSize")) | ||
431 | { | ||
432 | nextLine = nextLine.Remove(0, 8); | ||
433 | nextLine = nextLine.Trim(); | ||
434 | fontSize = Convert.ToSingle(nextLine, CultureInfo.InvariantCulture); | ||
435 | myFont = new Font(fontName, fontSize); | ||
436 | } | ||
437 | else if (nextLine.StartsWith("FontName")) | ||
438 | { | ||
439 | nextLine = nextLine.Remove(0, 8); | ||
440 | fontName = nextLine.Trim(); | ||
441 | myFont = new Font(fontName, fontSize); | ||
442 | } | ||
443 | else if (nextLine.StartsWith("PenSize")) | ||
444 | { | ||
445 | nextLine = nextLine.Remove(0, 7); | ||
446 | nextLine = nextLine.Trim(); | ||
447 | float size = Convert.ToSingle(nextLine, CultureInfo.InvariantCulture); | ||
448 | drawPen.Width = size; | ||
449 | } | ||
450 | else if (nextLine.StartsWith("PenColour")) | ||
451 | { | ||
452 | nextLine = nextLine.Remove(0, 9); | ||
453 | nextLine = nextLine.Trim(); | ||
454 | int hex = 0; | ||
455 | |||
456 | Color newColour; | ||
457 | if (Int32.TryParse(nextLine, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out hex)) | ||
458 | { | ||
459 | newColour = Color.FromArgb(hex); | ||
460 | } | ||
461 | else | ||
462 | { | ||
463 | // this doesn't fail, it just returns black if nothing is found | ||
464 | newColour = Color.FromName(nextLine); | ||
465 | } | ||
466 | |||
467 | myBrush.Color = newColour; | ||
468 | drawPen.Color = newColour; | ||
469 | } | ||
470 | } | ||
471 | } | ||
472 | |||
473 | private static void GetParams(char[] partsDelimiter, ref string line, int startLength, ref float x, ref float y) | ||
474 | { | ||
475 | line = line.Remove(0, startLength); | ||
476 | string[] parts = line.Split(partsDelimiter); | ||
477 | if (parts.Length == 2) | ||
478 | { | ||
479 | string xVal = parts[0].Trim(); | ||
480 | string yVal = parts[1].Trim(); | ||
481 | x = Convert.ToSingle(xVal, CultureInfo.InvariantCulture); | ||
482 | y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture); | ||
483 | } | ||
484 | else if (parts.Length > 2) | ||
485 | { | ||
486 | string xVal = parts[0].Trim(); | ||
487 | string yVal = parts[1].Trim(); | ||
488 | x = Convert.ToSingle(xVal, CultureInfo.InvariantCulture); | ||
489 | y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture); | ||
490 | |||
491 | line = ""; | ||
492 | for (int i = 2; i < parts.Length; i++) | ||
493 | { | ||
494 | line = line + parts[i].Trim(); | ||
495 | line = line + " "; | ||
496 | } | ||
497 | } | ||
498 | } | ||
499 | |||
500 | private Bitmap ImageHttpRequest(string url) | ||
501 | { | ||
502 | WebRequest request = HttpWebRequest.Create(url); | ||
503 | //Ckrinke: Comment out for now as 'str' is unused. Bring it back into play later when it is used. | ||
504 | //Ckrinke Stream str = null; | ||
505 | HttpWebResponse response = (HttpWebResponse) (request).GetResponse(); | ||
506 | if (response.StatusCode == HttpStatusCode.OK) | ||
507 | { | ||
508 | Bitmap image = new Bitmap(response.GetResponseStream()); | ||
509 | return image; | ||
510 | } | ||
511 | |||
512 | return null; | ||
513 | } | ||
514 | } | ||
515 | } | ||
diff --git a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs new file mode 100644 index 0000000..c363940 --- /dev/null +++ b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs | |||
@@ -0,0 +1,726 @@ | |||
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 OpenSim 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 OpenMetaverse; | ||
32 | using Nini.Config; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Region.Framework.Interfaces; | ||
35 | using OpenSim.Region.Framework.Scenes; | ||
36 | |||
37 | // using log4net; | ||
38 | // using System.Reflection; | ||
39 | |||
40 | |||
41 | /***************************************************** | ||
42 | * | ||
43 | * WorldCommModule | ||
44 | * | ||
45 | * | ||
46 | * Holding place for world comms - basically llListen | ||
47 | * function implementation. | ||
48 | * | ||
49 | * lLListen(integer channel, string name, key id, string msg) | ||
50 | * The name, id, and msg arguments specify the filtering | ||
51 | * criteria. You can pass the empty string | ||
52 | * (or NULL_KEY for id) for these to set a completely | ||
53 | * open filter; this causes the listen() event handler to be | ||
54 | * invoked for all chat on the channel. To listen only | ||
55 | * for chat spoken by a specific object or avatar, | ||
56 | * specify the name and/or id arguments. To listen | ||
57 | * only for a specific command, specify the | ||
58 | * (case-sensitive) msg argument. If msg is not empty, | ||
59 | * listener will only hear strings which are exactly equal | ||
60 | * to msg. You can also use all the arguments to establish | ||
61 | * the most restrictive filtering criteria. | ||
62 | * | ||
63 | * It might be useful for each listener to maintain a message | ||
64 | * digest, with a list of recent messages by UUID. This can | ||
65 | * be used to prevent in-world repeater loops. However, the | ||
66 | * linden functions do not have this capability, so for now | ||
67 | * thats the way it works. | ||
68 | * Instead it blocks messages originating from the same prim. | ||
69 | * (not Object!) | ||
70 | * | ||
71 | * For LSL compliance, note the following: | ||
72 | * (Tested again 1.21.1 on May 2, 2008) | ||
73 | * 1. 'id' has to be parsed into a UUID. None-UUID keys are | ||
74 | * to be replaced by the ZeroID key. (Well, TryParse does | ||
75 | * that for us. | ||
76 | * 2. Setting up an listen event from the same script, with the | ||
77 | * same filter settings (including step 1), returns the same | ||
78 | * handle as the original filter. | ||
79 | * 3. (TODO) handles should be script-local. Starting from 1. | ||
80 | * Might be actually easier to map the global handle into | ||
81 | * script-local handle in the ScriptEngine. Not sure if its | ||
82 | * worth the effort tho. | ||
83 | * | ||
84 | * **************************************************/ | ||
85 | |||
86 | namespace OpenSim.Region.CoreModules.Scripting.WorldComm | ||
87 | { | ||
88 | public class WorldCommModule : IRegionModule, IWorldComm | ||
89 | { | ||
90 | // private static readonly ILog m_log = | ||
91 | // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
92 | |||
93 | private ListenerManager m_listenerManager; | ||
94 | private Queue m_pending; | ||
95 | private Queue m_pendingQ; | ||
96 | private Scene m_scene; | ||
97 | private int m_whisperdistance = 10; | ||
98 | private int m_saydistance = 30; | ||
99 | private int m_shoutdistance = 100; | ||
100 | |||
101 | #region IRegionModule Members | ||
102 | |||
103 | public void Initialise(Scene scene, IConfigSource config) | ||
104 | { | ||
105 | // wrap this in a try block so that defaults will work if | ||
106 | // the config file doesn't specify otherwise. | ||
107 | int maxlisteners = 1000; | ||
108 | int maxhandles = 64; | ||
109 | try | ||
110 | { | ||
111 | m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance); | ||
112 | m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance); | ||
113 | m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance); | ||
114 | maxlisteners = config.Configs["Chat"].GetInt("max_listens_per_region", maxlisteners); | ||
115 | maxhandles = config.Configs["Chat"].GetInt("max_listens_per_script", maxhandles); | ||
116 | } | ||
117 | catch (Exception) | ||
118 | { | ||
119 | } | ||
120 | if (maxlisteners < 1) maxlisteners = int.MaxValue; | ||
121 | if (maxhandles < 1) maxhandles = int.MaxValue; | ||
122 | |||
123 | m_scene = scene; | ||
124 | m_scene.RegisterModuleInterface<IWorldComm>(this); | ||
125 | m_listenerManager = new ListenerManager(maxlisteners, maxhandles); | ||
126 | m_scene.EventManager.OnChatFromClient += DeliverClientMessage; | ||
127 | m_scene.EventManager.OnChatBroadcast += DeliverClientMessage; | ||
128 | m_pendingQ = new Queue(); | ||
129 | m_pending = Queue.Synchronized(m_pendingQ); | ||
130 | } | ||
131 | |||
132 | public void PostInitialise() | ||
133 | { | ||
134 | } | ||
135 | |||
136 | public void Close() | ||
137 | { | ||
138 | } | ||
139 | |||
140 | public string Name | ||
141 | { | ||
142 | get { return "WorldCommModule"; } | ||
143 | } | ||
144 | |||
145 | public bool IsSharedModule | ||
146 | { | ||
147 | get { return false; } | ||
148 | } | ||
149 | |||
150 | #endregion | ||
151 | |||
152 | #region IWorldComm Members | ||
153 | |||
154 | /// <summary> | ||
155 | /// Create a listen event callback with the specified filters. | ||
156 | /// The parameters localID,itemID are needed to uniquely identify | ||
157 | /// the script during 'peek' time. Parameter hostID is needed to | ||
158 | /// determine the position of the script. | ||
159 | /// </summary> | ||
160 | /// <param name="localID">localID of the script engine</param> | ||
161 | /// <param name="itemID">UUID of the script engine</param> | ||
162 | /// <param name="hostID">UUID of the SceneObjectPart</param> | ||
163 | /// <param name="channel">channel to listen on</param> | ||
164 | /// <param name="name">name to filter on</param> | ||
165 | /// <param name="id">key to filter on (user given, could be totally faked)</param> | ||
166 | /// <param name="msg">msg to filter on</param> | ||
167 | /// <returns>number of the scripts handle</returns> | ||
168 | public int Listen(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg) | ||
169 | { | ||
170 | return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg); | ||
171 | } | ||
172 | |||
173 | /// <summary> | ||
174 | /// Sets the listen event with handle as active (active = TRUE) or inactive (active = FALSE). | ||
175 | /// The handle used is returned from Listen() | ||
176 | /// </summary> | ||
177 | /// <param name="itemID">UUID of the script engine</param> | ||
178 | /// <param name="handle">handle returned by Listen()</param> | ||
179 | /// <param name="active">temp. activate or deactivate the Listen()</param> | ||
180 | public void ListenControl(UUID itemID, int handle, int active) | ||
181 | { | ||
182 | if (active == 1) | ||
183 | m_listenerManager.Activate(itemID, handle); | ||
184 | else if (active == 0) | ||
185 | m_listenerManager.Dectivate(itemID, handle); | ||
186 | } | ||
187 | |||
188 | /// <summary> | ||
189 | /// Removes the listen event callback with handle | ||
190 | /// </summary> | ||
191 | /// <param name="itemID">UUID of the script engine</param> | ||
192 | /// <param name="handle">handle returned by Listen()</param> | ||
193 | public void ListenRemove(UUID itemID, int handle) | ||
194 | { | ||
195 | m_listenerManager.Remove(itemID, handle); | ||
196 | } | ||
197 | |||
198 | /// <summary> | ||
199 | /// Removes all listen event callbacks for the given itemID | ||
200 | /// (script engine) | ||
201 | /// </summary> | ||
202 | /// <param name="itemID">UUID of the script engine</param> | ||
203 | public void DeleteListener(UUID itemID) | ||
204 | { | ||
205 | m_listenerManager.DeleteListener(itemID); | ||
206 | } | ||
207 | |||
208 | |||
209 | protected static Vector3 CenterOfRegion = new Vector3(128, 128, 20); | ||
210 | |||
211 | public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg) | ||
212 | { | ||
213 | Vector3 position; | ||
214 | SceneObjectPart source; | ||
215 | ScenePresence avatar; | ||
216 | |||
217 | if ((source = m_scene.GetSceneObjectPart(id)) != null) | ||
218 | position = source.AbsolutePosition; | ||
219 | else if ((avatar = m_scene.GetScenePresence(id)) != null) | ||
220 | position = avatar.AbsolutePosition; | ||
221 | else if (ChatTypeEnum.Region == type) | ||
222 | position = CenterOfRegion; | ||
223 | else | ||
224 | return; | ||
225 | |||
226 | DeliverMessage(type, channel, name, id, msg, position); | ||
227 | } | ||
228 | |||
229 | /// <summary> | ||
230 | /// This method scans over the objects which registered an interest in listen callbacks. | ||
231 | /// For everyone it finds, it checks if it fits the given filter. If it does, then | ||
232 | /// enqueue the message for delivery to the objects listen event handler. | ||
233 | /// The enqueued ListenerInfo no longer has filter values, but the actually trigged values. | ||
234 | /// Objects that do an llSay have their messages delivered here and for nearby avatars, | ||
235 | /// the OnChatFromClient event is used. | ||
236 | /// </summary> | ||
237 | /// <param name="type">type of delvery (whisper,say,shout or regionwide)</param> | ||
238 | /// <param name="channel">channel to sent on</param> | ||
239 | /// <param name="name">name of sender (object or avatar)</param> | ||
240 | /// <param name="id">key of sender (object or avatar)</param> | ||
241 | /// <param name="msg">msg to sent</param> | ||
242 | public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg, Vector3 position) | ||
243 | { | ||
244 | // m_log.DebugFormat("[WorldComm] got[2] type {0}, channel {1}, name {2}, id {3}, msg {4}", | ||
245 | // type, channel, name, id, msg); | ||
246 | |||
247 | // Determine which listen event filters match the given set of arguments, this results | ||
248 | // in a limited set of listeners, each belonging a host. If the host is in range, add them | ||
249 | // to the pending queue. | ||
250 | foreach (ListenerInfo li in m_listenerManager.GetListeners(UUID.Zero, channel, name, id, msg)) | ||
251 | { | ||
252 | // Dont process if this message is from yourself! | ||
253 | if (li.GetHostID().Equals(id)) | ||
254 | continue; | ||
255 | |||
256 | SceneObjectPart sPart = m_scene.GetSceneObjectPart(li.GetHostID()); | ||
257 | if (sPart == null) | ||
258 | continue; | ||
259 | |||
260 | double dis = Util.GetDistanceTo(sPart.AbsolutePosition, position); | ||
261 | switch (type) | ||
262 | { | ||
263 | case ChatTypeEnum.Whisper: | ||
264 | if (dis < m_whisperdistance) | ||
265 | { | ||
266 | lock (m_pending.SyncRoot) | ||
267 | { | ||
268 | m_pending.Enqueue(new ListenerInfo(li,name,id,msg)); | ||
269 | } | ||
270 | } | ||
271 | break; | ||
272 | |||
273 | case ChatTypeEnum.Say: | ||
274 | if (dis < m_saydistance) | ||
275 | { | ||
276 | lock (m_pending.SyncRoot) | ||
277 | { | ||
278 | m_pending.Enqueue(new ListenerInfo(li,name,id,msg)); | ||
279 | } | ||
280 | } | ||
281 | break; | ||
282 | |||
283 | case ChatTypeEnum.Shout: | ||
284 | if (dis < m_shoutdistance) | ||
285 | { | ||
286 | lock (m_pending.SyncRoot) | ||
287 | { | ||
288 | m_pending.Enqueue(new ListenerInfo(li,name,id,msg)); | ||
289 | } | ||
290 | } | ||
291 | break; | ||
292 | |||
293 | case ChatTypeEnum.Region: | ||
294 | lock (m_pending.SyncRoot) | ||
295 | { | ||
296 | m_pending.Enqueue(new ListenerInfo(li,name,id,msg)); | ||
297 | } | ||
298 | break; | ||
299 | } | ||
300 | } | ||
301 | } | ||
302 | |||
303 | /// <summary> | ||
304 | /// Are there any listen events ready to be dispatched? | ||
305 | /// </summary> | ||
306 | /// <returns>boolean indication</returns> | ||
307 | public bool HasMessages() | ||
308 | { | ||
309 | return (m_pending.Count > 0); | ||
310 | } | ||
311 | |||
312 | /// <summary> | ||
313 | /// Pop the first availlable listen event from the queue | ||
314 | /// </summary> | ||
315 | /// <returns>ListenerInfo with filter filled in</returns> | ||
316 | public IWorldCommListenerInfo GetNextMessage() | ||
317 | { | ||
318 | ListenerInfo li = null; | ||
319 | |||
320 | lock (m_pending.SyncRoot) | ||
321 | { | ||
322 | li = (ListenerInfo) m_pending.Dequeue(); | ||
323 | } | ||
324 | |||
325 | return li; | ||
326 | } | ||
327 | |||
328 | #endregion | ||
329 | |||
330 | /******************************************************************** | ||
331 | * | ||
332 | * Listener Stuff | ||
333 | * | ||
334 | * *****************************************************************/ | ||
335 | |||
336 | private void DeliverClientMessage(Object sender, OSChatMessage e) | ||
337 | { | ||
338 | if (null != e.Sender) | ||
339 | DeliverMessage(e.Type, e.Channel, e.Sender.Name, e.Sender.AgentId, e.Message, e.Position); | ||
340 | else | ||
341 | DeliverMessage(e.Type, e.Channel, e.From, UUID.Zero, e.Message, e.Position); | ||
342 | } | ||
343 | |||
344 | public Object[] GetSerializationData(UUID itemID) | ||
345 | { | ||
346 | return m_listenerManager.GetSerializationData(itemID); | ||
347 | } | ||
348 | |||
349 | public void CreateFromData(uint localID, UUID itemID, UUID hostID, | ||
350 | Object[] data) | ||
351 | { | ||
352 | m_listenerManager.AddFromData(localID, itemID, hostID, data); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | public class ListenerManager | ||
357 | { | ||
358 | private Dictionary<int, List<ListenerInfo>> m_listeners = new Dictionary<int, List<ListenerInfo>>(); | ||
359 | private int m_maxlisteners; | ||
360 | private int m_maxhandles; | ||
361 | private int m_curlisteners; | ||
362 | |||
363 | public ListenerManager(int maxlisteners, int maxhandles) | ||
364 | { | ||
365 | m_maxlisteners = maxlisteners; | ||
366 | m_maxhandles = maxhandles; | ||
367 | m_curlisteners = 0; | ||
368 | } | ||
369 | |||
370 | public int AddListener(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg) | ||
371 | { | ||
372 | // do we already have a match on this particular filter event? | ||
373 | List<ListenerInfo> coll = GetListeners(itemID, channel, name, id, msg); | ||
374 | |||
375 | if (coll.Count > 0) | ||
376 | { | ||
377 | // special case, called with same filter settings, return same handle | ||
378 | // (2008-05-02, tested on 1.21.1 server, still holds) | ||
379 | return coll[0].GetHandle(); | ||
380 | } | ||
381 | |||
382 | if (m_curlisteners < m_maxlisteners) | ||
383 | { | ||
384 | int newHandle = GetNewHandle(itemID); | ||
385 | |||
386 | if (newHandle > 0) | ||
387 | { | ||
388 | ListenerInfo li = new ListenerInfo(newHandle, localID, itemID, hostID, channel, name, id, msg); | ||
389 | |||
390 | lock (m_listeners) | ||
391 | { | ||
392 | List<ListenerInfo> listeners; | ||
393 | if (!m_listeners.TryGetValue(channel,out listeners)) | ||
394 | { | ||
395 | listeners = new List<ListenerInfo>(); | ||
396 | m_listeners.Add(channel, listeners); | ||
397 | } | ||
398 | listeners.Add(li); | ||
399 | m_curlisteners++; | ||
400 | } | ||
401 | |||
402 | return newHandle; | ||
403 | } | ||
404 | } | ||
405 | return -1; | ||
406 | } | ||
407 | |||
408 | public void Remove(UUID itemID, int handle) | ||
409 | { | ||
410 | lock (m_listeners) | ||
411 | { | ||
412 | foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners) | ||
413 | { | ||
414 | foreach (ListenerInfo li in lis.Value) | ||
415 | { | ||
416 | if (li.GetItemID().Equals(itemID) && li.GetHandle().Equals(handle)) | ||
417 | { | ||
418 | lis.Value.Remove(li); | ||
419 | if (lis.Value.Count == 0) | ||
420 | { | ||
421 | m_listeners.Remove(lis.Key); | ||
422 | m_curlisteners--; | ||
423 | } | ||
424 | // there should be only one, so we bail out early | ||
425 | return; | ||
426 | } | ||
427 | } | ||
428 | } | ||
429 | } | ||
430 | } | ||
431 | |||
432 | public void DeleteListener(UUID itemID) | ||
433 | { | ||
434 | List<int> emptyChannels = new List<int>(); | ||
435 | List<ListenerInfo> removedListeners = new List<ListenerInfo>(); | ||
436 | |||
437 | lock (m_listeners) | ||
438 | { | ||
439 | foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners) | ||
440 | { | ||
441 | foreach (ListenerInfo li in lis.Value) | ||
442 | { | ||
443 | if (li.GetItemID().Equals(itemID)) | ||
444 | { | ||
445 | // store them first, else the enumerated bails on us | ||
446 | removedListeners.Add(li); | ||
447 | } | ||
448 | } | ||
449 | foreach (ListenerInfo li in removedListeners) | ||
450 | { | ||
451 | lis.Value.Remove(li); | ||
452 | m_curlisteners--; | ||
453 | } | ||
454 | removedListeners.Clear(); | ||
455 | if (lis.Value.Count == 0) | ||
456 | { | ||
457 | // again, store first, remove later | ||
458 | emptyChannels.Add(lis.Key); | ||
459 | } | ||
460 | } | ||
461 | foreach (int channel in emptyChannels) | ||
462 | { | ||
463 | m_listeners.Remove(channel); | ||
464 | } | ||
465 | } | ||
466 | } | ||
467 | |||
468 | public void Activate(UUID itemID, int handle) | ||
469 | { | ||
470 | lock (m_listeners) | ||
471 | { | ||
472 | foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners) | ||
473 | { | ||
474 | foreach (ListenerInfo li in lis.Value) | ||
475 | { | ||
476 | if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle) | ||
477 | { | ||
478 | li.Activate(); | ||
479 | // only one, bail out | ||
480 | return; | ||
481 | } | ||
482 | } | ||
483 | } | ||
484 | } | ||
485 | } | ||
486 | |||
487 | public void Dectivate(UUID itemID, int handle) | ||
488 | { | ||
489 | lock (m_listeners) | ||
490 | { | ||
491 | foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners) | ||
492 | { | ||
493 | foreach (ListenerInfo li in lis.Value) | ||
494 | { | ||
495 | if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle) | ||
496 | { | ||
497 | li.Deactivate(); | ||
498 | // only one, bail out | ||
499 | return; | ||
500 | } | ||
501 | } | ||
502 | } | ||
503 | } | ||
504 | } | ||
505 | |||
506 | // non-locked access, since its always called in the context of the lock | ||
507 | private int GetNewHandle(UUID itemID) | ||
508 | { | ||
509 | List<int> handles = new List<int>(); | ||
510 | |||
511 | // build a list of used keys for this specific itemID... | ||
512 | foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners) | ||
513 | { | ||
514 | foreach (ListenerInfo li in lis.Value) | ||
515 | { | ||
516 | if (li.GetItemID().Equals(itemID)) | ||
517 | handles.Add(li.GetHandle()); | ||
518 | } | ||
519 | } | ||
520 | |||
521 | // Note: 0 is NOT a valid handle for llListen() to return | ||
522 | for (int i = 1; i <= m_maxhandles; i++) | ||
523 | { | ||
524 | if (!handles.Contains(i)) | ||
525 | return i; | ||
526 | } | ||
527 | |||
528 | return -1; | ||
529 | } | ||
530 | |||
531 | // Theres probably a more clever and efficient way to | ||
532 | // do this, maybe with regex. | ||
533 | // PM2008: Ha, one could even be smart and define a specialized Enumerator. | ||
534 | public List<ListenerInfo> GetListeners(UUID itemID, int channel, string name, UUID id, string msg) | ||
535 | { | ||
536 | List<ListenerInfo> collection = new List<ListenerInfo>(); | ||
537 | |||
538 | lock (m_listeners) | ||
539 | { | ||
540 | List<ListenerInfo> listeners; | ||
541 | if (!m_listeners.TryGetValue(channel,out listeners)) | ||
542 | { | ||
543 | return collection; | ||
544 | } | ||
545 | |||
546 | foreach (ListenerInfo li in listeners) | ||
547 | { | ||
548 | if (!li.IsActive()) | ||
549 | { | ||
550 | continue; | ||
551 | } | ||
552 | if (!itemID.Equals(UUID.Zero) && !li.GetItemID().Equals(itemID)) | ||
553 | { | ||
554 | continue; | ||
555 | } | ||
556 | if (li.GetName().Length > 0 && !li.GetName().Equals(name)) | ||
557 | { | ||
558 | continue; | ||
559 | } | ||
560 | if (!li.GetID().Equals(UUID.Zero) && !li.GetID().Equals(id)) | ||
561 | { | ||
562 | continue; | ||
563 | } | ||
564 | if (li.GetMessage().Length > 0 && !li.GetMessage().Equals(msg)) | ||
565 | { | ||
566 | continue; | ||
567 | } | ||
568 | collection.Add(li); | ||
569 | } | ||
570 | } | ||
571 | return collection; | ||
572 | } | ||
573 | |||
574 | public Object[] GetSerializationData(UUID itemID) | ||
575 | { | ||
576 | List<Object> data = new List<Object>(); | ||
577 | |||
578 | foreach (List<ListenerInfo> list in m_listeners.Values) | ||
579 | { | ||
580 | foreach (ListenerInfo l in list) | ||
581 | { | ||
582 | if (l.GetItemID() == itemID) | ||
583 | data.AddRange(l.GetSerializationData()); | ||
584 | } | ||
585 | } | ||
586 | return (Object[])data.ToArray(); | ||
587 | } | ||
588 | |||
589 | public void AddFromData(uint localID, UUID itemID, UUID hostID, | ||
590 | Object[] data) | ||
591 | { | ||
592 | int idx = 0; | ||
593 | Object[] item = new Object[6]; | ||
594 | |||
595 | while (idx < data.Length) | ||
596 | { | ||
597 | Array.Copy(data, idx, item, 0, 6); | ||
598 | |||
599 | ListenerInfo info = | ||
600 | ListenerInfo.FromData(localID, itemID, hostID, item); | ||
601 | |||
602 | if (!m_listeners.ContainsKey((int)item[2])) | ||
603 | m_listeners.Add((int)item[2], new List<ListenerInfo>()); | ||
604 | m_listeners[(int)item[2]].Add(info); | ||
605 | |||
606 | idx+=6; | ||
607 | } | ||
608 | } | ||
609 | } | ||
610 | |||
611 | public class ListenerInfo: IWorldCommListenerInfo | ||
612 | { | ||
613 | private bool m_active; // Listener is active or not | ||
614 | private int m_handle; // Assigned handle of this listener | ||
615 | private uint m_localID; // Local ID from script engine | ||
616 | private UUID m_itemID; // ID of the host script engine | ||
617 | private UUID m_hostID; // ID of the host/scene part | ||
618 | private int m_channel; // Channel | ||
619 | private UUID m_id; // ID to filter messages from | ||
620 | private string m_name; // Object name to filter messages from | ||
621 | private string m_message; // The message | ||
622 | |||
623 | public ListenerInfo(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, UUID id, string message) | ||
624 | { | ||
625 | Initialise(handle, localID, ItemID, hostID, channel, name, id, message); | ||
626 | } | ||
627 | |||
628 | public ListenerInfo(ListenerInfo li, string name, UUID id, string message) | ||
629 | { | ||
630 | Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message); | ||
631 | } | ||
632 | |||
633 | private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, | ||
634 | UUID id, string message) | ||
635 | { | ||
636 | m_active = true; | ||
637 | m_handle = handle; | ||
638 | m_localID = localID; | ||
639 | m_itemID = ItemID; | ||
640 | m_hostID = hostID; | ||
641 | m_channel = channel; | ||
642 | m_name = name; | ||
643 | m_id = id; | ||
644 | m_message = message; | ||
645 | } | ||
646 | |||
647 | public Object[] GetSerializationData() | ||
648 | { | ||
649 | Object[] data = new Object[6]; | ||
650 | |||
651 | data[0] = m_active; | ||
652 | data[1] = m_handle; | ||
653 | data[2] = m_channel; | ||
654 | data[3] = m_name; | ||
655 | data[4] = m_id; | ||
656 | data[5] = m_message; | ||
657 | |||
658 | return data; | ||
659 | } | ||
660 | |||
661 | public static ListenerInfo FromData(uint localID, UUID ItemID, UUID hostID, Object[] data) | ||
662 | { | ||
663 | ListenerInfo linfo = new ListenerInfo((int)data[1], localID, | ||
664 | ItemID, hostID, (int)data[2], (string)data[3], | ||
665 | (UUID)data[4], (string)data[5]); | ||
666 | linfo.m_active=(bool)data[0]; | ||
667 | |||
668 | return linfo; | ||
669 | } | ||
670 | |||
671 | public UUID GetItemID() | ||
672 | { | ||
673 | return m_itemID; | ||
674 | } | ||
675 | |||
676 | public UUID GetHostID() | ||
677 | { | ||
678 | return m_hostID; | ||
679 | } | ||
680 | |||
681 | public int GetChannel() | ||
682 | { | ||
683 | return m_channel; | ||
684 | } | ||
685 | |||
686 | public uint GetLocalID() | ||
687 | { | ||
688 | return m_localID; | ||
689 | } | ||
690 | |||
691 | public int GetHandle() | ||
692 | { | ||
693 | return m_handle; | ||
694 | } | ||
695 | |||
696 | public string GetMessage() | ||
697 | { | ||
698 | return m_message; | ||
699 | } | ||
700 | |||
701 | public string GetName() | ||
702 | { | ||
703 | return m_name; | ||
704 | } | ||
705 | |||
706 | public bool IsActive() | ||
707 | { | ||
708 | return m_active; | ||
709 | } | ||
710 | |||
711 | public void Deactivate() | ||
712 | { | ||
713 | m_active = false; | ||
714 | } | ||
715 | |||
716 | public void Activate() | ||
717 | { | ||
718 | m_active = true; | ||
719 | } | ||
720 | |||
721 | public UUID GetID() | ||
722 | { | ||
723 | return m_id; | ||
724 | } | ||
725 | } | ||
726 | } | ||
diff --git a/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs b/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs new file mode 100644 index 0000000..942c130 --- /dev/null +++ b/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs | |||
@@ -0,0 +1,726 @@ | |||
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 OpenSim 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.Net; | ||
32 | using System.Reflection; | ||
33 | using System.Threading; | ||
34 | using OpenMetaverse; | ||
35 | using log4net; | ||
36 | using Nini.Config; | ||
37 | using Nwc.XmlRpc; | ||
38 | using OpenSim.Framework; | ||
39 | using OpenSim.Framework.Servers; | ||
40 | using OpenSim.Region.Framework.Interfaces; | ||
41 | using OpenSim.Region.Framework.Scenes; | ||
42 | |||
43 | /***************************************************** | ||
44 | * | ||
45 | * XMLRPCModule | ||
46 | * | ||
47 | * Module for accepting incoming communications from | ||
48 | * external XMLRPC client and calling a remote data | ||
49 | * procedure for a registered data channel/prim. | ||
50 | * | ||
51 | * | ||
52 | * 1. On module load, open a listener port | ||
53 | * 2. Attach an XMLRPC handler | ||
54 | * 3. When a request is received: | ||
55 | * 3.1 Parse into components: channel key, int, string | ||
56 | * 3.2 Look up registered channel listeners | ||
57 | * 3.3 Call the channel (prim) remote data method | ||
58 | * 3.4 Capture the response (llRemoteDataReply) | ||
59 | * 3.5 Return response to client caller | ||
60 | * 3.6 If no response from llRemoteDataReply within | ||
61 | * RemoteReplyScriptTimeout, generate script timeout fault | ||
62 | * | ||
63 | * Prims in script must: | ||
64 | * 1. Open a remote data channel | ||
65 | * 1.1 Generate a channel ID | ||
66 | * 1.2 Register primid,channelid pair with module | ||
67 | * 2. Implement the remote data procedure handler | ||
68 | * | ||
69 | * llOpenRemoteDataChannel | ||
70 | * llRemoteDataReply | ||
71 | * remote_data(integer type, key channel, key messageid, string sender, integer ival, string sval) | ||
72 | * llCloseRemoteDataChannel | ||
73 | * | ||
74 | * **************************************************/ | ||
75 | |||
76 | namespace OpenSim.Region.CoreModules.Scripting.XMLRPC | ||
77 | { | ||
78 | public class XMLRPCModule : IRegionModule, IXMLRPC | ||
79 | { | ||
80 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
81 | |||
82 | private string m_name = "XMLRPCModule"; | ||
83 | |||
84 | // <channel id, RPCChannelInfo> | ||
85 | private Dictionary<UUID, RPCChannelInfo> m_openChannels; | ||
86 | private Dictionary<UUID, SendRemoteDataRequest> m_pendingSRDResponses; | ||
87 | private int m_remoteDataPort = 0; | ||
88 | |||
89 | private Dictionary<UUID, RPCRequestInfo> m_rpcPending; | ||
90 | private Dictionary<UUID, RPCRequestInfo> m_rpcPendingResponses; | ||
91 | private List<Scene> m_scenes = new List<Scene>(); | ||
92 | private int RemoteReplyScriptTimeout = 9000; | ||
93 | private int RemoteReplyScriptWait = 300; | ||
94 | private object XMLRPCListLock = new object(); | ||
95 | |||
96 | #region IRegionModule Members | ||
97 | |||
98 | public void Initialise(Scene scene, IConfigSource config) | ||
99 | { | ||
100 | // We need to create these early because the scripts might be calling | ||
101 | // But since this gets called for every region, we need to make sure they | ||
102 | // get called only one time (or we lose any open channels) | ||
103 | if (null == m_openChannels) | ||
104 | { | ||
105 | m_openChannels = new Dictionary<UUID, RPCChannelInfo>(); | ||
106 | m_rpcPending = new Dictionary<UUID, RPCRequestInfo>(); | ||
107 | m_rpcPendingResponses = new Dictionary<UUID, RPCRequestInfo>(); | ||
108 | m_pendingSRDResponses = new Dictionary<UUID, SendRemoteDataRequest>(); | ||
109 | |||
110 | try | ||
111 | { | ||
112 | m_remoteDataPort = config.Configs["Network"].GetInt("remoteDataPort", m_remoteDataPort); | ||
113 | } | ||
114 | catch (Exception) | ||
115 | { | ||
116 | } | ||
117 | } | ||
118 | |||
119 | if (!m_scenes.Contains(scene)) | ||
120 | { | ||
121 | m_scenes.Add(scene); | ||
122 | |||
123 | scene.RegisterModuleInterface<IXMLRPC>(this); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | public void PostInitialise() | ||
128 | { | ||
129 | if (IsEnabled()) | ||
130 | { | ||
131 | // Start http server | ||
132 | // Attach xmlrpc handlers | ||
133 | m_log.Info("[REMOTE_DATA]: " + | ||
134 | "Starting XMLRPC Server on port " + m_remoteDataPort + " for llRemoteData commands."); | ||
135 | BaseHttpServer httpServer = new BaseHttpServer((uint) m_remoteDataPort); | ||
136 | httpServer.AddXmlRPCHandler("llRemoteData", XmlRpcRemoteData); | ||
137 | httpServer.Start(); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | public void Close() | ||
142 | { | ||
143 | } | ||
144 | |||
145 | public string Name | ||
146 | { | ||
147 | get { return m_name; } | ||
148 | } | ||
149 | |||
150 | public bool IsSharedModule | ||
151 | { | ||
152 | get { return true; } | ||
153 | } | ||
154 | |||
155 | #endregion | ||
156 | |||
157 | #region IXMLRPC Members | ||
158 | |||
159 | public bool IsEnabled() | ||
160 | { | ||
161 | return (m_remoteDataPort > 0); | ||
162 | } | ||
163 | |||
164 | /********************************************** | ||
165 | * OpenXMLRPCChannel | ||
166 | * | ||
167 | * Generate a UUID channel key and add it and | ||
168 | * the prim id to dictionary <channelUUID, primUUID> | ||
169 | * | ||
170 | * A custom channel key can be proposed. | ||
171 | * Otherwise, passing UUID.Zero will generate | ||
172 | * and return a random channel | ||
173 | * | ||
174 | * First check if there is a channel assigned for | ||
175 | * this itemID. If there is, then someone called | ||
176 | * llOpenRemoteDataChannel twice. Just return the | ||
177 | * original channel. Other option is to delete the | ||
178 | * current channel and assign a new one. | ||
179 | * | ||
180 | * ********************************************/ | ||
181 | |||
182 | public UUID OpenXMLRPCChannel(uint localID, UUID itemID, UUID channelID) | ||
183 | { | ||
184 | UUID newChannel = UUID.Zero; | ||
185 | |||
186 | // This should no longer happen, but the check is reasonable anyway | ||
187 | if (null == m_openChannels) | ||
188 | { | ||
189 | m_log.Warn("[RemoteDataReply] Attempt to open channel before initialization is complete"); | ||
190 | return newChannel; | ||
191 | } | ||
192 | |||
193 | //Is a dupe? | ||
194 | foreach (RPCChannelInfo ci in m_openChannels.Values) | ||
195 | { | ||
196 | if (ci.GetItemID().Equals(itemID)) | ||
197 | { | ||
198 | // return the original channel ID for this item | ||
199 | newChannel = ci.GetChannelID(); | ||
200 | break; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | if (newChannel == UUID.Zero) | ||
205 | { | ||
206 | newChannel = (channelID == UUID.Zero) ? UUID.Random() : channelID; | ||
207 | RPCChannelInfo rpcChanInfo = new RPCChannelInfo(localID, itemID, newChannel); | ||
208 | lock (XMLRPCListLock) | ||
209 | { | ||
210 | m_openChannels.Add(newChannel, rpcChanInfo); | ||
211 | } | ||
212 | } | ||
213 | |||
214 | return newChannel; | ||
215 | } | ||
216 | |||
217 | // Delete channels based on itemID | ||
218 | // for when a script is deleted | ||
219 | public void DeleteChannels(UUID itemID) | ||
220 | { | ||
221 | if (m_openChannels != null) | ||
222 | { | ||
223 | ArrayList tmp = new ArrayList(); | ||
224 | |||
225 | lock (XMLRPCListLock) | ||
226 | { | ||
227 | foreach (RPCChannelInfo li in m_openChannels.Values) | ||
228 | { | ||
229 | if (li.GetItemID().Equals(itemID)) | ||
230 | { | ||
231 | tmp.Add(itemID); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | IEnumerator tmpEnumerator = tmp.GetEnumerator(); | ||
236 | while (tmpEnumerator.MoveNext()) | ||
237 | m_openChannels.Remove((UUID) tmpEnumerator.Current); | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | |||
242 | /********************************************** | ||
243 | * Remote Data Reply | ||
244 | * | ||
245 | * Response to RPC message | ||
246 | * | ||
247 | *********************************************/ | ||
248 | |||
249 | public void RemoteDataReply(string channel, string message_id, string sdata, int idata) | ||
250 | { | ||
251 | UUID message_key = new UUID(message_id); | ||
252 | UUID channel_key = new UUID(channel); | ||
253 | |||
254 | RPCRequestInfo rpcInfo = null; | ||
255 | |||
256 | if (message_key == UUID.Zero) | ||
257 | { | ||
258 | foreach (RPCRequestInfo oneRpcInfo in m_rpcPendingResponses.Values) | ||
259 | if (oneRpcInfo.GetChannelKey() == channel_key) | ||
260 | rpcInfo = oneRpcInfo; | ||
261 | } | ||
262 | else | ||
263 | { | ||
264 | m_rpcPendingResponses.TryGetValue(message_key, out rpcInfo); | ||
265 | } | ||
266 | |||
267 | if (rpcInfo != null) | ||
268 | { | ||
269 | rpcInfo.SetStrRetval(sdata); | ||
270 | rpcInfo.SetIntRetval(idata); | ||
271 | rpcInfo.SetProcessed(true); | ||
272 | m_rpcPendingResponses.Remove(message_key); | ||
273 | } | ||
274 | else | ||
275 | { | ||
276 | m_log.Warn("[RemoteDataReply]: Channel or message_id not found"); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | /********************************************** | ||
281 | * CloseXMLRPCChannel | ||
282 | * | ||
283 | * Remove channel from dictionary | ||
284 | * | ||
285 | *********************************************/ | ||
286 | |||
287 | public void CloseXMLRPCChannel(UUID channelKey) | ||
288 | { | ||
289 | if (m_openChannels.ContainsKey(channelKey)) | ||
290 | m_openChannels.Remove(channelKey); | ||
291 | } | ||
292 | |||
293 | |||
294 | public bool hasRequests() | ||
295 | { | ||
296 | lock (XMLRPCListLock) | ||
297 | { | ||
298 | if (m_rpcPending != null) | ||
299 | return (m_rpcPending.Count > 0); | ||
300 | else | ||
301 | return false; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | public IXmlRpcRequestInfo GetNextCompletedRequest() | ||
306 | { | ||
307 | if (m_rpcPending != null) | ||
308 | { | ||
309 | lock (XMLRPCListLock) | ||
310 | { | ||
311 | foreach (UUID luid in m_rpcPending.Keys) | ||
312 | { | ||
313 | RPCRequestInfo tmpReq; | ||
314 | |||
315 | if (m_rpcPending.TryGetValue(luid, out tmpReq)) | ||
316 | { | ||
317 | if (!tmpReq.IsProcessed()) return tmpReq; | ||
318 | } | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | return null; | ||
323 | } | ||
324 | |||
325 | public void RemoveCompletedRequest(UUID id) | ||
326 | { | ||
327 | lock (XMLRPCListLock) | ||
328 | { | ||
329 | RPCRequestInfo tmp; | ||
330 | if (m_rpcPending.TryGetValue(id, out tmp)) | ||
331 | { | ||
332 | m_rpcPending.Remove(id); | ||
333 | m_rpcPendingResponses.Add(id, tmp); | ||
334 | } | ||
335 | else | ||
336 | { | ||
337 | Console.WriteLine("UNABLE TO REMOVE COMPLETED REQUEST"); | ||
338 | } | ||
339 | } | ||
340 | } | ||
341 | |||
342 | public UUID SendRemoteData(uint localID, UUID itemID, string channel, string dest, int idata, string sdata) | ||
343 | { | ||
344 | SendRemoteDataRequest req = new SendRemoteDataRequest( | ||
345 | localID, itemID, channel, dest, idata, sdata | ||
346 | ); | ||
347 | m_pendingSRDResponses.Add(req.GetReqID(), req); | ||
348 | req.Process(); | ||
349 | return req.ReqID; | ||
350 | } | ||
351 | |||
352 | public IServiceRequest GetNextCompletedSRDRequest() | ||
353 | { | ||
354 | if (m_pendingSRDResponses != null) | ||
355 | { | ||
356 | lock (XMLRPCListLock) | ||
357 | { | ||
358 | foreach (UUID luid in m_pendingSRDResponses.Keys) | ||
359 | { | ||
360 | SendRemoteDataRequest tmpReq; | ||
361 | |||
362 | if (m_pendingSRDResponses.TryGetValue(luid, out tmpReq)) | ||
363 | { | ||
364 | if (tmpReq.Finished) | ||
365 | return tmpReq; | ||
366 | } | ||
367 | } | ||
368 | } | ||
369 | } | ||
370 | return null; | ||
371 | } | ||
372 | |||
373 | public void RemoveCompletedSRDRequest(UUID id) | ||
374 | { | ||
375 | lock (XMLRPCListLock) | ||
376 | { | ||
377 | SendRemoteDataRequest tmpReq; | ||
378 | if (m_pendingSRDResponses.TryGetValue(id, out tmpReq)) | ||
379 | { | ||
380 | m_pendingSRDResponses.Remove(id); | ||
381 | } | ||
382 | } | ||
383 | } | ||
384 | |||
385 | public void CancelSRDRequests(UUID itemID) | ||
386 | { | ||
387 | if (m_pendingSRDResponses != null) | ||
388 | { | ||
389 | lock (XMLRPCListLock) | ||
390 | { | ||
391 | foreach (SendRemoteDataRequest li in m_pendingSRDResponses.Values) | ||
392 | { | ||
393 | if (li.ItemID.Equals(itemID)) | ||
394 | m_pendingSRDResponses.Remove(li.GetReqID()); | ||
395 | } | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | |||
400 | #endregion | ||
401 | |||
402 | public XmlRpcResponse XmlRpcRemoteData(XmlRpcRequest request) | ||
403 | { | ||
404 | XmlRpcResponse response = new XmlRpcResponse(); | ||
405 | |||
406 | Hashtable requestData = (Hashtable) request.Params[0]; | ||
407 | bool GoodXML = (requestData.Contains("Channel") && requestData.Contains("IntValue") && | ||
408 | requestData.Contains("StringValue")); | ||
409 | |||
410 | if (GoodXML) | ||
411 | { | ||
412 | UUID channel = new UUID((string) requestData["Channel"]); | ||
413 | RPCChannelInfo rpcChanInfo; | ||
414 | if (m_openChannels.TryGetValue(channel, out rpcChanInfo)) | ||
415 | { | ||
416 | string intVal = Convert.ToInt32(requestData["IntValue"]).ToString(); | ||
417 | string strVal = (string) requestData["StringValue"]; | ||
418 | |||
419 | RPCRequestInfo rpcInfo; | ||
420 | |||
421 | lock (XMLRPCListLock) | ||
422 | { | ||
423 | rpcInfo = | ||
424 | new RPCRequestInfo(rpcChanInfo.GetLocalID(), rpcChanInfo.GetItemID(), channel, strVal, | ||
425 | intVal); | ||
426 | m_rpcPending.Add(rpcInfo.GetMessageID(), rpcInfo); | ||
427 | } | ||
428 | |||
429 | int timeoutCtr = 0; | ||
430 | |||
431 | while (!rpcInfo.IsProcessed() && (timeoutCtr < RemoteReplyScriptTimeout)) | ||
432 | { | ||
433 | Thread.Sleep(RemoteReplyScriptWait); | ||
434 | timeoutCtr += RemoteReplyScriptWait; | ||
435 | } | ||
436 | if (rpcInfo.IsProcessed()) | ||
437 | { | ||
438 | Hashtable param = new Hashtable(); | ||
439 | param["StringValue"] = rpcInfo.GetStrRetval(); | ||
440 | param["IntValue"] = rpcInfo.GetIntRetval(); | ||
441 | |||
442 | ArrayList parameters = new ArrayList(); | ||
443 | parameters.Add(param); | ||
444 | |||
445 | response.Value = parameters; | ||
446 | rpcInfo = null; | ||
447 | } | ||
448 | else | ||
449 | { | ||
450 | response.SetFault(-1, "Script timeout"); | ||
451 | rpcInfo = null; | ||
452 | } | ||
453 | } | ||
454 | else | ||
455 | { | ||
456 | response.SetFault(-1, "Invalid channel"); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | return response; | ||
461 | } | ||
462 | } | ||
463 | |||
464 | public class RPCRequestInfo: IXmlRpcRequestInfo | ||
465 | { | ||
466 | private UUID m_ChannelKey; | ||
467 | private string m_IntVal; | ||
468 | private UUID m_ItemID; | ||
469 | private uint m_localID; | ||
470 | private UUID m_MessageID; | ||
471 | private bool m_processed; | ||
472 | private int m_respInt; | ||
473 | private string m_respStr; | ||
474 | private string m_StrVal; | ||
475 | |||
476 | public RPCRequestInfo(uint localID, UUID itemID, UUID channelKey, string strVal, string intVal) | ||
477 | { | ||
478 | m_localID = localID; | ||
479 | m_StrVal = strVal; | ||
480 | m_IntVal = intVal; | ||
481 | m_ItemID = itemID; | ||
482 | m_ChannelKey = channelKey; | ||
483 | m_MessageID = UUID.Random(); | ||
484 | m_processed = false; | ||
485 | m_respStr = String.Empty; | ||
486 | m_respInt = 0; | ||
487 | } | ||
488 | |||
489 | public bool IsProcessed() | ||
490 | { | ||
491 | return m_processed; | ||
492 | } | ||
493 | |||
494 | public UUID GetChannelKey() | ||
495 | { | ||
496 | return m_ChannelKey; | ||
497 | } | ||
498 | |||
499 | public void SetProcessed(bool processed) | ||
500 | { | ||
501 | m_processed = processed; | ||
502 | } | ||
503 | |||
504 | public void SetStrRetval(string resp) | ||
505 | { | ||
506 | m_respStr = resp; | ||
507 | } | ||
508 | |||
509 | public string GetStrRetval() | ||
510 | { | ||
511 | return m_respStr; | ||
512 | } | ||
513 | |||
514 | public void SetIntRetval(int resp) | ||
515 | { | ||
516 | m_respInt = resp; | ||
517 | } | ||
518 | |||
519 | public int GetIntRetval() | ||
520 | { | ||
521 | return m_respInt; | ||
522 | } | ||
523 | |||
524 | public uint GetLocalID() | ||
525 | { | ||
526 | return m_localID; | ||
527 | } | ||
528 | |||
529 | public UUID GetItemID() | ||
530 | { | ||
531 | return m_ItemID; | ||
532 | } | ||
533 | |||
534 | public string GetStrVal() | ||
535 | { | ||
536 | return m_StrVal; | ||
537 | } | ||
538 | |||
539 | public int GetIntValue() | ||
540 | { | ||
541 | return int.Parse(m_IntVal); | ||
542 | } | ||
543 | |||
544 | public UUID GetMessageID() | ||
545 | { | ||
546 | return m_MessageID; | ||
547 | } | ||
548 | } | ||
549 | |||
550 | public class RPCChannelInfo | ||
551 | { | ||
552 | private UUID m_ChannelKey; | ||
553 | private UUID m_itemID; | ||
554 | private uint m_localID; | ||
555 | |||
556 | public RPCChannelInfo(uint localID, UUID itemID, UUID channelID) | ||
557 | { | ||
558 | m_ChannelKey = channelID; | ||
559 | m_localID = localID; | ||
560 | m_itemID = itemID; | ||
561 | } | ||
562 | |||
563 | public UUID GetItemID() | ||
564 | { | ||
565 | return m_itemID; | ||
566 | } | ||
567 | |||
568 | public UUID GetChannelID() | ||
569 | { | ||
570 | return m_ChannelKey; | ||
571 | } | ||
572 | |||
573 | public uint GetLocalID() | ||
574 | { | ||
575 | return m_localID; | ||
576 | } | ||
577 | } | ||
578 | |||
579 | public class SendRemoteDataRequest: IServiceRequest | ||
580 | { | ||
581 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
582 | |||
583 | public string Channel; | ||
584 | public string DestURL; | ||
585 | private bool _finished; | ||
586 | public bool Finished | ||
587 | { | ||
588 | get { return _finished; } | ||
589 | set { _finished = value; } | ||
590 | } | ||
591 | private Thread httpThread; | ||
592 | public int Idata; | ||
593 | private UUID _itemID; | ||
594 | public UUID ItemID | ||
595 | { | ||
596 | get { return _itemID; } | ||
597 | set { _itemID = value; } | ||
598 | } | ||
599 | private uint _localID; | ||
600 | public uint LocalID | ||
601 | { | ||
602 | get { return _localID; } | ||
603 | set { _localID = value; } | ||
604 | } | ||
605 | private UUID _reqID; | ||
606 | public UUID ReqID | ||
607 | { | ||
608 | get { return _reqID; } | ||
609 | set { _reqID = value; } | ||
610 | } | ||
611 | public XmlRpcRequest Request; | ||
612 | public int ResponseIdata; | ||
613 | public string ResponseSdata; | ||
614 | public string Sdata; | ||
615 | |||
616 | public SendRemoteDataRequest(uint localID, UUID itemID, string channel, string dest, int idata, string sdata) | ||
617 | { | ||
618 | this.Channel = channel; | ||
619 | DestURL = dest; | ||
620 | this.Idata = idata; | ||
621 | this.Sdata = sdata; | ||
622 | ItemID = itemID; | ||
623 | LocalID = localID; | ||
624 | |||
625 | ReqID = UUID.Random(); | ||
626 | } | ||
627 | |||
628 | public void Process() | ||
629 | { | ||
630 | httpThread = new Thread(SendRequest); | ||
631 | httpThread.Name = "HttpRequestThread"; | ||
632 | httpThread.Priority = ThreadPriority.BelowNormal; | ||
633 | httpThread.IsBackground = true; | ||
634 | _finished = false; | ||
635 | httpThread.Start(); | ||
636 | ThreadTracker.Add(httpThread); | ||
637 | } | ||
638 | |||
639 | /* | ||
640 | * TODO: More work on the response codes. Right now | ||
641 | * returning 200 for success or 499 for exception | ||
642 | */ | ||
643 | |||
644 | public void SendRequest() | ||
645 | { | ||
646 | Hashtable param = new Hashtable(); | ||
647 | |||
648 | // Check if channel is an UUID | ||
649 | // if not, use as method name | ||
650 | UUID parseUID; | ||
651 | string mName = "llRemoteData"; | ||
652 | if ((Channel != null) && (Channel != "")) | ||
653 | if (!UUID.TryParse(Channel, out parseUID)) | ||
654 | mName = Channel; | ||
655 | else | ||
656 | param["Channel"] = Channel; | ||
657 | |||
658 | param["StringValue"] = Sdata; | ||
659 | param["IntValue"] = Convert.ToString(Idata); | ||
660 | |||
661 | ArrayList parameters = new ArrayList(); | ||
662 | parameters.Add(param); | ||
663 | XmlRpcRequest req = new XmlRpcRequest(mName, parameters); | ||
664 | try | ||
665 | { | ||
666 | XmlRpcResponse resp = req.Send(DestURL, 30000); | ||
667 | if (resp != null) | ||
668 | { | ||
669 | Hashtable respParms; | ||
670 | if (resp.Value.GetType().Equals(typeof(System.Collections.Hashtable))) | ||
671 | { | ||
672 | respParms = (Hashtable) resp.Value; | ||
673 | } | ||
674 | else | ||
675 | { | ||
676 | ArrayList respData = (ArrayList) resp.Value; | ||
677 | respParms = (Hashtable) respData[0]; | ||
678 | } | ||
679 | if (respParms != null) | ||
680 | { | ||
681 | if (respParms.Contains("StringValue")) | ||
682 | { | ||
683 | Sdata = (string) respParms["StringValue"]; | ||
684 | } | ||
685 | if (respParms.Contains("IntValue")) | ||
686 | { | ||
687 | Idata = Convert.ToInt32((string) respParms["IntValue"]); | ||
688 | } | ||
689 | if (respParms.Contains("faultString")) | ||
690 | { | ||
691 | Sdata = (string) respParms["faultString"]; | ||
692 | } | ||
693 | if (respParms.Contains("faultCode")) | ||
694 | { | ||
695 | Idata = Convert.ToInt32(respParms["faultCode"]); | ||
696 | } | ||
697 | } | ||
698 | } | ||
699 | } | ||
700 | catch (Exception we) | ||
701 | { | ||
702 | Sdata = we.Message; | ||
703 | m_log.Warn("[SendRemoteDataRequest]: Request failed"); | ||
704 | m_log.Warn(we.StackTrace); | ||
705 | } | ||
706 | |||
707 | _finished = true; | ||
708 | } | ||
709 | |||
710 | public void Stop() | ||
711 | { | ||
712 | try | ||
713 | { | ||
714 | httpThread.Abort(); | ||
715 | } | ||
716 | catch (Exception) | ||
717 | { | ||
718 | } | ||
719 | } | ||
720 | |||
721 | public UUID GetReqID() | ||
722 | { | ||
723 | return ReqID; | ||
724 | } | ||
725 | } | ||
726 | } | ||