int main() { plugin::plugin plugin; moderation::component component; component.on_startup(&plugin); const std::string url="https://controller/api/moderation/runtime", token="ec_moderation_"+std::string(43,'a'); assert(!gsc::function::configure("server","http://controller/api/moderation/runtime",token,1)); assert(!gsc::function::configure("server","https://controller@evil/api/moderation/runtime",token,1)); assert(!gsc::function::configure("server",url,token+"\r\nX: leak",1)); assert(gsc::function::configure("server",url,token,1)); // A failed replacement config must not permanently poison the valid old config. assert(!gsc::function::configure("server",url,token,0)); assert(gsc::function::configure("server",url,token,1)); assert(!moderation::fault); const auto initial_session=moderation::current->session; const auto initial_sequence=moderation::current->request()["sessionSequence"]; assert(gsc::function::configure("server",url,token,1)); assert(moderation::current->session==initial_session && moderation::current->request()["sessionSequence"] == initial_sequence); // periodic configuration cannot invalidate connections scripting::entity player{{0,0}}, wrong{{0,1}}; assert(!gsc::method::observe(wrong,"123","one")); assert(!gsc::method::observe(player,"123; kick all","one")); assert(gsc::method::observe(player,"123","one")); const auto connection=moderation::current->players.at(0).connection; plugin.i.c.disconnect(0); assert(gsc::method::observe(player,"123","two")); assert(moderation::current->players.at(0).connection!=connection); auto now=moderation::now(); auto iso=[](long long seconds) {time_t time=seconds; auto tm=*gmtime(&time); std::ostringstream out; out<request()["receipts"][0]["state"]=="failed"); reply["actions"][0]["id"]="valid";reply["actions"][0]["connection"]=moderation::current->players.at(0).connection; reply["actions"][0]["kind"]="message";reply["actions"][0]["reason"]="a\";\\\r\nsay"; moderation::current->accept(reply.dump(),now); gsc::function::pump(); assert(game::messages.size()==1); assert(game::messages[0].first==0); assert(game::messages[0].second.find(';')==std::string::npos); assert(game::messages[0].second.find('\n')==std::string::npos); moderation::current->accept(reply.dump(),now); gsc::function::pump(); assert(game::messages.size()==1); reply["actions"][0]["id"]="shutdown"; reply["actions"][0]["kind"]="kick"; moderation::current->accept(reply.dump(),now); command::on_execute=[] { scripting::shutdown(); }; gsc::function::pump(); assert(!moderation::current); // callback cannot free the active frame command::on_execute={}; assert(gsc::function::configure("server",url,token,1)); assert(moderation::current->request()["receipts"].size()>=1); const auto old_session=moderation::current->session; assert(gsc::function::configure("server",url,"ec_moderation_"+std::string(43,'b'),1)); assert(moderation::current->session!=old_session && moderation::current->revision()==0); assert(moderation::current->request()["receipts"].empty()); scripting::shutdown(); }