From aa909853032cc1c641f0cdaffd43ffeefe22d33d Mon Sep 17 00:00:00 2001 From: darkrose Date: Sun, 7 Jul 2013 13:12:57 +1000 Subject: [PATCH 1/6] basics for http --- src/CMakeLists.txt | 5 +- src/http.cpp | 29 ++++++ src/http.h | 48 +++++++++ src/socket.cpp | 237 +++++++++++++++++++++++++++++++++++++++------ src/socket.h | 20 ++++ 5 files changed, 309 insertions(+), 30 deletions(-) create mode 100644 src/http.cpp create mode 100644 src/http.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 58e2773..01ac0ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -127,7 +127,8 @@ set(common_SRCS test.cpp sha1.cpp base64.cpp - ban.cpp + ban.cpp + http.cpp ) # This gives us the icon @@ -366,4 +367,4 @@ else (SQLITE3_FOUND) add_subdirectory(sqlite) endif (SQLITE3_FOUND) -#end +#end diff --git a/src/http.cpp b/src/http.cpp new file mode 100644 index 0000000..5acc902 --- /dev/null +++ b/src/http.cpp @@ -0,0 +1,29 @@ +/************************************************************************ +* Minetest-c55 +* Copyright (C) 2010 celeron55, Perttu Ahola +* +* http.cpp +* Copyright (C) Lisa 'darkrose' Milne 2013 +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +************************************************************************/ + +#include "socket.h" +#include "http.h" +#include "debug.h" +#include +#include +#include +#include +#include "utility.h" diff --git a/src/http.h b/src/http.h new file mode 100644 index 0000000..087d545 --- /dev/null +++ b/src/http.h @@ -0,0 +1,48 @@ +/************************************************************************ +* Minetest-c55 +* Copyright (C) 2010 celeron55, Perttu Ahola +* +* http.h +* Copyright (C) Lisa 'darkrose' Milne 2013 +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +************************************************************************/ + + +#ifndef HTTP_HEADER +#define HTTP_HEADER + +#include "socket.h" +#include "common_irrlicht.h" + +class HTTPServer +{ +public: + HTTPServer(); + ~HTTPServer(); +private: + TCPSocket &m_socket; + core::map m_peers; +}; + +class HTTPClient +{ +public: + HTTPClient(); + ~HTTPClient(); +private: + TCPSocket &m_socket; +}; + +#endif diff --git a/src/socket.cpp b/src/socket.cpp index 408e41d..7db3db0 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -152,29 +152,15 @@ UDPSocket::UDPSocket() if(g_sockets_initialized == false) throw SocketException("Sockets not initialized"); - m_handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + m_handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(DP) - dstream<20) + dstream<<"..."; + dstream<m_handle = client; + + return socket; +} + +int TCPSocket::GetHandle() +{ + return m_handle; +} + +void TCPSocket::setTimeoutMs(int timeout_ms) +{ + m_timeout_ms = timeout_ms; +} + +bool TCPSocket::WaitData(int timeout_ms) +{ + fd_set readset; + int result; + + // Initialize the set + FD_ZERO(&readset); + FD_SET(m_handle, &readset); + + // Initialize time out struct + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = timeout_ms * 1000; + // select() + result = select(m_handle+1, &readset, NULL, NULL, &tv); + + if (result == 0) { + return false; + }else if (result < 0 && errno == EINTR) { + return false; + }else if (result < 0) { + // Error +#ifndef DISABLE_ERRNO + dstream<<(int)m_handle<<": Select failed: "< Date: Sat, 20 Jul 2013 14:46:48 +1000 Subject: [PATCH 2/6] http part 2 --- minetest.conf.example | 2 + src/client.cpp | 37 ++++ src/client.h | 4 + src/clientserver.h | 12 + src/defaultsettings.cpp | 1 + src/http.cpp | 475 ++++++++++++++++++++++++++++++++++++++++ src/http.h | 171 ++++++++++++++- src/player.h | 15 +- src/server.cpp | 20 ++ src/server.h | 9 + src/servermain.cpp | 5 + src/tile.cpp | 2 +- 12 files changed, 746 insertions(+), 7 deletions(-) diff --git a/minetest.conf.example b/minetest.conf.example index a5febcb..b761578 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -158,6 +158,8 @@ #time_send_interval = 20 # Length of day/night cycle. 72=20min, 360=4min, 1=24hour #time_speed = 72 +# enable a http microserver for admin and player textures +#enable_http = true #server_unload_unused_data_timeout = 60 #server_map_save_interval = 60 #full_block_send_enable_min_time_from_building = 2.0 diff --git a/src/client.cpp b/src/client.cpp index 9166ce0..ff2d393 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "log.h" +#include "http.h" /* QueuedMeshUpdate @@ -211,6 +212,8 @@ Client::Client( //m_env_mutex.Init(); //m_con_mutex.Init(); + m_httpclient = new HTTPClient(this); + m_mesh_update_thread.Start(); /* @@ -248,6 +251,8 @@ void Client::connect(Address address) //JMutexAutoLock lock(m_con_mutex); //bulk comment-out m_con.SetTimeoutMs(0); m_con.Connect(address); + if (g_settings->getBool("enable_http")) + m_httpclient->start(address); } bool Client::connectedAndInitialized() @@ -760,6 +765,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) writeU16(&reply[0], TOSERVER_INIT2); // Send as reliable m_con.Send(PEER_ID_SERVER, 1, reply, true); + sendWantCookie(); return; } @@ -1046,6 +1052,8 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) } player->updateName((char*)&data[start+2]); + std::string p_name((char*)&data[start+2]); + m_httpclient->pushRequest(HTTPREQUEST_SKIN_HASH,p_name); start += item_size; } @@ -1507,6 +1515,22 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) event.deathscreen.camera_point_target_z = camera_point_target.Z; m_client_event_queue.push_back(event); } + else if(command == TOCLIENT_HAVECOOKIE) + { + /* + u16 command + u16 textlen + textdata + */ + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + u16 len = readU16(is); + char buff[len+1]; + is.read(buff,len); + buff[len] = 0; + printf("received cookie '%s'\n",buff); + } else { infostream<<"Client: Ignoring unknown command " @@ -1756,6 +1780,19 @@ void Client::sendRespawn() Send(0, data, true); } +void Client::sendWantCookie() +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + writeU16(os, TOSERVER_WANTCOOKIE); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + Send(0, data, true); +} + void Client::sendPlayerPos() { //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out diff --git a/src/client.h b/src/client.h index ada5088..59a9c38 100644 --- a/src/client.h +++ b/src/client.h @@ -139,6 +139,8 @@ struct ClientEvent }; }; +class HTTPClient; + class Client : public con::PeerHandler, public InventoryManager { public: @@ -199,6 +201,7 @@ public: const std::wstring newpassword); void sendDamage(u8 damage); void sendRespawn(); + void sendWantCookie(); // locks envlock void removeNode(v3s16 p); @@ -330,6 +333,7 @@ private: ClientEnvironment m_env; con::Connection m_con; + HTTPClient *m_httpclient; IrrlichtDevice *m_device; diff --git a/src/clientserver.h b/src/clientserver.h index cb5c764..4da541f 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -191,6 +191,13 @@ enum ToClientCommand u8 bool set camera point target v3f1000 camera point target (to point the death cause or whatever) */ + + TOCLIENT_HAVECOOKIE = 0x38, + /* + u16 command + u16 textlen + textdata + */ }; enum ToServerCommand @@ -344,6 +351,11 @@ enum ToServerCommand /* u16 TOSERVER_RESPAWN */ + + TOSERVER_WANTCOOKIE=0x39, + /* + u16 TOSERVER_WANTCOOKIE + */ }; inline SharedBuffer makePacket_TOCLIENT_TIME_OF_DAY(u16 time) diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 7bc561a..2a8fce8 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -96,6 +96,7 @@ void set_default_settings(Settings *settings) settings->setDefault("default_privs", "build, shout"); settings->setDefault("borderstone_radius","5"); + settings->setDefault("enable_http","true"); settings->setDefault("profiler_print_interval", "0"); settings->setDefault("enable_mapgen_debug_info", "false"); settings->setDefault("objectdata_interval", "0.2"); diff --git a/src/http.cpp b/src/http.cpp index 5acc902..1bc7ada 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -27,3 +27,478 @@ #include #include #include "utility.h" +#include "connection.h" +#include "log.h" +#include "sha1.h" + +void * HTTPServerThread::Thread() +{ + ThreadStarted(); + + log_register_thread("HTTPServerThread"); + + DSTACK(__FUNCTION_NAME); + + BEGIN_DEBUG_EXCEPTION_HANDLER + + while (getRun()) + { + try{ + m_server->step(); + }catch (con::NoIncomingDataException &e) { + }catch(con::PeerNotFoundException &e) { + } + } + + END_DEBUG_EXCEPTION_HANDLER(errorstream) + + return NULL; +} + +/* + * HTTPServer + */ + +HTTPServer::HTTPServer(Server &server): + m_thread(this) +{ + m_server = &server; +} + +HTTPServer::~HTTPServer() +{ +} + +void HTTPServer::start(u16 port) +{ + DSTACK(__FUNCTION_NAME); + // Stop thread if already running + m_thread.stop(); + + m_socket = new TCPSocket(); + m_socket->setTimeoutMs(30); + m_socket->Bind(port); + + // Start thread + m_thread.setRun(true); + m_thread.Start(); + + infostream<<"HTTPServer: Started on port "<WaitData(50)) { + TCPSocket *s = m_socket->Accept(); + HTTPRemoteClient *c = new HTTPRemoteClient(s,this); + m_peers.push_back(c); + } + + for (std::vector::iterator it = m_peers.begin(); it != m_peers.end(); ++it) { + HTTPRemoteClient *c = *it; + try{ + if (c->receive()) { + m_peers.erase(it); + delete c; + } + }catch (SocketException &e) { + // assume it's closed + m_peers.erase(it); + delete c; + continue; + } + } +} + +/* + * HTTPRemoteClient + */ + +HTTPRemoteClient::~HTTPRemoteClient() +{ +} + +int HTTPRemoteClient::receive() +{ + char buff[2048]; + if (!m_socket->WaitData(30)) + return 0; + int r = m_socket->Receive(buff,2048); + if (r<1) + return 1; + + m_recv_headers.clear(); + m_send_headers.clear(); + + int h = m_recv_headers.read(buff,r); + if (h == 1) { + return 1; + }else if (h == 2) { + //if (m_recv_headers.url() == "") + //return 1; + //while (m_socket->WaitData(1000)) { + //r = m_socket->Receive(buff,2048); + //if (r<1) + //return 1; + //h = m_recv_headers.read(buff,r); + //if (!h) + //break; + //if (h == 1) + //return 1; + //} + //if (h) + return 1; + } + + if (m_recv_headers.cookie() != "" && m_recv_headers.get("User") != "") { + if (m_recv_headers.cookie() == m_server->getPlayerCookie(m_recv_headers.get("User"))) { + m_send_headers.setCookie(m_recv_headers.cookie()); + m_auth = true; + } + } + + //std::string u; + //for (int i=0; (u = m_recv_headers.url(i)) != ""; i++) { + //printf("%d: '%s'\n",i,u.c_str()); + //} + + + //setResponse("404 Not Found"); + //std::string html("404 Not Found

404 Not Found

