20 template<
typename Container>
21 Container&
split(Container&
result,
const typename Container::value_type&
s,
28 next = s.find_first_not_of(delimiters, next + 1);
29 if (next == Container::value_type::npos)
34 next = s.find_first_of(delimiters, current);
35 result.push_back(s.substr(current, next - current));
36 }
while (next != Container::value_type::npos);
69 static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
70 "abcdefghijklmnopqrstuvwxyz"
73 static inline bool is_base64(
unsigned char c) {
74 return (isalnum(c) || (c ==
'+') || (c ==
'/'));
81 unsigned char char_array_3[3];
82 unsigned char char_array_4[4];
85 char_array_3[i++] = *(bytes_to_encode++);
87 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
88 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
89 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
90 char_array_4[3] = char_array_3[2] & 0x3f;
92 for (i = 0; (i < 4); i++)
93 ret += base64_chars[char_array_4[i]];
99 for (j = i; j < 3; j++)
100 char_array_3[j] =
'\0';
102 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
103 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
104 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
105 char_array_4[3] = char_array_3[2] & 0x3f;
107 for (j = 0; (j < i + 1); j++)
108 ret += base64_chars[char_array_4[j]];
120 int in_len = encoded_string.size();
124 unsigned char char_array_4[4], char_array_3[3];
127 while (in_len-- && (encoded_string[in_] !=
'=') && is_base64(encoded_string[in_])) {
128 char_array_4[i++] = encoded_string[in_];
131 for (i = 0; i < 4; i++)
132 char_array_4[i] = base64_chars.find(char_array_4[i]);
134 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
135 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
136 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
138 for (i = 0; (i < 3); i++)
139 ret += char_array_3[i];
145 for (j = i; j < 4; j++)
148 for (j = 0; j < 4; j++)
149 char_array_4[j] = base64_chars.find(char_array_4[j]);
151 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
152 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
153 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
155 for (j = 0; (j < i - 1); j++)
156 ret += char_array_3[j];
188 #include "mongoose.h"
191 #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
221 name.assign(newName);
227 passwd.assign(newPasswd);
257 : mPlayerId(playerId) {
265 foundPlayerIndex =
idx;
270 if (foundPlayerIndex < MAX_PLAYERS) {
316 SDL_mutexP(webapiCommandQueueMutex);
318 webapiCommandQueue.push_back(command);
320 SDL_mutexV(webapiCommandQueueMutex);
324 SDL_mutexP(webapiCommandQueueMutex);
332 webapiCommandQueue.clear();
334 SDL_mutexV(webapiCommandQueueMutex);
351 entries.push_back(entry);
354 if (entries.size() > 500) {
355 json_delete(entries.front().entity);
361 json_t *msgs = json_array();
363 for (std::list<LogResourceEntry>::iterator iter = entries.begin(); iter != entries.end(); ++iter) {
364 if (iter->timestamp >= after) {
365 json_t *msg = json_copy(iter->entity);
366 json_object_set_new(msg,
"timestamp", json_integer(iter->timestamp));
367 json_array_append(msgs, msg);
390 std::stringstream headerStream;
392 headerStream <<
"HTTP/1.0 ";
395 headerStream <<
"200 OK";
398 headerStream <<
"401 Unauthorized";
401 headerStream <<
"404 Not Found";
404 headerStream <<
"500 Internal Server Error";
407 headerStream <<
"\r\n";
409 if (data.length() > 0) {
410 headerStream <<
"Content-Length: " << data.length() <<
"\r\n";
411 headerStream <<
"Content-Type: application/json\r\n\r\n";
415 resultString += headerStream.str();
416 resultString +=
data;
418 mg_write(conn, resultString.c_str(), (
int) resultString.length());
421 static void sendJsonResponse(mg_connection *connection, json_t *responseEntity) {
422 char*
result = json_dumps(responseEntity, 0);
425 json_delete(responseEntity);
427 sendResponse(connection, resultString,
HTTP_200_OK);
447 return json_object();
451 json_t *
result = json_object();
463 const char*
name = json_string_value(json_object_get(context->
requestEntity,
"name"));
467 const char* passwd = json_string_value(json_object_get(context->
requestEntity,
"password"));
471 int framecap = atoi(json_string_value(json_object_get(context->
requestEntity,
"framecap")));
479 return json_object();
484 return json_object();
489 return json_object();
494 return json_object();
499 return json_object();
503 json_t *
obj = json_object();
505 json_object_set_new(obj,
"name", json_string(webapi_netgameInfo.
name));
506 json_object_set_new(obj,
"mission", json_string(webapi_netgameInfo.
mission_name));
507 json_object_set_new(obj,
"campaign", json_string(webapi_netgameInfo.
campaign_name));
509 json_object_set_new(obj,
"maxPlayers", json_integer(webapi_netgameInfo.
max_players));
510 json_object_set_new(obj,
"maxObservers", json_integer(webapi_netgameInfo.
options.
max_observers));
511 json_object_set_new(obj,
"respawn", json_integer(webapi_netgameInfo.
respawn));
513 json_object_set_new(obj,
"gameState", json_integer(webapi_netgameInfo.
game_state));
515 json_object_set_new(obj,
"security", json_integer(webapi_netgameInfo.
security));
520 json_t *fpsEntity = json_object();
522 json_object_set_new(fpsEntity,
"fps", json_real(webui_fps));
523 json_object_set_new(fpsEntity,
"time", json_real(webui_missiontime));
529 json_t *goals = json_array();
531 for (std::list<mission_goal>::iterator iter = webuiMissionGoals.begin(); iter != webuiMissionGoals.end(); ++iter) {
534 json_t *goalEntity = json_object();
536 json_object_set_new(goalEntity,
"name", json_string(goal.
name));
537 json_object_set_new(goalEntity,
"message", json_string(goal.
message));
538 json_object_set_new(goalEntity,
"score", json_integer(goal.
score));
539 json_object_set_new(goalEntity,
"team", json_integer(goal.
team));
544 typeString =
"primary";
547 typeString =
"secondary";
550 typeString =
"bonus";
553 typeString =
"error";
556 json_object_set_new(goalEntity,
"type", json_string(typeString));
561 statusString =
"failed";
564 statusString =
"complete";
567 statusString =
"incomplete";
570 statusString =
"error";
573 json_object_set_new(goalEntity,
"status", json_string(statusString));
575 json_array_append(goals, goalEntity);
582 json_t *playerList = json_array();
584 for (std::map<short, net_player>::iterator iter = webapiNetPlayers.begin(); iter != webapiNetPlayers.end();
592 json_t *
obj = json_object();
594 json_object_set(obj,
"id", json_integer(p.
player_id));
595 json_object_set(obj,
"address", json_string(address));
597 json_object_set(obj,
"host", (
MULTI_HOST(p)) ? json_true() : json_false());
598 json_object_set(obj,
"observer", (
MULTI_OBSERVER(p)) ? json_true() : json_false());
602 json_array_append(playerList, obj);
609 int playerId = atoi(context->
parameters[
"playerId"].c_str());
611 return json_object();
629 json_t *obj2 = json_object();
630 if (p == NULL || p->
m_player == NULL)
635 json_object_set_new(obj2,
"score", json_integer(scores->
m_score));
636 json_object_set_new(obj2,
"kills", json_integer(scores->
m_kill_count));
637 json_object_set_new(obj2,
"kills-enemy", json_integer(scores->
m_kill_count_ok));
638 json_object_set_new(obj2,
"kills-friendly", json_integer(scores->
m_bonehead_kills));
639 json_object_set_new(obj2,
"assists", json_integer(scores->
m_assists));
640 json_object_set_new(obj2,
"shots-primary", json_integer(scores->
mp_shots_fired));
641 json_object_set_new(obj2,
"shots-secondary", json_integer(scores->
ms_shots_fired));
642 json_object_set_new(obj2,
"hits-primary", json_integer(scores->
mp_shots_hit));
643 json_object_set_new(obj2,
"hits-secondary", json_integer(scores->
ms_shots_hit));
644 json_object_set_new(obj2,
"hits-friendly-primary", json_integer(scores->
mp_bonehead_hits));
645 json_object_set_new(obj2,
"hits-friendly-secondary", json_integer(scores->
ms_bonehead_hits));
653 json_t *
obj = json_object();
654 if (p == NULL || p->
m_player == NULL)
659 json_object_set_new(obj,
"score", json_integer(scores->
score));
660 json_object_set_new(obj,
"kills", json_integer(scores->
kill_count));
661 json_object_set_new(obj,
"kills-enemy", json_integer(scores->
kill_count_ok));
662 json_object_set_new(obj,
"kills-friendly", json_integer(scores->
bonehead_kills));
663 json_object_set_new(obj,
"assists", json_integer(scores->
assists));
664 json_object_set_new(obj,
"shots-primary", json_integer(scores->
p_shots_fired));
665 json_object_set_new(obj,
"shots-secondary", json_integer(scores->
s_shots_fired));
666 json_object_set_new(obj,
"hits-primary", json_integer(scores->
p_shots_hit));
667 json_object_set_new(obj,
"hits-secondary", json_integer(scores->
s_shots_hit));
668 json_object_set_new(obj,
"hits-friendly-primary", json_integer(scores->
p_bonehead_hits));
669 json_object_set_new(obj,
"hits-friendly-secondary", json_integer(scores->
s_bonehead_hits));
677 return atoi(iter->second.c_str());
710 {
"api/1/server/fs2net/reset",
"GET", &
fs2netReset },
718 {
"api/1/chat",
"GET", &
chatGet },
719 {
"api/1/chat",
"POST", &
chatPost },
720 {
"api/1/debug",
"GET", &
debugGet } };
722 static bool webserverApiRequest(mg_connection *conn,
const mg_request_info *ri) {
725 resourcePath.erase(0, 1);
738 if (resourcePathParts.size() == pathParts.size()) {
741 bool pathMatch =
true;
742 for (
size_t u = 0;
u < resourcePathParts.size();
u++) {
744 if (resourcePathParts.at(
u) ==
"*") {
746 }
else if (resourcePathParts.at(
u) != pathParts.at(
u)) {
757 userNameAndPassword +=
":";
762 basicAuthValue +=
base64_encode(reinterpret_cast<const unsigned char*>(userNameAndPassword.c_str()), userNameAndPassword.length());
764 const char* authValue = mg_get_header(conn,
"Authorization");
765 if (authValue == NULL || strcmp(authValue, basicAuthValue.c_str()) != 0) {
770 if (ri->query_string) {
781 if (temp.size() == 2) {
787 char entityBuffer[1024];
788 memset(entityBuffer, 0,
sizeof(entityBuffer));
790 mg_read(conn, &entityBuffer,
sizeof(entityBuffer));
792 json_error_t parseError;
793 context.
requestEntity = json_loads((
const char*) &entityBuffer, JSON_DISABLE_EOF_CHECK, &parseError);
796 SDL_mutexP(webapi_dataMutex);
798 SDL_mutexV(webapi_dataMutex);
806 sendJsonResponse(conn, result);
813 static void *webserverCallback(
enum mg_event
event,
struct mg_connection *conn) {
814 const struct mg_request_info *ri = mg_get_request_info(conn);
818 const char *msg = (
const char *) ri->ev_data;
819 mprintf((
"Webapi error: %s\n", msg));
822 case MG_NEW_REQUEST: {
823 bool requestHandled = webserverApiRequest(conn, ri);
824 return requestHandled ?
const_cast<char*
>(
"") : NULL;
835 if (webserverContext) {
836 mprintf((
"Webapi shutting down\n"));
837 mg_stop(webserverContext);
852 const char *mgOptions[] = {
853 "listening_ports",
buffer,
860 webserverContext = mg_start(&webserverCallback, NULL, mgOptions);
865 json_t *msg = json_object();
867 json_object_set_new(msg,
"message", json_string(text));
878 json_t *msg = json_object();
879 json_object_set_new(msg,
"message", json_string(str));
893 return std::find(bannedPlayers.begin(), bannedPlayers.end(),
std::string(name)) != bannedPlayers.end() ? 1 : 0;
908 SDL_mutexP(webapi_dataMutex);
913 webapiNetPlayers.clear();
926 webuiMissionGoals.clear();
931 SDL_mutexV(webapi_dataMutex);
void multi_update_valid_missions()
LogResource webapi_debugLog
void fs2netd_gameserver_start()
int cf_delete(const char *filename, int path_type)
Delete the specified file.
void std_debug_multilog_add_line(const char *str)
json_t * debugGet(ResourceContext *context)
json_t * serverGet(ResourceContext *context)
json_t * chatGet(ResourceContext *context)
json_t * chatPost(ResourceContext *context)
void std_multi_update_netgame_info_controls()
void std_destroy_gen_dialog()
std::string base64_decode(std::string const &encoded_string)
#define MULTI_VALID_MISSION_FILE
struct Resource resources[]
unsigned int mp_shots_fired
unsigned int mp_bonehead_hits
std::map< short, net_player > webapiNetPlayers
json_t * serverPut(ResourceContext *context)
void fs2netd_gameserver_disconnect()
unsigned int ms_bonehead_hits
GLenum GLuint GLenum GLsizei const GLchar * message
std::basic_string< char, std::char_traits< char >, std::allocator< char > > SCP_string
unsigned int s_shots_fired
mission_goal Mission_goals[MAX_GOALS]
char callsign[CALLSIGN_LEN+1]
unsigned int mp_shots_hit
Container & split(Container &result, const typename Container::value_type &s, const typename Container::value_type &delimiters, split_struct::empties_t empties=split_struct::empties_ok)
json_t * serverResetGame(ResourceContext *context)
void std_update_player_ping(net_player *p)
void std_multi_set_standalone_mission_name(char *mission_name)
json_t * playerGet(ResourceContext *context)
void std_connect_set_host_connect_status()
unsigned int ms_shots_fired
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
void std_init_standalone()
void multi_kick_player(int player_index, int ban, int reason)
GLdouble GLdouble GLdouble r
void addEntity(json_t *entity)
SCP_string webapiPassword
multi_server_options options
SCP_vector< WebapiCommand * > webapiCommandQueue
char name[MAX_GAMENAME_LEN+1]
void send_game_chat_packet(net_player *from, const char *msg, int msg_mode, net_player *to, const char *expr, int server_msg)
void fs2netd_reset_connection()
unsigned int p_shots_fired
json_t * fs2netReset(ResourceContext *context)
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
void std_gen_set_text(char *str, int field_num)
#define ARRAY_SIZE(array)
#define MULTI_OBSERVER(np)
void std_configLoaded(multi_global_options *options)
void std_add_chat_text(const char *text, int player_index, int add_id)
struct mg_context * webserverContext
net_player_server_info s_info
GLsizei const GLchar ** string
SCP_string webapiUsername
json_t * missionGoalsGet(ResourceContext *context)
char mission_name[NAME_LENGTH+1]
char campaign_name[NAME_LENGTH+1]
void std_reset_timestamps()
const char * XSTR(const char *str, int index)
void webapiAddCommand(WebapiCommand *command)
void std_debug_set_standalone_state_string(char *str)
void std_add_ban(const char *name)
std::string base64_encode(unsigned char const *bytes_to_encode, unsigned int in_len)
GLuint const GLchar * name
json_t * playerMissionScoreAlltimeGet(ResourceContext *context)
int multi_quit_game(int prompt, int notify_code, int err_code, int wsa_error)
void std_multi_set_standalone_missiontime(float mission_time)
json_t * refreshMissions(ResourceContext *context)
KickPlayerCommand(int playerId)
SDL_mutex * webapiCommandQueueMutex
json_t * playerDelete(ResourceContext *context)
int afterTimestamp(ResourceContext *context)
#define MULTI_CONNECTED(np)
json_t * missionGet(ResourceContext *context)
int std_remove_player(net_player *p)
json_t * netgameInfoGet(ResourceContext *context)
int std_player_is_banned(const char *name)
json_t * playerMissionScoreMissionGet(ResourceContext *context)
std::list< mission_goal > webuiMissionGoals
void std_connect_set_gamename(char *name)
unsigned int p_bonehead_hits
LogResource webapi_chatLog
void std_multi_update_goals()
SCP_vector< ship_info > Ship_info
char std_pname[STD_NAME_LEN+1]
GLenum GLsizei GLenum GLenum const GLvoid * data
SCP_map< SCP_string, SCP_string > parameters
GLsizei GLsizei GLuint * obj
json_t * getEntriesAfter(long after)
char message[MAX_GOAL_TEXT]
void std_create_gen_dialog(char *title)
void std_add_player(net_player *p)
net_player * playerForId(int playerId)
void webapiExecuteCommands()
json_t *(* resourceHandler)(ResourceContext *)
int std_connect_set_connect_count()
SDL_mutex * webapi_dataMutex
SCP_list< LogResourceEntry > entries
SCP_string webuiRootDirectory
void std_multi_setup_goal_tree()
#define MULTI_IS_TRACKER_GAME
net_player Net_players[MAX_PLAYERS]
void std_reset_standalone_gui()
unsigned int ms_shots_hit
void gameseq_post_event(int event)
multi_global_options Multi_options_g
std::vector< std::string > bannedPlayers
json_t * emptyResource(ResourceContext *context)
json_t * serverDelete(ResourceContext *context)
unsigned int s_bonehead_hits
netgame_info webapi_netgameInfo
void std_set_standalone_fps(float fps)
void std_multi_add_goals()
char std_passwd[STD_PASSWD_LEN+1]