[9924] Implement .server log filter and .server log level comamnds.
[getmangos.git] / src / mangosd / CliRunnable.cpp
blob9680afcfdb9c5846b89b139ea8357edf36ac262a
1 /*
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
20 /// @{
21 /// \file
23 #include "Common.h"
24 #include "Language.h"
25 #include "Log.h"
26 #include "World.h"
27 #include "ScriptCalls.h"
28 #include "ObjectMgr.h"
29 #include "WorldSession.h"
30 #include "Config/ConfigEnv.h"
31 #include "Util.h"
32 #include "AccountMgr.h"
33 #include "CliRunnable.h"
34 #include "MapManager.h"
35 #include "Player.h"
36 #include "Chat.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))
44 return;
46 char temp_buf[6000];
47 CharToOemBuffW(&wtemp_buf[0],&temp_buf[0],wtemp_len+1);
48 printf("%s", temp_buf);
49 #else
50 printf("%s", str);
51 #endif
54 void commandFinished(void*, bool sucess)
56 printf("mangos>");
57 fflush(stdout);
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)
64 if (!*args)
65 return false;
67 std::string account_name;
68 uint32 account_id = extractAccountId((char*)args,&account_name);
69 if (!account_id)
70 return false;
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))
76 return false;
78 AccountOpResult result = sAccountMgr.DeleteAccount(account_id);
79 switch(result)
81 case AOR_OK:
82 PSendSysMessage(LANG_ACCOUNT_DELETED,account_name.c_str());
83 break;
84 case AOR_NAME_NOT_EXIST:
85 PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str());
86 SetSentErrorMessage(true);
87 return false;
88 case AOR_DB_INTERNAL_ERROR:
89 PSendSysMessage(LANG_ACCOUNT_NOT_DELETED_SQL_ERROR,account_name.c_str());
90 SetSentErrorMessage(true);
91 return false;
92 default:
93 PSendSysMessage(LANG_ACCOUNT_NOT_DELETED,account_name.c_str());
94 SetSentErrorMessage(true);
95 return false;
98 return 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())
113 // search by GUID
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())));
116 // search by name
117 else
119 if(!normalizePlayerName(searchString))
120 return false;
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());
125 else
126 resultChar = CharacterDatabase.Query("SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL");
128 if (resultChar)
132 Field* fields = resultChar->Fetch();
134 DeletedInfo info;
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());
148 delete resultChar;
151 return true;
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
171 ++itr;
172 break;
175 DeletedInfoList::const_iterator itr2 = itr;
176 if(++itr2 != itr_end)
177 wherestr << "','";
179 wherestr << "')";
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)
195 if (!m_session)
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);
206 if (!m_session)
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());
210 else
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());
216 if (!m_session)
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))
234 return false;
236 // if no characters have been found, output a warning
237 if (foundList.empty())
239 SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY);
240 return false;
243 HandleCharacterDeletedListHelper(foundList);
244 return true;
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);
262 return;
265 // check character count
266 uint32 charcount = sAccountMgr.GetCharactersCount(delInfo.accountId);
267 if (charcount >= 10)
269 PSendSysMessage(LANG_CHARACTER_DELETED_SKIP_FULL, delInfo.name.c_str(), delInfo.lowguid, delInfo.accountId);
270 return;
273 if (sObjectMgr.GetPlayerGUIDByName(delInfo.name))
275 PSendSysMessage(LANG_CHARACTER_DELETED_SKIP_NAME, delInfo.name.c_str(), delInfo.lowguid, delInfo.accountId);
276 return;
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
297 if (!*args)
298 return false;
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))
310 return false;
312 if (foundList.empty())
314 SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY);
315 return false;
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();
331 // update name
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);
343 else
344 SendSysMessage(LANG_CHARACTER_DELETED_ERR_RENAME);
346 return true;
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
362 if (!*args)
363 return false;
365 DeletedInfoList foundList;
366 if (!GetDeletedCharacterInfoList(foundList, args))
367 return false;
369 if (foundList.empty())
371 SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY);
372 return false;
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);
382 return 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, " ");
401 if (px)
403 if (!isNumeric(px))
404 return false;
406 keepDays = atoi(px);
407 if (keepDays < 0)
408 return false;
410 // config option value 0 -> disabled and can't be used
411 else if (keepDays <= 0)
412 return false;
414 Player::DeleteOldCharacters((uint32)keepDays);
415 return true;
418 bool ChatHandler::HandleCharacterEraseCommand(const char* args)
420 if(!*args)
421 return false;
423 char *character_name_str = strtok((char*)args," ");
424 if(!character_name_str)
425 return false;
427 std::string character_name = character_name_str;
428 if(!normalizePlayerName(character_name))
429 return false;
431 uint64 character_guid;
432 uint32 account_id;
434 Player *player = sObjectMgr.GetPlayer(character_name.c_str());
435 if(player)
437 character_guid = player->GetGUID();
438 account_id = player->GetSession()->GetAccountId();
439 player->GetSession()->KickPlayer();
441 else
443 character_guid = sObjectMgr.GetPlayerGUIDByName(character_name);
444 if(!character_guid)
446 PSendSysMessage(LANG_NO_PLAYER,character_name.c_str());
447 SetSentErrorMessage(true);
448 return false;
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);
459 return true;
462 /// Close RA connection
463 bool ChatHandler::HandleQuitCommand(const char* /*args*/)
465 // processed in RASocket
466 SendSysMessage(LANG_QUIT_WRONG_USE_ERROR);
467 return true;
470 /// Exit the realm
471 bool ChatHandler::HandleServerExitCommand(const char* /*args*/)
473 SendSysMessage(LANG_COMMAND_EXIT);
474 World::StopNow(SHUTDOWN_EXIT_CODE);
475 return true;
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
485 // 0 1 2 3 4
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)
494 if(!*args)
495 return false;
497 ///- %Parse the command line arguments
498 char *szAcc = strtok((char*)args, " ");
499 char *szPassword = strtok(NULL, " ");
500 if(!szAcc || !szPassword)
501 return false;
503 // normalized in accmgr.CreateAccount
504 std::string account_name = szAcc;
505 std::string password = szPassword;
507 AccountOpResult result = sAccountMgr.CreateAccount(account_name, password);
508 switch(result)
510 case AOR_OK:
511 PSendSysMessage(LANG_ACCOUNT_CREATED,account_name.c_str());
512 break;
513 case AOR_NAME_TOO_LONG:
514 SendSysMessage(LANG_ACCOUNT_TOO_LONG);
515 SetSentErrorMessage(true);
516 return false;
517 case AOR_NAME_ALREDY_EXIST:
518 SendSysMessage(LANG_ACCOUNT_ALREADY_EXIST);
519 SetSentErrorMessage(true);
520 return false;
521 case AOR_DB_INTERNAL_ERROR:
522 PSendSysMessage(LANG_ACCOUNT_NOT_CREATED_SQL_ERROR,account_name.c_str());
523 SetSentErrorMessage(true);
524 return false;
525 default:
526 PSendSysMessage(LANG_ACCOUNT_NOT_CREATED,account_name.c_str());
527 SetSentErrorMessage(true);
528 return false;
531 return true;
534 /// Set the filters of logging
535 bool ChatHandler::HandleServerLogFilterCommand(const char* args)
537 if(!*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));
544 return true;
547 char *filtername = strtok((char*)args, " ");
548 if (!filtername)
549 return false;
551 char *value_str = strtok(NULL, " ");
552 if (!value_str)
553 return false;
555 bool value;
556 if (strncmp(value_str, "on", 3) == 0)
557 value = true;
558 else if (strncmp(value_str, "off", 4) == 0)
559 value = false;
560 else
562 SendSysMessage(LANG_USE_BOL);
563 SetSentErrorMessage(true);
564 return false;
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));
571 return true;
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));
580 return true;
584 return false;
587 /// Set the level of logging
588 bool ChatHandler::HandleServerLogLevelCommand(const char *args)
590 if(!*args)
592 PSendSysMessage("Log level: %u");
593 return true;
596 sLog.SetLogLevel((char*)args);
597 return true;
600 /// @}
602 #ifdef linux
603 // Non-blocking keypress detector, when return pressed, return 1, else always return 0
604 int kb_hit_return()
606 struct timeval tv;
607 fd_set fds;
608 tv.tv_sec = 0;
609 tv.tv_usec = 0;
610 FD_ZERO(&fds);
611 FD_SET(STDIN_FILENO, &fds);
612 select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
613 return FD_ISSET(STDIN_FILENO, &fds);
615 #endif
617 /// %Thread start
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
626 sLog.outString();
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
633 printf("mangos>");
635 ///- As long as the World is running (no World::m_stopEvent), get the command line and handle it
636 while (!World::IsStopped())
638 fflush(stdout);
639 #ifdef linux
640 while (!kb_hit_return() && !World::IsStopped())
641 // With this, we limit CLI to 10commands/second
642 usleep(100);
643 if (World::IsStopped())
644 break;
645 #endif
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')
652 command_str[x]=0;
653 break;
657 if(!*command_str)
659 printf("mangos>");
660 continue;
663 std::string command;
664 if(!consoleToUtf8(command_str,command)) // convert from console encoding to utf8
666 printf("mangos>");
667 continue;
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