changed: now we do not use std::string and char* in the Winix API
everywhere we are using std::wstring and wchar_t* (std::string and char* is used only locally in some places especially when creating a path to OS file system etc.) added: to the special thread when winix closes: a write function for curl: FetchPageOnExitCurlCallback() without this function the curl library will print the page's content to the standart output changed: TextStream<> class from core can make UTF8<->wide strings conversions removed: from config: utf8 option now winix expects UTF8 from the user's input (html forms, url-es) and outputs strings in the UTF8 format git-svn-id: svn://ttmath.org/publicrep/winix/trunk@965 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
298
core/app.cpp
298
core/app.cpp
@@ -114,12 +114,70 @@ App::App()
|
||||
|
||||
|
||||
|
||||
bool App::InitFCGI(char * sock, char * sock_user, char * sock_group)
|
||||
{
|
||||
if( !WideToUTF8(config.fcgi_socket, sock, WINIX_OS_PATH_SIZE) )
|
||||
return false;
|
||||
|
||||
if( !WideToUTF8(config.fcgi_socket_user, sock_user, WINIX_OS_USERNAME_SIZE) )
|
||||
return false;
|
||||
|
||||
if( !WideToUTF8(config.fcgi_socket_group, sock_group, WINIX_OS_USERNAME_SIZE) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* chmod and chown of the socket are set before winix drops privileges
|
||||
*/
|
||||
bool App::InitFCGIChmodChownSocket(char * sock, char * sock_user, char * sock_group)
|
||||
{
|
||||
if( chmod(sock, config.fcgi_socket_chmod) < 0 )
|
||||
{
|
||||
log << log1 << "App: I cannot chmod a FastCGI socket, check fcgi_socket_chmod in the config" << logend;
|
||||
return false;
|
||||
}
|
||||
|
||||
passwd * pw = getpwnam(sock_user);
|
||||
|
||||
if( !pw )
|
||||
{
|
||||
log << log1 << "App: there is no a user: " << config.fcgi_socket_user << logend;
|
||||
return false;
|
||||
}
|
||||
|
||||
group * gr = getgrnam(sock_group);
|
||||
|
||||
if( !gr )
|
||||
{
|
||||
log << log1 << "App: there is no a group: " << config.fcgi_socket_group << logend;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( chown(sock, pw->pw_uid, gr->gr_gid) < 0 )
|
||||
{
|
||||
log << log1 << "App: I cannot chown a FastCGI socket, check fcgi_socket_user "
|
||||
<< "and fcgi_socket_group in the config" << logend;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool App::InitFCGI()
|
||||
{
|
||||
const char * sock = config.fcgi_socket.c_str();
|
||||
unlink(sock);
|
||||
char sock[WINIX_OS_PATH_SIZE];
|
||||
char sock_user[WINIX_OS_USERNAME_SIZE];
|
||||
char sock_group[WINIX_OS_USERNAME_SIZE];
|
||||
|
||||
fcgi_socket = FCGX_OpenSocket(sock, 100); // !! dodac 100 do konfiga
|
||||
if( !InitFCGI(sock, sock_user, sock_group) )
|
||||
return false;
|
||||
|
||||
unlink(sock);
|
||||
fcgi_socket = FCGX_OpenSocket(sock, config.fcgi_socket_listen);
|
||||
|
||||
if( fcgi_socket < 0 )
|
||||
{
|
||||
@@ -128,25 +186,9 @@ bool App::InitFCGI()
|
||||
}
|
||||
|
||||
log << log3 << "App: FastCGI socket number: " << fcgi_socket << logend;
|
||||
chmod(sock, config.fcgi_socket_chmod);
|
||||
|
||||
passwd * pw = getpwnam(config.fcgi_socket_user.c_str());
|
||||
|
||||
if( !pw )
|
||||
{
|
||||
log << log1 << "App: there is no user: " << config.fcgi_socket_user << logend;
|
||||
if( !InitFCGIChmodChownSocket(sock, sock_user, sock_group) )
|
||||
return false;
|
||||
}
|
||||
|
||||
group * gr = getgrnam(config.fcgi_socket_group.c_str());
|
||||
|
||||
if( !gr )
|
||||
{
|
||||
log << log1 << "App: there is no group: " << config.fcgi_socket_group << logend;
|
||||
return false;
|
||||
}
|
||||
|
||||
chown(sock, pw->pw_uid, gr->gr_gid);
|
||||
|
||||
if( FCGX_Init() != 0 )
|
||||
{
|
||||
@@ -185,12 +227,9 @@ bool App::Init()
|
||||
|
||||
CreateStaticTree();
|
||||
|
||||
post_parser.UTF8(config.utf8);
|
||||
post_parser.LogValueSize(config.log_post_value_size);
|
||||
// post_multi_parser has a pointer to the config
|
||||
|
||||
cookie_parser.UTF8(config.utf8);
|
||||
|
||||
plugin.Call((Session*)0, WINIX_PLUGIN_INIT);
|
||||
|
||||
return true;
|
||||
@@ -218,7 +257,7 @@ void App::BaseUrlRedirect(int code, bool add_subdomain)
|
||||
}
|
||||
|
||||
cur.request->redirect_to += config.base_url;
|
||||
AssignString(cur.request->env_request_uri, cur.request->redirect_to, false);
|
||||
cur.request->redirect_to += cur.request->env_request_uri;
|
||||
// cur.request->env_request_uri should not be UrlEncoded because it contains slashes
|
||||
cur.request->redirect_type = code;
|
||||
}
|
||||
@@ -241,7 +280,7 @@ bool App::BaseUrlRedirect()
|
||||
if( cur.request->method == Request::post )
|
||||
return false;
|
||||
|
||||
if( Equal(config.base_url.c_str(), cur.request->env_http_host) )
|
||||
if( config.base_url == cur.request->env_http_host )
|
||||
return false;
|
||||
|
||||
BaseUrlRedirect(config.base_url_redirect_code, false);
|
||||
@@ -345,7 +384,28 @@ void App::ProcessRequestThrow()
|
||||
}
|
||||
|
||||
plugin.Call(WINIX_SESSION_CHANGED);
|
||||
functions.Parse(); // parsing directories,files,functions and parameters
|
||||
|
||||
if( cur.request->env_request_uri.size() <= WINIX_URL_MAX_SIZE )
|
||||
{
|
||||
functions.Parse(); // parsing directories, files, functions and parameters
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* IMPROVE ME
|
||||
* it will not have the root directory set
|
||||
* so as a response only a blank page is shown
|
||||
* (root directory is set in funcions.Parse())
|
||||
*
|
||||
* IMPROVE ME
|
||||
* we can add a better return code (http status):
|
||||
* http://www.ietf.org/rfc/rfc2616.txt
|
||||
* "A server SHOULD return 414 (Request-URI Too Long) status if a URI is longer than the server can handle"
|
||||
*
|
||||
*/
|
||||
cur.request->status = WINIX_ERR_PERMISSION_DENIED;
|
||||
log << log1 << "App: the URL is too long: " << cur.request->env_request_uri.size() << logend;
|
||||
}
|
||||
|
||||
cur.mount = system.mounts.CalcCurMount();
|
||||
|
||||
@@ -637,17 +697,21 @@ void App::LogEnvironmentVariables()
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* reading the request (without GET parameters in the URL)
|
||||
*/
|
||||
void App::ReadRequest()
|
||||
{
|
||||
ReadEnvVariables();
|
||||
ReadEnvRemoteIP();
|
||||
CheckRequestMethod();
|
||||
CheckSSL();
|
||||
SetSubdomain();
|
||||
|
||||
LogAccess();
|
||||
|
||||
ReadGetPostVars();
|
||||
ReadPostVars();
|
||||
|
||||
cookie_parser.Parse(cur.request->env_http_cookie, cur.request->cookie_tab);
|
||||
accept_encoding_parser.ParseAndLog(cur.request->env_http_accept_encoding);
|
||||
|
||||
@@ -663,51 +727,63 @@ void App::ReadRequest()
|
||||
|
||||
|
||||
|
||||
void App::SetEnv(const char * & env, const char * name)
|
||||
void App::SetEnv(const char * name, std::wstring & env)
|
||||
{
|
||||
const char * v = FCGX_GetParam(name, fcgi_request.envp);
|
||||
const char * v = FCGX_GetParam(name, fcgi_request.envp);
|
||||
|
||||
if( v )
|
||||
env = v;
|
||||
|
||||
// by default env is set to an empty string (in cur.request->Clear() method)
|
||||
{
|
||||
PT::UTF8ToWide(v, env);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void App::ReadEnvVariables()
|
||||
{
|
||||
// we store that values because FCGX_GetParam has O(n) complexity
|
||||
// with this variables (env_*) we have O(1)
|
||||
|
||||
SetEnv(cur.request->env_request_method, "REQUEST_METHOD"); // !! mozna nie uzywac tego, teraz mamy w strukturze fcgi_request
|
||||
SetEnv(cur.request->env_request_uri, "REQUEST_URI");
|
||||
SetEnv(cur.request->env_http_cookie, "HTTP_COOKIE");
|
||||
SetEnv(cur.request->env_remote_addr, "REMOTE_ADDR");
|
||||
SetEnv(cur.request->env_http_host, "HTTP_HOST");
|
||||
SetEnv(cur.request->env_http_user_agent, "HTTP_USER_AGENT");
|
||||
SetEnv(cur.request->env_fcgi_role, "FCGI_ROLE");
|
||||
SetEnv(cur.request->env_content_type, "CONTENT_TYPE");
|
||||
SetEnv(cur.request->env_http_accept_encoding, "HTTP_ACCEPT_ENCODING");
|
||||
SetEnv(cur.request->env_https, "HTTPS");
|
||||
|
||||
cur.request->ip = (int)inet_addr(cur.request->env_remote_addr);
|
||||
SetEnv("REQUEST_METHOD", cur.request->env_request_method);
|
||||
SetEnv("REQUEST_URI", cur.request->env_request_uri);
|
||||
SetEnv("HTTP_COOKIE", cur.request->env_http_cookie);
|
||||
SetEnv("REMOTE_ADDR", cur.request->env_remote_addr);
|
||||
SetEnv("HTTP_HOST", cur.request->env_http_host);
|
||||
SetEnv("HTTP_USER_AGENT", cur.request->env_http_user_agent);
|
||||
SetEnv("FCGI_ROLE", cur.request->env_fcgi_role);
|
||||
SetEnv("CONTENT_TYPE", cur.request->env_content_type);
|
||||
SetEnv("HTTP_ACCEPT_ENCODING", cur.request->env_http_accept_encoding);
|
||||
SetEnv("HTTPS", cur.request->env_https);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void App::ReadEnvRemoteIP()
|
||||
{
|
||||
const char * v = FCGX_GetParam("REMOTE_ADDR", fcgi_request.envp);
|
||||
|
||||
if( v )
|
||||
{
|
||||
cur.request->ip = (int)inet_addr(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void App::CheckRequestMethod()
|
||||
{
|
||||
cur.request->method = Request::unknown_method;
|
||||
|
||||
if( ToSmall(cur.request->env_request_method[0]) == 'g' )
|
||||
cur.request->method = Request::get;
|
||||
else
|
||||
if( ToSmall(cur.request->env_request_method[0]) == 'p' )
|
||||
cur.request->method = Request::post;
|
||||
else
|
||||
if( ToSmall(cur.request->env_request_method[0]) == 'h' )
|
||||
cur.request->method = Request::head;
|
||||
|
||||
if( !cur.request->env_request_method.empty() )
|
||||
{
|
||||
if( ToSmall(cur.request->env_request_method[0]) == 'g' )
|
||||
cur.request->method = Request::get;
|
||||
else
|
||||
if( ToSmall(cur.request->env_request_method[0]) == 'p' )
|
||||
cur.request->method = Request::post;
|
||||
else
|
||||
if( ToSmall(cur.request->env_request_method[0]) == 'h' )
|
||||
cur.request->method = Request::head;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -718,14 +794,14 @@ void App::CheckSSL()
|
||||
// value "on" exists in lighttpd server
|
||||
// make sure that for other servers is "on" too
|
||||
|
||||
if( EqualNoCase(cur.request->env_https, "on") )
|
||||
if( EqualNoCase(cur.request->env_https.c_str(), L"on") )
|
||||
cur.request->using_ssl = true;
|
||||
}
|
||||
|
||||
|
||||
void App::SetSubdomain()
|
||||
{
|
||||
CreateSubdomain(config.base_url.c_str(), cur.request->env_http_host, cur.request->subdomain);
|
||||
CreateSubdomain(config.base_url.c_str(), cur.request->env_http_host.c_str(), cur.request->subdomain);
|
||||
}
|
||||
|
||||
|
||||
@@ -747,15 +823,11 @@ void App::LogAccess()
|
||||
|
||||
|
||||
|
||||
|
||||
void App::ReadGetPostVars()
|
||||
void App::ReadPostVars()
|
||||
{
|
||||
// get parameters we have always
|
||||
//get_parser.Parse(cur.request->env_request_uri, cur.request->get_tab);
|
||||
|
||||
if( cur.request->method == Request::post )
|
||||
{
|
||||
if( IsSubStringNoCase("multipart/form-data", cur.request->env_content_type) )
|
||||
if( IsSubStringNoCase(L"multipart/form-data", cur.request->env_content_type.c_str()) )
|
||||
{
|
||||
log << log3 << "App: post content type: multipart/form-data" << logend;
|
||||
post_multi_parser.Parse(fcgi_request.in, cur.request->post_tab, cur.request->post_file_tab);
|
||||
@@ -772,24 +844,16 @@ void App::ReadGetPostVars()
|
||||
|
||||
void App::CheckIE()
|
||||
{
|
||||
const char * msie = strstr(cur.request->env_http_user_agent, "MSIE");
|
||||
|
||||
if( msie )
|
||||
cur.request->browser_msie = true;
|
||||
else
|
||||
cur.request->browser_msie = false;
|
||||
size_t msie = cur.request->env_http_user_agent.find(L"MSIE");
|
||||
cur.request->browser_msie = (msie != std::wstring::npos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void App::CheckKonqueror()
|
||||
{
|
||||
const char * kon = strstr(cur.request->env_http_user_agent, "Konqueror");
|
||||
|
||||
if( kon )
|
||||
cur.request->browser_konqueror = true;
|
||||
else
|
||||
cur.request->browser_konqueror = false;
|
||||
size_t kon = cur.request->env_http_user_agent.find(L"Konqueror");
|
||||
cur.request->browser_konqueror = (kon != std::wstring::npos);
|
||||
}
|
||||
|
||||
|
||||
@@ -875,7 +939,7 @@ bool App::PrepareHeadersStaticCreateResource(PT::WTextStream & out_path)
|
||||
}
|
||||
|
||||
size_t how_many_dirs = system.dirs.DirLevel(dir->id);
|
||||
const char * path = SkipDirs(cur.request->env_request_uri, how_many_dirs);
|
||||
const wchar_t * path = SkipDirs(cur.request->env_request_uri.c_str(), how_many_dirs);
|
||||
|
||||
// the path begins with a slash only if how_many_dirs is zero
|
||||
while( *path == '/' )
|
||||
@@ -954,7 +1018,7 @@ void App::PrepareHeaderContentType()
|
||||
}
|
||||
}
|
||||
|
||||
if( value && config.utf8 )
|
||||
if( value )
|
||||
*value += L"; charset=UTF-8";
|
||||
}
|
||||
}
|
||||
@@ -1066,11 +1130,19 @@ void App::SendHeaders()
|
||||
FCGX_PutS("\r\n", fcgi_request.out);
|
||||
|
||||
if( config.log_http_answer_headers )
|
||||
log << "HTTP Header: " << aheader_name << ": " << aheader_value << logend;
|
||||
log << log1 << "HTTP Header: " << aheader_name << ": " << aheader_value << logend;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class RawType>
|
||||
DbTextStream::RawText<RawType> R(const RawType & par)
|
||||
{
|
||||
return DbTextStream::RawText<RawType>(par);
|
||||
}
|
||||
|
||||
|
||||
void App::SendCookies()
|
||||
{
|
||||
PT::Space::TableSingle::iterator i;
|
||||
@@ -1090,7 +1162,7 @@ void App::SendCookies()
|
||||
FCGX_PutS("\r\n", fcgi_request.out);
|
||||
|
||||
if( config.log_http_answer_headers )
|
||||
log << "HTTP Header: " << "Set-Cookie: " << aheader_name << "=" << aheader_value << logend;
|
||||
log << log1 << "HTTP Header: Set-Cookie: " << aheader_name << "=" << aheader_value << logend;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1296,10 +1368,7 @@ size_t output_size = 0;
|
||||
|
||||
SelectCompression(source->length(), compressing, compress_encoding);
|
||||
|
||||
if( config.utf8 )
|
||||
PT::WideToUTF8(*source, output_8bit);
|
||||
else
|
||||
AssignString(*source, output_8bit);
|
||||
PT::WideToUTF8(*source, output_8bit);
|
||||
|
||||
// !! IMPROVE ME add to log the binary stream as well
|
||||
if( config.log_server_answer )
|
||||
@@ -1525,13 +1594,27 @@ void App::LogUserGroups()
|
||||
|
||||
|
||||
|
||||
bool App::DropPrivileges(const std::string & user, uid_t uid, gid_t gid, bool additional_groups)
|
||||
|
||||
bool App::DropPrivileges(char * user, char * group)
|
||||
{
|
||||
if( !WideToUTF8(config.user, user, WINIX_OS_USERNAME_SIZE) )
|
||||
return false;
|
||||
|
||||
if( !WideToUTF8(config.group, group, WINIX_OS_USERNAME_SIZE) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool App::DropPrivileges(const char * user, uid_t uid, gid_t gid, bool additional_groups)
|
||||
{
|
||||
if( additional_groups )
|
||||
{
|
||||
if( initgroups(user.c_str(), gid) < 0 )
|
||||
if( initgroups(user, gid) < 0 )
|
||||
{
|
||||
log << log1 << "App: I can't init groups for user: " << user << logend;
|
||||
log << log1 << "App: I can't init groups for a user: " << user << logend;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1545,7 +1628,7 @@ bool App::DropPrivileges(const std::string & user, uid_t uid, gid_t gid, bool ad
|
||||
}
|
||||
|
||||
// for setting real and saved gid too
|
||||
if( setgid(gid) )
|
||||
if( setgid(gid) < 0 )
|
||||
{
|
||||
log << log1 << "App: I can't change real and saved gid" << logend;
|
||||
return false;
|
||||
@@ -1568,8 +1651,12 @@ return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool App::DropPrivileges()
|
||||
{
|
||||
char user_name[WINIX_OS_USERNAME_SIZE];
|
||||
char group_name[WINIX_OS_USERNAME_SIZE];
|
||||
|
||||
if( getuid()!=0 && geteuid()!=0 )
|
||||
return true;
|
||||
|
||||
@@ -1577,20 +1664,23 @@ bool App::DropPrivileges()
|
||||
|
||||
if( config.user.empty() )
|
||||
{
|
||||
log << log1 << "App: you should specify user name in the config file "
|
||||
log << log1 << "App: in the config file you should specify a user name and a group "
|
||||
<< "to which I have to drop privileges" << logend;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( config.group.empty() )
|
||||
{
|
||||
log << log1 << "App: you should specify group name in the config file "
|
||||
log << log1 << "App: you should specify a group name in the config file "
|
||||
<< "to which I have to drop privileges" << logend;
|
||||
return false;
|
||||
}
|
||||
|
||||
passwd * p = getpwnam(config.user.c_str());
|
||||
group * g = getgrnam(config.group.c_str());
|
||||
if( !DropPrivileges(user_name, group_name) )
|
||||
return false;
|
||||
|
||||
passwd * p = getpwnam(user_name);
|
||||
group * g = getgrnam(group_name);
|
||||
|
||||
if( !p )
|
||||
{
|
||||
@@ -1604,7 +1694,7 @@ bool App::DropPrivileges()
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !DropPrivileges(config.user, p->pw_uid, g->gr_gid, config.additional_groups) )
|
||||
if( !DropPrivileges(user_name, p->pw_uid, g->gr_gid, config.additional_groups) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -1674,6 +1764,16 @@ void App::WaitForThreads()
|
||||
|
||||
|
||||
|
||||
size_t App::FetchPageOnExitCurlCallback(char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
/*
|
||||
* without this function the curl library will print the page's content
|
||||
* to the standart output
|
||||
*/
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
|
||||
void App::FetchPageOnExit()
|
||||
{
|
||||
// stupid trick to break FCGX_Accept_r() function
|
||||
@@ -1690,7 +1790,15 @@ void App::FetchPageOnExit()
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url_to_fetch_on_exit.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_perform(curl);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, FetchPageOnExitCurlCallback);
|
||||
|
||||
if( curl_easy_perform(curl) != 0 )
|
||||
{
|
||||
Lock();
|
||||
log << log1 << "App: I cannot correctly fetch a page from the special thread" << logend << logsave;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user