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