"); + //send(html); + send((char*)"grar"); + + return 0;//m_recv_headers.keepAlive() == true ? 0 : 1; +} + +void HTTPRemoteClient::send(char* data) +{ + int l = strlen(data); + m_send_headers.set("Content-Type","text/plain"); + m_send_headers.setLength(l); + sendHeaders(); + m_socket->Send(data,l); +} + +void HTTPRemoteClient::sendHTML(char* data) +{ + int l = strlen(data); + m_send_headers.set("Content-Type","text/html"); + m_send_headers.setLength(l); + sendHeaders(); + m_socket->Send(data,l); +} + +void HTTPRemoteClient::sendFile(std::string &file) +{ + m_send_headers.set("Content-Type","text/plain"); + m_send_headers.setLength(0); + sendHeaders(); +} + +void HTTPRemoteClient::sendHeaders() +{ + std::string v; + int s; + char buff[1024]; + + //v = m_response; + //if (v == "") + //v = std::string("200 OK"); + + //s = snprintf(buff,1024,"HTTP/1.1 %s\r\n",v.c_str()); + //m_socket->Send(buff,s); + m_socket->Send("HTTP/1.1 200 OK\r\n",17); + + //v = m_send_headers.get("Content-Type"); + //if (v == "") { + m_socket->Send("Content-Type: text/plain\r\n",26); + //}else{ + //s = snprintf(buff,1024,"Content-Type: %s\r\n",v.c_str()); + //m_socket->Send(buff,s); + //} + + s = m_send_headers.length(); + s = snprintf(buff,1024,"Content-Length: %d\r\n",s); + m_socket->Send(buff,s); + + //v = m_send_headers.cookie(); + //if (v != "") { + //s = snprintf(buff,1024,"Set-Cookie: MTID=%s\r\n",v.c_str()); + //m_socket->Send(buff,s); + //} + + m_socket->Send("\r\n",2); +} + +#ifndef SERVER +#include "client.h" + +void * HTTPClientThread::Thread() +{ + ThreadStarted(); + + log_register_thread("HTTPClientThread"); + + DSTACK(__FUNCTION_NAME); + + BEGIN_DEBUG_EXCEPTION_HANDLER + + while (getRun()) + { + try{ + m_client->step(); + }catch (con::NoIncomingDataException &e) { + }catch(con::PeerNotFoundException &e) { + } + } + + END_DEBUG_EXCEPTION_HANDLER(errorstream) + + return NULL; +} + +/* + * HTTPClient + */ + +HTTPClient::HTTPClient(Client *client): + m_cookie(""), + m_thread(this) +{ + m_client = client; + m_req_mutex.Init(); +} + +HTTPClient::~HTTPClient() +{ +} + +void HTTPClient::start(const Address &address) +{ + DSTACK(__FUNCTION_NAME); + // Stop thread if already running + m_thread.stop(); + + m_socket = new TCPSocket(); + m_socket->setTimeoutMs(30); + m_socket->Connect(address); + + // Start thread + m_thread.setRun(true); + m_thread.Start(); + + infostream<<"HTTPClient: Started"</skin + * request the skin texture for + * response: + * if skin exists: + * 200 OK + texture data + * else: + * 303 See Other + Location header to /texture/character.png + * this client will ignore the redirect, while browsers will see the default skin + */ + case HTTPREQUEST_SKIN: + { + std::string url("/player/"); + url += data + "/skin"; + HTTPRequest r(url); + m_req_mutex.Lock(); + m_requests.push_back(r); + m_req_mutex.Unlock(); + break; + } + /* + * /player//skin/hash/ + * sends the sha1 hash of the skin texture for + * response: + * if hash is the same on client and server: + * 204 No Content + * if client is authenticated as : + * 304 Not Modified + * else: + * 200 OK + texture data + */ + case HTTPREQUEST_SKIN_HASH: + { + std::string tex = std::string("players/player_")+data+".png"; + std::string ptex = getTexturePath(tex); + if (ptex == "") { + pushRequest(HTTPREQUEST_SKIN,data); + return; + } + break; + } + /* + * /player//skin + * PUT request type, sends the skin to the server + * response: + * if client is authenticated as : + * 201 Created + * else: + * 405 Method Not Allowed + */ + case HTTPREQUEST_SKIN_SEND: + { + break; + } + default:; + } +} + +void HTTPClient::pushRequest(std::string &url, std::string &data) +{ + HTTPRequest r(url,data); + m_req_mutex.Lock(); + m_requests.push_back(r); + m_req_mutex.Unlock(); +} + +void HTTPClient::get(std::string &url) +{ +} + +void HTTPClient::post(std::string &url, char* data) +{ +} + +void HTTPClient::postFile(std::string &url, std::string &file) +{ +} + +HTTPRequest HTTPClient::popRequest() +{ + m_req_mutex.Lock(); + HTTPRequest r = m_requests.front(); + m_requests.erase(m_requests.begin()); + m_req_mutex.Unlock(); + return r; +} + +void HTTPClient::sendHeaders() +{ +} +#endif + +/* + * HTTPHeaders + */ + +int HTTPHeaders::read(char* buff, int length) +{ + char nbuff[1024]; + char vbuff[1024]; + int n = 1; + int o = 0; + int c = m_url == "" ? 0 : 1; + + for (int i=0; i + +class HTTPServer; + +class HTTPServerThread : public SimpleThread +{ + HTTPServer *m_server; + +public: + + HTTPServerThread(HTTPServer *server): + SimpleThread(), + m_server(server) + { + } + + void * Thread(); +}; + +class HTTPHeaders +{ +public: + HTTPHeaders() + { + clear(); + } + ~HTTPHeaders() {}; + void clear() + { + m_contentLength = 0; + m_keepalive = false; + m_cookie = std::string(""); + m_url = std::string(""); + m_url_split.clear(); + } + int read(char* buff, int length); + u32 length() {return m_contentLength;} + bool keepAlive() {return m_keepalive;} + std::string &cookie() {return m_cookie;} + std::string &url() {return m_url;} + std::string &url(int index) {return m_url_split[index];} + std::string &get(std::string name) {return m_headers[name];} + void set(std::string name, std::string value) {m_headers[name] = value;} + void setLength(u32 length) {m_contentLength = length;} + void setKeepAlive(bool ka) {m_keepalive = ka;} + void setCookie(std::string cookie) {std::string c(cookie.c_str()); m_cookie = c;} + void setUrl(std::string url) {std::string u(url); m_url = u;} + void setMethod(std::string method) {std::string m(method); m_method = m;} + void setProtocol(std::string proto) {std::string p(proto); m_protocol = p;} +private: + std::map m_headers; + u32 m_contentLength; + bool m_keepalive; + std::string m_cookie; + std::string m_url; + std::vector m_url_split; + std::string m_method; + std::string m_protocol; +}; + +class HTTPRemoteClient +{ +public: + HTTPRemoteClient(TCPSocket *sock, HTTPServer *server): + m_response(""), + m_auth(false) + { + m_socket = sock; + m_server = server; + } + ~HTTPRemoteClient(); + int receive(); + void send(char* data); + void send(std::string &data) {send((char*)data.c_str());} + void sendHTML(char* data); + void sendHTML(std::string &data) {sendHTML((char*)data.c_str());} + void sendFile(std::string &file); + void setResponse(const char* response) {std::string r(response); m_response = r;} +private: + void sendHeaders(); + HTTPHeaders m_recv_headers; + HTTPHeaders m_send_headers; + std::string m_response; + bool m_auth; + TCPSocket *m_socket; + HTTPServer *m_server; +}; class HTTPServer { public: - HTTPServer(); + HTTPServer(Server &server); ~HTTPServer(); + void start(u16 port); + void stop(); + void step(); + std::string getPlayerCookie(std::string &name) {return m_server->getPlayerCookie(name);} private: - TCPSocket &m_socket; - core::map m_peers; + TCPSocket *m_socket; + std::vector m_peers; + HTTPServerThread m_thread; + Server *m_server; +}; + +enum HTTPRequestType { + HTTPREQUEST_NULL, + HTTPREQUEST_SKIN, + HTTPREQUEST_SKIN_HASH, + HTTPREQUEST_SKIN_SEND +}; + +struct HTTPRequest +{ + HTTPRequest(): + url(""), + post("") + { + } + HTTPRequest(std::string &u, std::string &p) + { + url = u; + post = p; + } + HTTPRequest(std::string &u) + { + url = u; + post = ""; + } + std::string url; + std::string post; +}; + +#ifndef SERVER +#include "client.h" +class HTTPClient; + +class HTTPClientThread : public SimpleThread +{ + HTTPClient *m_client; + +public: + + HTTPClientThread(HTTPClient *client): + SimpleThread(), + m_client(client) + { + } + + void * Thread(); }; class HTTPClient { public: - HTTPClient(); + HTTPClient(Client *client); ~HTTPClient(); + void start(const Address &address); + void stop(); + void step(); + void setCookie(std::string &cookie) {std::string c(cookie.c_str()); m_cookie = c;} + void pushRequest(HTTPRequestType type, std::string &data); + void pushRequest(std::string &url, std::string &data); private: - TCPSocket &m_socket; + void get(std::string &url); + void post(std::string &url, char* data); + void postFile(std::string &url, std::string &file); + HTTPRequest popRequest(); + void sendHeaders(); + TCPSocket *m_socket; + HTTPHeaders m_recv_headers; + HTTPHeaders m_send_headers; + std::string m_cookie; + std::vector m_requests; + JMutex m_req_mutex; + HTTPClientThread m_thread; + Client *m_client; }; +#endif #endif diff --git a/src/player.h b/src/player.h index a763ec1..627065e 100644 --- a/src/player.h +++ b/src/player.h @@ -138,6 +138,7 @@ public: { light = light_at_pos; } + virtual std::string getCookie() {return "";} virtual void updateAnim(u8 anim_id) {} @@ -194,7 +195,8 @@ class ServerRemotePlayer : public Player { public: ServerRemotePlayer(): - m_addr("") + m_addr(""), + m_cookie("") { } virtual ~ServerRemotePlayer() @@ -214,7 +216,18 @@ public: std::string getAddress() {return m_addr;} private: + virtual std::string getCookie() + { + if (m_cookie == "") { + char buff[256]; + snprintf(buff,256,"%s%.4X",getName(),peer_id); + m_cookie = std::string(buff); + } + return m_cookie; + } + std::string m_addr; + std::string m_cookie; }; #ifndef SERVER diff --git a/src/server.cpp b/src/server.cpp index fcd0e2e..b78f765 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -4090,6 +4090,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) actionstream<getName()<<" respawns at " <getPosition()/BS)<peer_id, player->hp); } +void Server::SendPlayerCookie(Player *player) +{ + std::ostringstream os(std::ios_base::binary); + std::string c = player->getCookie(); + + writeU16(os, TOCLIENT_HAVECOOKIE); + writeU16(os, c.size()); + os.write(c.c_str(),c.size()); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(player->peer_id, 0, data, true); +} + void Server::SendMovePlayer(Player *player) { DSTACK(__FUNCTION_NAME); diff --git a/src/server.h b/src/server.h index 58fb618..e19e9f9 100644 --- a/src/server.h +++ b/src/server.h @@ -448,6 +448,14 @@ public: } } + std::string getPlayerCookie(std::string &name) + { + Player *p = m_env.getPlayer(name.c_str()); + if (p == NULL) + return ""; + return p->getCookie(); + } + // Saves g_settings to configpath given at initialization void saveConfig(); @@ -511,6 +519,7 @@ private: void SendChatMessage(u16 peer_id, const std::wstring &message); void BroadcastChatMessage(const std::wstring &message); void SendPlayerHP(Player *player); + void SendPlayerCookie(Player *player); /* Send a node removal/addition event to all clients except ignore_id. Additionally, if far_players!=NULL, players further away than diff --git a/src/servermain.cpp b/src/servermain.cpp index ced6e68..6407c7c 100644 --- a/src/servermain.cpp +++ b/src/servermain.cpp @@ -74,6 +74,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "log.h" +#include "http.h" /* Settings. @@ -363,9 +364,13 @@ int main(int argc, char *argv[]) // Create server Server server(map_dir.c_str(), configpath); server.start(port); + HTTPServer http_server(server); + if (g_settings->getBool("enable_http")) + http_server.start(port); // Run server dedicated_server_loop(server, kill); + http_server.stop(); } //try catch(con::PeerNotFoundException &e) diff --git a/src/tile.cpp b/src/tile.cpp index d52bb2e..49d31eb 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -113,7 +113,7 @@ std::string getTexturePath(const std::string &filename) Check from cache */ bool incache = g_texturename_to_path_cache.get(filename, &fullpath); - if(incache) + if (incache && fullpath != "") return fullpath; std::string rel_path = std::string("textures")+DIR_DELIM+filename; From f13cf019809aaa37b5a7098e6dd6e7ad2f696edb Mon Sep 17 00:00:00 2001 From: darkrose Date: Sun, 6 Oct 2013 00:30:07 +1000 Subject: [PATCH 3/6] http part 3 --- data/html/footer.html | 4 + data/html/header.html | 52 ++++ src/CMakeLists.txt | 1 + src/content_cao.cpp | 2 +- src/farmesh.cpp | 2 +- src/game.cpp | 1 + src/http.cpp | 618 +++++++++++++++++++++++++++++++++++------- src/http.h | 78 ++++-- src/main.cpp | 1 + src/path.cpp | 191 +++++++++++++ src/path.h | 39 +++ src/player.cpp | 2 +- src/server.h | 13 + src/sha1.cpp | 27 ++ src/sha1.h | 4 +- src/tile.cpp | 188 +------------ src/tile.h | 17 -- 17 files changed, 911 insertions(+), 329 deletions(-) create mode 100644 data/html/footer.html create mode 100644 data/html/header.html create mode 100644 src/path.cpp create mode 100644 src/path.h diff --git a/data/html/footer.html b/data/html/footer.html new file mode 100644 index 0000000..0f47b89 --- /dev/null +++ b/data/html/footer.html @@ -0,0 +1,4 @@ + + + + diff --git a/data/html/header.html b/data/html/header.html new file mode 100644 index 0000000..565a261 --- /dev/null +++ b/data/html/header.html @@ -0,0 +1,52 @@ + + + + Minetest Classic + + + + +
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 01ac0ae..3f3b66c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -129,6 +129,7 @@ set(common_SRCS base64.cpp ban.cpp http.cpp + path.cpp ) # This gives us the icon diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 3d30eec..1929930 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "content_cao.h" -#include "tile.h" +#include "path.h" #include "environment.h" #include "settings.h" #include diff --git a/src/farmesh.cpp b/src/farmesh.cpp index aea63cb..537746c 100644 --- a/src/farmesh.cpp +++ b/src/farmesh.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "noise.h" #include "map.h" #include "client.h" - +#include "path.h" #include "mapgen.h" FarMesh::FarMesh( diff --git a/src/game.cpp b/src/game.cpp index fda7e6f..ebf0812 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -43,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gettext.h" #include "log.h" #include "filesys.h" +#include "path.h" /* TODO: Move content-aware stuff to separate file by adding properties diff --git a/src/http.cpp b/src/http.cpp index 1bc7ada..1aa717a 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -21,6 +21,9 @@ #include "socket.h" #include "http.h" +#include "main.h" +#include "settings.h" +#include "filesys.h" #include "debug.h" #include #include @@ -30,7 +33,10 @@ #include "connection.h" #include "log.h" #include "sha1.h" +#include "path.h" +#include "config.h" +/* server thread main loop */ void * HTTPServerThread::Thread() { ThreadStarted(); @@ -59,16 +65,19 @@ void * HTTPServerThread::Thread() * HTTPServer */ +/* constructor */ HTTPServer::HTTPServer(Server &server): m_thread(this) { m_server = &server; } +/* destructor */ HTTPServer::~HTTPServer() { } +/* start the server running */ void HTTPServer::start(u16 port) { DSTACK(__FUNCTION_NAME); @@ -86,6 +95,7 @@ void HTTPServer::start(u16 port) infostream<<"HTTPServer: Started on port "<WaitData(50)) { @@ -104,19 +115,22 @@ void HTTPServer::step() m_peers.push_back(c); } - for (std::vector::iterator it = m_peers.begin(); it != m_peers.end(); ++it) { - HTTPRemoteClient *c = *it; + std::vector p; + + p.swap(m_peers); + + for (std::vector::iterator i = p.begin(); i != p.end(); ++i) { + HTTPRemoteClient *c = *i; try{ if (c->receive()) { - m_peers.erase(it); delete c; + continue; } }catch (SocketException &e) { - // assume it's closed - m_peers.erase(it); delete c; continue; } + m_peers.push_back(c); } } @@ -124,119 +138,443 @@ void HTTPServer::step() * HTTPRemoteClient */ +/* destructor */ HTTPRemoteClient::~HTTPRemoteClient() { + delete m_socket; } +/* receive and handle data from a remote http client */ int HTTPRemoteClient::receive() { - char buff[2048]; - if (!m_socket->WaitData(30)) + int r = fillBuffer(); + if (!r) return 0; - int r = m_socket->Receive(buff,2048); if (r<1) return 1; m_recv_headers.clear(); m_send_headers.clear(); - int h = m_recv_headers.read(buff,r); - if (h == 1) { + int h = m_recv_headers.read(m_buff,r); + if (h == -1) { return 1; - }else if (h == 2) { - //if (m_recv_headers.url() == "") - //return 1; - //while (m_socket->WaitData(1000)) { - //r = m_socket->Receive(buff,2048); - //if (r<1) - //return 1; - //h = m_recv_headers.read(buff,r); - //if (!h) - //break; - //if (h == 1) - //return 1; - //} - //if (h) + }else if (h == -2) { + if (m_recv_headers.getUrl() == "") + return 1; + while (m_socket->WaitData(1000)) { + r = fillBuffer(); + if (r<1) + return 1; + h = m_recv_headers.read(m_buff,r); + if (h > -1) + break; + if (h == -1) + return 1; + } + if (h < 0) return 1; } - if (m_recv_headers.cookie() != "" && m_recv_headers.get("User") != "") { - if (m_recv_headers.cookie() == m_server->getPlayerCookie(m_recv_headers.get("User"))) { - m_send_headers.setCookie(m_recv_headers.cookie()); + if (m_recv_headers.getCookie() != "" && m_recv_headers.getHeader("User") != "") { + if (m_recv_headers.getCookie() == m_server->getPlayerCookie(m_recv_headers.getHeader("User"))) { + m_send_headers.setCookie(m_recv_headers.getCookie()); m_auth = true; } } - //std::string u; - //for (int i=0; (u = m_recv_headers.url(i)) != ""; i++) { - //printf("%d: '%s'\n",i,u.c_str()); - //} + if (m_recv_headers.getUrl(0) == "texture") { + return handleTexture(); + }else if (m_recv_headers.getUrl(0) == "player") { + return handlePlayer(); + }else if (m_recv_headers.getUrl(0) == "") { + return handleIndex(); + } - - //setResponse("404 Not Found"); - //std::string html("404 Not Found

