2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 /// \addtogroup mangosd
27 #include "ScriptCalls.h"
28 #include "ObjectMgr.h"
29 #include "WorldSession.h"
30 #include "Config/ConfigEnv.h"
32 #include "AccountMgr.h"
33 #include "CliRunnable.h"
34 #include "MapManager.h"
38 void utf8print(void* arg
, const char* str
)
40 #if PLATFORM == PLATFORM_WINDOWS
41 wchar_t wtemp_buf
[6000];
42 size_t wtemp_len
= 6000-1;
43 if(!Utf8toWStr(str
,strlen(str
),wtemp_buf
,wtemp_len
))
47 CharToOemBuffW(&wtemp_buf
[0],&temp_buf
[0],wtemp_len
+1);
48 printf("%s", temp_buf
);
54 void commandFinished(void*, bool sucess
)
60 /// Delete a user account and all associated characters in this realm
61 /// \todo This function has to be enhanced to respect the login/realm split (delete char, delete account chars in realm, delete account chars in realm then delete account
62 bool ChatHandler::HandleAccountDeleteCommand(const char* args
)
67 std::string account_name
;
68 uint32 account_id
= extractAccountId((char*)args
,&account_name
);
72 /// Commands not recommended call from chat, but support anyway
73 /// can delete only for account with less security
74 /// This is also reject self apply in fact
75 if(HasLowerSecurityAccount (NULL
,account_id
,true))
78 AccountOpResult result
= sAccountMgr
.DeleteAccount(account_id
);
82 PSendSysMessage(LANG_ACCOUNT_DELETED
,account_name
.c_str());
84 case AOR_NAME_NOT_EXIST
:
85 PSendSysMessage(LANG_ACCOUNT_NOT_EXIST
,account_name
.c_str());
86 SetSentErrorMessage(true);
88 case AOR_DB_INTERNAL_ERROR
:
89 PSendSysMessage(LANG_ACCOUNT_NOT_DELETED_SQL_ERROR
,account_name
.c_str());
90 SetSentErrorMessage(true);
93 PSendSysMessage(LANG_ACCOUNT_NOT_DELETED
,account_name
.c_str());
94 SetSentErrorMessage(true);
102 * Collects all GUIDs (and related info) from deleted characters which are still in the database.
104 * @param foundList a reference to an std::list which will be filled with info data
105 * @param searchString the search string which either contains a player GUID or a part fo the character-name
106 * @return returns false if there was a problem while selecting the characters (e.g. player name not normalizeable)
108 bool ChatHandler::GetDeletedCharacterInfoList(DeletedInfoList
& foundList
, std::string searchString
)
110 QueryResult
* resultChar
;
111 if (!searchString
.empty())
114 if (isNumeric(searchString
))
115 resultChar
= CharacterDatabase
.PQuery("SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND guid = %u", uint64(atoi(searchString
.c_str())));
119 if(!normalizePlayerName(searchString
))
122 resultChar
= CharacterDatabase
.PQuery("SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND deleteInfos_Name " _LIKE_
" " _CONCAT3_("'%%'", "'%s'", "'%%'"), searchString
.c_str());
126 resultChar
= CharacterDatabase
.Query("SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL");
132 Field
* fields
= resultChar
->Fetch();
136 info
.lowguid
= fields
[0].GetUInt32();
137 info
.name
= fields
[1].GetCppString();
138 info
.accountId
= fields
[2].GetUInt32();
140 // account name will be empty for not existed account
141 sAccountMgr
.GetName (info
.accountId
, info
.accountName
);
143 info
.deleteDate
= time_t(fields
[3].GetUInt64());
145 foundList
.push_back(info
);
146 } while (resultChar
->NextRow());
155 * Generate WHERE guids list by deleted info in way preventing return too long where list for existed query string length limit.
157 * @param itr a reference to an deleted info list iterator, it updated in function for possible next function call if list to long
158 * @param itr_end a reference to an deleted info list iterator end()
159 * @return returns generated where list string in form: 'guid IN (gui1, guid2, ...)'
161 std::string
ChatHandler::GenerateDeletedCharacterGUIDsWhereStr(DeletedInfoList::const_iterator
& itr
, DeletedInfoList::const_iterator
const& itr_end
)
163 std::ostringstream wherestr
;
164 wherestr
<< "guid IN ('";
165 for(; itr
!= itr_end
; ++itr
)
167 wherestr
<< itr
->lowguid
;
169 if (wherestr
.str().size() > MAX_QUERY_LEN
- 50) // near to max query
175 DeletedInfoList::const_iterator itr2
= itr
;
176 if(++itr2
!= itr_end
)
180 return wherestr
.str();
184 * Shows all deleted characters which matches the given search string, expected non empty list
186 * @see ChatHandler::HandleCharacterDeletedListCommand
187 * @see ChatHandler::HandleCharacterDeletedRestoreCommand
188 * @see ChatHandler::HandleCharacterDeletedDeleteCommand
189 * @see ChatHandler::DeletedInfoList
191 * @param foundList contains a list with all found deleted characters
193 void ChatHandler::HandleCharacterDeletedListHelper(DeletedInfoList
const& foundList
)
197 SendSysMessage(LANG_CHARACTER_DELETED_LIST_BAR
);
198 SendSysMessage(LANG_CHARACTER_DELETED_LIST_HEADER
);
199 SendSysMessage(LANG_CHARACTER_DELETED_LIST_BAR
);
202 for (DeletedInfoList::const_iterator itr
= foundList
.begin(); itr
!= foundList
.end(); ++itr
)
204 std::string dateStr
= TimeToTimestampStr(itr
->deleteDate
);
207 PSendSysMessage(LANG_CHARACTER_DELETED_LIST_LINE_CONSOLE
,
208 itr
->lowguid
, itr
->name
.c_str(), itr
->accountName
.empty() ? "<Not existed>" : itr
->accountName
.c_str(),
209 itr
->accountId
, dateStr
.c_str());
211 PSendSysMessage(LANG_CHARACTER_DELETED_LIST_LINE_CHAT
,
212 itr
->lowguid
, itr
->name
.c_str(), itr
->accountName
.empty() ? "<Not existed>" : itr
->accountName
.c_str(),
213 itr
->accountId
, dateStr
.c_str());
217 SendSysMessage(LANG_CHARACTER_DELETED_LIST_BAR
);
221 * Handles the '.character deleted list' command, which shows all deleted characters which matches the given search string
223 * @see ChatHandler::HandleCharacterDeletedListHelper
224 * @see ChatHandler::HandleCharacterDeletedRestoreCommand
225 * @see ChatHandler::HandleCharacterDeletedDeleteCommand
226 * @see ChatHandler::DeletedInfoList
228 * @param args the search string which either contains a player GUID or a part fo the character-name
230 bool ChatHandler::HandleCharacterDeletedListCommand(const char* args
)
232 DeletedInfoList foundList
;
233 if (!GetDeletedCharacterInfoList(foundList
, args
))
236 // if no characters have been found, output a warning
237 if (foundList
.empty())
239 SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY
);
243 HandleCharacterDeletedListHelper(foundList
);
248 * Restore a previously deleted character
250 * @see ChatHandler::HandleCharacterDeletedListHelper
251 * @see ChatHandler::HandleCharacterDeletedRestoreCommand
252 * @see ChatHandler::HandleCharacterDeletedDeleteCommand
253 * @see ChatHandler::DeletedInfoList
255 * @param delInfo the informations about the character which will be restored
257 void ChatHandler::HandleCharacterDeletedRestoreHelper(DeletedInfo
const& delInfo
)
259 if (delInfo
.accountName
.empty()) // account not exist
261 PSendSysMessage(LANG_CHARACTER_DELETED_SKIP_ACCOUNT
, delInfo
.name
.c_str(), delInfo
.lowguid
, delInfo
.accountId
);
265 // check character count
266 uint32 charcount
= sAccountMgr
.GetCharactersCount(delInfo
.accountId
);
269 PSendSysMessage(LANG_CHARACTER_DELETED_SKIP_FULL
, delInfo
.name
.c_str(), delInfo
.lowguid
, delInfo
.accountId
);
273 if (sObjectMgr
.GetPlayerGUIDByName(delInfo
.name
))
275 PSendSysMessage(LANG_CHARACTER_DELETED_SKIP_NAME
, delInfo
.name
.c_str(), delInfo
.lowguid
, delInfo
.accountId
);
279 CharacterDatabase
.PExecute("UPDATE characters SET name='%s', account='%u', deleteDate=NULL, deleteInfos_Name=NULL, deleteInfos_Account=NULL WHERE deleteDate IS NOT NULL AND guid = %u",
280 delInfo
.name
.c_str(), delInfo
.accountId
, delInfo
.lowguid
);
284 * Handles the '.character deleted restore' command, which restores all deleted characters which matches the given search string
286 * The command automatically calls '.character deleted list' command with the search string to show all restored characters.
288 * @see ChatHandler::HandleCharacterDeletedRestoreHelper
289 * @see ChatHandler::HandleCharacterDeletedListCommand
290 * @see ChatHandler::HandleCharacterDeletedDeleteCommand
292 * @param args the search string which either contains a player GUID or a part of the character-name
294 bool ChatHandler::HandleCharacterDeletedRestoreCommand(const char* args
)
296 // It is required to submit at least one argument
300 std::string searchString
;
301 std::string newCharName
;
302 uint32 newAccount
= 0;
304 // GCC by some strange reason fail build code without temporary variable
305 std::istringstream
params(args
);
306 params
>> searchString
>> newCharName
>> newAccount
;
308 DeletedInfoList foundList
;
309 if (!GetDeletedCharacterInfoList(foundList
, searchString
))
312 if (foundList
.empty())
314 SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY
);
318 SendSysMessage(LANG_CHARACTER_DELETED_RESTORE
);
319 HandleCharacterDeletedListHelper(foundList
);
321 if (newCharName
.empty())
323 // Drop not existed account cases
324 for (DeletedInfoList::iterator itr
= foundList
.begin(); itr
!= foundList
.end(); ++itr
)
325 HandleCharacterDeletedRestoreHelper(*itr
);
327 else if (foundList
.size() == 1 && normalizePlayerName(newCharName
))
329 DeletedInfo delInfo
= foundList
.front();
332 delInfo
.name
= newCharName
;
334 // if new account provided update deleted info
335 if (newAccount
&& newAccount
!= delInfo
.accountId
)
337 delInfo
.accountId
= newAccount
;
338 sAccountMgr
.GetName (newAccount
, delInfo
.accountName
);
341 HandleCharacterDeletedRestoreHelper(delInfo
);
344 SendSysMessage(LANG_CHARACTER_DELETED_ERR_RENAME
);
350 * Handles the '.character deleted delete' command, which completely deletes all deleted characters which matches the given search string
352 * @see Player::GetDeletedCharacterGUIDs
353 * @see Player::DeleteFromDB
354 * @see ChatHandler::HandleCharacterDeletedListCommand
355 * @see ChatHandler::HandleCharacterDeletedRestoreCommand
357 * @param args the search string which either contains a player GUID or a part fo the character-name
359 bool ChatHandler::HandleCharacterDeletedDeleteCommand(const char* args
)
361 // It is required to submit at least one argument
365 DeletedInfoList foundList
;
366 if (!GetDeletedCharacterInfoList(foundList
, args
))
369 if (foundList
.empty())
371 SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY
);
375 SendSysMessage(LANG_CHARACTER_DELETED_DELETE
);
376 HandleCharacterDeletedListHelper(foundList
);
378 // Call the appropriate function to delete them (current account for deleted characters is 0)
379 for(DeletedInfoList::const_iterator itr
= foundList
.begin(); itr
!= foundList
.end(); ++itr
)
380 Player::DeleteFromDB(itr
->lowguid
, 0, false, true);
386 * Handles the '.character deleted old' command, which completely deletes all deleted characters deleted with some days ago
388 * @see Player::DeleteOldCharacters
389 * @see Player::DeleteFromDB
390 * @see ChatHandler::HandleCharacterDeletedDeleteCommand
391 * @see ChatHandler::HandleCharacterDeletedListCommand
392 * @see ChatHandler::HandleCharacterDeletedRestoreCommand
394 * @param args the search string which either contains a player GUID or a part fo the character-name
396 bool ChatHandler::HandleCharacterDeletedOldCommand(const char* args
)
398 int32 keepDays
= sWorld
.getConfig(CONFIG_UINT32_CHARDELETE_KEEP_DAYS
);
400 char* px
= strtok((char*)args
, " ");
410 // config option value 0 -> disabled and can't be used
411 else if (keepDays
<= 0)
414 Player::DeleteOldCharacters((uint32
)keepDays
);
418 bool ChatHandler::HandleCharacterEraseCommand(const char* args
)
423 char *character_name_str
= strtok((char*)args
," ");
424 if(!character_name_str
)
427 std::string character_name
= character_name_str
;
428 if(!normalizePlayerName(character_name
))
431 uint64 character_guid
;
434 Player
*player
= sObjectMgr
.GetPlayer(character_name
.c_str());
437 character_guid
= player
->GetGUID();
438 account_id
= player
->GetSession()->GetAccountId();
439 player
->GetSession()->KickPlayer();
443 character_guid
= sObjectMgr
.GetPlayerGUIDByName(character_name
);
446 PSendSysMessage(LANG_NO_PLAYER
,character_name
.c_str());
447 SetSentErrorMessage(true);
451 account_id
= sObjectMgr
.GetPlayerAccountIdByGUID(character_guid
);
454 std::string account_name
;
455 sAccountMgr
.GetName (account_id
,account_name
);
457 Player::DeleteFromDB(character_guid
, account_id
, true, true);
458 PSendSysMessage(LANG_CHARACTER_DELETED
,character_name
.c_str(),GUID_LOPART(character_guid
),account_name
.c_str(), account_id
);
462 /// Close RA connection
463 bool ChatHandler::HandleQuitCommand(const char* /*args*/)
465 // processed in RASocket
466 SendSysMessage(LANG_QUIT_WRONG_USE_ERROR
);
471 bool ChatHandler::HandleServerExitCommand(const char* /*args*/)
473 SendSysMessage(LANG_COMMAND_EXIT
);
474 World::StopNow(SHUTDOWN_EXIT_CODE
);
478 /// Display info on users currently in the realm
479 bool ChatHandler::HandleAccountOnlineListCommand(const char* args
)
481 char* limit_str
= *args
? strtok((char*)args
, " ") : NULL
;
482 uint32 limit
= limit_str
? atoi (limit_str
) : 100;
484 ///- Get the list of accounts ID logged to the realm
486 QueryResult
*result
= loginDatabase
.PQuery("SELECT id, username, last_ip, gmlevel, expansion FROM account WHERE active_realm_id = %u", realmID
);
488 return ShowAccountListHelper(result
,&limit
);
491 /// Create an account
492 bool ChatHandler::HandleAccountCreateCommand(const char* args
)
497 ///- %Parse the command line arguments
498 char *szAcc
= strtok((char*)args
, " ");
499 char *szPassword
= strtok(NULL
, " ");
500 if(!szAcc
|| !szPassword
)
503 // normalized in accmgr.CreateAccount
504 std::string account_name
= szAcc
;
505 std::string password
= szPassword
;
507 AccountOpResult result
= sAccountMgr
.CreateAccount(account_name
, password
);
511 PSendSysMessage(LANG_ACCOUNT_CREATED
,account_name
.c_str());
513 case AOR_NAME_TOO_LONG
:
514 SendSysMessage(LANG_ACCOUNT_TOO_LONG
);
515 SetSentErrorMessage(true);
517 case AOR_NAME_ALREDY_EXIST
:
518 SendSysMessage(LANG_ACCOUNT_ALREADY_EXIST
);
519 SetSentErrorMessage(true);
521 case AOR_DB_INTERNAL_ERROR
:
522 PSendSysMessage(LANG_ACCOUNT_NOT_CREATED_SQL_ERROR
,account_name
.c_str());
523 SetSentErrorMessage(true);
526 PSendSysMessage(LANG_ACCOUNT_NOT_CREATED
,account_name
.c_str());
527 SetSentErrorMessage(true);
534 /// Set the filters of logging
535 bool ChatHandler::HandleServerLogFilterCommand(const char* args
)
539 uint32 logfiler
= sLog
.getLogFilter();
541 SendSysMessage(LANG_LOG_FILTERS_STATE_HEADER
);
542 for(int i
= 0; i
< LOG_FILTER_COUNT
; ++i
)
543 PSendSysMessage(" %-20s = %s",logFilterData
[i
].name
,(logfiler
& (1 << i
)) !=0 ? GetMangosString(LANG_ON
) : GetMangosString(LANG_OFF
));
547 char *filtername
= strtok((char*)args
, " ");
551 char *value_str
= strtok(NULL
, " ");
556 if (strncmp(value_str
, "on", 3) == 0)
558 else if (strncmp(value_str
, "off", 4) == 0)
562 SendSysMessage(LANG_USE_BOL
);
563 SetSentErrorMessage(true);
567 if (strncmp(filtername
, "all", 4) == 0)
569 sLog
.SetLogFilter(LogFilters(0xFFFFFFFF),value
);
570 PSendSysMessage(LANG_ALL_LOG_FILTERS_SET_TO_S
, value
? GetMangosString(LANG_ON
) : GetMangosString(LANG_OFF
));
574 for(int i
= 0; i
< LOG_FILTER_COUNT
; ++i
)
576 if (!strncmp(filtername
,logFilterData
[i
].name
,strlen(filtername
)))
578 sLog
.SetLogFilter(LogFilters(1 << i
),value
);
579 PSendSysMessage(" %-20s = %s",logFilterData
[i
].name
,value
? GetMangosString(LANG_ON
) : GetMangosString(LANG_OFF
));
587 /// Set the level of logging
588 bool ChatHandler::HandleServerLogLevelCommand(const char *args
)
592 PSendSysMessage("Log level: %u");
596 sLog
.SetLogLevel((char*)args
);
603 // Non-blocking keypress detector, when return pressed, return 1, else always return 0
611 FD_SET(STDIN_FILENO
, &fds
);
612 select(STDIN_FILENO
+1, &fds
, NULL
, NULL
, &tv
);
613 return FD_ISSET(STDIN_FILENO
, &fds
);
618 void CliRunnable::run()
620 ///- Init new SQL thread for the world database (one connection call enough)
621 WorldDatabase
.ThreadStart(); // let thread do safe mySQL requests
623 char commandbuf
[256];
625 ///- Display the list of available CLI functions then beep
628 if(sConfig
.GetBoolDefault("BeepAtStart", true))
629 printf("\a"); // \a = Alert
631 // print this here the first time
632 // later it will be printed after command queue updates
635 ///- As long as the World is running (no World::m_stopEvent), get the command line and handle it
636 while (!World::IsStopped())
640 while (!kb_hit_return() && !World::IsStopped())
641 // With this, we limit CLI to 10commands/second
643 if (World::IsStopped())
646 char *command_str
= fgets(commandbuf
,sizeof(commandbuf
),stdin
);
647 if (command_str
!= NULL
)
649 for(int x
=0;command_str
[x
];x
++)
650 if(command_str
[x
]=='\r'||command_str
[x
]=='\n')
664 if(!consoleToUtf8(command_str
,command
)) // convert from console encoding to utf8
670 sWorld
.QueueCliCommand(new CliCommandHolder(0, SEC_CONSOLE
, NULL
, command
.c_str(), &utf8print
, &commandFinished
));
672 else if (feof(stdin
))
674 World::StopNow(SHUTDOWN_EXIT_CODE
);
678 ///- End the database thread
679 WorldDatabase
.ThreadEnd(); // free mySQL thread resources