clockwerk-opensim-stable – Blame information for rev 1
?pathlinks?
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 | } |