opensim – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | eva | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ |
||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. |
||
4 | * |
||
5 | * Redistribution and use in source and binary forms, with or without |
||
6 | * modification, are permitted provided that the following conditions are met: |
||
7 | * * Redistributions of source code must retain the above copyright |
||
8 | * notice, this list of conditions and the following disclaimer. |
||
9 | * * Redistributions in binary form must reproduce the above copyright |
||
10 | * notice, this list of conditions and the following disclaimer in the |
||
11 | * documentation and/or other materials provided with the distribution. |
||
12 | * * Neither the name of the OpenSimulator Project nor the |
||
13 | * names of its contributors may be used to endorse or promote products |
||
14 | * derived from this software without specific prior written permission. |
||
15 | * |
||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY |
||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY |
||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
26 | */ |
||
27 | |||
28 | using System; |
||
29 | using System.Collections; |
||
30 | using System.Collections.Generic; |
||
31 | using System.Linq; |
||
32 | using System.Net; |
||
33 | using System.Reflection; |
||
34 | using System.Text.RegularExpressions; |
||
35 | |||
36 | using log4net; |
||
37 | using Nini.Config; |
||
38 | using OpenMetaverse; |
||
39 | |||
40 | using OpenSim.Framework; |
||
41 | using OpenSim.Framework.Console; |
||
42 | using OpenSim.Server.Base; |
||
43 | using OpenSim.Services.Interfaces; |
||
44 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; |
||
45 | using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; |
||
46 | using OpenSim.Services.Connectors.Hypergrid; |
||
47 | |||
48 | namespace OpenSim.Services.LLLoginService |
||
49 | { |
||
50 | public class LLLoginService : ILoginService |
||
51 | { |
||
52 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
||
53 | private static readonly string LogHeader = "[LLOGIN SERVICE]"; |
||
54 | |||
55 | private static bool Initialized = false; |
||
56 | |||
57 | protected IUserAccountService m_UserAccountService; |
||
58 | protected IGridUserService m_GridUserService; |
||
59 | protected IAuthenticationService m_AuthenticationService; |
||
60 | protected IInventoryService m_InventoryService; |
||
61 | protected IGridService m_GridService; |
||
62 | protected IPresenceService m_PresenceService; |
||
63 | protected ISimulationService m_LocalSimulationService; |
||
64 | protected ISimulationService m_RemoteSimulationService; |
||
65 | protected ILibraryService m_LibraryService; |
||
66 | protected IFriendsService m_FriendsService; |
||
67 | protected IAvatarService m_AvatarService; |
||
68 | protected IUserAgentService m_UserAgentService; |
||
69 | |||
70 | protected GatekeeperServiceConnector m_GatekeeperConnector; |
||
71 | |||
72 | protected string m_DefaultRegionName; |
||
73 | protected string m_WelcomeMessage; |
||
74 | protected bool m_RequireInventory; |
||
75 | protected int m_MinLoginLevel; |
||
76 | protected string m_GatekeeperURL; |
||
77 | protected bool m_AllowRemoteSetLoginLevel; |
||
78 | protected string m_MapTileURL; |
||
79 | protected string m_ProfileURL; |
||
80 | protected string m_OpenIDURL; |
||
81 | protected string m_SearchURL; |
||
82 | protected string m_Currency; |
||
83 | protected string m_ClassifiedFee; |
||
84 | protected string m_DestinationGuide; |
||
85 | protected string m_AvatarPicker; |
||
86 | |||
87 | protected string m_AllowedClients; |
||
88 | protected string m_DeniedClients; |
||
89 | |||
90 | protected string m_DSTZone; |
||
91 | |||
92 | IConfig m_LoginServerConfig; |
||
93 | // IConfig m_ClientsConfig; |
||
94 | |||
95 | public LLLoginService(IConfigSource config, ISimulationService simService, ILibraryService libraryService) |
||
96 | { |
||
97 | m_LoginServerConfig = config.Configs["LoginService"]; |
||
98 | if (m_LoginServerConfig == null) |
||
99 | throw new Exception(String.Format("No section LoginService in config file")); |
||
100 | |||
101 | string accountService = m_LoginServerConfig.GetString("UserAccountService", String.Empty); |
||
102 | string gridUserService = m_LoginServerConfig.GetString("GridUserService", String.Empty); |
||
103 | string agentService = m_LoginServerConfig.GetString("UserAgentService", String.Empty); |
||
104 | string authService = m_LoginServerConfig.GetString("AuthenticationService", String.Empty); |
||
105 | string invService = m_LoginServerConfig.GetString("InventoryService", String.Empty); |
||
106 | string gridService = m_LoginServerConfig.GetString("GridService", String.Empty); |
||
107 | string presenceService = m_LoginServerConfig.GetString("PresenceService", String.Empty); |
||
108 | string libService = m_LoginServerConfig.GetString("LibraryService", String.Empty); |
||
109 | string friendsService = m_LoginServerConfig.GetString("FriendsService", String.Empty); |
||
110 | string avatarService = m_LoginServerConfig.GetString("AvatarService", String.Empty); |
||
111 | string simulationService = m_LoginServerConfig.GetString("SimulationService", String.Empty); |
||
112 | |||
113 | m_DefaultRegionName = m_LoginServerConfig.GetString("DefaultRegion", String.Empty); |
||
114 | m_WelcomeMessage = m_LoginServerConfig.GetString("WelcomeMessage", "Welcome to OpenSim!"); |
||
115 | m_RequireInventory = m_LoginServerConfig.GetBoolean("RequireInventory", true); |
||
116 | m_AllowRemoteSetLoginLevel = m_LoginServerConfig.GetBoolean("AllowRemoteSetLoginLevel", false); |
||
117 | m_MinLoginLevel = m_LoginServerConfig.GetInt("MinLoginLevel", 0); |
||
118 | m_GatekeeperURL = Util.GetConfigVarFromSections<string>(config, "GatekeeperURI", |
||
119 | new string[] { "Startup", "Hypergrid", "LoginService" }, String.Empty); |
||
120 | m_MapTileURL = m_LoginServerConfig.GetString("MapTileURL", string.Empty); |
||
121 | m_ProfileURL = m_LoginServerConfig.GetString("ProfileServerURL", string.Empty); |
||
122 | m_OpenIDURL = m_LoginServerConfig.GetString("OpenIDServerURL", String.Empty); |
||
123 | m_SearchURL = m_LoginServerConfig.GetString("SearchURL", string.Empty); |
||
124 | m_Currency = m_LoginServerConfig.GetString("Currency", string.Empty); |
||
125 | m_ClassifiedFee = m_LoginServerConfig.GetString("ClassifiedFee", string.Empty); |
||
126 | m_DestinationGuide = m_LoginServerConfig.GetString ("DestinationGuide", string.Empty); |
||
127 | m_AvatarPicker = m_LoginServerConfig.GetString ("AvatarPicker", string.Empty); |
||
128 | |||
129 | m_AllowedClients = m_LoginServerConfig.GetString("AllowedClients", string.Empty); |
||
130 | m_DeniedClients = m_LoginServerConfig.GetString("DeniedClients", string.Empty); |
||
131 | |||
132 | m_DSTZone = m_LoginServerConfig.GetString("DSTZone", "America/Los_Angeles;Pacific Standard Time"); |
||
133 | |||
134 | // Clean up some of these vars |
||
135 | if (m_MapTileURL != String.Empty) |
||
136 | { |
||
137 | m_MapTileURL = m_MapTileURL.Trim(); |
||
138 | if (!m_MapTileURL.EndsWith("/")) |
||
139 | m_MapTileURL = m_MapTileURL + "/"; |
||
140 | } |
||
141 | |||
142 | // These are required; the others aren't |
||
143 | if (accountService == string.Empty || authService == string.Empty) |
||
144 | throw new Exception("LoginService is missing service specifications"); |
||
145 | |||
146 | // replace newlines in welcome message |
||
147 | m_WelcomeMessage = m_WelcomeMessage.Replace("\\n", "\n"); |
||
148 | |||
149 | Object[] args = new Object[] { config }; |
||
150 | m_UserAccountService = ServerUtils.LoadPlugin<IUserAccountService>(accountService, args); |
||
151 | m_GridUserService = ServerUtils.LoadPlugin<IGridUserService>(gridUserService, args); |
||
152 | m_AuthenticationService = ServerUtils.LoadPlugin<IAuthenticationService>(authService, args); |
||
153 | m_InventoryService = ServerUtils.LoadPlugin<IInventoryService>(invService, args); |
||
154 | |||
155 | if (gridService != string.Empty) |
||
156 | m_GridService = ServerUtils.LoadPlugin<IGridService>(gridService, args); |
||
157 | if (presenceService != string.Empty) |
||
158 | m_PresenceService = ServerUtils.LoadPlugin<IPresenceService>(presenceService, args); |
||
159 | if (avatarService != string.Empty) |
||
160 | m_AvatarService = ServerUtils.LoadPlugin<IAvatarService>(avatarService, args); |
||
161 | if (friendsService != string.Empty) |
||
162 | m_FriendsService = ServerUtils.LoadPlugin<IFriendsService>(friendsService, args); |
||
163 | if (simulationService != string.Empty) |
||
164 | m_RemoteSimulationService = ServerUtils.LoadPlugin<ISimulationService>(simulationService, args); |
||
165 | if (agentService != string.Empty) |
||
166 | m_UserAgentService = ServerUtils.LoadPlugin<IUserAgentService>(agentService, args); |
||
167 | |||
168 | // |
||
169 | // deal with the services given as argument |
||
170 | // |
||
171 | m_LocalSimulationService = simService; |
||
172 | if (libraryService != null) |
||
173 | { |
||
174 | m_log.DebugFormat("[LLOGIN SERVICE]: Using LibraryService given as argument"); |
||
175 | m_LibraryService = libraryService; |
||
176 | } |
||
177 | else if (libService != string.Empty) |
||
178 | { |
||
179 | m_log.DebugFormat("[LLOGIN SERVICE]: Using instantiated LibraryService"); |
||
180 | m_LibraryService = ServerUtils.LoadPlugin<ILibraryService>(libService, args); |
||
181 | } |
||
182 | |||
183 | m_GatekeeperConnector = new GatekeeperServiceConnector(); |
||
184 | |||
185 | if (!Initialized) |
||
186 | { |
||
187 | Initialized = true; |
||
188 | RegisterCommands(); |
||
189 | } |
||
190 | |||
191 | m_log.DebugFormat("[LLOGIN SERVICE]: Starting..."); |
||
192 | |||
193 | } |
||
194 | |||
195 | public LLLoginService(IConfigSource config) : this(config, null, null) |
||
196 | { |
||
197 | } |
||
198 | |||
199 | public Hashtable SetLevel(string firstName, string lastName, string passwd, int level, IPEndPoint clientIP) |
||
200 | { |
||
201 | Hashtable response = new Hashtable(); |
||
202 | response["success"] = "false"; |
||
203 | |||
204 | if (!m_AllowRemoteSetLoginLevel) |
||
205 | return response; |
||
206 | |||
207 | try |
||
208 | { |
||
209 | UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, firstName, lastName); |
||
210 | if (account == null) |
||
211 | { |
||
212 | m_log.InfoFormat("[LLOGIN SERVICE]: Set Level failed, user {0} {1} not found", firstName, lastName); |
||
213 | return response; |
||
214 | } |
||
215 | |||
216 | if (account.UserLevel < 200) |
||
217 | { |
||
218 | m_log.InfoFormat("[LLOGIN SERVICE]: Set Level failed, reason: user level too low"); |
||
219 | return response; |
||
220 | } |
||
221 | |||
222 | // |
||
223 | // Authenticate this user |
||
224 | // |
||
225 | // We don't support clear passwords here |
||
226 | // |
||
227 | string token = m_AuthenticationService.Authenticate(account.PrincipalID, passwd, 30); |
||
228 | UUID secureSession = UUID.Zero; |
||
229 | if ((token == string.Empty) || (token != string.Empty && !UUID.TryParse(token, out secureSession))) |
||
230 | { |
||
231 | m_log.InfoFormat("[LLOGIN SERVICE]: SetLevel failed, reason: authentication failed"); |
||
232 | return response; |
||
233 | } |
||
234 | } |
||
235 | catch (Exception e) |
||
236 | { |
||
237 | m_log.Error("[LLOGIN SERVICE]: SetLevel failed, exception " + e.ToString()); |
||
238 | return response; |
||
239 | } |
||
240 | |||
241 | m_MinLoginLevel = level; |
||
242 | m_log.InfoFormat("[LLOGIN SERVICE]: Login level set to {0} by {1} {2}", level, firstName, lastName); |
||
243 | |||
244 | response["success"] = true; |
||
245 | return response; |
||
246 | } |
||
247 | |||
248 | public LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, |
||
249 | string clientVersion, string channel, string mac, string id0, IPEndPoint clientIP) |
||
250 | { |
||
251 | bool success = false; |
||
252 | UUID session = UUID.Random(); |
||
253 | |||
254 | m_log.InfoFormat("[LLOGIN SERVICE]: Login request for {0} {1} at {2} using viewer {3}, channel {4}, IP {5}, Mac {6}, Id0 {7}", |
||
255 | firstName, lastName, startLocation, clientVersion, channel, clientIP.Address.ToString(), mac, id0); |
||
256 | |||
257 | try |
||
258 | { |
||
259 | // |
||
260 | // Check client |
||
261 | // |
||
262 | if (m_AllowedClients != string.Empty) |
||
263 | { |
||
264 | Regex arx = new Regex(m_AllowedClients); |
||
265 | Match am = arx.Match(clientVersion); |
||
266 | |||
267 | if (!am.Success) |
||
268 | { |
||
269 | m_log.InfoFormat( |
||
270 | "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: client {2} is not allowed", |
||
271 | firstName, lastName, clientVersion); |
||
272 | return LLFailedLoginResponse.LoginBlockedProblem; |
||
273 | } |
||
274 | } |
||
275 | |||
276 | if (m_DeniedClients != string.Empty) |
||
277 | { |
||
278 | Regex drx = new Regex(m_DeniedClients); |
||
279 | Match dm = drx.Match(clientVersion); |
||
280 | |||
281 | if (dm.Success) |
||
282 | { |
||
283 | m_log.InfoFormat( |
||
284 | "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: client {2} is denied", |
||
285 | firstName, lastName, clientVersion); |
||
286 | return LLFailedLoginResponse.LoginBlockedProblem; |
||
287 | } |
||
288 | } |
||
289 | |||
290 | // |
||
291 | // Get the account and check that it exists |
||
292 | // |
||
293 | UserAccount account = m_UserAccountService.GetUserAccount(scopeID, firstName, lastName); |
||
294 | if (account == null) |
||
295 | { |
||
296 | m_log.InfoFormat( |
||
297 | "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: user not found", firstName, lastName); |
||
298 | return LLFailedLoginResponse.UserProblem; |
||
299 | } |
||
300 | |||
301 | if (account.UserLevel < m_MinLoginLevel) |
||
302 | { |
||
303 | m_log.InfoFormat( |
||
304 | "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: user level is {2} but minimum login level is {3}", |
||
305 | firstName, lastName, account.UserLevel, m_MinLoginLevel); |
||
306 | return LLFailedLoginResponse.LoginBlockedProblem; |
||
307 | } |
||
308 | |||
309 | // If a scope id is requested, check that the account is in |
||
310 | // that scope, or unscoped. |
||
311 | // |
||
312 | if (scopeID != UUID.Zero) |
||
313 | { |
||
314 | if (account.ScopeID != scopeID && account.ScopeID != UUID.Zero) |
||
315 | { |
||
316 | m_log.InfoFormat( |
||
317 | "[LLOGIN SERVICE]: Login failed, reason: user {0} {1} not found", firstName, lastName); |
||
318 | return LLFailedLoginResponse.UserProblem; |
||
319 | } |
||
320 | } |
||
321 | else |
||
322 | { |
||
323 | scopeID = account.ScopeID; |
||
324 | } |
||
325 | |||
326 | // |
||
327 | // Authenticate this user |
||
328 | // |
||
329 | if (!passwd.StartsWith("$1$")) |
||
330 | passwd = "$1$" + Util.Md5Hash(passwd); |
||
331 | passwd = passwd.Remove(0, 3); //remove $1$ |
||
332 | string token = m_AuthenticationService.Authenticate(account.PrincipalID, passwd, 30); |
||
333 | UUID secureSession = UUID.Zero; |
||
334 | if ((token == string.Empty) || (token != string.Empty && !UUID.TryParse(token, out secureSession))) |
||
335 | { |
||
336 | m_log.InfoFormat( |
||
337 | "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: authentication failed", |
||
338 | firstName, lastName); |
||
339 | return LLFailedLoginResponse.UserProblem; |
||
340 | } |
||
341 | |||
342 | // |
||
343 | // Get the user's inventory |
||
344 | // |
||
345 | if (m_RequireInventory && m_InventoryService == null) |
||
346 | { |
||
347 | m_log.WarnFormat( |
||
348 | "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: inventory service not set up", |
||
349 | firstName, lastName); |
||
350 | return LLFailedLoginResponse.InventoryProblem; |
||
351 | } |
||
352 | |||
353 | List<InventoryFolderBase> inventorySkel = m_InventoryService.GetInventorySkeleton(account.PrincipalID); |
||
354 | if (m_RequireInventory && ((inventorySkel == null) || (inventorySkel != null && inventorySkel.Count == 0))) |
||
355 | { |
||
356 | m_log.InfoFormat( |
||
357 | "[LLOGIN SERVICE]: Login failed, for {0} {1}, reason: unable to retrieve user inventory", |
||
358 | firstName, lastName); |
||
359 | return LLFailedLoginResponse.InventoryProblem; |
||
360 | } |
||
361 | |||
362 | // Get active gestures |
||
363 | List<InventoryItemBase> gestures = m_InventoryService.GetActiveGestures(account.PrincipalID); |
||
364 | // m_log.DebugFormat("[LLOGIN SERVICE]: {0} active gestures", gestures.Count); |
||
365 | |||
366 | // |
||
367 | // Login the presence |
||
368 | // |
||
369 | if (m_PresenceService != null) |
||
370 | { |
||
371 | success = m_PresenceService.LoginAgent(account.PrincipalID.ToString(), session, secureSession); |
||
372 | |||
373 | if (!success) |
||
374 | { |
||
375 | m_log.InfoFormat( |
||
376 | "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: could not login presence", |
||
377 | firstName, lastName); |
||
378 | return LLFailedLoginResponse.GridProblem; |
||
379 | } |
||
380 | } |
||
381 | |||
382 | // |
||
383 | // Change Online status and get the home region |
||
384 | // |
||
385 | GridRegion home = null; |
||
386 | GridUserInfo guinfo = m_GridUserService.LoggedIn(account.PrincipalID.ToString()); |
||
387 | |||
388 | // We are only going to complain about no home if the user actually tries to login there, to avoid |
||
389 | // spamming the console. |
||
390 | if (guinfo != null) |
||
391 | { |
||
392 | if (guinfo.HomeRegionID == UUID.Zero && startLocation == "home") |
||
393 | { |
||
394 | m_log.WarnFormat( |
||
395 | "[LLOGIN SERVICE]: User {0} tried to login to a 'home' start location but they have none set", |
||
396 | account.Name); |
||
397 | } |
||
398 | else if (m_GridService != null) |
||
399 | { |
||
400 | home = m_GridService.GetRegionByUUID(scopeID, guinfo.HomeRegionID); |
||
401 | |||
402 | if (home == null && startLocation == "home") |
||
403 | { |
||
404 | m_log.WarnFormat( |
||
405 | "[LLOGIN SERVICE]: User {0} tried to login to a 'home' start location with ID {1} but this was not found.", |
||
406 | account.Name, guinfo.HomeRegionID); |
||
407 | } |
||
408 | } |
||
409 | } |
||
410 | else |
||
411 | { |
||
412 | // something went wrong, make something up, so that we don't have to test this anywhere else |
||
413 | m_log.DebugFormat("{0} Failed to fetch GridUserInfo. Creating empty GridUserInfo as home", LogHeader); |
||
414 | guinfo = new GridUserInfo(); |
||
415 | guinfo.LastPosition = guinfo.HomePosition = new Vector3(128, 128, 30); |
||
416 | } |
||
417 | |||
418 | // |
||
419 | // Find the destination region/grid |
||
420 | // |
||
421 | string where = string.Empty; |
||
422 | Vector3 position = Vector3.Zero; |
||
423 | Vector3 lookAt = Vector3.Zero; |
||
424 | GridRegion gatekeeper = null; |
||
425 | TeleportFlags flags; |
||
426 | GridRegion destination = FindDestination(account, scopeID, guinfo, session, startLocation, home, out gatekeeper, out where, out position, out lookAt, out flags); |
||
427 | if (destination == null) |
||
428 | { |
||
429 | m_PresenceService.LogoutAgent(session); |
||
430 | |||
431 | m_log.InfoFormat( |
||
432 | "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: destination not found", |
||
433 | firstName, lastName); |
||
434 | return LLFailedLoginResponse.GridProblem; |
||
435 | } |
||
436 | else |
||
437 | { |
||
438 | m_log.DebugFormat( |
||
439 | "[LLOGIN SERVICE]: Found destination {0}, endpoint {1} for {2} {3}", |
||
440 | destination.RegionName, destination.ExternalEndPoint, firstName, lastName); |
||
441 | } |
||
442 | |||
443 | if (account.UserLevel >= 200) |
||
444 | flags |= TeleportFlags.Godlike; |
||
445 | // |
||
446 | // Get the avatar |
||
447 | // |
||
448 | AvatarAppearance avatar = null; |
||
449 | if (m_AvatarService != null) |
||
450 | { |
||
451 | avatar = m_AvatarService.GetAppearance(account.PrincipalID); |
||
452 | } |
||
453 | |||
454 | // |
||
455 | // Instantiate/get the simulation interface and launch an agent at the destination |
||
456 | // |
||
457 | string reason = string.Empty; |
||
458 | GridRegion dest; |
||
459 | AgentCircuitData aCircuit = LaunchAgentAtGrid(gatekeeper, destination, account, avatar, session, secureSession, position, where, |
||
460 | clientVersion, channel, mac, id0, clientIP, flags, out where, out reason, out dest); |
||
461 | destination = dest; |
||
462 | if (aCircuit == null) |
||
463 | { |
||
464 | m_PresenceService.LogoutAgent(session); |
||
465 | m_log.InfoFormat("[LLOGIN SERVICE]: Login failed for {0} {1}, reason: {2}", firstName, lastName, reason); |
||
466 | return new LLFailedLoginResponse("key", reason, "false"); |
||
467 | |||
468 | } |
||
469 | // Get Friends list |
||
470 | FriendInfo[] friendsList = new FriendInfo[0]; |
||
471 | if (m_FriendsService != null) |
||
472 | { |
||
473 | friendsList = m_FriendsService.GetFriends(account.PrincipalID); |
||
474 | // m_log.DebugFormat("[LLOGIN SERVICE]: Retrieved {0} friends", friendsList.Length); |
||
475 | } |
||
476 | |||
477 | // |
||
478 | // Finally, fill out the response and return it |
||
479 | // |
||
480 | LLLoginResponse response |
||
481 | = new LLLoginResponse( |
||
482 | account, aCircuit, guinfo, destination, inventorySkel, friendsList, m_LibraryService, |
||
483 | where, startLocation, position, lookAt, gestures, m_WelcomeMessage, home, clientIP, |
||
484 | m_MapTileURL, m_ProfileURL, m_OpenIDURL, m_SearchURL, m_Currency, m_DSTZone, |
||
485 | m_DestinationGuide, m_AvatarPicker, m_ClassifiedFee); |
||
486 | |||
487 | m_log.DebugFormat("[LLOGIN SERVICE]: All clear. Sending login response to {0} {1}", firstName, lastName); |
||
488 | |||
489 | return response; |
||
490 | } |
||
491 | catch (Exception e) |
||
492 | { |
||
493 | m_log.WarnFormat("[LLOGIN SERVICE]: Exception processing login for {0} {1}: {2} {3}", firstName, lastName, e.ToString(), e.StackTrace); |
||
494 | if (m_PresenceService != null) |
||
495 | m_PresenceService.LogoutAgent(session); |
||
496 | return LLFailedLoginResponse.InternalError; |
||
497 | } |
||
498 | } |
||
499 | |||
500 | protected GridRegion FindDestination( |
||
501 | UserAccount account, UUID scopeID, GridUserInfo pinfo, UUID sessionID, string startLocation, |
||
502 | GridRegion home, out GridRegion gatekeeper, |
||
503 | out string where, out Vector3 position, out Vector3 lookAt, out TeleportFlags flags) |
||
504 | { |
||
505 | flags = TeleportFlags.ViaLogin; |
||
506 | |||
507 | m_log.DebugFormat( |
||
508 | "[LLOGIN SERVICE]: Finding destination matching start location {0} for {1}", |
||
509 | startLocation, account.Name); |
||
510 | |||
511 | gatekeeper = null; |
||
512 | where = "home"; |
||
513 | position = new Vector3(128, 128, 0); |
||
514 | lookAt = new Vector3(0, 1, 0); |
||
515 | |||
516 | if (m_GridService == null) |
||
517 | return null; |
||
518 | |||
519 | if (startLocation.Equals("home")) |
||
520 | { |
||
521 | // logging into home region |
||
522 | if (pinfo == null) |
||
523 | return null; |
||
524 | |||
525 | GridRegion region = null; |
||
526 | |||
527 | bool tryDefaults = false; |
||
528 | |||
529 | if (home == null) |
||
530 | { |
||
531 | tryDefaults = true; |
||
532 | } |
||
533 | else |
||
534 | { |
||
535 | region = home; |
||
536 | |||
537 | position = pinfo.HomePosition; |
||
538 | lookAt = pinfo.HomeLookAt; |
||
539 | flags |= TeleportFlags.ViaHome; |
||
540 | } |
||
541 | |||
542 | if (tryDefaults) |
||
543 | { |
||
544 | List<GridRegion> defaults = m_GridService.GetDefaultRegions(scopeID); |
||
545 | if (defaults != null && defaults.Count > 0) |
||
546 | { |
||
547 | region = defaults[0]; |
||
548 | where = "safe"; |
||
549 | } |
||
550 | else |
||
551 | { |
||
552 | m_log.WarnFormat("[LLOGIN SERVICE]: User {0} {1} does not have a valid home and this grid does not have default locations. Attempting to find random region", |
||
553 | account.FirstName, account.LastName); |
||
554 | region = FindAlternativeRegion(scopeID); |
||
555 | if (region != null) |
||
556 | where = "safe"; |
||
557 | } |
||
558 | } |
||
559 | |||
560 | return region; |
||
561 | } |
||
562 | else if (startLocation.Equals("last")) |
||
563 | { |
||
564 | // logging into last visited region |
||
565 | where = "last"; |
||
566 | |||
567 | if (pinfo == null) |
||
568 | return null; |
||
569 | |||
570 | GridRegion region = null; |
||
571 | |||
572 | if (pinfo.LastRegionID.Equals(UUID.Zero) || (region = m_GridService.GetRegionByUUID(scopeID, pinfo.LastRegionID)) == null) |
||
573 | { |
||
574 | List<GridRegion> defaults = m_GridService.GetDefaultRegions(scopeID); |
||
575 | if (defaults != null && defaults.Count > 0) |
||
576 | { |
||
577 | region = defaults[0]; |
||
578 | where = "safe"; |
||
579 | } |
||
580 | else |
||
581 | { |
||
582 | m_log.Info("[LLOGIN SERVICE]: Last Region Not Found Attempting to find random region"); |
||
583 | region = FindAlternativeRegion(scopeID); |
||
584 | if (region != null) |
||
585 | where = "safe"; |
||
586 | } |
||
587 | |||
588 | } |
||
589 | else |
||
590 | { |
||
591 | position = pinfo.LastPosition; |
||
592 | lookAt = pinfo.LastLookAt; |
||
593 | } |
||
594 | |||
595 | return region; |
||
596 | } |
||
597 | else |
||
598 | { |
||
599 | flags |= TeleportFlags.ViaRegionID; |
||
600 | |||
601 | // free uri form |
||
602 | // e.g. New Moon&135&46 New Moon@osgrid.org:8002&153&34 |
||
603 | where = "url"; |
||
604 | GridRegion region = null; |
||
605 | Regex reURI = new Regex(@"^uri:(?<region>[^&]+)&(?<x>\d+)&(?<y>\d+)&(?<z>\d+)$"); |
||
606 | Match uriMatch = reURI.Match(startLocation); |
||
607 | if (uriMatch == null) |
||
608 | { |
||
609 | m_log.InfoFormat("[LLLOGIN SERVICE]: Got Custom Login URI {0}, but can't process it", startLocation); |
||
610 | return null; |
||
611 | } |
||
612 | else |
||
613 | { |
||
614 | position = new Vector3(float.Parse(uriMatch.Groups["x"].Value, Culture.NumberFormatInfo), |
||
615 | float.Parse(uriMatch.Groups["y"].Value, Culture.NumberFormatInfo), |
||
616 | float.Parse(uriMatch.Groups["z"].Value, Culture.NumberFormatInfo)); |
||
617 | |||
618 | string regionName = uriMatch.Groups["region"].ToString(); |
||
619 | if (regionName != null) |
||
620 | { |
||
621 | if (!regionName.Contains("@")) |
||
622 | { |
||
623 | List<GridRegion> regions = m_GridService.GetRegionsByName(scopeID, regionName, 1); |
||
624 | if ((regions == null) || (regions != null && regions.Count == 0)) |
||
625 | { |
||
626 | m_log.InfoFormat("[LLLOGIN SERVICE]: Got Custom Login URI {0}, can't locate region {1}. Trying defaults.", startLocation, regionName); |
||
627 | regions = m_GridService.GetDefaultRegions(scopeID); |
||
628 | if (regions != null && regions.Count > 0) |
||
629 | { |
||
630 | where = "safe"; |
||
631 | return regions[0]; |
||
632 | } |
||
633 | else |
||
634 | { |
||
635 | m_log.Info("[LLOGIN SERVICE]: Last Region Not Found Attempting to find random region"); |
||
636 | region = FindAlternativeRegion(scopeID); |
||
637 | if (region != null) |
||
638 | { |
||
639 | where = "safe"; |
||
640 | return region; |
||
641 | } |
||
642 | else |
||
643 | { |
||
644 | m_log.InfoFormat("[LLLOGIN SERVICE]: Got Custom Login URI {0}, Grid does not provide default regions and no alternative found.", startLocation); |
||
645 | return null; |
||
646 | } |
||
647 | } |
||
648 | } |
||
649 | return regions[0]; |
||
650 | } |
||
651 | else |
||
652 | { |
||
653 | if (m_UserAgentService == null) |
||
654 | { |
||
655 | m_log.WarnFormat("[LLLOGIN SERVICE]: This llogin service is not running a user agent service, as such it can't lauch agents at foreign grids"); |
||
656 | return null; |
||
657 | } |
||
658 | string[] parts = regionName.Split(new char[] { '@' }); |
||
659 | if (parts.Length < 2) |
||
660 | { |
||
661 | m_log.InfoFormat("[LLLOGIN SERVICE]: Got Custom Login URI {0}, can't locate region {1}", startLocation, regionName); |
||
662 | return null; |
||
663 | } |
||
664 | // Valid specification of a remote grid |
||
665 | |||
666 | regionName = parts[0]; |
||
667 | string domainLocator = parts[1]; |
||
668 | parts = domainLocator.Split(new char[] {':'}); |
||
669 | string domainName = parts[0]; |
||
670 | uint port = 0; |
||
671 | if (parts.Length > 1) |
||
672 | UInt32.TryParse(parts[1], out port); |
||
673 | |||
674 | // GridRegion region = FindForeignRegion(domainName, port, regionName, out gatekeeper); |
||
675 | region = FindForeignRegion(domainName, port, regionName, out gatekeeper); |
||
676 | return region; |
||
677 | } |
||
678 | } |
||
679 | else |
||
680 | { |
||
681 | List<GridRegion> defaults = m_GridService.GetDefaultRegions(scopeID); |
||
682 | if (defaults != null && defaults.Count > 0) |
||
683 | { |
||
684 | where = "safe"; |
||
685 | return defaults[0]; |
||
686 | } |
||
687 | else |
||
688 | return null; |
||
689 | } |
||
690 | } |
||
691 | //response.LookAt = "[r0,r1,r0]"; |
||
692 | //// can be: last, home, safe, url |
||
693 | //response.StartLocation = "url"; |
||
694 | |||
695 | } |
||
696 | |||
697 | } |
||
698 | |||
699 | private GridRegion FindAlternativeRegion(UUID scopeID) |
||
700 | { |
||
701 | List<GridRegion> hyperlinks = null; |
||
702 | List<GridRegion> regions = m_GridService.GetFallbackRegions(scopeID, (int)Util.RegionToWorldLoc(1000), (int)Util.RegionToWorldLoc(1000)); |
||
703 | if (regions != null && regions.Count > 0) |
||
704 | { |
||
705 | hyperlinks = m_GridService.GetHyperlinks(scopeID); |
||
706 | IEnumerable<GridRegion> availableRegions = regions.Except(hyperlinks); |
||
707 | if (availableRegions.Count() > 0) |
||
708 | return availableRegions.ElementAt(0); |
||
709 | } |
||
710 | // No fallbacks, try to find an arbitrary region that is not a hyperlink |
||
711 | // maxNumber is fixed for now; maybe use some search pattern with increasing maxSize here? |
||
712 | regions = m_GridService.GetRegionsByName(scopeID, "", 10); |
||
713 | if (regions != null && regions.Count > 0) |
||
714 | { |
||
715 | if (hyperlinks == null) |
||
716 | hyperlinks = m_GridService.GetHyperlinks(scopeID); |
||
717 | IEnumerable<GridRegion> availableRegions = regions.Except(hyperlinks); |
||
718 | if (availableRegions.Count() > 0) |
||
719 | return availableRegions.ElementAt(0); |
||
720 | } |
||
721 | return null; |
||
722 | } |
||
723 | |||
724 | private GridRegion FindForeignRegion(string domainName, uint port, string regionName, out GridRegion gatekeeper) |
||
725 | { |
||
726 | m_log.Debug("[LLLOGIN SERVICE]: attempting to findforeignregion " + domainName + ":" + port.ToString() + ":" + regionName); |
||
727 | gatekeeper = new GridRegion(); |
||
728 | gatekeeper.ExternalHostName = domainName; |
||
729 | gatekeeper.HttpPort = port; |
||
730 | gatekeeper.RegionName = regionName; |
||
731 | gatekeeper.InternalEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 0); |
||
732 | |||
733 | UUID regionID; |
||
734 | ulong handle; |
||
735 | string imageURL = string.Empty, reason = string.Empty; |
||
736 | if (m_GatekeeperConnector.LinkRegion(gatekeeper, out regionID, out handle, out domainName, out imageURL, out reason)) |
||
737 | { |
||
738 | GridRegion destination = m_GatekeeperConnector.GetHyperlinkRegion(gatekeeper, regionID); |
||
739 | return destination; |
||
740 | } |
||
741 | |||
742 | return null; |
||
743 | } |
||
744 | |||
745 | private string hostName = string.Empty; |
||
746 | private int port = 0; |
||
747 | |||
748 | private void SetHostAndPort(string url) |
||
749 | { |
||
750 | try |
||
751 | { |
||
752 | Uri uri = new Uri(url); |
||
753 | hostName = uri.Host; |
||
754 | port = uri.Port; |
||
755 | } |
||
756 | catch |
||
757 | { |
||
758 | m_log.WarnFormat("[LLLogin SERVICE]: Unable to parse GatekeeperURL {0}", url); |
||
759 | } |
||
760 | } |
||
761 | |||
762 | protected AgentCircuitData LaunchAgentAtGrid(GridRegion gatekeeper, GridRegion destination, UserAccount account, AvatarAppearance avatar, |
||
763 | UUID session, UUID secureSession, Vector3 position, string currentWhere, string viewer, string channel, string mac, string id0, |
||
764 | IPEndPoint clientIP, TeleportFlags flags, out string where, out string reason, out GridRegion dest) |
||
765 | { |
||
766 | where = currentWhere; |
||
767 | ISimulationService simConnector = null; |
||
768 | reason = string.Empty; |
||
769 | uint circuitCode = 0; |
||
770 | AgentCircuitData aCircuit = null; |
||
771 | |||
772 | if (m_UserAgentService == null) |
||
773 | { |
||
774 | // HG standalones have both a localSimulatonDll and a remoteSimulationDll |
||
775 | // non-HG standalones have just a localSimulationDll |
||
776 | // independent login servers have just a remoteSimulationDll |
||
777 | if (m_LocalSimulationService != null) |
||
778 | simConnector = m_LocalSimulationService; |
||
779 | else if (m_RemoteSimulationService != null) |
||
780 | simConnector = m_RemoteSimulationService; |
||
781 | } |
||
782 | else // User Agent Service is on |
||
783 | { |
||
784 | if (gatekeeper == null) // login to local grid |
||
785 | { |
||
786 | if (hostName == string.Empty) |
||
787 | SetHostAndPort(m_GatekeeperURL); |
||
788 | |||
789 | gatekeeper = new GridRegion(destination); |
||
790 | gatekeeper.ExternalHostName = hostName; |
||
791 | gatekeeper.HttpPort = (uint)port; |
||
792 | gatekeeper.ServerURI = m_GatekeeperURL; |
||
793 | } |
||
794 | m_log.Debug("[LLLOGIN SERVICE]: no gatekeeper detected..... using " + m_GatekeeperURL); |
||
795 | } |
||
796 | |||
797 | bool success = false; |
||
798 | |||
799 | if (m_UserAgentService == null && simConnector != null) |
||
800 | { |
||
801 | circuitCode = (uint)Util.RandomClass.Next(); ; |
||
802 | aCircuit = MakeAgent(destination, account, avatar, session, secureSession, circuitCode, position, clientIP.Address.ToString(), viewer, channel, mac, id0); |
||
803 | success = LaunchAgentDirectly(simConnector, destination, aCircuit, flags, out reason); |
||
804 | if (!success && m_GridService != null) |
||
805 | { |
||
806 | // Try the fallback regions |
||
807 | List<GridRegion> fallbacks = m_GridService.GetFallbackRegions(account.ScopeID, destination.RegionLocX, destination.RegionLocY); |
||
808 | if (fallbacks != null) |
||
809 | { |
||
810 | foreach (GridRegion r in fallbacks) |
||
811 | { |
||
812 | success = LaunchAgentDirectly(simConnector, r, aCircuit, flags | TeleportFlags.ViaRegionID, out reason); |
||
813 | if (success) |
||
814 | { |
||
815 | where = "safe"; |
||
816 | destination = r; |
||
817 | break; |
||
818 | } |
||
819 | } |
||
820 | } |
||
821 | } |
||
822 | } |
||
823 | |||
824 | if (m_UserAgentService != null) |
||
825 | { |
||
826 | circuitCode = (uint)Util.RandomClass.Next(); ; |
||
827 | aCircuit = MakeAgent(destination, account, avatar, session, secureSession, circuitCode, position, clientIP.Address.ToString(), viewer, channel, mac, id0); |
||
828 | aCircuit.teleportFlags |= (uint)flags; |
||
829 | success = LaunchAgentIndirectly(gatekeeper, destination, aCircuit, clientIP, out reason); |
||
830 | if (!success && m_GridService != null) |
||
831 | { |
||
832 | // Try the fallback regions |
||
833 | List<GridRegion> fallbacks = m_GridService.GetFallbackRegions(account.ScopeID, destination.RegionLocX, destination.RegionLocY); |
||
834 | if (fallbacks != null) |
||
835 | { |
||
836 | foreach (GridRegion r in fallbacks) |
||
837 | { |
||
838 | success = LaunchAgentIndirectly(gatekeeper, r, aCircuit, clientIP, out reason); |
||
839 | if (success) |
||
840 | { |
||
841 | where = "safe"; |
||
842 | destination = r; |
||
843 | break; |
||
844 | } |
||
845 | } |
||
846 | } |
||
847 | } |
||
848 | } |
||
849 | dest = destination; |
||
850 | if (success) |
||
851 | return aCircuit; |
||
852 | else |
||
853 | return null; |
||
854 | } |
||
855 | |||
856 | private AgentCircuitData MakeAgent(GridRegion region, UserAccount account, |
||
857 | AvatarAppearance avatar, UUID session, UUID secureSession, uint circuit, Vector3 position, |
||
858 | string ipaddress, string viewer, string channel, string mac, string id0) |
||
859 | { |
||
860 | AgentCircuitData aCircuit = new AgentCircuitData(); |
||
861 | |||
862 | aCircuit.AgentID = account.PrincipalID; |
||
863 | if (avatar != null) |
||
864 | aCircuit.Appearance = new AvatarAppearance(avatar); |
||
865 | else |
||
866 | aCircuit.Appearance = new AvatarAppearance(); |
||
867 | |||
868 | //aCircuit.BaseFolder = irrelevant |
||
869 | aCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); |
||
870 | aCircuit.child = false; // the first login agent is root |
||
871 | aCircuit.ChildrenCapSeeds = new Dictionary<ulong, string>(); |
||
872 | aCircuit.circuitcode = circuit; |
||
873 | aCircuit.firstname = account.FirstName; |
||
874 | //aCircuit.InventoryFolder = irrelevant |
||
875 | aCircuit.lastname = account.LastName; |
||
876 | aCircuit.SecureSessionID = secureSession; |
||
877 | aCircuit.SessionID = session; |
||
878 | aCircuit.startpos = position; |
||
879 | aCircuit.IPAddress = ipaddress; |
||
880 | aCircuit.Viewer = viewer; |
||
881 | aCircuit.Channel = channel; |
||
882 | aCircuit.Mac = mac; |
||
883 | aCircuit.Id0 = id0; |
||
884 | SetServiceURLs(aCircuit, account); |
||
885 | |||
886 | return aCircuit; |
||
887 | |||
888 | //m_UserAgentService.LoginAgentToGrid(aCircuit, GatekeeperServiceConnector, region, out reason); |
||
889 | //if (simConnector.CreateAgent(region, aCircuit, 0, out reason)) |
||
890 | // return aCircuit; |
||
891 | |||
892 | //return null; |
||
893 | |||
894 | } |
||
895 | |||
896 | private void SetServiceURLs(AgentCircuitData aCircuit, UserAccount account) |
||
897 | { |
||
898 | aCircuit.ServiceURLs = new Dictionary<string, object>(); |
||
899 | if (account.ServiceURLs == null) |
||
900 | return; |
||
901 | |||
902 | // Old style: get the service keys from the DB |
||
903 | foreach (KeyValuePair<string, object> kvp in account.ServiceURLs) |
||
904 | { |
||
905 | if (kvp.Value != null) |
||
906 | { |
||
907 | aCircuit.ServiceURLs[kvp.Key] = kvp.Value; |
||
908 | |||
909 | if (!aCircuit.ServiceURLs[kvp.Key].ToString().EndsWith("/")) |
||
910 | aCircuit.ServiceURLs[kvp.Key] = aCircuit.ServiceURLs[kvp.Key] + "/"; |
||
911 | } |
||
912 | } |
||
913 | |||
914 | // New style: service keys start with SRV_; override the previous |
||
915 | string[] keys = m_LoginServerConfig.GetKeys(); |
||
916 | |||
917 | if (keys.Length > 0) |
||
918 | { |
||
919 | bool newUrls = false; |
||
920 | IEnumerable<string> serviceKeys = keys.Where(value => value.StartsWith("SRV_")); |
||
921 | foreach (string serviceKey in serviceKeys) |
||
922 | { |
||
923 | string keyName = serviceKey.Replace("SRV_", ""); |
||
924 | string keyValue = m_LoginServerConfig.GetString(serviceKey, string.Empty); |
||
925 | if (!keyValue.EndsWith("/")) |
||
926 | keyValue = keyValue + "/"; |
||
927 | |||
928 | if (!account.ServiceURLs.ContainsKey(keyName) || (account.ServiceURLs.ContainsKey(keyName) && account.ServiceURLs[keyName] != keyValue)) |
||
929 | { |
||
930 | account.ServiceURLs[keyName] = keyValue; |
||
931 | newUrls = true; |
||
932 | } |
||
933 | aCircuit.ServiceURLs[keyName] = keyValue; |
||
934 | |||
935 | m_log.DebugFormat("[LLLOGIN SERVICE]: found new key {0} {1}", keyName, aCircuit.ServiceURLs[keyName]); |
||
936 | } |
||
937 | |||
938 | // The grid operator decided to override the defaults in the |
||
939 | // [LoginService] configuration. Let's store the correct ones. |
||
940 | if (newUrls) |
||
941 | m_UserAccountService.StoreUserAccount(account); |
||
942 | } |
||
943 | |||
944 | } |
||
945 | |||
946 | private bool LaunchAgentDirectly(ISimulationService simConnector, GridRegion region, AgentCircuitData aCircuit, TeleportFlags flags, out string reason) |
||
947 | { |
||
948 | return simConnector.CreateAgent(region, aCircuit, (uint)flags, out reason); |
||
949 | } |
||
950 | |||
951 | private bool LaunchAgentIndirectly(GridRegion gatekeeper, GridRegion destination, AgentCircuitData aCircuit, IPEndPoint clientIP, out string reason) |
||
952 | { |
||
953 | m_log.Debug("[LLOGIN SERVICE] Launching agent at " + destination.RegionName); |
||
954 | if (m_UserAgentService.LoginAgentToGrid(aCircuit, gatekeeper, destination, true, out reason)) |
||
955 | return true; |
||
956 | return false; |
||
957 | } |
||
958 | |||
959 | #region Console Commands |
||
960 | private void RegisterCommands() |
||
961 | { |
||
962 | //MainConsole.Instance.Commands.AddCommand |
||
963 | MainConsole.Instance.Commands.AddCommand("Users", false, "login level", |
||
964 | "login level <level>", |
||
965 | "Set the minimum user level to log in", HandleLoginCommand); |
||
966 | |||
967 | MainConsole.Instance.Commands.AddCommand("Users", false, "login reset", |
||
968 | "login reset", |
||
969 | "Reset the login level to allow all users", |
||
970 | HandleLoginCommand); |
||
971 | |||
972 | MainConsole.Instance.Commands.AddCommand("Users", false, "login text", |
||
973 | "login text <text>", |
||
974 | "Set the text users will see on login", HandleLoginCommand); |
||
975 | |||
976 | } |
||
977 | |||
978 | private void HandleLoginCommand(string module, string[] cmd) |
||
979 | { |
||
980 | string subcommand = cmd[1]; |
||
981 | |||
982 | switch (subcommand) |
||
983 | { |
||
984 | case "level": |
||
985 | // Set the minimum level to allow login |
||
986 | // Useful to allow grid update without worrying about users. |
||
987 | // or fixing critical issues |
||
988 | // |
||
989 | if (cmd.Length > 2) |
||
990 | { |
||
991 | if (Int32.TryParse(cmd[2], out m_MinLoginLevel)) |
||
992 | MainConsole.Instance.OutputFormat("Set minimum login level to {0}", m_MinLoginLevel); |
||
993 | else |
||
994 | MainConsole.Instance.OutputFormat("ERROR: {0} is not a valid login level", cmd[2]); |
||
995 | } |
||
996 | break; |
||
997 | |||
998 | case "reset": |
||
999 | m_MinLoginLevel = 0; |
||
1000 | MainConsole.Instance.OutputFormat("Reset min login level to {0}", m_MinLoginLevel); |
||
1001 | break; |
||
1002 | |||
1003 | case "text": |
||
1004 | if (cmd.Length > 2) |
||
1005 | { |
||
1006 | m_WelcomeMessage = cmd[2]; |
||
1007 | MainConsole.Instance.OutputFormat("Login welcome message set to '{0}'", m_WelcomeMessage); |
||
1008 | } |
||
1009 | break; |
||
1010 | } |
||
1011 | } |
||
1012 | } |
||
1013 | |||
1014 | #endregion |
||
1015 | } |