404 Not Found

"); - //send(html); - send((char*)"grar"); - - return 0;//m_recv_headers.keepAlive() == true ? 0 : 1; + return handleSpecial("404 Not Found"); } +/* read data from a remote http client */ +int HTTPRemoteClient::read(char* buff, int size) +{ + if (size > 2048) { + int r = 0; + int l; + int c = 2048; + int s = size; + while (fillBuffer()) { + c = 2048; + if (sWaitData(30)) + return m_end; + + return m_end+m_socket->Receive(m_buff+m_end,l); +} + +/* handle /player/ url's */ +int HTTPRemoteClient::handlePlayer() +{ + char buff[2048]; + /* player list */ + if (m_recv_headers.getUrl(1) == "") { + core::list players = m_server->getGameServer()->getPlayers(); + std::string html("

Players

\n"); + for (core::list::Iterator i = players.begin(); i != players.end(); i++) { + Player *player = *i; + html += "

getName(); + html += "\" class=\"secret\">"; + html += player->getName(); + html += "

"; + html += "

getName(); + html += "/skin\" />

"; + snprintf(buff, 2048,"% .1f, % .1f, % .1f",player->getPosition().X/BS,player->getPosition().Y/BS,player->getPosition().Z/BS); + if (player->peer_id == 0) { + html += "

Offline

"; + html += "

Last seen at: "; + }else{ + html += "

Online

"; + html += "

Currently at: "; + } + html += buff; + html += "

Privileges: "; + html += m_server->getPlayerPrivs(player->getName()); + html += "

"; + } + sendHTML((char*)html.c_str()); + return 1; + /* player skin */ + }else if (m_recv_headers.getUrl(2) == "skin") { + std::string data_path = g_settings->get("data_path"); + if (data_path == "") + data_path = "data"; + std::string file = data_path + DIR_DELIM + "textures" + DIR_DELIM + "players" + DIR_DELIM + "player_" + m_recv_headers.getUrl(1) + ".png"; + /* compare hash */ + if (m_recv_headers.getUrl(3) != "") { + FILE *f; + f = fopen(file.c_str(),"rb"); + if (!f) + return handleSpecial("204 No Content"); + fclose(f); + SHA1 s; + s.addFile(file.c_str()); + s.getDigest(buff); + if (std::string(buff) == m_recv_headers.getUrl(3)) { + return handleSpecial("204 No Content"); + }else if (m_auth && m_recv_headers.getHeader("User") == m_recv_headers.getUrl(1)) { + return handleSpecial("304 Not Modified"); + } + m_send_headers.setHeader("Content-Type","image/png"); + sendFile(file); + return 1; + } + /* get file */ + if (m_recv_headers.getMethod() != "PUT") { + m_send_headers.setHeader("Content-Type","image/png"); + sendFile(file); + return 1; + } + /* put only works for the owner */ + if (!m_auth || m_recv_headers.getHeader("User") != m_server->getPlayerFromCookie(m_recv_headers.getCookie())) + return handleSpecial("405 Method Not Allowed"); + FILE *f; + f = fopen(file.c_str(),"wb"); + if (!f) + return handleSpecial("500 Internal Server Error"); + size_t s = m_recv_headers.getLength(); + if (!s) + return handleSpecial("411 Length Required"); + size_t l; + size_t c = 2048; + if (c > s) + c = s; + if (c) { + while ((l = read(buff,c)) > 0) { + s -= l; + c = fwrite(buff,1,l,f); + if (c != l) { + fclose(f); + return handleSpecial("500 Internal Server Error"); + } + c = 2048; + if (c > s) + c = s; + if (!c) + break; + } + } + fclose(f); + return handleSpecial("201 Created"); + }else if (m_server->getGameServer()->getPlayer(m_recv_headers.getUrl(1))) { + std::string html("

