Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
NetAdminServer.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: NetAdminServer.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  HTTP Servlet Engine for Multiplayer Admin
13 */
14 
15 
16 #include "MemDebug.h"
17 #include "NetAdminServer.h"
18 #include "NetLobbyServer.h"
19 #include "NetServerConfig.h"
20 #include "NetClientConfig.h"
21 #include "NetAdminChat.h"
22 #include "NetUser.h"
23 #include "NetChat.h"
24 
25 #include "StarServer.h"
26 #include "HttpServlet.h"
27 #include "NetLayer.h"
28 
29 #include "DataLoader.h"
30 #include "FormatUtil.h"
31 #include "MachineInfo.h"
32 
33 extern const char* versionInfo;
34 
35 // +-------------------------------------------------------------------+
36 // +-------------------------------------------------------------------+
37 // +-------------------------------------------------------------------+
38 
40 {
41 public:
43  virtual ~NetAdminLogin() { }
44 
45  virtual bool DoGet(HttpRequest& request, HttpResponse& response) {
47 
48  Text admin_name = "system";
49  Text admin_pass = "manager";
50 
51  if (config) {
52  admin_name = config->GetAdminName();
53  admin_pass = config->GetAdminPass();
54  }
55 
56  Text name = request.GetParam("user");
57  Text pass = request.GetParam("pass");
58 
59  Sleep(500);
60 
61  if (CheckUser(request, response)) {
63  response.SetHeader("MIME-Version", "1.0");
64  response.SetHeader("Content-Type", "text/html");
65  response.SetHeader("Cache-Control", "no-cache");
66  response.SetHeader("Expires", "-1");
67  response.SetHeader("Location", "/home");
68 
69  response.SetContent(GetHead("Login") +
70  "<body><br>You are already logged in.<br>" +
71  GetBodyClose());
72  }
73 
74  else if (name == admin_name && pass == admin_pass) {
75  user = new(__FILE__,__LINE__) NetUser(name);
76  user->SetAddress(request.GetClientAddr());
77 
78  if (session)
80 
81  admin->AddUser(user);
82 
84  response.SetHeader("MIME-Version", "1.0");
85  response.SetHeader("Content-Type", "text/html");
86  response.SetHeader("Cache-Control", "no-cache");
87  response.SetHeader("Expires", "-1");
88  response.SetHeader("Location", "/home");
89 
90  response.SetContent(GetHead("Login") +
91  "<body><br>You have successfully logged in.<br>" +
92  GetBodyClose());
93  }
94 
95  else {
97  response.SetHeader("MIME-Version", "1.0");
98  response.SetHeader("Content-Type", "text/html");
99  response.SetHeader("Cache-Control", "no-cache");
100  response.SetHeader("Expires", "-1");
101 
102  response.SetContent(GetHead("Login") +
103  GetTitleBar(0, "onLoad=\"self.focus();document.loginForm.user.focus();\"") +
104  GetContent() +
105  GetBodyClose());
106  }
107 
108  return true;
109  }
110 
111  virtual Text GetContent() { Text content =
112  " <table border=\"0\" cellspacing=\"0\" cellpadding=\"4\" align=\"left\" width =\"100%\">\n\
113  <tr>\n\
114  <td width=\"100\">&nbsp;&nbsp;</td>\n\
115  <td valign=\"top\" align=\"left\" width=\"400\"><br><br>\n\
116  <span class=\"subhead\">Welcome to the Starshatter Server!</span><br><br>\n\
117  <span class=\"std\">Login to access the server <b>";
118 
120  if (config)
121  content += config->Name();
122  else
123  content += "server";
124 
125  content += "</b></span><br>\n\
126  <form name=\"loginForm\" method=\"get\" action=\"/login\">\n\
127  <table border=\"0\" cellspacing=\"0\" cellpadding=\"4\" width=\"100\">\n\
128  <tr>\n\
129  <td align=\"left\" valign=\"middle\" width=\"80\"><span class=\"std\">Username:</span></td>\n\
130  <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"user\" size=\"25\"></td>\n\
131  </tr>\n\
132  <tr>\n\
133  <td align=\"left\" valign=\"middle\" width=\"80\"><span class=\"std\">Password:</span></td>\n\
134  <td align=\"left\" valign=\"middle\"><input type=\"password\" name=\"pass\" size=\"25\"></td>\n\
135  </tr>\n\
136  <tr>\n\
137  <td>&nbsp;</td>\n\
138  <td align=\"right\"><input type=\"submit\" value=\"Login\"></td>\n\
139  </tr>\n\
140  </table>\n\
141  </form>\n\
142  <td>&nbsp;</td>\n\
143  </tr>\n\
144 </table>\n";
145 
146  return content;
147  }
148 };
149 
150 // +-------------------------------------------------------------------+
151 // +-------------------------------------------------------------------+
152 // +-------------------------------------------------------------------+
153 
155 {
156 public:
158  virtual ~NetAdminServerMgr() { }
159 
160  virtual bool DoGet(HttpRequest& request, HttpResponse& response) {
161  if (CheckUser(request, response)) {
162  Text action = request.GetParam("action");
163  action.setSensitive(false);
164 
165  bool completed = false;
166 
167  if (action == "restart") {
169 
170  if (svr) {
171  svr->Shutdown(true);
172  completed = true;
173 
174  response.SetStatus(HttpResponse::SC_OK);
175  response.SetHeader("MIME-Version", "1.0");
176  response.SetHeader("Content-Type", "text/html");
177  response.SetHeader("Cache-Control", "no-cache");
178  response.SetHeader("Expires", "-1");
179 
180  response.SetContent(GetHead("Restart") +
181  GetTitleBar() +
182  "<div class=\"content\"><b>The Starshatter Server will restart in three (3) seconds.</b><br></div>" +
183  GetBodyClose());
184  }
185  }
186 
187  else if (action == "shutdown") {
189 
190  if (svr) {
191  svr->Shutdown(false);
192  completed = true;
193 
194  response.SetStatus(HttpResponse::SC_OK);
195  response.SetHeader("MIME-Version", "1.0");
196  response.SetHeader("Content-Type", "text/html");
197  response.SetHeader("Cache-Control", "no-cache");
198  response.SetHeader("Expires", "-1");
199 
200  response.SetContent(GetHead("Restart") +
201  GetTitleBar() +
202  "<div class=\"content\"><b>The Starshatter Server will shutdown in three (3) seconds.</b><br></div>" +
203  GetBodyClose());
204  }
205  }
206 
207  if (!completed) {
209  response.SetHeader("MIME-Version", "1.0");
210  response.SetHeader("Content-Type", "text/html");
211  response.SetHeader("Cache-Control", "no-cache");
212  response.SetHeader("Expires", "-1");
213  response.SetHeader("Location", "/home");
214 
215  response.SetContent(GetHead("Login") +
216  "<body><br>Unknown Action.<br>" +
217  GetBodyClose());
218  }
219  }
220 
221  return true;
222  }
223 };
224 
225 // +-------------------------------------------------------------------+
226 // +-------------------------------------------------------------------+
227 // +-------------------------------------------------------------------+
228 
230 {
231 public:
233  virtual ~NetAdminFile() { }
234 
235  virtual bool DoGet(HttpRequest& request, HttpResponse& response) {
236  if (!CheckUser(request, response))
237  return true;
238 
239  Text content;
240  Text path = request.GetParam("path");
241  Text name = request.GetParam("name");
242 
243  if (name.length()) {
244  BYTE* buffer = 0;
245  DataLoader* loader = DataLoader::GetLoader();
246 
247  if (loader) {
248  bool use_file_system = loader->IsFileSystemEnabled();
249 
250  loader->UseFileSystem(true);
251  loader->SetDataPath(path);
252  int len = loader->LoadBuffer(name, buffer);
253 
254  if (len) {
255  content = Text((const char*) buffer, len);
256  }
257 
258  loader->ReleaseBuffer(buffer);
259  loader->SetDataPath(0);
260  loader->UseFileSystem(use_file_system);
261  }
262  }
263 
264  response.SetStatus(HttpResponse::SC_OK);
265  response.AddHeader("MIME-Version", "1.0");
266  response.AddHeader("Cache-Control", "no-cache");
267  response.AddHeader("Expires", "-1");
268  response.AddHeader("Content-Type", "text/plain");
269  response.SetContent(content);
270 
271  return true;
272  }
273 };
274 
275 // +-------------------------------------------------------------------+
276 // +-------------------------------------------------------------------+
277 // +-------------------------------------------------------------------+
278 
280 {
281 public:
283  virtual ~NetAdminUserList() { }
284 
285  virtual bool DoGet(HttpRequest& request, HttpResponse& response) {
286  if (CheckUser(request, response)) {
287  response.SetStatus(HttpResponse::SC_OK);
288  response.SetHeader("MIME-Version", "1.0");
289  response.SetHeader("Content-Type", "text/html");
290  response.SetHeader("Cache-Control", "no-cache");
291  response.SetHeader("Expires", "-1");
292 
293  response.SetContent(GetHead("User List") +
294  GetTitleBar() +
295  GetContent() +
296  GetBodyClose());
297  }
298 
299  return true;
300  }
301 
302  virtual Text GetContent() {
303  Text content =
304  "<script LANGUAGE=\"JavaScript\">\n\
305 <!--\n\
306 function doConfirm() {\n\
307 return confirm(\"Are you sure you want to ban this player?\");\n\
308 }\n\
309 // -->\n\
310 </script>\n\
311 <div class=\"content\">\n\
312 <table border=\"0\" width=\"95%\">\n\
313  <tr class=\"heading\">\n\
314  <td nowrap valign=\"middle\" align=\"left\">\n\
315  <span class=\"heading\">&nbsp;User List</span>\n\
316  </td>\n\
317  </tr>\n\
318 </table>\n\n";
319 
320  content +=
321  " <table border=\"0\" width=\"95%\" class=\"std\">\n\
322  <tr>\n\
323  <td nowrap width=\"1%\">&nbsp;</td>\n\
324  <td nowrap width=\"20%\" valign=\"middle\" align=\"left\"><b>Name</b></td>\n\
325  <td nowrap width=\"10%\" valign=\"middle\" align=\"left\"><b>Address</b></td>\n\
326  <td nowrap width=\"10%\" valign=\"middle\" align=\"center\"><b>Is Host</b></td>\n\
327  <td nowrap width=\"20%\" valign=\"middle\" align=\"left\"><b>Squadron</b></td>\n\
328  <td nowrap width=\"20%\" valign=\"middle\" align=\"center\"><b>Stats</b></td>\n\
329  <td nowrap width=\"19%\" valign=\"middle\" align=\"center\"><b>Ban</b></td>\n\
330  <td></td>\n\
331 </tr>\n";
332 
334 
335  if (lobby) {
336  ListIter<NetUser> u_iter = lobby->GetUsers();
337  while (++u_iter) {
338  NetUser* u = u_iter.value();
339  NetAddr a = u->GetAddress();
340 
341  char addr_dotted[32];
342  char addr_hex[16];
343  char user_stats[16];
344 
345  sprintf_s(addr_dotted, "%d.%d.%d.%d", a.B1(), a.B2(), a.B3(), a.B4());
346  sprintf_s(addr_hex, "%08x", a.IPAddr());
347  sprintf_s(user_stats, "%d / %d / %d", u->Missions(), u->Kills(), u->Losses());
348 
349  content += "<tr>\n<td nowrap width=\"1%\">&nbsp;</td>\n\
350  <td nowrap valign=\"middle\" align=\"left\">";
351  content += u->Name();
352  content += "</td><td nowrap valign=\"middle\" align=\"left\">";
353  content += addr_dotted;
354  content += "</td><td nowrap valign=\"middle\" align=\"center\">";
355  content += u->IsHost() ? "*" : "&nbsp;";
356  content += "</td><td nowrap valign=\"middle\" align=\"left\">";
357  content += u->Squadron();
358  content += "</td><td nowrap valign=\"middle\" align=\"center\">";
359  content += user_stats;
360  content += "</td><td nowrap valign=\"middle\" align=\"center\">";
361  content += "<a onclick=\"return doConfirm()\" href=\"/ban?name=";
362  content += HttpRequest::EncodeParam(u->Name());
363  content += "&addr=";
364  content += addr_hex;
365  content += "\">BAN</a></td></tr>\n";
366  }
367  }
368 
369  content += " </table>\n\n";
370 
371  content += "</div>\n\n";
372  content += GetCopyright();
373  return content;
374  }
375 };
376 
377 // +-------------------------------------------------------------------+
378 // +-------------------------------------------------------------------+
379 // +-------------------------------------------------------------------+
380 
382 {
383 public:
385  virtual ~NetAdminBanUser() { }
386 
387  virtual bool DoGet(HttpRequest& request, HttpResponse& response) {
388  if (CheckUser(request, response)) {
389  Text name = request.GetParam("name");
390  bool completed = false;
391 
393 
394  if (lobby) {
395  ListIter<NetUser> u_iter = lobby->GetUsers();
396  while (++u_iter && !completed) {
397  NetUser* u = u_iter.value();
398 
399  if (u->Name() == name) {
401 
402  if (nls) {
403  nls->BanUser(u);
404  completed = true;
405  }
406  }
407  }
408  }
409 
411  response.SetHeader("MIME-Version", "1.0");
412  response.SetHeader("Content-Type", "text/html");
413  response.SetHeader("Cache-Control", "no-cache");
414  response.SetHeader("Expires", "-1");
415  response.SetHeader("Location", "/users");
416 
417  response.SetContent(GetHead("User List") +
418  GetTitleBar() +
419  "<div class=\"content\">User Banned.<br></div>" +
420  GetBodyClose());
421  }
422 
423  return true;
424  }
425 };
426 
427 // +-------------------------------------------------------------------+
428 // +-------------------------------------------------------------------+
429 // +-------------------------------------------------------------------+
430 
431 static NetAdminServer* net_Admin_server = 0;
432 
435 {
436  if (!net_Admin_server && port > 0)
437  net_Admin_server = new(__FILE__,__LINE__) NetAdminServer(port);
438 
439  return net_Admin_server;
440 }
441 
443 : HttpServletExec(port)
444 {
445  http_server_name = Text("Starshatter NetAdminServer ") + versionInfo;
446 }
447 
449 {
450  if (net_Admin_server == this)
451  net_Admin_server = 0;
452 }
453 
454 // +--------------------------------------------------------------------+
455 
458 {
459  Text path = request.URI();
460  path.setSensitive(false);
461 
462  if (path.indexOf("/login") == 0)
463  return new(__FILE__,__LINE__) NetAdminLogin;
464 
465  if (path.indexOf("/chat") == 0)
466  return new(__FILE__,__LINE__) NetAdminChat;
467 
468  if (path.indexOf("/server") == 0)
469  return new(__FILE__,__LINE__) NetAdminServerMgr;
470 
471  if (path.indexOf("/file") == 0)
472  return new(__FILE__,__LINE__) NetAdminFile;
473 
474  if (path.indexOf("/user") == 0)
475  return new(__FILE__,__LINE__) NetAdminUserList;
476 
477  if (path.indexOf("/ban") == 0)
478  return new(__FILE__,__LINE__) NetAdminBanUser;
479 
480  return new(__FILE__,__LINE__) NetAdminServlet;
481 }
482 
483 // +-------------------------------------------------------------------+
484 
485 void
486 NetAdminServer::AddChat(NetUser* user, const char* msg)
487 {
488  if (user && msg && *msg) {
490 
491  if (lobby)
492  lobby->AddChat(user, msg);
493  }
494 }
495 
498 {
500 
501  if (lobby)
502  return lobby->GetChat();
503 
504  static List<NetChatEntry> idle_chatter;
505  return idle_chatter;
506 }
507 
508 // +-------------------------------------------------------------------+
509 
510 void
512 {
513  if (user && !admin_users.contains(user))
514  admin_users.append(user);
515 }
516 
517 void
519 {
520  if (user) {
521  admin_users.remove(user);
522  delete user;
523  }
524 }
525 
526 int
528 {
529  return admin_users.size();
530 }
531 
532 
535 {
536  return admin_users;
537 }
538 
539 bool
541 {
542  bool result = false;
543 
545 
546  if (lobby)
547  result = lobby->HasHost();
548 
549  return result;
550 }
551 
552 NetUser*
554 {
556  while (++iter) {
557  NetUser* u = iter.value();
558  if (u->GetSessionID() == id)
559  return u;
560  }
561 
562  return 0;
563 }
564 
565 void
567 {
569  while (++iter) {
570  NetUser* u = iter.value();
571 
572  bool found = false;
573 
575  while (++s_iter && !found) {
576  HttpSession* s = s_iter.value();
577 
578  if (s->GetID() == u->GetSessionID())
579  found = true;
580  }
581 
582  if (!found)
583  delete iter.removeItem();
584  }
585 }
586 
587 
588 // +-------------------------------------------------------------------+
589 // +-------------------------------------------------------------------+
590 // +-------------------------------------------------------------------+
591 
593 {
595  user = 0;
596 }
597 
598 // +-------------------------------------------------------------------+
599 
600 bool
602 {
603  if (CheckUser(request, response)) {
604 
605  if (request.URI() == "/home")
606  response.SetStatus(HttpResponse::SC_OK);
607  else
609 
610  response.SetHeader("MIME-Version", "1.0");
611  response.SetHeader("Content-Type", "text/html");
612  response.SetHeader("Cache-Control", "no-cache");
613  response.SetHeader("Expires", "-1");
614  response.SetHeader("Location", "/home");
615 
616  response.SetContent(GetHead() +
618  GetContent() +
619  GetBodyClose());
620  }
621 
622  return true;
623 }
624 
625 // +-------------------------------------------------------------------+
626 
627 bool
629 {
630  if (!user) {
631  if (session)
633 
634  if (!user) {
636  response.SetHeader("MIME-Version", "1.0");
637  response.SetHeader("Content-Type", "text/plain");
638  response.SetHeader("Cache-Control", "no-cache");
639  response.SetHeader("Expires", "-1");
640  response.SetHeader("Location", "/login");
641  response.SetContent("You are not logged in.");
642  }
643  }
644 
645  return user != 0;
646 }
647 
648 // +-------------------------------------------------------------------+
649 
650 Text
652 {
653  return
654 
655  "body { font-family:arial,helvetica,sans-serif; color:black; background-color:white }\n\
656 a:link { text-decoration:none; font-weight:normal; font-size:10pt; color:black }\n\
657 a:visited { text-decoration:none; font-weight:normal; font-size:10pt; color:black }\n\
658 a:hover { text-decoration:underline; font-weight:normal; font-size:10pt; color:black }\n\
659 .std { font-size:10pt }\n\
660 .tiny { font-size:8pt }\n\
661 .heading { font-size:14pt; font-weight:bold; background-color:#99BBEE }\n\
662 .subhead { font-size:11pt; font-weight:bold }\n\
663 .status { font-size:10pt; color:white }\n\
664 .content { padding-right: 4pt; padding-left: 4pt; padding-bottom: 4pt; padding-top: 4pt; margin: 4pt; }\n\
665 .copy { font-size:8pt; }\n\
666 .top-bar { color: white; background-color: #336699 }\n\
667 .top-line { color: yellow; background-color: black }\n\
668 .topbarbig { line-height:24px; color:white; font-size:18px; font-weight:bold; }\n\
669 .topbarsmall { line-height:18px; color:white; font-size:14px; }\n";
670 }
671 
672 Text
673 NetAdminServlet::GetHead(const char* title)
674 {
675  Text head = "<html>\n<head>\n<title>Starshatter Server";
676 
677  if (title && *title) {
678  head += " - ";
679  head += title;
680  }
681 
682  head += "</title>\n<style type=\"text/css\" media=\"screen\">\n";
683  head += GetCSS();
684  head += "</style>\n</head>\n";
685 
686  return head;
687 }
688 
689 Text
691 {
692  return GetTitleBar(GetStatLine()) +
693  GetContent() +
694  GetBodyClose();
695 }
696 
697 Text
698 NetAdminServlet::GetTitleBar(const char* statline, const char* onload)
699 {
700  Text bar = "<body ";
701 
702  if (onload && *onload)
703  bar += onload;
704 
705  bar += " leftmargin=\"0\" topmargin=\"0\" marginwidth=\"0\" marginheight=\"0\">\n\
706 <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\" class=\"top-bar\">\n\
707  <tr height=\"50\">\n\
708  <td>&nbsp;</td>\n\
709  <td valign=\"middle\" align=\"left\">\n\
710  <span class=\"topbarsmall\">Administration Console</span><br>\n";
711 
712  if (statline) {
713  bar += "<a href=\"/home\">";
714  }
715 
716  bar += "<span class=\"topbarbig\">Starshatter Server ";
717  bar += versionInfo;
718  bar += "</span>";
719 
720  if (statline) {
721  bar += "</a>";
722  }
723 
724  bar += "\n\
725  </td>\n\
726  </tr>\n\
727  <tr class=\"top-line\">\n\
728  <td colspan=\"2\">";
729 
730  if (statline && *statline)
731  bar += statline;
732  else
733  bar += "&nbsp;";
734 
735  bar += "</td>\n\
736  </tr>\n\
737 </table>\n\n";
738 
739  return bar;
740 }
741 
742 Text
744 {
746 
747  Text line =
748  " <table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" border=\"0\">\n\
749  <tr>\n\
750  <td nowrap width=\"33%\" class=\"top-line\" align=\"left\">\n\
751  <span class=\"status\">&nbsp;&nbsp;Connected to <b>";
752 
753  char buffer[256];
754  sprintf_s(buffer, "%s:%d", config->Name().data(), config->GetAdminPort());
755  line += buffer;
756 
757  line += "</b></span>\n\
758  </td>\n\
759  <td nowrap width=\"34%\" class=\"top-line\" align=\"center\">\n\
760  <span class=\"status\">Server Mode: <b>";
761 
763  if (lobby) {
764  switch (lobby->GetStatus()) {
765  default:
766  case NetServerInfo::OFFLINE: line += "Offline"; break;
767  case NetServerInfo::LOBBY: line += "Lobby"; break;
768  case NetServerInfo::BRIEFING: line += "Briefing"; break;
769  case NetServerInfo::ACTIVE: line += "Active"; break;
770  case NetServerInfo::DEBRIEFING: line += "Debriefing"; break;
771  case NetServerInfo::PERSISTENT: line += "PERSISTENT"; break;
772  }
773  }
774  else {
775  line += "Unknown";
776  }
777 
778  line += "</b></span>\n\
779  </td>\n\
780  <td nowrap width=\"33%\" class=\"top-line\" align=\"right\">\n\
781  <span class=\"status\">";
782 
783  line += FormatTimeString();
784 
785  line += "&nbsp;&nbsp;</span>\n\
786  </td>\n\
787  </tr>\n\
788  </table>\n";
789 
790  return line;
791 }
792 
793 Text
795 {
796  Text content =
797  "<script LANGUAGE=\"JavaScript\">\n\
798 <!--\n\
799 function doConfirm() {\n\
800 return confirm(\"Are you sure you want to do this?\");\n\
801 }\n\
802 // -->\n\
803 </script>\n\
804 <div class=\"content\">\n\
805 <table border=\"0\" width=\"95%\">\n\
806  <tr class=\"heading\">\n\
807  <td nowrap valign=\"middle\" align=\"left\">\n\
808  <span class=\"heading\">&nbsp;Game Admin Functions</span>\n\
809  </td>\n\
810  </tr>\n\
811 </table>\n\n\
812 <table border=\"0\" width=\"95%\">\n\
813  <tr>\n\
814  <td nowrap width=\"1%\">&nbsp;</td>\n\
815  <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
816  <a href=\"/chat\">Lobby Chat</a>\n\
817  </td>\n\
818  <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
819  <a href=\"/home\">Mission List</a>\n\
820  </td>\n\
821  <td></td>\n\
822  </tr>\n\
823  <tr>\n\
824  <td nowrap width=\"1%\">&nbsp;</td>\n\
825  <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
826  <a href=\"/file?name=errlog.txt\">View Error Log</a>\n\
827  </td>\n\
828  <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
829  <a href=\"/users\">Player List</a>\n\
830  </td>\n\
831  <td></td>\n\
832  </tr>\n\
833  <tr>\n\
834  <td nowrap width=\"1%\">&nbsp;</td>\n\
835  <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
836  <a href=\"/file?name=serverlog.txt\">View Server Log</a>\n\
837  </td>\n\
838  <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
839  <a href=\"/home\">Ban List</a>\n\
840  </td>\n\
841  <td></td>\n\
842  </tr>\n\
843  <tr>\n\
844  <td nowrap width=\"1%\">&nbsp;</td>\n\
845  <td nowrap width=\"33%\" valign=\"middle\" align=\"left\"></td>\n\
846  <td nowrap width=\"33%\" valign=\"middle\" align=\"left\"></td>\n\
847  <td></td>\n\
848 </tr>\n\
849 </table>\n\n";
850 
851  content +=
852  " <table border=\"0\" width=\"95%\">\n\
853  <tr class=\"heading\">\n\
854  <td nowrap valign=\"middle\" align=\"left\">\n\
855  <span class=\"heading\">&nbsp;Server Admin Functions</span>\n\
856  </td>\n\
857  </tr>\n\
858 </table>\n\n\
859 <table border=\"0\" width=\"95%\">\n\
860  <tr>\n\
861  <td nowrap width=\"1%\">&nbsp;</td>\n\
862  <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
863  <a onclick=\"return doConfirm()\" href=\"/server?action=restart\">Restart Server</a>\n\
864  </td>\n\
865  <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
866  <a onclick=\"return doConfirm()\" href=\"/server?action=shutdown\">Shutdown Server</a>\n\
867  </td>\n\
868  <td></td>\n\
869 </tr>\n\
870 </table>\n\n";
871 
872  content += "</div>\n\n";
873  content += GetCopyright();
874  return content;
875 }
876 
877 Text
879 {
880  return "\n\n</body>\n</html>\n";
881 }
882 
883 Text
885 {
886  return "<br><span class=\"copy\">&nbsp;&nbsp;&nbsp;&nbsp;Copyright &copy; 1997-2004 Destroyer Studios. All rights reserved.</span><br>";
887 }