diff --git a/qw/include/server.h b/qw/include/server.h index 1fb457728..75c5c3ded 100644 --- a/qw/include/server.h +++ b/qw/include/server.h @@ -223,7 +223,9 @@ typedef struct client_s // getting kicked off by the server operator // a program error, like an overflowed reliable buffer -//============================================================================= +extern qboolean rcon_from_user; // current command is a from a user + +//============================================================================ #define STATFRAMES 100 diff --git a/qw/source/sv_ccmds.c b/qw/source/sv_ccmds.c index bde33fa09..477547ac2 100644 --- a/qw/source/sv_ccmds.c +++ b/qw/source/sv_ccmds.c @@ -484,11 +484,8 @@ SV_Status_f (void) Con_Printf ("\n"); } -/* - SV_ConSay_f -*/ void -SV_ConSay_f (void) +SV_ConSay (const char *prefix) { client_t *client; int j; @@ -498,23 +495,45 @@ SV_ConSay_f (void) if (Cmd_Argc () < 2) return; - strcpy (text, "console: "); p = Cmd_Args (); - if (*p == '"') { p++; p[strlen (p) - 1] = 0; } - - strncat (text, p, sizeof (text) - strlen (text)); + strcpy (text, prefix); // bold header + strcat (text, "\x8d "); // and arrow + j = strlen (text); + strncat (text, p, sizeof (text) - j); + while (text[j]) + text[j++] |= 0x80; // non-bold text for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { - if (client->state != cs_spawned) + if (client->state != cs_spawned) // kk just has !client->state continue; - SV_ClientPrintf (client, PRINT_CHAT, "%s\n", text); + SV_ClientPrintf (client, PRINT_HIGH, "%s\n", text); + if (*prefix != 'I') // beep, except for Info says + SV_ClientPrintf(client, PRINT_CHAT, "%s", ""); } } +/* + SV_ConSay_f +*/ +void +SV_ConSay_f (void) +{ + if (rcon_from_user) + SV_ConSay("Admin"); + else + SV_ConSay("Console"); +} + +void +SV_ConSay_Info_f (void) +{ + SV_ConSay("Info"); +} + /* SV_Heartbeat_f @@ -833,7 +852,6 @@ SV_InitOperatorCommands (void) "setmaster 192.246.40.12:27002\n" "setmaster 192.246.40.12:27002 192.246.40.12:27004"); - Cmd_AddCommand ("say", SV_ConSay_f, "Say something to everyone on the server, will show up as the name 'console' in game"); Cmd_AddCommand ("heartbeat", SV_Heartbeat_f, "Force a heartbeat to be sent to the master server.\n" "A heartbeat tells the Master the server's IP address and that it is still alive."); Cmd_AddCommand ("quit", SV_Quit_f, "Shut down the server"); @@ -880,8 +898,16 @@ SV_InitOperatorCommands (void) "(floodprot (number of messages) (number of seconds) (silence time in seconds))"); Cmd_AddCommand ("floodprotmsg", SV_Floodprotmsg_f, "Sets the message displayed after flood protection is invoked (floodprotmsg message)"); + Cmd_AddCommand ("maplist", COM_Maplist_f, "List all maps on the server"); + Cmd_AddCommand ("say", SV_ConSay_f, "Say something to everyone on the server, will show up as the name 'Console' (or 'Admin') in game"); + Cmd_AddCommand ("sayinfo", SV_ConSay_Info_f, "Say something to everyone on the server, will show up as the name 'Info' in game"); + //XXX Cmd_AddCommand ("cuff", SV_Cuff_f); + //XXX Cmd_AddCommand ("mute", SV_Mute_f); + //XXX Cmd_AddCommand ("tell", SV_Tell_f); + //XXX Cmd_AddCommand ("ban", SV_Ban_f); + cl_warncmd = Cvar_Get ("cl_warncmd", "1", CVAR_NONE, NULL, "Toggles the display of error messages for unknown commands"); diff --git a/qw/source/sv_main.c b/qw/source/sv_main.c index 71e252e4b..872734a5f 100644 --- a/qw/source/sv_main.c +++ b/qw/source/sv_main.c @@ -63,6 +63,8 @@ quakeparms_t host_parms; qboolean host_initialized; // true if into command execution +qboolean rcon_from_user; + double sv_frametime; double realtime; // without any filtering or bounding @@ -97,6 +99,7 @@ cvar_t *zombietime; // seconds to sink messages after // disconnect cvar_t *rcon_password; // password for remote server +cvar_t *admin_password; // password for admin // commands @@ -873,17 +876,36 @@ SVC_DirectConnect (void) } int -Rcon_Validate (void) +Rcon_Validate (cvar_t *pass) { - if (!strlen (rcon_password->string)) + if (!strlen (pass->string)) return 0; - if (strcmp (Cmd_Argv (1), rcon_password->string)) + if (strcmp (Cmd_Argv (1), pass->string)) return 0; return 1; } +char * +Name_of_sender (void) +{ + int i; + client_t *cl; + + for (i=0, cl=svs.clients ; istate == cs_free) + continue; + if (!NET_CompareBaseAdr(net_from, cl->netchan.remote_address)) + continue; + if (cl->netchan.remote_address.port != net_from.port) + continue; + return cl->name; + } + return NULL; +} + + /* SVC_RemoteCommand @@ -897,20 +919,42 @@ SVC_RemoteCommand (void) int i; int len = 0; char remaining[1024]; + char *name; + qboolean do_cmd = false; + qboolean admin_cmd = false; if (CheckForFlood (FLOOD_RCON)) return; - if (!Rcon_Validate ()) { - Con_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (net_from), - net_message->message->data + 4); - - SV_BeginRedirect (RD_PACKET); - - Con_Printf ("Bad rcon_password.\n"); - - } else { - + if (Rcon_Validate (rcon_password)) { + do_cmd = true; + } else if (Rcon_Validate (admin_password)) { + admin_cmd = true; + if (strcmp(Cmd_Argv(2), "say") == 0 + || strcmp(Cmd_Argv(2), "kick") == 0 + || strcmp(Cmd_Argv(2), "ban") == 0 + || strcmp(Cmd_Argv(2), "mute") == 0 + || strcmp(Cmd_Argv(2), "cuff") == 0 + || strcmp(Cmd_Argv(2), "exec") == 0 + || strcmp(Cmd_Argv(2), "addip") == 0 + || strcmp(Cmd_Argv(2), "listip") == 0 + || strcmp(Cmd_Argv(2), "writeip") == 0 + || strcmp(Cmd_Argv(2), "removeip") == 0) + do_cmd = true; + } + if ((name = Name_of_sender())) { // log issuer + if (do_cmd && admin_cmd) { + SV_BroadcastPrintf (PRINT_HIGH, "Admin %s issued %s command:\n", + name, Cmd_Argv(2)); + } else if (admin_cmd) { + Con_Printf ("Admin %s issued %s command:\n", name, Cmd_Argv(2)); + } else { + Con_Printf("User %s %s rcon command:\n", name, + do_cmd? "issued":"attempted"); + } + } + + if (do_cmd) { remaining[0] = 0; for (i = 2; i < Cmd_Argc (); i++) { @@ -923,9 +967,20 @@ SVC_RemoteCommand (void) NET_AdrToString (net_from), remaining); SV_BeginRedirect (RD_PACKET); - + if (name) + rcon_from_user = true; Cmd_ExecuteString (remaining, src_command); + rcon_from_user = false; + } else { + Con_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (net_from), + net_message->message->data + 4); + SV_BeginRedirect (RD_PACKET); + if (admin_cmd) { + Con_Printf("Command not valid with admin password.\n"); + } else { + Con_Printf ("Bad rcon_password.\n"); + } } SV_EndRedirect (); @@ -1524,7 +1579,8 @@ SV_InitLocal (void) SV_UserInit (); - rcon_password = Cvar_Get ("rcon_password", "", CVAR_NONE, NULL, "Set the password for rcon commands"); + rcon_password = Cvar_Get ("rcon_password", "", CVAR_NONE, NULL, "Set the password for rcon 'root' commands"); + admin_password = Cvar_Get ("admin_password", "", CVAR_NONE, NULL, "Set the password for rcon admin commands"); password = Cvar_Get ("password", "", CVAR_NONE, NULL, "Set the server password for players"); spectator_password = Cvar_Get ("spectator_password", "", CVAR_NONE, NULL, "Set the spectator password");