Players

\n"); + Player *player = m_server->getGameServer()->getPlayer(m_recv_headers.getUrl(1)); + html += "

"; + html += player->getName(); + html += "

"; + html += "

getName(); + html += "/skin\" />

"; + snprintf(buff, 2048,"% .1f, % .1f, % .1f",player->getPosition().X/BS,player->getPosition().Y/BS,player->getPosition().Z/BS); + if (player->peer_id == 0) { + html += "

Offline

"; + html += "

Last seen at: "; + }else{ + html += "

Online

"; + html += "

Currently at: "; + } + html += buff; + html += "

Privileges: "; + html += m_server->getPlayerPrivs(player->getName()); + html += "

"; + sendHTML((char*)html.c_str()); + return 1; + } + return handleSpecial("404 Not Found"); +} + +/* handle /texture/ url's */ +int HTTPRemoteClient::handleTexture() +{ + std::string file = getTexturePath(m_recv_headers.getUrl(1)); + m_send_headers.setHeader("Content-Type","image/png"); + sendFile(file); + return 1; +} + +/* handle /model/ url's */ +int HTTPRemoteClient::handleModel() +{ + std::string file = getModelPath(m_recv_headers.getUrl(1)); + m_send_headers.setHeader("Content-Type","application/octet-stream"); + sendFile(file); + return 1; +} + +/* handle /map/// url's */ +int HTTPRemoteClient::handleMap() +{ + return handleSpecial("404 Not Found"); +} + +/* handle / url's */ +int HTTPRemoteClient::handleIndex() +{ + int c = 0; + std::string html("

"); + html += g_settings->get("motd"); + html += "

Version: "; + html += VERSION_STRING; + html += "
Players: "; + core::list players = m_server->getGameServer()->getPlayers(); + for (core::list::Iterator i = players.begin(); i != players.end(); i++) { + Player *player = *i; + if (player->peer_id != 0) { + if (c++) + html += ", "; + html += "getName(); + html += "\" class=\"secret\">"; + html += player->getName(); + html += ""; + } + } + html += "

"; + sendHTML((char*)html.c_str()); + return 1; +} + +/* simple wrapper for sending html content and/or errors */ +int HTTPRemoteClient::handleSpecial(const char* response, std::string content) +{ + setResponse(response); + std::string html("

"); + html += std::string(response) + "

" + content; + sendHTML(html); + return 1; +} + +/* send text data to a remote http client */ void HTTPRemoteClient::send(char* data) { int l = strlen(data); - m_send_headers.set("Content-Type","text/plain"); + m_send_headers.setHeader("Content-Type","text/plain"); m_send_headers.setLength(l); sendHeaders(); m_socket->Send(data,l); } +/* send html data to a remote http client */ void HTTPRemoteClient::sendHTML(char* data) { - int l = strlen(data); - m_send_headers.set("Content-Type","text/html"); - m_send_headers.setLength(l); + FILE *h; + FILE *f; + int l[4]; + char* b; + std::string data_path = g_settings->get("data_path"); + if (data_path == "") + data_path = "data"; + std::string file = data_path + DIR_DELIM + "html" + DIR_DELIM + "header.html"; + h = fopen(file.c_str(),"r"); + if (!h && data_path != "data") { + file = std::string("data") + DIR_DELIM + "html" + DIR_DELIM + "header.html"; + h = fopen(file.c_str(),"r"); + } + file = data_path + DIR_DELIM + "html" + DIR_DELIM + "footer.html"; + f = fopen(file.c_str(),"r"); + if (!f && data_path != "data") { + file = std::string("data") + DIR_DELIM + "html" + DIR_DELIM + "footer.html"; + f = fopen(file.c_str(),"r"); + } + + if (h) { + fseek(h,0,SEEK_END); + l[0] = ftell(h); + fseek(h,0,SEEK_SET); + }else{ + l[0] = 0; + } + l[1] = strlen(data); + if (f) { + fseek(f,0,SEEK_END); + l[2] = ftell(f); + fseek(f,0,SEEK_SET); + }else{ + l[2] = 0; + } + + if (l[0] > l[2]) { + b = (char*)alloca(l[0]); + }else{ + b = (char*)alloca(l[2]); + } + + l[3] = l[0]+l[1]+l[2]; + m_send_headers.setHeader("Content-Type","text/html"); + m_send_headers.setLength(l[3]); sendHeaders(); - m_socket->Send(data,l); + if (h) { + l[3] = fread(b,1,l[0],h); + m_socket->Send(b,l[3]); + fclose(h); + } + m_socket->Send(data,l[1]); + if (f) { + l[3] = fread(b,1,l[2],f); + m_socket->Send(b,l[3]); + fclose(f); + } } +/* send a file to a remote http client */ void HTTPRemoteClient::sendFile(std::string &file) { - m_send_headers.set("Content-Type","text/plain"); - m_send_headers.setLength(0); + FILE *f; + f = fopen(file.c_str(),"rb"); + if (!f) { + std::string data_path = g_settings->get("data_path"); + if (data_path == "") + data_path = "data"; + if (file.substr(data_path.size()+1,24) == "textures/players/player_") { + m_send_headers.setHeader("Location","/texture/character.png"); + handleSpecial("303 See Other","

/texture/character.png

