root/trunk/src/trinitycore/CliRunnable.cpp @ 39

Revision 39, 40.7 kB (checked in by yumileroy, 17 years ago)

[svn] * Various small changes here and there.
* Implementing MangChat? IRC system.
* Added new config option, MAX_WHO, can be used to set the limit of characters being sent in a /who request from client.

Original author: XTZGZoReX
Date: 2008-10-12 14:03:38-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
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.
8 *
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.
13 *
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
17 */
18
19/// \addtogroup mangosd
20/// @{
21/// \file
22
23#include "Common.h"
24#include "Language.h"
25#include "Log.h"
26#include "World.h"
27#include "ScriptCalls.h"
28#include "GlobalEvents.h"
29#include "ObjectMgr.h"
30#include "WorldSession.h"
31#include "SystemConfig.h"
32#include "Config/ConfigEnv.h"
33#include "Util.h"
34#include "AccountMgr.h"
35#include "CliRunnable.h"
36#include "MapManager.h"
37#include "PlayerDump.h"
38#include "Player.h"
39#include "IRCClient.h"
40
41//CliCommand and CliCommandHolder are defined in World.h to avoid cyclic deps
42
43//func prototypes must be defined
44
45void CliHelp(char*,pPrintf);
46void CliInfo(char*,pPrintf);
47void CliBan(char*,pPrintf);
48void CliBanList(char*,pPrintf);
49void CliRemoveBan(char*,pPrintf);
50void CliSetGM(char*,pPrintf);
51void CliListGM(char*,pPrintf);
52void CliVersion(char*,pPrintf);
53void CliExit(char*,pPrintf);
54void CliIdleRestart(char*,pPrintf zprintf);
55void CliRestart(char*,pPrintf zprintf);
56void CliIdleShutdown(char*,pPrintf zprintf);
57void CliShutdown(char*,pPrintf zprintf);
58void CliBroadcast(char*,pPrintf);
59void CliCreate(char*,pPrintf);
60void CliDelete(char*,pPrintf);
61void CliCharDelete(char *,pPrintf);
62void CliLoadScripts(char*,pPrintf);
63void CliKick(char*,pPrintf);
64void CliTele(char*,pPrintf);
65void CliMotd(char*,pPrintf);
66void CliCorpses(char*,pPrintf);
67void CliSetLogLevel(char*,pPrintf);
68void CliUpTime(char*,pPrintf);
69void CliSetTBC(char*,pPrintf);
70void CliWritePlayerDump(char*,pPrintf);
71void CliLoadPlayerDump(char*,pPrintf);
72void CliSave(char*,pPrintf);
73void CliSend(char*,pPrintf);
74void CliPLimit(char*,pPrintf);
75void CliSetPassword(char*,pPrintf);
76/// Table of known commands
77const CliCommand Commands[]=
78{
79    {"help", & CliHelp,"Display this help message"},
80    {"broadcast", & CliBroadcast,"Announce in-game message"},
81    {"create", & CliCreate,"Create account"},
82    {"delete", & CliDelete,"Delete account and characters"},
83    {"chardelete", & CliCharDelete,"Delete character"},
84    {"info", & CliInfo,"Display Server infomation"},
85    {"uptime", & CliUpTime, "Displays the server uptime"},
86    {"motd", & CliMotd,"Change or display motd"},
87    {"kick", & CliKick,"Kick user"},
88    {"ban", & CliBan,"Ban account|ip"},
89    {"listbans", & CliBanList,"List bans"},
90    {"unban", & CliRemoveBan,"Remove ban from account|ip"},
91    {"setgm", & CliSetGM,"Edit user privileges"},
92    {"setpass", & CliSetPassword,"Set password for account"},
93    {"setbc", & CliSetTBC,"Set user expansion allowed"},
94    {"listgm", & CliListGM,"Display user privileges"},
95    {"loadscripts", & CliLoadScripts,"Load script library"},
96    {"setloglevel", & CliSetLogLevel,"Set Log Level"},
97    {"corpses", & CliCorpses,"Manually call corpses erase global even code"},
98    {"version", & CliVersion,"Display server version"},
99    {"idlerestart", & CliIdleRestart,"Restart server with some delay when there are no active connections remaining"},
100    {"restart", & CliRestart,"Restart server with some delay"},
101    {"idleshutdown", & CliIdleShutdown,"Shutdown server with some delay when there are no active connections remaining"},
102    {"shutdown", & CliShutdown,"Shutdown server with some delay"},
103    {"exit", & CliExit,"Shutdown server NOW"},
104    {"writepdump", &CliWritePlayerDump,"Write a player dump to a file"},
105    {"loadpdump", &CliLoadPlayerDump,"Load a player dump from a file"},
106    {"saveall", &CliSave,"Save all players"},
107    {"send", &CliSend,"Send message to a player"},
108    {"tele", &CliTele,"Teleport player to location"},
109    {"plimit", &CliPLimit,"Show or set player login limitations"}
110};
111/// \todo Need some pragma pack? Else explain why in a comment.
112#define CliTotalCmds sizeof(Commands)/sizeof(CliCommand)
113
114#if PLATFORM == PLATFORM_WINDOWS
115int utf8printf(const char* str,...)
116{
117    UTF8PRINTF(stdout,str,1);
118    return 0;
119}
120#define UTF8ZPRINTF utf8printf
121#else
122#define UTF8ZPRINTF printf
123#endif
124
125/// Create a character dump file
126void CliWritePlayerDump(char*command,pPrintf zprintf)
127{
128    char * file = strtok(command, " ");
129    char * p2 = strtok(NULL, " ");
130    if(!file || !p2)
131    {
132        zprintf("Syntax is: writepdump $filename $playerNameOrGUID\r\n");
133        return;
134    }
135
136    std::string name;
137    if(!consoleToUtf8(p2,name))                             // convert from console encoding to utf8
138        return;
139
140    if(!normalizePlayerName(name))
141    {
142        zprintf("Syntax is: writepdump $filename $playerNameOrGUID\r\n");
143        return;
144    }
145
146    uint32 guid = objmgr.GetPlayerGUIDByName(name);
147    if(!guid)
148        guid = atoi(p2);
149
150    if(!guid)
151    {
152        zprintf("Syntax is: writepdump $filename $playerNameOrGUID\r\n");
153        return;
154    }
155
156    PlayerDumpWriter().WriteDump(file, guid);
157}
158
159/// Load a character from a dump file
160void CliLoadPlayerDump(char*command,pPrintf zprintf)
161{
162    char * file = strtok(command, " ");
163    char * acc = strtok(NULL, " ");
164    if (!file ||!acc)
165    {
166        zprintf("Syntax is: loadpdump $filename $account ($newname) ($newguid)\r\n");
167        return;
168    }
169
170    uint32 account_id = objmgr.GetAccountByAccountName(acc);
171    if(!account_id)
172    {
173        account_id = atoi(acc);
174        if(account_id)
175        {
176            std::string acc_name;
177            if(!objmgr.GetAccountNameByAccount(account_id,acc_name))
178            {
179                zprintf("Failed to load the character! Account not exist.\r\n");
180                return;
181            }
182        }
183        else
184        {
185            zprintf("Failed to load the character! Account not exist.\r\n");
186            return;
187        }
188    }
189
190    char * name_str = strtok(NULL, " ");
191    char * guid_str = name_str ? strtok(NULL, " ") : NULL;
192
193    uint32 guid = guid_str ? atoi(guid_str) : 0;
194
195    std::string name;
196    if(name_str)
197    {
198        if(!consoleToUtf8(name_str,name))                   // convert from console encoding to utf8
199            return;
200
201        if(!normalizePlayerName(name))
202        {
203            zprintf("Syntax is: loadpdump $filename $account ($newname) ($newguid)\r\n");
204            return;
205        }
206    }
207
208    if(PlayerDumpReader().LoadDump(file, account_id, name, guid))
209        zprintf("Character loaded successfully!\r\n");
210    else
211        zprintf("Failed to load the character!\r\n");
212}
213
214/// Reload the scripts and notify the players
215void CliLoadScripts(char*command,pPrintf zprintf)
216{
217    char const *del=strtok(command," ");
218    if (!del)
219        del="";
220    if(!LoadScriptingModule(del))                           // Error report is already done by LoadScriptingModule
221        return;
222
223    sWorld.SendWorldText(LANG_SCRIPTS_RELOADED);
224}
225
226/// Delete a user account and all associated characters in this realm
227/// \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
228void CliDelete(char*command,pPrintf zprintf)
229{
230    ///- Get the account name from the command line
231    char *account_name_str=strtok(command," ");
232    if(!account_name_str)
233    {
234        // \r\n is used because this function can also be called from RA
235        zprintf("Syntax is: delete $account\r\n");
236        return;
237    }
238
239    std::string account_name;
240    if(!consoleToUtf8(account_name_str,account_name))       // convert from console encoding to utf8
241        return;
242
243    AccountOpResult result = accmgr.DeleteAccount(accmgr.GetId(account_name));
244    switch(result)
245    {
246        case AOR_OK:
247            zprintf("We deleted account: %s\r\n",account_name.c_str());
248            break;
249        case AOR_NAME_NOT_EXIST:
250            zprintf("User %s does not exist\r\n",account_name.c_str());
251            break;
252        case AOR_DB_INTERNAL_ERROR:
253            zprintf("User %s NOT deleted (probably sql file format was updated)\r\n",account_name.c_str());
254            break;
255        default:
256            zprintf("User %s NOT deleted (unknown error)\r\n",account_name.c_str());
257            break;
258    }
259}
260
261void CliCharDelete(char*command,pPrintf zprintf)
262{
263    char *character_name_str = strtok(command," ");
264
265    if(!character_name_str)
266    {
267        zprintf("Syntax is: chardelete $character_name\r\n");
268        return;
269    }
270
271    std::string character_name;
272    if(!consoleToUtf8(character_name_str,character_name))   // convert from console encoding to utf8
273        return;
274
275    if(!normalizePlayerName(character_name))
276    {
277        zprintf("Syntax is: chardelete $character_name\r\n");
278        return;
279    }
280
281    Player *player = objmgr.GetPlayer(character_name.c_str());
282
283    uint64 character_guid;
284    uint32 account_id;
285
286    if(player)
287    {
288        character_guid = player->GetGUID();
289        account_id = player->GetSession()->GetAccountId();
290        player->GetSession()->KickPlayer();
291    }
292    else
293    {
294        character_guid = objmgr.GetPlayerGUIDByName(character_name);
295        if(!character_guid)
296        {
297            zprintf("Player %s not found!\r\n",character_name.c_str());
298            return;
299        }
300
301        account_id = objmgr.GetPlayerAccountIdByGUID(character_guid);
302    }
303
304    Player::DeleteFromDB(character_guid, account_id, true); 
305    zprintf("Player %s (Guid: %u AccountId: %u) deleted\r\n",character_name.c_str(),GUID_LOPART(character_guid),account_id);
306}
307
308/// Broadcast a message to the World
309void CliBroadcast(char *text,pPrintf zprintf)
310{
311    std::string textUtf8;
312    if(!consoleToUtf8(text,textUtf8))                       // convert from console encoding to utf8
313        return;
314
315    sWorld.SendWorldText(LANG_SYSTEMMESSAGE,textUtf8.c_str());
316    zprintf("Broadcasting to the world: %s\r\n",textUtf8.c_str());
317
318    if((sIRC.BOTMASK & 256) != 0)
319    {
320        std::string ircchan = "#";
321        ircchan += sIRC._irc_chan[sIRC.anchn].c_str();
322        sIRC.Send_IRC_Channel(ircchan, sIRC.MakeMsg("\00304,08\037/!\\\037\017\00304 System Message \00304,08\037/!\\\037\017 %s", "%s", text), true);
323    }
324
325}
326
327/// Print the list of commands and associated description
328void CliHelp(char*,pPrintf zprintf)
329{
330    for (unsigned int x=0;x<CliTotalCmds;x++)
331        zprintf("%-13s - %s.\r\n",Commands[x].cmd ,Commands[x].description);
332}
333
334/// Exit the realm
335void CliExit(char*,pPrintf zprintf)
336{
337    zprintf( "Exiting daemon...\r\n" );
338    World::m_stopEvent = true;
339}
340
341/// Restart the server (with some delay) as soon as no active connections remain on the server
342void CliIdleRestart(char* command,pPrintf zprintf)
343{
344    char *args = strtok(command," ");
345
346    if(!args)
347    {
348        zprintf("Syntax is: idlerestart $seconds|cancel\r\n");
349        return;
350    }
351
352    if(std::string(args)=="cancel")
353    {
354        sWorld.ShutdownCancel();
355    }
356    else
357    {
358
359        uint32 time = atoi(args);
360
361        ///- Prevent interpret wrong arg value as 0 secs shutdown time
362        if(time==0 && (args[0]!='0' || args[1]!='\0') || time < 0)
363        {
364            zprintf("Syntax is: idlerestart $seconds|cancel\r\n");
365            return;
366        }
367
368        sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART|SHUTDOWN_MASK_IDLE);
369    }
370}
371
372/// Restart the server with some delay
373void CliRestart(char* command,pPrintf zprintf)
374{
375    char *args = strtok(command," ");
376
377    if(!args)
378    {
379        zprintf("Syntax is: restart $seconds|cancel\r\n");
380        return;
381    }
382
383    if(std::string(args)=="cancel")
384    {
385        sWorld.ShutdownCancel();
386    }
387    else
388    {
389        int32 time = atoi(args);
390
391        ///- Prevent interpret wrong arg value as 0 secs shutdown time
392        if(time==0 && (args[0]!='0' || args[1]!='\0') || time < 0)
393        {
394            zprintf("Syntax is: restart $seconds|cancel\r\n");
395            return;
396        }
397
398        sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART);
399    }
400}
401
402/// Shutdown the server (with some delay) as soon as no active connections remain on the server
403void CliIdleShutdown(char* command,pPrintf zprintf)
404{
405    char *args = strtok(command," ");
406
407    if(!args)
408    {
409        zprintf("Syntax is: idleshutdown $seconds|cancel\r\n");
410        return;
411    }
412
413    if(std::string(args)=="cancel")
414    {
415        sWorld.ShutdownCancel();
416    }
417    else
418    {
419
420        uint32 time = atoi(args);
421
422        ///- Prevent interpret wrong arg value as 0 secs shutdown time
423        if(time==0 && (args[0]!='0' || args[1]!='\0') || time < 0)
424        {
425            zprintf("Syntax is: idleshutdown $seconds|cancel\r\n");
426            return;
427        }
428
429        sWorld.ShutdownServ(time,SHUTDOWN_MASK_IDLE);
430    }
431}
432
433/// Shutdown the server with some delay
434void CliShutdown(char* command,pPrintf zprintf)
435{
436    char *args = strtok(command," ");
437
438    if(!args)
439    {
440        zprintf("Syntax is: shutdown $seconds|cancel\r\n");
441        return;
442    }
443
444    if(std::string(args)=="cancel")
445    {
446        sWorld.ShutdownCancel();
447    }
448    else
449    {
450        int32 time = atoi(args);
451
452        ///- Prevent interpret wrong arg value as 0 secs shutdown time
453        if(time==0 && (args[0]!='0' || args[1]!='\0') || time < 0)
454        {
455            zprintf("Syntax is: shutdown $seconds|cancel\r\n");
456            return;
457        }
458
459        sWorld.ShutdownServ(time);
460    }
461}
462
463/// Display info on users currently in the realm
464void CliInfo(char*,pPrintf zprintf)
465{
466    uint32 activeClientsNum = sWorld.GetActiveSessionCount();
467    uint32 queuedClientsNum = sWorld.GetQueuedSessionCount();
468    uint32 maxActiveClientsNum = sWorld.GetMaxActiveSessionCount();
469    uint32 maxQueuedClientsNum = sWorld.GetMaxQueuedSessionCount();
470    std::string timeStr = secsToTimeString(sWorld.GetUptime(),true);
471
472    zprintf("Online players: %u (max: %u) queued: %u (max: %u) Uptime: %s\r\n",activeClientsNum,maxActiveClientsNum,queuedClientsNum,maxQueuedClientsNum,timeStr.c_str());
473
474    ///- Get the list of accounts ID logged to the realm
475    QueryResult *resultDB = CharacterDatabase.Query("SELECT name,account FROM characters WHERE online > 0");
476
477    if (!resultDB)
478        return;
479
480    ///- Display the list of account/characters online
481    zprintf("=====================================================================\r\n");
482    zprintf("|    Account    |       Character      |       IP        | GM | TBC |\r\n");
483    zprintf("=====================================================================\r\n");
484
485    ///- Circle through accounts
486    do
487    {
488        Field *fieldsDB = resultDB->Fetch();
489        std::string name = fieldsDB[0].GetCppString();
490        uint32 account = fieldsDB[1].GetUInt32();
491
492        ///- Get the username, last IP and GM level of each account
493        // No SQL injection. account is uint32.
494        //                                                      0         1        2        3
495        QueryResult *resultLogin = loginDatabase.PQuery("SELECT username, last_ip, gmlevel, tbc FROM account WHERE id = '%u'",account);
496
497        if(resultLogin)
498        {
499            Field *fieldsLogin = resultLogin->Fetch();
500            zprintf("|%15s| %20s | %15s |%4d|%5d|\r\n",
501                fieldsLogin[0].GetString(),name.c_str(),fieldsLogin[1].GetString(),fieldsLogin[2].GetUInt32(),fieldsLogin[3].GetUInt32());
502
503            delete resultLogin;
504        }
505        else
506            zprintf("|<Error>        | %20s |<Error>          |<Er>|<Err>|\r\n",name.c_str());
507
508    }while(resultDB->NextRow());
509
510    delete resultDB;
511
512    zprintf("=====================================================================\r\n");
513}
514
515/// Display a list of banned accounts and ip addresses
516void CliBanList(char*,pPrintf zprintf)
517{
518    bool found = false;
519    ///- Get the list of banned accounts and display them
520    QueryResult *result = loginDatabase.Query("SELECT id,username FROM account WHERE id IN (SELECT id FROM account_banned WHERE active = 1)");
521    if(result)
522    {
523        found = true;
524
525        zprintf("Currently Banned Accounts:\r\n");
526        zprintf("===============================================================================\r\n");
527        zprintf("|    Account    |   BanDate    |   UnbanDate  |  Banned By    |   Ban Reason  |\r\n");
528        do
529        {
530            zprintf("-------------------------------------------------------------------------------\r\n");
531            Field *fields = result->Fetch();
532            // No SQL injection. id is uint32.
533            QueryResult *banInfo = loginDatabase.PQuery("SELECT bandate,unbandate,bannedby,banreason FROM account_banned WHERE id = %u AND active = 1 ORDER BY unbandate", fields[0].GetUInt32());
534            if (banInfo)
535            {
536                Field *fields2 = banInfo->Fetch();
537                do
538                {
539                    time_t t_ban = fields2[0].GetUInt64();
540                    tm* aTm_ban = localtime(&t_ban);
541                    zprintf("|%-15.15s|", fields[1].GetString());
542                    zprintf("%02d-%02d-%02d %02d:%02d|", aTm_ban->tm_year%100, aTm_ban->tm_mon+1, aTm_ban->tm_mday, aTm_ban->tm_hour, aTm_ban->tm_min);
543                    if ( fields2[0].GetUInt64() == fields2[1].GetUInt64() )
544                        zprintf("   permanent  |");
545                    else
546                    {
547                        time_t t_unban = fields2[1].GetUInt64();
548                        tm* aTm_unban = localtime(&t_unban);
549                        zprintf("%02d-%02d-%02d %02d:%02d|",aTm_unban->tm_year%100, aTm_unban->tm_mon+1, aTm_unban->tm_mday, aTm_unban->tm_hour, aTm_unban->tm_min);
550                    }
551                    zprintf("%-15.15s|%-15.15s|\r\n",fields2[2].GetString(),fields2[3].GetString());
552                }while ( banInfo->NextRow() );
553                delete banInfo;
554            }
555        }while( result->NextRow() );
556        zprintf("===============================================================================\r\n");
557        delete result;
558    }
559
560    ///- Get the list of banned IP addresses and display them
561    result = loginDatabase.Query( "SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned WHERE (bandate=unbandate OR unbandate>UNIX_TIMESTAMP()) ORDER BY unbandate" );
562    if(result)
563    {
564        found = true;
565
566        zprintf("Currently Banned IPs:\r\n");
567        zprintf("===============================================================================\r\n");
568        zprintf("|      IP       |   BanDate    |   UnbanDate  |  Banned By    |   Ban Reason  |\r\n");
569        do
570        {
571            zprintf("-------------------------------------------------------------------------------\r\n");
572            Field *fields = result->Fetch();
573            time_t t_ban = fields[1].GetUInt64();
574            tm* aTm_ban = localtime(&t_ban);
575            zprintf("|%-15.15s|", fields[0].GetString());
576            zprintf("%02d-%02d-%02d %02d:%02d|", aTm_ban->tm_year%100, aTm_ban->tm_mon+1, aTm_ban->tm_mday, aTm_ban->tm_hour, aTm_ban->tm_min);
577            if ( fields[1].GetUInt64() == fields[2].GetUInt64() )
578                zprintf("   permanent  |");
579            else
580            {
581                time_t t_unban = fields[2].GetUInt64();
582                tm* aTm_unban = localtime(&t_unban);
583                zprintf("%02d-%02d-%02d %02d:%02d|", aTm_unban->tm_year%100, aTm_unban->tm_mon+1, aTm_unban->tm_mday, aTm_unban->tm_hour, aTm_unban->tm_min);
584            }
585            zprintf("%-15.15s|%-15.15s|\r\n", fields[3].GetString(), fields[4].GetString());
586        }while( result->NextRow() );
587        zprintf("===============================================================================\r\n");
588        delete result;
589    }
590
591    if(!found)
592        zprintf("We do not have banned users\r\n");
593}
594
595/// Ban an IP address or a user account
596void CliBan(char*command,pPrintf zprintf)
597{
598    ///- Get the command parameter
599    char* type_str = strtok((char*)command, " ");
600    char* nameOrIP_str = strtok(NULL, " ");
601    char* duration_str = strtok(NULL," ");
602    char* reason_str = strtok(NULL,"");
603
604    if(!type_str||!nameOrIP_str||!duration_str||!reason_str)// ?!? input of single char "0"-"9" wouldn't detect when with: || !atoi(duration)
605    {
606        zprintf("Syntax: ban account|ip|character $AccountOrIpOrCharacter $duration[s|m|h|d] $reason \r\n");
607        return;
608    }
609
610    std::string type;
611    if(!consoleToUtf8(type_str,type))                       // convert from console encoding to utf8
612        return;
613
614    std::string nameOrIP;
615    if(!consoleToUtf8(nameOrIP_str,nameOrIP))               // convert from console encoding to utf8
616        return;
617
618    std::string duration;
619    if(!consoleToUtf8(duration_str,duration))               // convert from console encoding to utf8
620        return;
621
622    std::string reason;
623    if(!consoleToUtf8(reason_str,reason))                   // convert from console encoding to utf8
624        return;
625
626    switch (sWorld.BanAccount(type, nameOrIP, duration, reason, "Set by console."))
627    {
628        case BAN_SUCCESS:
629            if(atoi(duration_str)>0)
630                zprintf("%s is banned for %s. Reason: %s.\r\n",nameOrIP.c_str(),secsToTimeString(TimeStringToSecs(duration_str),true,false).c_str(),reason.c_str());
631            else
632                zprintf("%s is banned permanently. Reason: %s.\r\n",nameOrIP.c_str(),reason.c_str());
633            break;
634        case BAN_NOTFOUND:
635            zprintf("%s %s not found\r\n", type.c_str(), nameOrIP.c_str());
636            break;
637        case BAN_SYNTAX_ERROR:
638            zprintf("Syntax: ban account|ip|character $AccountOrIpOrCharacter $duration[s|m|h|d] $reason \r\n");
639            break;
640    }
641}
642
643/// Display %MaNGOS version
644void CliVersion(char*,pPrintf zprintf)
645{
646                                                            //<--maybe better append to info cmd
647    zprintf( "%s (world-daemon)\r\n", _FULLVERSION );
648}
649
650/// Unban an IP adress or a user account
651void CliRemoveBan(char *command,pPrintf zprintf)
652{
653    ///- Get the command parameter
654    char *type_str = strtok(command," ");
655    char *nameorip_str = strtok(NULL," ");
656    if(!nameorip_str||!type_str)
657    {
658        zprintf("Syntax is: unban account|ip|character $nameorip\r\n");
659        return;
660    }
661
662    std::string type;
663    if(!consoleToUtf8(type_str,type))                       // convert from console encoding to utf8
664        return;
665
666    std::string nameorip;
667    if(!consoleToUtf8(nameorip_str,nameorip))               // convert from console encoding to utf8
668        return;
669
670    if (!sWorld.RemoveBanAccount(type, nameorip))
671        zprintf("%s %s not found\r\n", type.c_str(), nameorip.c_str());
672    else
673        zprintf("We removed ban from %s: %s\r\n",type_str,nameorip.c_str());
674}
675
676/// Display the list of GMs
677void CliListGM(char*,pPrintf zprintf)
678{
679
680    ///- Get the accounts with GM Level >0
681    Field *fields;
682
683    QueryResult *result = loginDatabase.Query( "SELECT username,gmlevel FROM account WHERE gmlevel > 0" );
684    if(result)
685    {
686
687        zprintf("Current gamemasters:\r\n");
688        zprintf("========================\r\n");
689        zprintf("|    Account    |  GM  |\r\n");
690        zprintf("========================\r\n");
691
692        ///- Circle through them. Display username and GM level
693        do
694        {
695            fields = result->Fetch();
696            zprintf("|%15s|", fields[0].GetString());
697            zprintf("%6s|\r\n",fields[1].GetString());
698        }while( result->NextRow() );
699
700        zprintf("========================\r\n");
701        delete result;
702    }
703    else
704    {
705        zprintf("No gamemasters\r\n");
706    }
707}
708
709/// Set the GM level of an account
710void CliSetGM(char *command,pPrintf zprintf)
711{
712    ///- Get the command line arguments
713    char *szAcc = strtok(command," ");
714    char *szLevel =  strtok(NULL," ");
715
716    if(!szAcc||!szLevel)                                    //wrong syntax 'setgm' without name
717    {
718        zprintf("Syntax is: setgm $account $number (0 - normal, 3 - gamemaster)>\r\n");
719        return;
720    }
721
722    //wow it's ok,let's hope it was integer given
723    int lev=atoi(szLevel);                                  //get int anyway (0 if error)
724
725    std::string safe_account_name;
726    if(!consoleToUtf8(szAcc,safe_account_name))             // convert from console encoding to utf8
727        return;
728
729    ///- Convert Account name to Upper Format
730    AccountMgr::normilizeString(safe_account_name);
731
732    ///- Escape the account name to allow quotes in names
733    loginDatabase.escape_string(safe_account_name);
734
735    ///- Try to find the account, then update the GM level
736    // No SQL injection (account name is escaped)
737    QueryResult *result = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",safe_account_name.c_str());
738
739    if (result)
740    {
741        Field *fields = result->Fetch();
742        uint32 account_id = fields[0].GetUInt32();
743        delete result;
744
745        WorldSession* session = sWorld.FindSession(account_id);
746        if(session)
747            session->SetSecurity(lev);
748
749        // No SQL injection (account name is escaped)
750        loginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE username = '%s'",lev,safe_account_name.c_str());
751        zprintf("We set %s gmlevel %d\r\n",safe_account_name.c_str(),lev);
752    }
753    else
754    {
755        zprintf("No account %s found\r\n",safe_account_name.c_str());
756    }
757}
758
759/// Set password for account
760void CliSetPassword(char *command,pPrintf zprintf)
761{
762    ///- Get the command line arguments
763    char *szAcc = strtok(command," ");
764    char *szPassword1 =  strtok(NULL," ");
765    char *szPassword2 =  strtok(NULL," ");
766
767    if(!szAcc||!szPassword1 || !szPassword2)
768    {
769        zprintf("Syntax is: setpass $account $password $password\r\n");
770        return;
771    }
772
773    std::string account_name;
774    if(!consoleToUtf8(szAcc,account_name))                  // convert from console encoding to utf8
775        return;
776
777    std::string pass1;
778    if(!consoleToUtf8(szPassword1,pass1))                   // convert from console encoding to utf8
779        return;
780
781    std::string pass2;
782    if(!consoleToUtf8(szPassword2,pass2))                   // convert from console encoding to utf8
783        return;
784
785    uint32 acc_id = accmgr.GetId(szAcc);
786    if (!acc_id)
787    {
788        zprintf("Account '%s' does not exist!\r\n", account_name.c_str());
789        return;
790    }
791
792    if (pass1 != pass2)
793    {
794        zprintf("Password does not match the confirm password, password not changed!\r\n");
795        return;
796    }
797
798    AccountOpResult result = accmgr.ChangePassword(acc_id, pass1);
799
800    switch(result)
801    {
802        case AOR_OK:
803            zprintf("The password was changed for account '%s' (ID: %u).\r\n",account_name.c_str(),acc_id);
804            break;
805        case AOR_PASS_TOO_LONG:
806            zprintf("Password can't be longer than 16 characters (client limit), password not changed!\r\n");
807            break;
808        case AOR_NAME_NOT_EXIST:
809            zprintf("Account '%s' does not exist!\r\n", account_name.c_str());
810            break;
811        case AOR_DB_INTERNAL_ERROR:
812            zprintf("Password not changed! (probably sql file format was updated)\r\n");
813            break;
814        default:
815            zprintf("Password not changed! (unknown error\r\n");
816            break;
817    }
818}
819
820/// Create an account
821void CliCreate(char *command,pPrintf zprintf)
822{
823    //I see no need in this function (why would an admin personally create accounts
824    //instead of using account registration page or accessing db directly?)
825    //but still let it be
826
827    ///- %Parse the command line arguments
828    char *szAcc = strtok(command, " ");
829    char *szPassword = strtok(NULL, " ");
830    if(!szAcc || !szPassword)
831    {
832        zprintf("Syntax is: create $username $password\r\n");
833        return;
834    }
835
836    std::string account_name;
837    if(!consoleToUtf8(szAcc,account_name))                   // convert from console encoding to utf8
838        return;
839
840    std::string password;
841    if(!consoleToUtf8(szPassword,password))                 // convert from console encoding to utf8
842        return;
843
844    AccountOpResult result = accmgr.CreateAccount(account_name, password);
845    switch(result)
846    {
847        case AOR_OK:
848            zprintf("User %s with password %s created successfully\r\n",account_name.c_str(),password.c_str());
849            break;
850        case AOR_NAME_TOO_LONG:
851            zprintf("Username %s is too long\r\n", account_name.c_str());
852            break;
853        case AOR_NAME_ALREDY_EXIST:
854            zprintf("User %s already exists\r\n",account_name.c_str());
855            break;
856        case AOR_DB_INTERNAL_ERROR:
857            zprintf("User %s with password %s NOT created (probably sql file format was updated)\r\n",account_name.c_str(),password.c_str());
858            break;
859        default:
860            zprintf("User %s with password %s NOT created (unknown error)\r\n",account_name.c_str(),password.c_str());
861            break;
862    }
863}
864
865/// Command parser and dispatcher
866void ParseCommand( pPrintf zprintf, char* input)
867{
868    unsigned int x;
869    bool bSuccess=false;
870    if (!input)
871        return;
872
873    unsigned int l=strlen(input);
874    char *supposedCommand=NULL,* arguments=(char*)("");
875    if(l)
876    {
877        ///- Get the command and the arguments
878        supposedCommand = strtok(input," ");
879        if (supposedCommand)
880        {
881            if (l>strlen(supposedCommand))
882                arguments=&input[strlen(supposedCommand)+1];
883
884            ///- Circle through the command table and, if found, put the command in the queue
885            for ( x=0;x<CliTotalCmds;x++)
886                if(!strcmp(Commands[x].cmd,supposedCommand))
887            {
888                sWorld.QueueCliCommand(new CliCommandHolder(&Commands[x], arguments, zprintf));
889                bSuccess=true;
890                break;
891            }
892
893            ///- Display an error message if the command is unknown
894            if(x==CliTotalCmds)
895                zprintf("Unknown command: %s\r\n", input);
896        }
897    }
898    if (!bSuccess)
899        zprintf("mangos>");
900}
901
902/// Kick a character out of the realm
903void CliKick(char*command,pPrintf zprintf)
904{
905    char *kickName = strtok(command, " ");
906
907    if (!kickName)
908    {
909        zprintf("Syntax is: kick $charactername\r\n");
910        return;
911    }
912
913    std::string name;
914    if(!consoleToUtf8(kickName,name))                       // convert from console encoding to utf8
915        return;
916   
917    if(!normalizePlayerName(name))
918        return;
919
920    sWorld.KickPlayer(name);
921}
922
923/// Teleport a character to location
924void CliTele(char*command,pPrintf zprintf)
925{
926    char *charName = strtok(command, " ");
927    char *locName = strtok(NULL, " ");
928
929    if (!charName || !locName)
930    {
931        zprintf("Syntax is: tele $charactername $location\r\n");
932        return;
933    }
934
935    std::string name = charName;
936    if(!consoleToUtf8(charName,name))                       // convert from console encoding to utf8
937        return;
938   
939    if(!normalizePlayerName(name))
940        return;
941
942    std::string location;
943    if(!consoleToUtf8(locName,location))                    // convert from console encoding to utf8
944        return;
945
946    WorldDatabase.escape_string(location);
947    QueryResult *result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM game_tele WHERE name = '%s'",location.c_str());
948    if (!result)
949    {
950        zprintf(objmgr.GetMangosStringForDBCLocale(LANG_COMMAND_TELE_NOTFOUND),"\r\n");
951        return;
952    }
953
954    Field *fields = result->Fetch();
955    float x = fields[0].GetFloat();
956    float y = fields[1].GetFloat();
957    float z = fields[2].GetFloat();
958    float ort = fields[3].GetFloat();
959    int mapid = fields[4].GetUInt16();
960    delete result;
961
962    if(!MapManager::IsValidMapCoord(mapid,x,y,z,ort))
963    {
964        zprintf(objmgr.GetMangosStringForDBCLocale(LANG_INVALID_TARGET_COORD),"\r\n",x,y,mapid);
965        return;
966    }
967
968    Player *chr = objmgr.GetPlayer(name.c_str());
969    if (chr)
970    {
971
972        if(chr->IsBeingTeleported()==true)
973        {
974            zprintf(objmgr.GetMangosStringForDBCLocale(LANG_IS_TELEPORTED),"\r\n",chr->GetName());
975            return;
976        }
977
978        if(chr->isInFlight())
979        {
980            zprintf(objmgr.GetMangosStringForDBCLocale(LANG_CHAR_IN_FLIGHT),"\r\n",chr->GetName());
981            return;
982        }
983
984        zprintf(objmgr.GetMangosStringForDBCLocale(LANG_TELEPORTING_TO),"\r\n",chr->GetName(),"", location.c_str());
985
986        chr->SaveRecallPosition();
987
988        chr->TeleportTo(mapid,x,y,z,chr->GetOrientation());
989    }
990    else if (uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str()))
991    {
992        zprintf(objmgr.GetMangosStringForDBCLocale(LANG_TELEPORTING_TO),"\r\n",name.c_str(), objmgr.GetMangosStringForDBCLocale(LANG_OFFLINE), location.c_str());
993        Player::SavePositionInDB(mapid,x,y,z,ort,MapManager::Instance().GetZoneId(mapid,x,y),guid);
994    }
995    else
996        zprintf(objmgr.GetMangosStringForDBCLocale(LANG_NO_PLAYER),"\r\n",name.c_str());
997}
998
999/// Display/Define the 'Message of the day' for the realm
1000void CliMotd(char*command,pPrintf zprintf)
1001{
1002
1003    if (strlen(command) == 0)
1004    {
1005        zprintf("Current Message of the day: \r\n%s\r\n", sWorld.GetMotd());
1006        return;
1007    }
1008    else
1009    {
1010        std::string commandUtf8;
1011        if(!consoleToUtf8(command,commandUtf8))             // convert from console encoding to utf8
1012            return;
1013
1014        sWorld.SetMotd(commandUtf8);
1015        zprintf("Message of the day changed to:\r\n%s\r\n", commandUtf8.c_str());
1016    }
1017}
1018
1019/// Comment me
1020/// \todo What is CorpsesErase for?
1021void CliCorpses(char*,pPrintf)
1022{
1023    CorpsesErase();
1024}
1025
1026/// Set the level of logging
1027void CliSetLogLevel(char*command,pPrintf zprintf)
1028{
1029    char *NewLevel = strtok(command, " ");
1030    if (!NewLevel)
1031    {
1032        zprintf("Syntax is: setloglevel $loglevel\r\n");
1033        return;
1034    }
1035    sLog.SetLogLevel(NewLevel);
1036}
1037
1038/// Display the server uptime
1039void CliUpTime(char*,pPrintf zprintf)
1040{
1041    uint32 uptime = sWorld.GetUptime();
1042    std::string suptime = secsToTimeString(uptime,true,(uptime > 86400));
1043    zprintf("Server has been up for: %s\r\n", suptime.c_str());
1044}
1045
1046/// Set/Unset the TBC flag for an account
1047void CliSetTBC(char *command,pPrintf zprintf)
1048{
1049    ///- Get the command line arguments
1050    char *szAcc = strtok(command," ");
1051    char *szTBC =  strtok(NULL," ");
1052
1053    if(!szAcc||!szTBC)
1054    {
1055        zprintf("Syntax is: setbc $account $number (0 - normal, 1 - tbc)>\r\n");
1056        return;
1057    }
1058
1059    int lev=atoi(szTBC);                                    //get int anyway (0 if error)
1060
1061    if((lev > 1)|| (lev < 0))
1062    {
1063        zprintf("Syntax is: setbc $account $number (0 - normal, 1 - tbc)>\r\n");
1064        return;
1065    }
1066
1067    ///- Escape the account name to allow quotes in names
1068    std::string safe_account_name;
1069    if(!consoleToUtf8(szAcc,safe_account_name))             // convert from console encoding to utf8
1070        return;
1071
1072    ///- Convert Account name to Upper Format
1073    AccountMgr::normilizeString(safe_account_name);
1074
1075    ///- Escape the account name to allow quotes in names
1076    loginDatabase.escape_string(safe_account_name);
1077
1078    // No SQL injection (account name is escaped)
1079    QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE username = '%s'",safe_account_name.c_str());
1080
1081    if (result)
1082    {
1083        // No SQL injection (account name is escaped)
1084        loginDatabase.PExecute("UPDATE account SET tbc = '%d' WHERE username = '%s'",lev,safe_account_name.c_str());
1085        zprintf("We set %s to expansion allowed %d\r\n",safe_account_name.c_str(),lev);
1086
1087        delete result;
1088    }
1089    else
1090    {
1091        zprintf("No account %s found\r\n",safe_account_name.c_str());
1092    }
1093}
1094
1095/// Save all players
1096void CliSave(char*,pPrintf zprintf)
1097{
1098    ///- Save players
1099    ObjectAccessor::Instance().SaveAllPlayers();
1100    zprintf( objmgr.GetMangosStringForDBCLocale(LANG_PLAYERS_SAVED) );
1101
1102    ///- Send a message
1103    sWorld.SendWorldText(LANG_PLAYERS_SAVED);
1104}
1105
1106/// Send a message to a player in game
1107void CliSend(char *playerN,pPrintf zprintf)
1108{
1109    ///- Get the command line arguments
1110    char* name_str = strtok((char*)playerN, " ");
1111    char* msg_str = strtok(NULL, "");
1112
1113    if(!name_str || !msg_str)
1114    {
1115        zprintf("Syntax: send $player $message (Player name is case sensitive)\r\n");
1116        return;
1117    }
1118
1119    std::string name;
1120    if(!consoleToUtf8(name_str,name))                       // convert from console encoding to utf8
1121        return;
1122
1123    std::string msg;
1124    if(!consoleToUtf8(msg_str,msg))             // convert from console encoding to utf8
1125        return;
1126
1127    if(!normalizePlayerName(name))
1128    {
1129        zprintf("Syntax: send $player $message (Player name is case sensitive)\r\n");
1130        return;
1131    }
1132
1133    ///- Find the player and check that he is not logging out.
1134    Player *rPlayer = objmgr.GetPlayer(name.c_str());
1135    if(!rPlayer)
1136    {
1137        zprintf("Player %s not found!\r\n", name.c_str());
1138        return;
1139    }
1140
1141    if (rPlayer->GetSession()->isLogingOut())
1142    {
1143        zprintf("Cannot send message while player %s is logging out!\r\n",name.c_str());
1144        return;
1145    }
1146
1147    ///- Send the message
1148    //Use SendAreaTriggerMessage for fastest delivery.
1149    rPlayer->GetSession()->SendAreaTriggerMessage("%s", msg.c_str());
1150    rPlayer->GetSession()->SendAreaTriggerMessage("|cffff0000[Message from administrator]:|r");
1151
1152    //Confirmation message
1153    zprintf("Message '%s' sent to %s\r\n",msg.c_str(), name.c_str());
1154}
1155
1156void CliPLimit(char *args,pPrintf zprintf)
1157{
1158    if(*args)
1159    {
1160        char* param = strtok((char*)args, " ");
1161        if(!param || !*param)
1162            return;
1163
1164        int l = strlen(param);
1165
1166        if(     strncmp(param,"player",l) == 0 )
1167            sWorld.SetPlayerLimit(-SEC_PLAYER);
1168        else if(strncmp(param,"moderator",l) == 0 )
1169            sWorld.SetPlayerLimit(-SEC_MODERATOR);
1170        else if(strncmp(param,"gamemaster",l) == 0 )
1171            sWorld.SetPlayerLimit(-SEC_GAMEMASTER);
1172        else if(strncmp(param,"administrator",l) == 0 )
1173            sWorld.SetPlayerLimit(-SEC_ADMINISTRATOR);
1174        else if(strncmp(param,"reset",l) == 0 )
1175            sWorld.SetPlayerLimit(sConfig.GetIntDefault("PlayerLimit", DEFAULT_PLAYER_LIMIT));
1176        else
1177        {
1178            int val = atoi(param);
1179            if(val < -SEC_ADMINISTRATOR) val = -SEC_ADMINISTRATOR;
1180
1181            sWorld.SetPlayerLimit(val);
1182        }
1183
1184        // kick all low security level players
1185        if(sWorld.GetPlayerAmountLimit() > SEC_PLAYER)
1186            sWorld.KickAllLess(sWorld.GetPlayerSecurityLimit());
1187    }
1188
1189    uint32 pLimit = sWorld.GetPlayerAmountLimit();
1190    AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit();
1191    char const* secName = "";
1192    switch(allowedAccountType)
1193    {
1194        case SEC_PLAYER:        secName = "Player";        break;
1195        case SEC_MODERATOR:     secName = "Moderator";     break;
1196        case SEC_GAMEMASTER:    secName = "Gamemaster";    break;
1197        case SEC_ADMINISTRATOR: secName = "Administrator"; break;
1198        default:                secName = "<unknown>";     break;
1199    }
1200
1201    zprintf("Player limits: amount %u, min. security level %s.\r\n",pLimit,secName);
1202}
1203
1204/// @}
1205
1206#ifdef linux
1207// Non-blocking keypress detector, when return pressed, return 1, else always return 0
1208int kb_hit_return()
1209{
1210    struct timeval tv;
1211    fd_set fds;
1212    tv.tv_sec = 0;
1213    tv.tv_usec = 0;
1214    FD_ZERO(&fds);
1215    FD_SET(STDIN_FILENO, &fds);
1216    select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
1217    return FD_ISSET(STDIN_FILENO, &fds);
1218}
1219#endif
1220
1221/// %Thread start
1222void CliRunnable::run()
1223{
1224    ///- Init new SQL thread for the world database (one connection call enough)
1225    WorldDatabase.ThreadStart();                                // let thread do safe mySQL requests
1226
1227    char commandbuf[256];
1228
1229    ///- Display the list of available CLI functions then beep
1230    sLog.outString();
1231    /// \todo Shoudn't we use here also the sLog singleton?
1232    CliHelp(NULL,&UTF8ZPRINTF);
1233
1234    if(sConfig.GetBoolDefault("BeepAtStart", true))
1235    {
1236        printf("\a");                                       // \a = Alert
1237    }
1238
1239    // print this here the first time
1240    // later it will be printed after command queue updates
1241    printf("mangos>");
1242
1243    ///- As long as the World is running (no World::m_stopEvent), get the command line and handle it
1244    while (!World::m_stopEvent)
1245    {
1246        fflush(stdout);
1247        #ifdef linux
1248        while (!kb_hit_return() && !World::m_stopEvent)
1249            // With this, we limit CLI to 10commands/second
1250            usleep(100);
1251        if (World::m_stopEvent)
1252            break;
1253        #endif
1254        char *command = fgets(commandbuf,sizeof(commandbuf),stdin);
1255        if (command != NULL)
1256        {
1257            for(int x=0;command[x];x++)
1258                if(command[x]=='\r'||command[x]=='\n')
1259            {
1260                command[x]=0;
1261                break;
1262            }
1263            //// \todo Shoudn't we use here also the sLog singleton?
1264            ParseCommand(&UTF8ZPRINTF,command);
1265        }
1266        else if (feof(stdin))
1267        {
1268            World::m_stopEvent = true;
1269        }
1270    }
1271
1272    ///- End the database thread
1273    WorldDatabase.ThreadEnd();                                  // free mySQL thread resources
1274}
Note: See TracBrowser for help on using the browser.