clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections.Generic;
30 using System.Reflection;
31 using System.Text.RegularExpressions;
32 using DotNetOpenMail;
33 using DotNetOpenMail.SmtpAuth;
34 using log4net;
35 using Nini.Config;
36 using OpenMetaverse;
37 using OpenSim.Framework;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.Framework.Scenes;
40 using Mono.Addins;
41  
42 namespace OpenSim.Region.CoreModules.Scripting.EmailModules
43 {
44 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "EmailModule")]
45 public class EmailModule : ISharedRegionModule, IEmailModule
46 {
47 //
48 // Log
49 //
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51  
52 //
53 // Module vars
54 //
55 private IConfigSource m_Config;
56 private string m_HostName = string.Empty;
57 //private string m_RegionName = string.Empty;
58 private string SMTP_SERVER_HOSTNAME = string.Empty;
59 private int SMTP_SERVER_PORT = 25;
60 private string SMTP_SERVER_LOGIN = string.Empty;
61 private string SMTP_SERVER_PASSWORD = string.Empty;
62  
63 private int m_MaxQueueSize = 50; // maximum size of an object mail queue
64 private Dictionary<UUID, List<Email>> m_MailQueues = new Dictionary<UUID, List<Email>>();
65 private Dictionary<UUID, DateTime> m_LastGetEmailCall = new Dictionary<UUID, DateTime>();
66 private TimeSpan m_QueueTimeout = new TimeSpan(2, 0, 0); // 2 hours without llGetNextEmail drops the queue
67 private string m_InterObjectHostname = "lsl.opensim.local";
68  
69 private int m_MaxEmailSize = 4096; // largest email allowed by default, as per lsl docs.
70  
71 // Scenes by Region Handle
72 private Dictionary<ulong, Scene> m_Scenes =
73 new Dictionary<ulong, Scene>();
74  
75 private bool m_Enabled = false;
76  
77 #region ISharedRegionModule
78  
79 public void Initialise(IConfigSource config)
80 {
81 m_Config = config;
82 IConfig SMTPConfig;
83  
84 //FIXME: RegionName is correct??
85 //m_RegionName = scene.RegionInfo.RegionName;
86  
87 IConfig startupConfig = m_Config.Configs["Startup"];
88  
89 m_Enabled = (startupConfig.GetString("emailmodule", "DefaultEmailModule") == "DefaultEmailModule");
90  
91 //Load SMTP SERVER config
92 try
93 {
94 if ((SMTPConfig = m_Config.Configs["SMTP"]) == null)
95 {
96 m_Enabled = false;
97 return;
98 }
99  
100 if (!SMTPConfig.GetBoolean("enabled", false))
101 {
102 m_Enabled = false;
103 return;
104 }
105  
106 m_HostName = SMTPConfig.GetString("host_domain_header_from", m_HostName);
107 m_InterObjectHostname = SMTPConfig.GetString("internal_object_host", m_InterObjectHostname);
108 SMTP_SERVER_HOSTNAME = SMTPConfig.GetString("SMTP_SERVER_HOSTNAME", SMTP_SERVER_HOSTNAME);
109 SMTP_SERVER_PORT = SMTPConfig.GetInt("SMTP_SERVER_PORT", SMTP_SERVER_PORT);
110 SMTP_SERVER_LOGIN = SMTPConfig.GetString("SMTP_SERVER_LOGIN", SMTP_SERVER_LOGIN);
111 SMTP_SERVER_PASSWORD = SMTPConfig.GetString("SMTP_SERVER_PASSWORD", SMTP_SERVER_PASSWORD);
112 m_MaxEmailSize = SMTPConfig.GetInt("email_max_size", m_MaxEmailSize);
113 }
114 catch (Exception e)
115 {
116 m_log.Error("[EMAIL] DefaultEmailModule not configured: " + e.Message);
117 m_Enabled = false;
118 return;
119 }
120  
121 }
122  
123 public void AddRegion(Scene scene)
124 {
125 if (!m_Enabled)
126 return;
127  
128 // It's a go!
129 lock (m_Scenes)
130 {
131 // Claim the interface slot
132 scene.RegisterModuleInterface<IEmailModule>(this);
133  
134 // Add to scene list
135 if (m_Scenes.ContainsKey(scene.RegionInfo.RegionHandle))
136 {
137 m_Scenes[scene.RegionInfo.RegionHandle] = scene;
138 }
139 else
140 {
141 m_Scenes.Add(scene.RegionInfo.RegionHandle, scene);
142 }
143 }
144  
145 m_log.Info("[EMAIL] Activated DefaultEmailModule");
146 }
147  
148 public void RemoveRegion(Scene scene)
149 {
150 }
151  
152 public void PostInitialise()
153 {
154 }
155  
156 public void Close()
157 {
158 }
159  
160 public string Name
161 {
162 get { return "DefaultEmailModule"; }
163 }
164  
165 public Type ReplaceableInterface
166 {
167 get { return null; }
168 }
169  
170 public void RegionLoaded(Scene scene)
171 {
172 }
173  
174 #endregion
175  
176 public void InsertEmail(UUID to, Email email)
177 {
178 // It's tempting to create the queue here. Don't; objects which have
179 // not yet called GetNextEmail should have no queue, and emails to them
180 // should be silently dropped.
181  
182 lock (m_MailQueues)
183 {
184 if (m_MailQueues.ContainsKey(to))
185 {
186 if (m_MailQueues[to].Count >= m_MaxQueueSize)
187 {
188 // fail silently
189 return;
190 }
191  
192 lock (m_MailQueues[to])
193 {
194 m_MailQueues[to].Add(email);
195 }
196 }
197 }
198 }
199  
200 private bool IsLocal(UUID objectID)
201 {
202 string unused;
203 return (null != findPrim(objectID, out unused));
204 }
205  
206 private SceneObjectPart findPrim(UUID objectID, out string ObjectRegionName)
207 {
208 lock (m_Scenes)
209 {
210 foreach (Scene s in m_Scenes.Values)
211 {
212 SceneObjectPart part = s.GetSceneObjectPart(objectID);
213 if (part != null)
214 {
215 ObjectRegionName = s.RegionInfo.RegionName;
216 uint localX = (s.RegionInfo.RegionLocX * (int)Constants.RegionSize);
217 uint localY = (s.RegionInfo.RegionLocY * (int)Constants.RegionSize);
218 ObjectRegionName = ObjectRegionName + " (" + localX + ", " + localY + ")";
219 return part;
220 }
221 }
222 }
223 ObjectRegionName = string.Empty;
224 return null;
225 }
226  
227 private void resolveNamePositionRegionName(UUID objectID, out string ObjectName, out string ObjectAbsolutePosition, out string ObjectRegionName)
228 {
229 string m_ObjectRegionName;
230 int objectLocX;
231 int objectLocY;
232 int objectLocZ;
233 SceneObjectPart part = findPrim(objectID, out m_ObjectRegionName);
234 if (part != null)
235 {
236 objectLocX = (int)part.AbsolutePosition.X;
237 objectLocY = (int)part.AbsolutePosition.Y;
238 objectLocZ = (int)part.AbsolutePosition.Z;
239 ObjectAbsolutePosition = "(" + objectLocX + ", " + objectLocY + ", " + objectLocZ + ")";
240 ObjectName = part.Name;
241 ObjectRegionName = m_ObjectRegionName;
242 return;
243 }
244 objectLocX = (int)part.AbsolutePosition.X;
245 objectLocY = (int)part.AbsolutePosition.Y;
246 objectLocZ = (int)part.AbsolutePosition.Z;
247 ObjectAbsolutePosition = "(" + objectLocX + ", " + objectLocY + ", " + objectLocZ + ")";
248 ObjectName = part.Name;
249 ObjectRegionName = m_ObjectRegionName;
250 return;
251 }
252  
253 /// <summary>
254 /// SendMail function utilized by llEMail
255 /// </summary>
256 /// <param name="objectID"></param>
257 /// <param name="address"></param>
258 /// <param name="subject"></param>
259 /// <param name="body"></param>
260 public void SendEmail(UUID objectID, string address, string subject, string body)
261 {
262 //Check if address is empty
263 if (address == string.Empty)
264 return;
265  
266 //FIXED:Check the email is correct form in REGEX
267 string EMailpatternStrict = @"^(([^<>()[\]\\.,;:\s@\""]+"
268 + @"(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\""))@"
269 + @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
270 + @"\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+"
271 + @"[a-zA-Z]{2,}))$";
272 Regex EMailreStrict = new Regex(EMailpatternStrict);
273 bool isEMailStrictMatch = EMailreStrict.IsMatch(address);
274 if (!isEMailStrictMatch)
275 {
276 m_log.Error("[EMAIL] REGEX Problem in EMail Address: "+address);
277 return;
278 }
279 if ((subject.Length + body.Length) > m_MaxEmailSize)
280 {
281 m_log.Error("[EMAIL] subject + body larger than limit of " + m_MaxEmailSize + " bytes");
282 return;
283 }
284  
285 string LastObjectName = string.Empty;
286 string LastObjectPosition = string.Empty;
287 string LastObjectRegionName = string.Empty;
288  
289 resolveNamePositionRegionName(objectID, out LastObjectName, out LastObjectPosition, out LastObjectRegionName);
290  
291 if (!address.EndsWith(m_InterObjectHostname))
292 {
293 // regular email, send it out
294 try
295 {
296 //Creation EmailMessage
297 EmailMessage emailMessage = new EmailMessage();
298 //From
299 emailMessage.FromAddress = new EmailAddress(objectID.ToString() + "@" + m_HostName);
300 //To - Only One
301 emailMessage.AddToAddress(new EmailAddress(address));
302 //Subject
303 emailMessage.Subject = subject;
304 //TEXT Body
305 resolveNamePositionRegionName(objectID, out LastObjectName, out LastObjectPosition, out LastObjectRegionName);
306 emailMessage.BodyText = "Object-Name: " + LastObjectName +
307 "\nRegion: " + LastObjectRegionName + "\nLocal-Position: " +
308 LastObjectPosition + "\n\n" + body;
309  
310 //Config SMTP Server
311 //Set SMTP SERVER config
312 SmtpServer smtpServer=new SmtpServer(SMTP_SERVER_HOSTNAME,SMTP_SERVER_PORT);
313 // Add authentication only when requested
314 //
315 if (SMTP_SERVER_LOGIN != String.Empty && SMTP_SERVER_PASSWORD != String.Empty)
316 {
317 //Authentication
318 smtpServer.SmtpAuthToken=new SmtpAuthToken(SMTP_SERVER_LOGIN, SMTP_SERVER_PASSWORD);
319 }
320 //Send Email Message
321 emailMessage.Send(smtpServer);
322  
323 //Log
324 m_log.Info("[EMAIL] EMail sent to: " + address + " from object: " + objectID.ToString() + "@" + m_HostName);
325 }
326 catch (Exception e)
327 {
328 m_log.Error("[EMAIL] DefaultEmailModule Exception: " + e.Message);
329 }
330 }
331 else
332 {
333 // inter object email, keep it in the family
334 Email email = new Email();
335 email.time = ((int)((DateTime.UtcNow - new DateTime(1970,1,1,0,0,0)).TotalSeconds)).ToString();
336 email.subject = subject;
337 email.sender = objectID.ToString() + "@" + m_InterObjectHostname;
338 email.message = "Object-Name: " + LastObjectName +
339 "\nRegion: " + LastObjectRegionName + "\nLocal-Position: " +
340 LastObjectPosition + "\n\n" + body;
341  
342 string guid = address.Substring(0, address.IndexOf("@"));
343 UUID toID = new UUID(guid);
344  
345 if (IsLocal(toID)) // TODO FIX check to see if it is local
346 {
347 // object in this region
348 InsertEmail(toID, email);
349 }
350 else
351 {
352 // object on another region
353 // TODO FIX
354 }
355 }
356 }
357  
358 /// <summary>
359 ///
360 /// </summary>
361 /// <param name="objectID"></param>
362 /// <param name="sender"></param>
363 /// <param name="subject"></param>
364 /// <returns></returns>
365 public Email GetNextEmail(UUID objectID, string sender, string subject)
366 {
367 List<Email> queue = null;
368  
369 lock (m_LastGetEmailCall)
370 {
371 if (m_LastGetEmailCall.ContainsKey(objectID))
372 {
373 m_LastGetEmailCall.Remove(objectID);
374 }
375  
376 m_LastGetEmailCall.Add(objectID, DateTime.Now);
377  
378 // Hopefully this isn't too time consuming. If it is, we can always push it into a worker thread.
379 DateTime now = DateTime.Now;
380 List<UUID> removal = new List<UUID>();
381 foreach (UUID uuid in m_LastGetEmailCall.Keys)
382 {
383 if ((now - m_LastGetEmailCall[uuid]) > m_QueueTimeout)
384 {
385 removal.Add(uuid);
386 }
387 }
388  
389 foreach (UUID remove in removal)
390 {
391 m_LastGetEmailCall.Remove(remove);
392 lock (m_MailQueues)
393 {
394 m_MailQueues.Remove(remove);
395 }
396 }
397 }
398  
399 lock (m_MailQueues)
400 {
401 if (m_MailQueues.ContainsKey(objectID))
402 {
403 queue = m_MailQueues[objectID];
404 }
405 }
406  
407 if (queue != null)
408 {
409 lock (queue)
410 {
411 if (queue.Count > 0)
412 {
413 int i;
414  
415 for (i = 0; i < queue.Count; i++)
416 {
417 if ((sender == null || sender.Equals("") || sender.Equals(queue[i].sender)) &&
418 (subject == null || subject.Equals("") || subject.Equals(queue[i].subject)))
419 {
420 break;
421 }
422 }
423  
424 if (i != queue.Count)
425 {
426 Email ret = queue[i];
427 queue.Remove(ret);
428 ret.numLeft = queue.Count;
429 return ret;
430 }
431 }
432 }
433 }
434 else
435 {
436 lock (m_MailQueues)
437 {
438 m_MailQueues.Add(objectID, new List<Email>());
439 }
440 }
441  
442 return null;
443 }
444 }
445 }