"); + return; + } + handleSpecial("404 Not Found"); + return; + } + fseek(f,0,SEEK_END); + size_t l = ftell(f); + fseek(f,0,SEEK_SET); + + m_send_headers.setLength(l); sendHeaders(); + + char buff[1024]; + while ((l = fread(buff,1,1024,f)) > 0) { + m_socket->Send(buff,l); + } + + fclose(f); } +/* send response headers to a remote http client */ void HTTPRemoteClient::sendHeaders() { std::string v; int s; char buff[1024]; - //v = m_response; - //if (v == "") - //v = std::string("200 OK"); + v = m_response; + if (v == "") + v = std::string("200 OK"); - //s = snprintf(buff,1024,"HTTP/1.1 %s\r\n",v.c_str()); - //m_socket->Send(buff,s); - m_socket->Send("HTTP/1.1 200 OK\r\n",17); + s = snprintf(buff,1024,"HTTP/1.0 %s\r\n",v.c_str()); + m_socket->Send(buff,s); + //m_socket->Send("HTTP/1.0 200 OK\r\n",17); - //v = m_send_headers.get("Content-Type"); - //if (v == "") { + v = m_send_headers.getHeader("Content-Type"); + if (v == "") { m_socket->Send("Content-Type: text/plain\r\n",26); - //}else{ - //s = snprintf(buff,1024,"Content-Type: %s\r\n",v.c_str()); - //m_socket->Send(buff,s); - //} + }else{ + s = snprintf(buff,1024,"Content-Type: %s\r\n",v.c_str()); + m_socket->Send(buff,s); + } s = m_send_headers.length(); s = snprintf(buff,1024,"Content-Length: %d\r\n",s); m_socket->Send(buff,s); - //v = m_send_headers.cookie(); - //if (v != "") { - //s = snprintf(buff,1024,"Set-Cookie: MTID=%s\r\n",v.c_str()); - //m_socket->Send(buff,s); - //} + v = m_send_headers.getCookie(); + if (v != "") { + s = snprintf(buff,1024,"Set-Cookie: MTID=%s\r\n",v.c_str()); + m_socket->Send(buff,s); + } + + v = m_send_headers.getHeader("Location"); + if (v != "") { + s = snprintf(buff,1024,"Location: %s\r\n",v.c_str()); + m_socket->Send(buff,s); + } + + v = m_send_headers.getHeader("Refresh"); + if (v != "") { + s = snprintf(buff,1024,"Refresh: %s\r\n",v.c_str()); + m_socket->Send(buff,s); + } m_socket->Send("\r\n",2); } @@ -244,6 +582,7 @@ void HTTPRemoteClient::sendHeaders() #ifndef SERVER #include "client.h" +/* main loop for the client http fetcher */ void * HTTPClientThread::Thread() { ThreadStarted(); @@ -272,6 +611,7 @@ void * HTTPClientThread::Thread() * HTTPClient */ +/* constructor */ HTTPClient::HTTPClient(Client *client): m_cookie(""), m_thread(this) @@ -280,10 +620,12 @@ HTTPClient::HTTPClient(Client *client): m_req_mutex.Init(); } +/* destructor */ HTTPClient::~HTTPClient() { } +/* start the client http fetcher thread */ void HTTPClient::start(const Address &address) { DSTACK(__FUNCTION_NAME); @@ -301,6 +643,7 @@ void HTTPClient::start(const Address &address) infostream<<"HTTPClient: Started"< class HTTPServer; @@ -57,34 +58,49 @@ public: void clear() { m_contentLength = 0; - m_keepalive = false; m_cookie = std::string(""); m_url = std::string(""); m_url_split.clear(); } - int read(char* buff, int length); + virtual int read(char* buff, int length) = 0; u32 length() {return m_contentLength;} - bool keepAlive() {return m_keepalive;} - std::string &cookie() {return m_cookie;} - std::string &url() {return m_url;} - std::string &url(int index) {return m_url_split[index];} - std::string &get(std::string name) {return m_headers[name];} - void set(std::string name, std::string value) {m_headers[name] = value;} - void setLength(u32 length) {m_contentLength = length;} - void setKeepAlive(bool ka) {m_keepalive = ka;} + std::string getCookie() {return m_cookie;} + std::string getUrl() {return m_url;} + std::string getUrl(unsigned int index) { if (m_url_split.size() > index) return m_url_split[index]; return std::string("");} + std::string getHeader(std::string name) {return m_headers[name];} + std::string getMethod() {return m_method;} + u32 getLength() {return m_contentLength;} + void setCookie(std::string cookie) {std::string c(cookie.c_str()); m_cookie = c;} void setUrl(std::string url) {std::string u(url); m_url = u;} + void addUrl(std::string url) {m_url_split.push_back(url);} + void setHeader(std::string name, std::string value) {m_headers[name] = value;} void setMethod(std::string method) {std::string m(method); m_method = m;} - void setProtocol(std::string proto) {std::string p(proto); m_protocol = p;} + void setLength(u32 length) {m_contentLength = length;} private: std::map m_headers; u32 m_contentLength; - bool m_keepalive; std::string m_cookie; std::string m_url; std::vector m_url_split; std::string m_method; - std::string m_protocol; +}; + +class HTTPRequestHeaders : public HTTPHeaders +{ +public: + virtual int read(char* buff, int length); +private: +}; + +class HTTPResponseHeaders : public HTTPHeaders +{ +public: + virtual int read(char* buff, int length); + void setResponse(int r) {m_response = r;} + int getResponse() {return m_response;} +private: + int m_response; }; class HTTPRemoteClient @@ -96,19 +112,36 @@ public: { m_socket = sock; m_server = server; + m_start = 0; + m_end = 0; } ~HTTPRemoteClient(); int receive(); +private: + int read(char* buff, int size); + int fillBuffer(); + void sendHeaders(); + + int handlePlayer(); + int handleTexture(); + int handleModel(); + int handleMap(); + int handleIndex(); + int handleSpecial(const char* response, std::string content); + int handleSpecial(const char* response) {return handleSpecial(response,"");} + void send(char* data); void send(std::string &data) {send((char*)data.c_str());} void sendHTML(char* data); void sendHTML(std::string &data) {sendHTML((char*)data.c_str());} void sendFile(std::string &file); void setResponse(const char* response) {std::string r(response); m_response = r;} -private: - void sendHeaders(); - HTTPHeaders m_recv_headers; - HTTPHeaders m_send_headers; + + char m_buff[2048]; + int m_start; + int m_end; + HTTPRequestHeaders m_recv_headers; + HTTPResponseHeaders m_send_headers; std::string m_response; bool m_auth; TCPSocket *m_socket; @@ -123,7 +156,10 @@ public: void start(u16 port); void stop(); void step(); - std::string getPlayerCookie(std::string &name) {return m_server->getPlayerCookie(name);} + std::string getPlayerCookie(std::string name) {return m_server->getPlayerCookie(name);} + std::string getPlayerFromCookie(std::string cookie) {return m_server->getPlayerFromCookie(cookie);} + std::string getPlayerPrivs(std::string name) {return privsToString(m_server->getPlayerAuthPrivs(name));} + Server *getGameServer() {return m_server;} private: TCPSocket *m_socket; std::vector m_peers; @@ -192,12 +228,12 @@ public: private: void get(std::string &url); void post(std::string &url, char* data); - void postFile(std::string &url, std::string &file); + void put(std::string &url, std::string &file); HTTPRequest popRequest(); void sendHeaders(); TCPSocket *m_socket; - HTTPHeaders m_recv_headers; - HTTPHeaders m_send_headers; + HTTPResponseHeaders m_recv_headers; + HTTPRequestHeaders m_send_headers; std::string m_cookie; std::vector m_requests; JMutex m_req_mutex; diff --git a/src/main.cpp b/src/main.cpp index ceb7669..b121928 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -439,6 +439,7 @@ Doing currently: #include "settings.h" #include "profiler.h" #include "log.h" +#include "path.h" // This makes textures ITextureSource *g_texturesource = NULL; diff --git a/src/path.cpp b/src/path.cpp new file mode 100644 index 0000000..81d6726 --- /dev/null +++ b/src/path.cpp @@ -0,0 +1,191 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "path.h" +#include "debug.h" +#include "main.h" // for g_settings +#include "filesys.h" +#include "utility.h" +#include "settings.h" + + +/* + A cache from texture name to texture path +*/ +MutexedMap g_texturename_to_path_cache; +MutexedMap g_modelname_to_path_cache; + +/* + Replaces the filename extension. + eg: + std::string image = "a/image.png" + replace_ext(image, "jpg") + -> image = "a/image.jpg" + Returns true on success. +*/ +static bool replace_ext(std::string &path, const char *ext) +{ + if(ext == NULL) + return false; + // Find place of last dot, fail if \ or / found. + s32 last_dot_i = -1; + for(s32 i=path.size()-1; i>=0; i--) + { + if(path[i] == '.') + { + last_dot_i = i; + break; + } + + if(path[i] == '\\' || path[i] == '/') + break; + } + // If not found, return an empty string + if(last_dot_i == -1) + return false; + // Else make the new path + path = path.substr(0, last_dot_i+1) + ext; + return true; +} + +/* + Find out the full path of an image by trying different filename + extensions. + + If failed, return "". +*/ +static std::string getImagePath(std::string path) +{ + // A NULL-ended list of possible image extensions + const char *extensions[] = { + "png", "jpg", "bmp", "tga", + "pcx", "ppm", "psd", "wal", "rgb", + NULL + }; + + const char **ext = extensions; + do{ + bool r = replace_ext(path, *ext); + if(r == false) + return ""; + if(fs::PathExists(path)) + return path; + } + while((++ext) != NULL); + + return ""; +} + +/* + Gets the path to a texture by first checking if the texture exists + in data_path and if not, using the default data path. + + Checks all supported extensions by replacing the original extension. + + If not found, returns "". + + Utilizes a thread-safe cache. +*/ +std::string getTexturePath(const std::string &filename) +{ + std::string fullpath = ""; + /* + Check from cache + */ + bool incache = g_texturename_to_path_cache.get(filename, &fullpath); + if (incache && fullpath != "") + return fullpath; + + std::string rel_path = std::string("textures")+DIR_DELIM+filename; + /* + Check from data_path /textures + */ + std::string data_path = g_settings->get("data_path"); + if(data_path != "") + { + std::string testpath = data_path + DIR_DELIM + rel_path; + // Check all filename extensions. Returns "" if not found. + fullpath = getImagePath(testpath); + } + + /* + Check from default data directory + */ + if(fullpath == "") + { + std::string testpath = porting::path_data + DIR_DELIM + rel_path; + // Check all filename extensions. Returns "" if not found. + fullpath = getImagePath(testpath); + } + + // Add to cache (also an empty result is cached) + g_texturename_to_path_cache.set(filename, fullpath); + + // Finally return it + return fullpath; +} + +/* + Gets the path to a model by first checking if the model exists + in data_path and if not, using the default data path. + + Checks all supported extensions by replacing the original extension. + + If not found, returns "". + + Utilizes a thread-safe cache. +*/ +std::string getModelPath(const std::string &filename) +{ + std::string fullpath = ""; + /* + Check from cache + */ + bool incache = g_modelname_to_path_cache.get(filename, &fullpath); + if(incache) + return fullpath; + + std::string rel_path = std::string("models")+DIR_DELIM+filename; + /* + Check from data_path /models + */ + std::string data_path = g_settings->get("data_path"); + if(data_path != "") + { + std::string testpath = data_path + DIR_DELIM + rel_path; + if(fs::PathExists(testpath)) + fullpath = std::string(testpath); + } + + /* + Check from default data directory + */ + if(fullpath == "") + { + std::string testpath = porting::path_data + DIR_DELIM + rel_path; + if(fs::PathExists(testpath)) + fullpath = std::string(testpath); + } + + // Add to cache (also an empty result is cached) + g_modelname_to_path_cache.set(filename, fullpath); + + // Finally return it + return fullpath; +} diff --git a/src/path.h b/src/path.h new file mode 100644 index 0000000..fd4ea2f --- /dev/null +++ b/src/path.h @@ -0,0 +1,39 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef PATH_HEADER +#define PATH_HEADER + +#include "filesys.h" +#include + +/* + Gets the path to a texture/model by first checking if it exists + in data_path and if not, using the default data path. + + Checks all supported extensions by replacing the original extension. + + If not found, returns "". + + Utilizes a thread-safe cache. +*/ +std::string getTexturePath(const std::string &filename); +std::string getModelPath(const std::string &filename); + +#endif diff --git a/src/player.cpp b/src/player.cpp index cca2e78..6b82ef6 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mesh.h" #endif #include "settings.h" -#include "filesys.h" +#include "path.h" Player::Player(): touching_ground(false), diff --git a/src/server.h b/src/server.h index e19e9f9..71f5e18 100644 --- a/src/server.h +++ b/src/server.h @@ -455,6 +455,19 @@ public: return ""; return p->getCookie(); } + std::string getPlayerFromCookie(std::string &cookie) + { + core::list players = m_env.getPlayers(); + for (core::list::Iterator i = players.begin(); i != players.end(); i++) { + Player *player = *i; + if (player->getCookie() == cookie) + return player->getName(); + } + return std::string(""); + } + Player *getPlayer(std::string name) {return m_env.getPlayer(name.c_str());} + core::list getPlayers() {return m_env.getPlayers();} + core::list getPlayers(bool ign_disconnected) {return m_env.getPlayers(ign_disconnected);} // Saves g_settings to configpath given at initialization void saveConfig(); diff --git a/src/sha1.cpp b/src/sha1.cpp index 8fc6d35..ec0763a 100644 --- a/src/sha1.cpp +++ b/src/sha1.cpp @@ -169,6 +169,20 @@ void SHA1::addBytes( const char* data, int num ) } } +void SHA1::addFile(const char* file) +{ + char buff[2048]; + size_t l; + FILE *f; + f = fopen(file,"rb"); + if (!f) + return; + while ((l = fread(buff,1,2048,f)) > 0) { + addBytes(buff,l); + } + fclose(f); +} + // digest ************************************************************ unsigned char* SHA1::getDigest() { @@ -205,3 +219,16 @@ unsigned char* SHA1::getDigest() // return the digest return digest; } + +int SHA1::getDigest(char* buff) +{ + int l; + int t = 0; + unsigned char* d = getDigest(); + for (int i=0; i<20; i++) { + l = sprintf(buff,"%02x",d[i]); + buff += l; + t += l; + } + return t; +} diff --git a/src/sha1.h b/src/sha1.h index c049473..7c886bc 100644 --- a/src/sha1.h +++ b/src/sha1.h @@ -39,8 +39,10 @@ class SHA1 public: SHA1(); ~SHA1(); - void addBytes( const char* data, int num ); + void addBytes(const char* data, int num ); + void addFile(const char* file); unsigned char* getDigest(); + int getDigest(char* buff); // utility methods static Uint32 lrot( Uint32 x, int bits ); static void storeBigEndianUint32( unsigned char* byte, Uint32 num ); diff --git a/src/tile.cpp b/src/tile.cpp index 49d31eb..563138b 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -28,193 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "mapnode.h" // For texture atlas making #include "mineral.h" // For texture atlas making - -/* - A cache from texture name to texture path -*/ -MutexedMap g_texturename_to_path_cache; -MutexedMap g_modelname_to_path_cache; - -/* - Replaces the filename extension. - eg: - std::string image = "a/image.png" - replace_ext(image, "jpg") - -> image = "a/image.jpg" - Returns true on success. -*/ -static bool replace_ext(std::string &path, const char *ext) -{ - if(ext == NULL) - return false; - // Find place of last dot, fail if \ or / found. - s32 last_dot_i = -1; - for(s32 i=path.size()-1; i>=0; i--) - { - if(path[i] == '.') - { - last_dot_i = i; - break; - } - - if(path[i] == '\\' || path[i] == '/') - break; - } - // If not found, return an empty string - if(last_dot_i == -1) - return false; - // Else make the new path - path = path.substr(0, last_dot_i+1) + ext; - return true; -} - -/* - Find out the full path of an image by trying different filename - extensions. - - If failed, return "". -*/ -static std::string getImagePath(std::string path) -{ - // A NULL-ended list of possible image extensions - const char *extensions[] = { - "png", "jpg", "bmp", "tga", - "pcx", "ppm", "psd", "wal", "rgb", - NULL - }; - - const char **ext = extensions; - do{ - bool r = replace_ext(path, *ext); - if(r == false) - return ""; - if(fs::PathExists(path)) - return path; - } - while((++ext) != NULL); - - return ""; -} - -/* - Gets the path to a texture by first checking if the texture exists - in data_path and if not, using the default data path. - - Checks all supported extensions by replacing the original extension. - - If not found, returns "". - - Utilizes a thread-safe cache. -*/ -std::string getTexturePath(const std::string &filename) -{ - std::string fullpath = ""; - /* - Check from cache - */ - bool incache = g_texturename_to_path_cache.get(filename, &fullpath); - if (incache && fullpath != "") - return fullpath; - - std::string rel_path = std::string("textures")+DIR_DELIM+filename; - - /* - Check from user data directory - */ - if(fullpath == "") - { - std::string testpath = porting::path_userdata + DIR_DELIM + rel_path; - // Check all filename extensions. Returns "" if not found. - fullpath = getImagePath(testpath); - } - - /* - Check from data_path /textures - */ - std::string data_path = g_settings->get("data_path"); - if(data_path != "") - { - std::string testpath = data_path + DIR_DELIM + rel_path; - // Check all filename extensions. Returns "" if not found. - fullpath = getImagePath(testpath); - } - - /* - Check from default data directory - */ - if(fullpath == "") - { - std::string testpath = porting::path_data + DIR_DELIM + rel_path; - // Check all filename extensions. Returns "" if not found. - fullpath = getImagePath(testpath); - } - - // Add to cache (also an empty result is cached) - g_texturename_to_path_cache.set(filename, fullpath); - - // Finally return it - return fullpath; -} - -/* - Gets the path to a model by first checking if the model exists - in data_path and if not, using the default data path. - - Checks all supported extensions by replacing the original extension. - - If not found, returns "". - - Utilizes a thread-safe cache. -*/ -std::string getModelPath(const std::string &filename) -{ - std::string fullpath = ""; - /* - Check from cache - */ - bool incache = g_modelname_to_path_cache.get(filename, &fullpath); - if(incache) - return fullpath; - - std::string rel_path = std::string("models")+DIR_DELIM+filename; - - /* - Check from user data directory - */ - if(fullpath == "") - { - std::string testpath = porting::path_userdata + DIR_DELIM + rel_path; - // Check all filename extensions. Returns "" if not found. - fullpath = getImagePath(testpath); - } - - /* - Check from data_path /models - */ - std::string data_path = g_settings->get("data_path"); - if(data_path != "") - { - std::string testpath = data_path + DIR_DELIM + rel_path; - if(fs::PathExists(testpath)) - fullpath = std::string(testpath); - } - - /* - Check from default data directory - */ - if(fullpath == "") - { - std::string testpath = porting::path_data + DIR_DELIM + rel_path; - if(fs::PathExists(testpath)) - fullpath = std::string(testpath); - } - - // Add to cache (also an empty result is cached) - g_modelname_to_path_cache.set(filename, fullpath); - - // Finally return it - return fullpath; -} +#include "path.h" /* TextureSource diff --git a/src/tile.h b/src/tile.h index 1c1d135..262c2ba 100644 --- a/src/tile.h +++ b/src/tile.h @@ -31,23 +31,6 @@ using namespace jthread; tile.{h,cpp}: Texture handling stuff. */ -/* - Gets the path to a texture/model by first checking if it exists - in data_path and if not, using the default data path. - - Checks all supported extensions by replacing the original extension. - - If not found, returns "". - - Utilizes a thread-safe cache. -*/ -std::string getTexturePath(const std::string &filename); -std::string getModelPath(const std::string &filename); - -u32 parseImageTransform(const std::string& s); -core::dimension2d imageTransformDimension(u32 transform, core::dimension2d dim); -void imageTransform(u32 transform, video::IImage *src, video::IImage *dst); - /* Specifies a texture in an atlas. From 7a9a1dc7b89294213b172d51b1033532a9a6db47 Mon Sep 17 00:00:00 2001 From: darkrose Date: Wed, 16 Oct 2013 19:44:23 +1000 Subject: [PATCH 4/6] some merge fixes --- src/camera.cpp | 1 + src/tile.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/camera.cpp b/src/camera.cpp index fc4b04d..520307a 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "settings.h" +#include "path.h" Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control): m_smgr(smgr), diff --git a/src/tile.h b/src/tile.h index 262c2ba..6545059 100644 --- a/src/tile.h +++ b/src/tile.h @@ -31,6 +31,10 @@ using namespace jthread; tile.{h,cpp}: Texture handling stuff. */ +u32 parseImageTransform(const std::string& s); +core::dimension2d imageTransformDimension(u32 transform, core::dimension2d dim); +void imageTransform(u32 transform, video::IImage *src, video::IImage *dst); + /* Specifies a texture in an atlas. From f25e384a0a4da0b2d702b2ab8ca77b3e841e0b38 Mon Sep 17 00:00:00 2001 From: darkrose Date: Fri, 18 Oct 2013 23:11:58 +1000 Subject: [PATCH 5/6] get player skins transfering between client and server --- src/camera.cpp | 1 - src/client.cpp | 7 +- src/http.cpp | 372 ++++++++++++++++++++++++++++++++++++++++--------- src/http.h | 26 +++- src/path.cpp | 149 +++++++++----------- src/path.h | 13 +- src/player.cpp | 6 +- src/porting.h | 2 +- 8 files changed, 414 insertions(+), 162 deletions(-) diff --git a/src/camera.cpp b/src/camera.cpp index 520307a..c9cbc4b 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -491,7 +491,6 @@ void Camera::wield(const InventoryItem* item) m_wieldnode->setArm(); m_wieldnode_baserotation = v3f(-30, 130, 20); m_wieldnode_baseposition = v3f(45, -43, 65); - //m_wieldnode->setSprite(g_texturesource->getTextureRaw("hand.png")); m_wieldnode->setScale(v3f(40)); m_wieldnode->setVisible(true); } diff --git a/src/client.cpp b/src/client.cpp index ff2d393..e10c646 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -239,6 +239,8 @@ Client::~Client() //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out m_con.Disconnect(); } + if (g_settings->getBool("enable_http")) + m_httpclient->stop(); m_mesh_update_thread.setRun(false); while(m_mesh_update_thread.IsRunning()) @@ -1529,7 +1531,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) char buff[len+1]; is.read(buff,len); buff[len] = 0; - printf("received cookie '%s'\n",buff); + std::string c(buff); + std::string p(getLocalPlayer()->getName()); + m_httpclient->setCookie(c); + m_httpclient->pushRequest(HTTPREQUEST_SKIN_HASH,p); } else { diff --git a/src/http.cpp b/src/http.cpp index 1aa717a..461972a 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -53,6 +53,7 @@ void * HTTPServerThread::Thread() m_server->step(); }catch (con::NoIncomingDataException &e) { }catch(con::PeerNotFoundException &e) { + }catch(SendFailedException &e) { } } @@ -279,35 +280,43 @@ int HTTPRemoteClient::handlePlayer() return 1; /* player skin */ }else if (m_recv_headers.getUrl(2) == "skin") { - std::string data_path = g_settings->get("data_path"); - if (data_path == "") - data_path = "data"; - std::string file = data_path + DIR_DELIM + "textures" + DIR_DELIM + "players" + DIR_DELIM + "player_" + m_recv_headers.getUrl(1) + ".png"; - /* compare hash */ - if (m_recv_headers.getUrl(3) != "") { - FILE *f; - f = fopen(file.c_str(),"rb"); - if (!f) - return handleSpecial("204 No Content"); - fclose(f); - SHA1 s; - s.addFile(file.c_str()); - s.getDigest(buff); - if (std::string(buff) == m_recv_headers.getUrl(3)) { - return handleSpecial("204 No Content"); - }else if (m_auth && m_recv_headers.getHeader("User") == m_recv_headers.getUrl(1)) { - return handleSpecial("304 Not Modified"); - } - m_send_headers.setHeader("Content-Type","image/png"); - sendFile(file); - return 1; - } - /* get file */ + std::string file = getPath("player","player_" + m_recv_headers.getUrl(1) + ".png",true); if (m_recv_headers.getMethod() != "PUT") { + if (file == "") { + if (m_auth && m_recv_headers.getHeader("User") == m_recv_headers.getUrl(1)) + return handleSpecial("304 Not Modified"); + if (m_recv_headers.getUrl(3) == "hash") + return handleSpecial("204 No Content"); + m_send_headers.setHeader("Location","/texture/character.png"); + return handleSpecial("303 See Other","

/texture/character.png

"); + } + /* compare hash */ + if (m_recv_headers.getUrl(3) == "hash") { + FILE *f; + f = fopen(file.c_str(),"rb"); + if (!f) + return handleSpecial("204 No Content"); + fseek(f,0,SEEK_END); + size_t l = ftell(f); + fclose(f); + if (!l) + return handleSpecial("204 No Content"); + SHA1 s; + s.addFile(file.c_str()); + s.getDigest(buff); + if (std::string(buff) == m_recv_headers.getUrl(4)) { + return handleSpecial("204 No Content"); + }else if (m_auth && m_recv_headers.getHeader("User") == m_recv_headers.getUrl(1)) { + return handleSpecial("304 Not Modified"); + } + } + /* get file */ m_send_headers.setHeader("Content-Type","image/png"); sendFile(file); return 1; } + if (file == "") + file = getPath("player","player_" + m_recv_headers.getUrl(1) + ".png",false); /* put only works for the owner */ if (!m_auth || m_recv_headers.getHeader("User") != m_server->getPlayerFromCookie(m_recv_headers.getCookie())) return handleSpecial("405 Method Not Allowed"); @@ -316,15 +325,19 @@ int HTTPRemoteClient::handlePlayer() if (!f) return handleSpecial("500 Internal Server Error"); size_t s = m_recv_headers.getLength(); - if (!s) + if (!s) { + fclose(f); return handleSpecial("411 Length Required"); + } size_t l; size_t c = 2048; + size_t t = 0; if (c > s) c = s; if (c) { while ((l = read(buff,c)) > 0) { s -= l; + t += l; c = fwrite(buff,1,l,f); if (c != l) { fclose(f); @@ -337,6 +350,7 @@ int HTTPRemoteClient::handlePlayer() break; } } +printf("recieved: %lu %lu\n",s,l); fclose(f); return handleSpecial("201 Created"); }else if (m_server->getGameServer()->getPlayer(m_recv_headers.getUrl(1))) { @@ -369,7 +383,9 @@ int HTTPRemoteClient::handlePlayer() /* handle /texture/ url's */ int HTTPRemoteClient::handleTexture() { - std::string file = getTexturePath(m_recv_headers.getUrl(1)); + std::string file = getPath("texture",m_recv_headers.getUrl(1),true); + if (file == "") + return handleSpecial("404 Not Found"); m_send_headers.setHeader("Content-Type","image/png"); sendFile(file); return 1; @@ -378,7 +394,9 @@ int HTTPRemoteClient::handleTexture() /* handle /model/ url's */ int HTTPRemoteClient::handleModel() { - std::string file = getModelPath(m_recv_headers.getUrl(1)); + std::string file = getPath("model",m_recv_headers.getUrl(1),true); + if (file == "") + return handleSpecial("404 Not Found"); m_send_headers.setHeader("Content-Type","application/octet-stream"); sendFile(file); return 1; @@ -444,21 +462,10 @@ void HTTPRemoteClient::sendHTML(char* data) FILE *f; int l[4]; char* b; - std::string data_path = g_settings->get("data_path"); - if (data_path == "") - data_path = "data"; - std::string file = data_path + DIR_DELIM + "html" + DIR_DELIM + "header.html"; + std::string file = getPath("html","header.html",true); h = fopen(file.c_str(),"r"); - if (!h && data_path != "data") { - file = std::string("data") + DIR_DELIM + "html" + DIR_DELIM + "header.html"; - h = fopen(file.c_str(),"r"); - } - file = data_path + DIR_DELIM + "html" + DIR_DELIM + "footer.html"; + file = getPath("html","footer.html",true); f = fopen(file.c_str(),"r"); - if (!f && data_path != "data") { - file = std::string("data") + DIR_DELIM + "html" + DIR_DELIM + "footer.html"; - f = fopen(file.c_str(),"r"); - } if (h) { fseek(h,0,SEEK_END); @@ -505,28 +512,24 @@ void HTTPRemoteClient::sendFile(std::string &file) FILE *f; f = fopen(file.c_str(),"rb"); if (!f) { - std::string data_path = g_settings->get("data_path"); - if (data_path == "") - data_path = "data"; - if (file.substr(data_path.size()+1,24) == "textures/players/player_") { - m_send_headers.setHeader("Location","/texture/character.png"); - handleSpecial("303 See Other","

/texture/character.png

"); - return; - } handleSpecial("404 Not Found"); return; } fseek(f,0,SEEK_END); size_t l = ftell(f); fseek(f,0,SEEK_SET); + size_t s = l; + size_t t = 0; m_send_headers.setLength(l); sendHeaders(); char buff[1024]; while ((l = fread(buff,1,1024,f)) > 0) { + t += l; m_socket->Send(buff,l); } +printf("sent: %lu %lu\n",s,t); fclose(f); } @@ -632,9 +635,7 @@ void HTTPClient::start(const Address &address) // Stop thread if already running m_thread.stop(); - m_socket = new TCPSocket(); - m_socket->setTimeoutMs(30); - m_socket->Connect(address); + m_address = address; // Start thread m_thread.setRun(true); @@ -649,7 +650,6 @@ void HTTPClient::stop() DSTACK(__FUNCTION_NAME); m_thread.stop(); - delete m_socket; infostream<<"HTTPClient: Threads stopped"< 0 || m_cookie == "") + //m_client->sendWantCookie(); + sleep(1); + return; + } + + std::vector p; + + m_req_mutex.Lock(); + p.swap(m_requests); + m_req_mutex.Unlock(); + + for (std::vector::iterator i = p.begin(); i != p.end(); ++i) { + HTTPRequest q = *i; + m_socket = new TCPSocket(); + if (m_socket == NULL) { + m_req_mutex.Lock(); + m_requests.push_back(q); + m_req_mutex.Unlock(); + continue; + } + if (m_socket->Connect(m_address) == false) { + delete m_socket; + m_req_mutex.Lock(); + m_requests.push_back(q); + m_req_mutex.Unlock(); + continue; + } + + m_recv_headers.clear(); + m_send_headers.clear(); + + if (q.post == "") { + get(q.url); + }else{ + put(q.url,q.post); + } + + int r = 0; + int h = -1; + m_start = 0; + m_end = 0; + while (m_socket->WaitData(1000)) { + r = fillBuffer(); + if (r<1) { + delete m_socket; + m_req_mutex.Lock(); + m_requests.push_back(q); + m_req_mutex.Unlock(); + h = -1; + break; + } + h = m_recv_headers.read(m_buff,r); + if (h > -1) + break; + if (h == -1 || m_recv_headers.getResponse() == 500) { + delete m_socket; + m_req_mutex.Lock(); + m_requests.push_back(q); + m_req_mutex.Unlock(); + r = 0; + break; + } + } + if (h < 0) { + delete m_socket; + continue; + } + + r = m_recv_headers.getResponse(); + + if (q.post == "") { + if (r == 204 || r == 303) { + delete m_socket; + continue; + }else if (r == 304) { + std::string d(m_client->getLocalPlayer()->getName()); + pushRequest(HTTPREQUEST_SKIN_SEND,d); + delete m_socket; + continue; + } + if (r != 200) { + errorstream << "receive skin returned " << r << std::endl; + delete m_socket; + continue; + } + if (q.data == "") { + errorstream << "receive skin returned successful for no player?" << std::endl; + delete m_socket; + continue; + } + if (q.data == m_client->getLocalPlayer()->getName()) { + delete m_socket; + continue; + } + char buff[2048]; + std::string file = getPath("player",std::string("player_")+q.data+".png",false); + FILE *f; + f = fopen(file.c_str(),"wb"); + if (!f) { + delete m_socket; + continue; + } + size_t s = m_recv_headers.getLength(); + if (!s) { + delete m_socket; + fclose(f); + continue; + } + size_t l; + size_t c = 2048; + if (c > s) + c = s; + if (c) { + while ((l = read(buff,c)) > 0) { + s -= l; + c = fwrite(buff,1,l,f); + if (c != l) + break; + c = 2048; + if (c > s) + c = s; + if (!c) + break; + } + } + fclose(f); + }else if (r == 405) { + errorstream << "send skin returned 405 Method Not Allowed" << std::endl; + } + delete m_socket; + } + p.clear(); } /* add a request to the http client queue */ void HTTPClient::pushRequest(HTTPRequestType type, std::string &data) { + if (m_thread.getRun() == false) + return; switch (type) { case HTTPREQUEST_NULL: break; @@ -681,6 +818,7 @@ void HTTPClient::pushRequest(HTTPRequestType type, std::string &data) std::string url("/player/"); url += data + "/skin"; HTTPRequest r(url); + r.data = data; m_req_mutex.Lock(); m_requests.push_back(r); m_req_mutex.Unlock(); @@ -699,8 +837,8 @@ void HTTPClient::pushRequest(HTTPRequestType type, std::string &data) */ case HTTPREQUEST_SKIN_HASH: { - std::string tex = std::string("players") + DIR_DELIM + "player_" + data + ".png"; - std::string ptex = getTexturePath(tex); + std::string tex = std::string("player_") + data + ".png"; + std::string ptex = getPath("player",tex,true); if (ptex == "") { pushRequest(HTTPREQUEST_SKIN,data); return; @@ -710,8 +848,9 @@ void HTTPClient::pushRequest(HTTPRequestType type, std::string &data) s.addFile(ptex.c_str()); s.getDigest(buff); std::string url("/player/"); - url += data + "/skin/" + buff; + url += data + "/skin/hash/" + buff; HTTPRequest r(url); + r.data = data; m_req_mutex.Lock(); m_requests.push_back(r); m_req_mutex.Unlock(); @@ -729,13 +868,13 @@ void HTTPClient::pushRequest(HTTPRequestType type, std::string &data) case HTTPREQUEST_SKIN_SEND: { std::string tex = std::string("player.png"); - std::string ptex = getTexturePath(tex); + std::string ptex = getPath("texture",tex,true); if (ptex == "") return; std::string url("/player/"); url += data + "/skin"; - HTTPRequest r(url,ptex); + HTTPRequest r(url,ptex,data); m_req_mutex.Lock(); m_requests.push_back(r); m_req_mutex.Unlock(); @@ -748,6 +887,8 @@ void HTTPClient::pushRequest(HTTPRequestType type, std::string &data) /* add a request to the http client queue */ void HTTPClient::pushRequest(std::string &url, std::string &data) { + if (m_thread.getRun() == false) + return; HTTPRequest r(url,data); m_req_mutex.Lock(); m_requests.push_back(r); @@ -755,33 +896,126 @@ void HTTPClient::pushRequest(std::string &url, std::string &data) } /* send a http GET request to the server */ -void HTTPClient::get(std::string &url) +bool HTTPClient::get(std::string &url) { + m_send_headers.setLength(0); + m_send_headers.setMethod("GET"); + m_send_headers.setUrl(url); + + sendHeaders(); + return true; } /* send a http POST request to the server */ -void HTTPClient::post(std::string &url, char* data) +bool HTTPClient::post(std::string &url, char* data) { + return false; } /* send a file to the server with a http PUT request */ -void HTTPClient::put(std::string &url, std::string &file) +bool HTTPClient::put(std::string &url, std::string &file) { + FILE *f; + u32 s; + f = fopen(file.c_str(),"r"); + if (!f) + return false; + fseek(f,0,SEEK_END); + s = ftell(f); +printf("filesize: %u\n",s); + fseek(f,0,SEEK_SET); + m_send_headers.setLength(s); + m_send_headers.setHeader("Content-Type","image/png"); + m_send_headers.setMethod("PUT"); + m_send_headers.setUrl(url); + + sendHeaders(); + + char buff[1024]; + while ((s = fread(buff,1,1024,f)) > 0) { + m_socket->Send(buff,s); + } + fclose(f); + return true; } -/* get a request from the client queue */ -HTTPRequest HTTPClient::popRequest() +/* read data from a remote http client */ +int HTTPClient::read(char* buff, int size) { - m_req_mutex.Lock(); - HTTPRequest r = m_requests.front(); - m_requests.erase(m_requests.begin()); - m_req_mutex.Unlock(); - return r; + if (size > 2048) { + int r = 0; + int l; + int c = 2048; + int s = size; + while (fillBuffer()) { + c = 2048; + if (sWaitData(30)) + return m_end; + + return m_end+m_socket->Receive(m_buff+m_end,l); } /* send http headers to the server */ void HTTPClient::sendHeaders() { + std::string v; + int s; + char buff[1024]; + + s = snprintf(buff,1024,"%s %s HTTP/1.0\r\n",m_send_headers.getMethod().c_str(),m_send_headers.getUrl().c_str()); + m_socket->Send(buff,s); + + v = m_send_headers.getHeader("Content-Type"); + if (v != "") { + s = snprintf(buff,1024,"Content-Type: %s\r\n",v.c_str()); + m_socket->Send(buff,s); + } + + s = m_send_headers.length(); + s = snprintf(buff,1024,"Content-Length: %d\r\n",s); + m_socket->Send(buff,s); + + s = snprintf(buff,1024,"Cookie: MTID=%s\r\n",m_cookie.c_str()); + m_socket->Send(buff,s); + + s = snprintf(buff,1024,"User: %s\r\n",m_client->getLocalPlayer()->getName()); + m_socket->Send(buff,s); + + m_socket->Send("\r\n",2); } #endif @@ -870,7 +1104,7 @@ int HTTPResponseHeaders::read(char* buff, int length) int n = 1; int o = 0; int i = 0; - int c = getUrl() == "" ? 0 : 1; + int c = getResponse(); for (i=0; iget("data_path"); - if(data_path != "") - { - std::string testpath = data_path + DIR_DELIM + rel_path; - // Check all filename extensions. Returns "" if not found. - fullpath = getImagePath(testpath); - } - - /* - Check from default data directory - */ - if(fullpath == "") - { - std::string testpath = porting::path_data + DIR_DELIM + rel_path; - // Check all filename extensions. Returns "" if not found. - fullpath = getImagePath(testpath); - } - - // Add to cache (also an empty result is cached) - g_texturename_to_path_cache.set(filename, fullpath); - - // Finally return it - return fullpath; -} - -/* - Gets the path to a model by first checking if the model exists - in data_path and if not, using the default data path. - - Checks all supported extensions by replacing the original extension. - - If not found, returns "". - - Utilizes a thread-safe cache. -*/ -std::string getModelPath(const std::string &filename) -{ - std::string fullpath = ""; - /* - Check from cache - */ + /* check from cache */ bool incache = g_modelname_to_path_cache.get(filename, &fullpath); - if(incache) - return fullpath; + if (incache) { + if (must_exist == true) { + if (!fs::PathExists(fullpath)) + fullpath = ""; + } + if (fullpath != "") + return fullpath; + } - std::string rel_path = std::string("models")+DIR_DELIM+filename; - /* - Check from data_path /models - */ + std::string rel_path = std::string(""); + if (type == "model") { + rel_path += std::string("models")+DIR_DELIM+filename; + }else if (type == "texture") { + rel_path += std::string("textures")+DIR_DELIM+filename; + }else if (type == "html") { + rel_path += std::string("html")+DIR_DELIM+filename; + }else if (type == "player") { + rel_path += std::string("textures")+DIR_DELIM+"players"+DIR_DELIM+filename; + } + + /* check from data_path */ std::string data_path = g_settings->get("data_path"); - if(data_path != "") - { + if (data_path != "") { std::string testpath = data_path + DIR_DELIM + rel_path; - if(fs::PathExists(testpath)) - fullpath = std::string(testpath); + if (type == "model" || type == "html") { + if (fs::PathExists(testpath)) + fullpath = std::string(testpath); + }else{ + fullpath = getImagePath(testpath); + } } - /* - Check from default data directory - */ - if(fullpath == "") - { + /* check from user data directory */ + if (fullpath == "") { + std::string testpath = porting::path_userdata + DIR_DELIM + rel_path; + if (type == "model" || type == "html") { + if (fs::PathExists(testpath)) + fullpath = std::string(testpath); + }else{ + fullpath = getImagePath(testpath); + } + } + + /* check from default data directory */ + if (fullpath == "") { std::string testpath = porting::path_data + DIR_DELIM + rel_path; - if(fs::PathExists(testpath)) - fullpath = std::string(testpath); + if (type == "model" || type == "html") { + if (fs::PathExists(testpath)) + fullpath = std::string(testpath); + }else{ + fullpath = getImagePath(testpath); + } } - // Add to cache (also an empty result is cached) + if (must_exist == false && fullpath == "") +#ifdef RUN_IN_PLACE + fullpath = porting::path_data + DIR_DELIM + rel_path; +#else + fullpath = porting::path_userdata + DIR_DELIM + rel_path; +#endif + + /* add to cache (also an empty result is cached) */ g_modelname_to_path_cache.set(filename, fullpath); - // Finally return it + /* finally return it */ return fullpath; } diff --git a/src/path.h b/src/path.h index fd4ea2f..c97f585 100644 --- a/src/path.h +++ b/src/path.h @@ -33,7 +33,16 @@ with this program; if not, write to the Free Software Foundation, Inc., Utilizes a thread-safe cache. */ -std::string getTexturePath(const std::string &filename); -std::string getModelPath(const std::string &filename); +std::string getPath(const char* type, const std::string &filename, bool must_exist); + +/* wrappers for the old functions, because too lazy to replace them all */ +inline std::string getTexturePath(const std::string &filename) +{ + return getPath("texture",filename,true); +} +inline std::string getModelPath(const std::string &filename) +{ + return getPath("model",filename,true); +} #endif diff --git a/src/player.cpp b/src/player.cpp index 6b82ef6..e5ad478 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -256,9 +256,9 @@ void RemotePlayer::updateName(const char *name) mbstowcs(wname, m_name, strlen(m_name)+1); m_text->setText(wname); } - if (m_node != NULL) { - std::string tex = std::string("players") + DIR_DELIM + "player_" + m_name + ".png"; - std::string ptex = getTexturePath(tex); + if (m_node != NULL && !isLocal()) { + std::string tex = std::string("player_") + m_name + ".png"; + std::string ptex = getPath("player",tex,true); printf("'%s' '%s'\n",tex.c_str(),ptex.c_str()); if (ptex == "") return; diff --git a/src/porting.h b/src/porting.h index 4970a84..55e4312 100644 --- a/src/porting.h +++ b/src/porting.h @@ -71,7 +71,7 @@ extern std::string path_userdata; /* Get full path of stuff in data directory. - Example: "stone.png" -> "../data/stone.png" + Example: "stone.png" -> "../data/textures/stone.png" */ std::string getDataPath(const char *subpath); From 4a735d140ebe7d6f9709bf38507def3939e39a51 Mon Sep 17 00:00:00 2001 From: darkrose Date: Sat, 19 Oct 2013 21:52:11 +1000 Subject: [PATCH 6/6] http - fix some bugs, add some bugs --- src/client.h | 1 + src/http.cpp | 71 ++++++++++++++++++++++++++++++++++++-------------- src/http.h | 1 + src/socket.cpp | 12 ++++++--- 4 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/client.h b/src/client.h index 59a9c38..58c94cc 100644 --- a/src/client.h +++ b/src/client.h @@ -217,6 +217,7 @@ public: // Wrapper to Map NodeMetadata* getNodeMetadata(v3s16 p); + Player* getPlayer(const char* name) { return m_env.getPlayer(name);} LocalPlayer* getLocalPlayer(); void setPlayerControl(PlayerControl &control); diff --git a/src/http.cpp b/src/http.cpp index 461972a..165ba17 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -294,13 +294,19 @@ int HTTPRemoteClient::handlePlayer() if (m_recv_headers.getUrl(3) == "hash") { FILE *f; f = fopen(file.c_str(),"rb"); - if (!f) + if (!f) { + if (m_auth && m_recv_headers.getHeader("User") == m_recv_headers.getUrl(1)) + return handleSpecial("304 Not Modified"); return handleSpecial("204 No Content"); + } fseek(f,0,SEEK_END); size_t l = ftell(f); fclose(f); - if (!l) + if (!l) { + if (m_auth && m_recv_headers.getHeader("User") == m_recv_headers.getUrl(1)) + return handleSpecial("304 Not Modified"); return handleSpecial("204 No Content"); + } SHA1 s; s.addFile(file.c_str()); s.getDigest(buff); @@ -334,11 +340,16 @@ int HTTPRemoteClient::handlePlayer() size_t t = 0; if (c > s) c = s; +printf("pending: %lu %lu %d %d\n",c,s,m_start,m_end); if (c) { +printf("pending: %lu %lu %d %d\n",c,s,m_start,m_end); + if (m_start == m_end) + m_socket->WaitData(5000); while ((l = read(buff,c)) > 0) { s -= l; t += l; c = fwrite(buff,1,l,f); +printf("written: %lu\n",c); if (c != l) { fclose(f); return handleSpecial("500 Internal Server Error"); @@ -350,9 +361,13 @@ int HTTPRemoteClient::handlePlayer() break; } } -printf("recieved: %lu %lu\n",s,l); +printf("received: %lu %lu\n",s,t); + l = ftell(f); fclose(f); - return handleSpecial("201 Created"); + if (s == t) + return handleSpecial("201 Created"); + fs::RecursiveDelete(file.c_str()); + return handleSpecial("500 Internal Server Error"); }else if (m_server->getGameServer()->getPlayer(m_recv_headers.getUrl(1))) { std::string html("

Players

\n"); Player *player = m_server->getGameServer()->getPlayer(m_recv_headers.getUrl(1)); @@ -518,7 +533,7 @@ void HTTPRemoteClient::sendFile(std::string &file) fseek(f,0,SEEK_END); size_t l = ftell(f); fseek(f,0,SEEK_SET); - size_t s = l; + //size_t s = l; size_t t = 0; m_send_headers.setLength(l); @@ -529,7 +544,7 @@ void HTTPRemoteClient::sendFile(std::string &file) t += l; m_socket->Send(buff,l); } -printf("sent: %lu %lu\n",s,t); +//printf("sent: %lu %lu\n",s,t); fclose(f); } @@ -636,6 +651,7 @@ void HTTPClient::start(const Address &address) m_thread.stop(); m_address = address; + m_connection_failures = 0; // Start thread m_thread.setRun(true); @@ -686,8 +702,16 @@ void HTTPClient::step() m_req_mutex.Lock(); m_requests.push_back(q); m_req_mutex.Unlock(); + m_connection_failures++; + /* assume the server has no http */ + if (m_connection_failures > 4) { + stop(); + p.clear(); + return; + } continue; } + m_connection_failures = 0; m_recv_headers.clear(); m_send_headers.clear(); @@ -702,13 +726,10 @@ void HTTPClient::step() int h = -1; m_start = 0; m_end = 0; + m_recv_headers.setResponse(0); while (m_socket->WaitData(1000)) { r = fillBuffer(); if (r<1) { - delete m_socket; - m_req_mutex.Lock(); - m_requests.push_back(q); - m_req_mutex.Unlock(); h = -1; break; } @@ -716,16 +737,17 @@ void HTTPClient::step() if (h > -1) break; if (h == -1 || m_recv_headers.getResponse() == 500) { - delete m_socket; - m_req_mutex.Lock(); - m_requests.push_back(q); - m_req_mutex.Unlock(); + sleep(1); r = 0; + h = -1; break; } } if (h < 0) { delete m_socket; + m_req_mutex.Lock(); + m_requests.push_back(q); + m_req_mutex.Unlock(); continue; } @@ -840,8 +862,14 @@ void HTTPClient::pushRequest(HTTPRequestType type, std::string &data) std::string tex = std::string("player_") + data + ".png"; std::string ptex = getPath("player",tex,true); if (ptex == "") { - pushRequest(HTTPREQUEST_SKIN,data); - return; + if (data == m_client->getLocalPlayer()->getName()) { + ptex = getPath("texture","player.png",true); + if (ptex == "") + return; + }else{ + pushRequest(HTTPREQUEST_SKIN,data); + return; + } } char buff[100]; SHA1 s; @@ -922,7 +950,6 @@ bool HTTPClient::put(std::string &url, std::string &file) return false; fseek(f,0,SEEK_END); s = ftell(f); -printf("filesize: %u\n",s); fseek(f,0,SEEK_SET); m_send_headers.setLength(s); m_send_headers.setHeader("Content-Type","image/png"); @@ -931,10 +958,15 @@ printf("filesize: %u\n",s); sendHeaders(); + u32 t = 0; + u32 l; + char buff[1024]; - while ((s = fread(buff,1,1024,f)) > 0) { - m_socket->Send(buff,s); + while ((l = fread(buff,1,1024,f)) > 0) { + t += l; + m_socket->Send(buff,l); } +printf("sent: %u %u\n",t,s); fclose(f); return true; } @@ -1112,6 +1144,7 @@ int HTTPResponseHeaders::read(char* buff, int length) if (buff[i] == '\n') { if (!c) { nbuff[o] = 0; +printf("%d '%s'\n",__LINE__,nbuff); char* r = strchr(nbuff,' '); if (!r) return -1; diff --git a/src/http.h b/src/http.h index 3d706fe..a3abdc7 100644 --- a/src/http.h +++ b/src/http.h @@ -255,6 +255,7 @@ private: JMutex m_req_mutex; HTTPClientThread m_thread; Client *m_client; + int m_connection_failures; }; #endif diff --git a/src/socket.cpp b/src/socket.cpp index 7db3db0..04d9f76 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -361,8 +361,10 @@ TCPSocket::TCPSocket() } int a=1; - setsockopt(m_handle, SOL_SOCKET, SO_REUSEADDR, &a, sizeof(a)); - + setsockopt(m_handle, SOL_SOCKET, SO_REUSEADDR, &a, sizeof(int)); +#if defined(__FreeBSD__) + setsockopt(m_handle, SOL_SOCKET, SO_NOSIGPIPE, &a, sizeof(int)); +#endif setTimeoutMs(0); } @@ -421,7 +423,11 @@ bool TCPSocket::Connect(const Address &destination) void TCPSocket::Send(const void *data, int size) { - int sent = send(m_handle, (const char*)data, size, 0); + int flags = 0; +#if defined(linux) + flags = MSG_NOSIGNAL; +#endif + int sent = send(m_handle, (const char*)data, size, flags); if (sent != size) throw SendFailedException("Failed to send data");