fixed: permissions to symlinks and directories with redirect flag were incorrectly checked

(there was no session set and the request was treated the same as from a not logged user)
fixed: in BaseThread there was used 'log' in the main thread (this logger is only for the other thread)
added: in BaseThread we have a main_log now - logger which puts to the main log buffer from the main thread




git-svn-id: svn://ttmath.org/publicrep/winix/trunk@1182 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
Tomasz Sowa 2019-03-19 18:08:09 +00:00
parent ae03922491
commit 4c2efc08fd
8 changed files with 106 additions and 104 deletions

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (c) 2010-2018, Tomasz Sowa * Copyright (c) 2010-2019, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -151,10 +151,8 @@ App::App()
session_manager.set_dependency(&winix_model); // zobaczyc czy wskoczy do przeciazonej metody w session manager session_manager.set_dependency(&winix_model); // zobaczyc czy wskoczy do przeciazonej metody w session manager
session_manager.SetLastContainer(&system.users.last); session_manager.SetLastContainer(&system.users.last);
session_manager.SetConfig(&config);
session_manager.SetCur(&cur); session_manager.SetCur(&cur);
session_manager.SetSystem(&system); session_manager.SetSystem(&system);
session_manager.SetSynchro(&synchro);
post_multi_parser.set_dependency(&winix_base); post_multi_parser.set_dependency(&winix_base);
post_multi_parser.SetConfig(&config); post_multi_parser.SetConfig(&config);
@ -500,10 +498,10 @@ void App::ProcessRequestThrow()
if( !cur.request->dir_tab.empty() ) if( !cur.request->dir_tab.empty() )
{ {
cur.mount = system.mounts.CalcCurMount(); cur.mount = system.mounts.CalcCurMount();
functions.CheckFunctionAndSymlink();
session_manager.PrepareSession(); cur.session = session_manager.PrepareSession();
cur.session = session_manager.GetCurSession(); functions.CheckFunctionAndSymlink(); // here a function can be changed
cur.session = session_manager.CheckIfFunctionRequireSession();
SetLocale(); SetLocale();
if( cur.session->new_session ) if( cur.session->new_session )
@ -567,7 +565,7 @@ void App::ProcessRequest()
log << log2 << config.log_delimiter << logend; log << log2 << config.log_delimiter << logend;
ProcessRequestThrow(); ProcessRequestThrow();
SaveSessionsIfNeeded(); // !! IMPROVE ME move to the session's thread SaveSessionsIfNeeded();
system.load_avg.StopRequest(); system.load_avg.StopRequest();
} }

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (c) 2010-2018, Tomasz Sowa * Copyright (c) 2010-2019, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -54,13 +54,17 @@ BaseThread::~BaseThread()
} }
void BaseThread::set_main_log_buffer(PT::WTextStream * log_buffer)
void BaseThread::SetSynchro(Synchro * psynchro)
{ {
synchro = psynchro; main_log.SetLogBuffer(log_buffer);
} }
void BaseThread::set_main_file_log(PT::FileLog * file_log)
{
main_log.SetFileLog(file_log);
}
void BaseThread::Mode(int mode) void BaseThread::Mode(int mode)
{ {

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (c) 2010-2018, Tomasz Sowa * Copyright (c) 2010-2019, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -54,8 +54,9 @@ public:
BaseThread(); BaseThread();
virtual ~BaseThread(); virtual ~BaseThread();
// synchro object (must be set) void set_main_log_buffer(PT::WTextStream * log_buffer);
void SetSynchro(Synchro * psynchro); void set_main_file_log(PT::FileLog * file_log);
// work mode // work mode
// we have two modes: // we have two modes:
@ -100,6 +101,13 @@ public:
pthread_t ThreadId(); pthread_t ThreadId();
protected:
// logger for the main thread
Log main_log;
// log from WinixBase is meant to be used by the other thread
protected: protected:
@ -126,7 +134,6 @@ protected:
protected: protected:
Synchro * synchro;
pthread_t thread_id; // thread id - set by StartThread() pthread_t thread_id; // thread id - set by StartThread()
pthread_cond_t thread_signal; pthread_cond_t thread_signal;
int work_mode; int work_mode;

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (c) 2012-2014, Tomasz Sowa * Copyright (c) 2012-2019, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -183,9 +183,7 @@ void Job::DoJob(PT::Space & job)
// second thread (objects not locked) // second thread (objects not locked)
void Job::DoWinixJob(PT::Space & job) void Job::DoWinixJob(PT::Space & job)
{ {
Lock();
//log << log1 << "standard winix job: " << job.Text(L"type") << logend; //log << log1 << "standard winix job: " << job.Text(L"type") << logend;
Unlock();
} }

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (c) 2008-2018, Tomasz Sowa * Copyright (c) 2008-2019, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -77,13 +77,6 @@ void SessionManager::SetCur(Cur * pcur)
} }
void SessionManager::SetConfig(Config * pconfig)
{
config = pconfig;
session_tab.SetConfig(pconfig);
}
void SessionManager::SetSystem(System * psystem) void SessionManager::SetSystem(System * psystem)
{ {
system = psystem; system = psystem;
@ -195,7 +188,7 @@ SessionContainer::Iterator i = session_tab.End();
} }
else else
{ {
log << log2 << "SM: sessions limit exceeded (" << config->session_max << ")" << logend; main_log << log2 << "SM: sessions limit exceeded (" << config->session_max << ")" << logend;
} }
if( i != session_tab.End() ) if( i != session_tab.End() )
@ -206,12 +199,12 @@ SessionContainer::Iterator i = session_tab.End();
session->id_index = (unsigned int)session->id; session->id_index = (unsigned int)session->id;
session->id_index += std::rand(); session->id_index += std::rand();
log << log2 << "SM: created a new session: " << session->id << logend; main_log << log2 << "SM: created a new session: " << session->id << logend;
} }
else else
{ {
// there is a problem with generating a new session id // there is a problem with generating a new session id
log << log1 << "SM: cannot create a session id" << logend; main_log << log1 << "SM: cannot create a session id" << logend;
} }
} }
@ -223,7 +216,7 @@ void SessionManager::SetTemporarySession()
session->SetTimesTo(cur->request->start_time); session->SetTimesTo(cur->request->start_time);
session->new_session = false; // temporary session was initialized at the beginning session->new_session = false; // temporary session was initialized at the beginning
log << log2 << "SM: using temporary session" << logend; main_log << log2 << "SM: using temporary session" << logend;
} }
@ -242,15 +235,15 @@ return difference;
void SessionManager::SetSessionPutLogInfo(Session & ses, bool has_index, unsigned int difference) void SessionManager::SetSessionPutLogInfo(Session & ses, bool has_index, unsigned int difference)
{ {
log << log2 << "SM: session: " << ses.id; main_log << log2 << "SM: session: " << ses.id;
if( has_index ) if( has_index )
log << ", index difference: " << (size_t)difference; main_log << ", index difference: " << (size_t)difference;
if( ses.puser ) if( ses.puser )
log << log2 << ", user: " << ses.puser->name << ", id: " << ses.puser->id; main_log << log2 << ", user: " << ses.puser->name << ", id: " << ses.puser->id;
log << log2 << logend; main_log << log2 << logend;
} }
@ -264,7 +257,7 @@ void SessionManager::IncrementBanLevel(IPBan * ip_ban)
cur->request->start_time + (time_t)config->ban_level_3_delay); cur->request->start_time + (time_t)config->ban_level_3_delay);
PT::Date date(ip_ban->expires); PT::Date date(ip_ban->expires);
log << log2 << "SM: this IP address has been banned to: " << date << " UTC" << logend; main_log << log2 << "SM: this IP address has been banned to: " << date << " UTC" << logend;
} }
@ -290,7 +283,7 @@ void SessionManager::BrokenCookieCheckBan()
} }
else else
{ {
log << log2 << "SM: too many incorrect encoded cookies were sent from this IP" << logend; main_log << log2 << "SM: too many incorrect encoded cookies were sent from this IP" << logend;
IncrementBanLevel(current_ip_ban); IncrementBanLevel(current_ip_ban);
} }
} }
@ -308,14 +301,14 @@ void SessionManager::IncorrectSessionCheckBan()
} }
else else
{ {
log << log2 << "SM: too many incorrect sessions identifiers were sent from this IP" << logend; main_log << log2 << "SM: too many incorrect sessions identifiers were sent from this IP" << logend;
IncrementBanLevel(current_ip_ban); IncrementBanLevel(current_ip_ban);
} }
} }
bool SessionManager::ShouldNoSessionCookieGenerateTmpSession() void SessionManager::NoSessionCookieWasSent()
{ {
if( !current_ip_ban ) if( !current_ip_ban )
current_ip_ban = &AddIPToBanList(cur->request->ip, cur->request->start_time); current_ip_ban = &AddIPToBanList(cur->request->ip, cur->request->start_time);
@ -324,16 +317,13 @@ bool SessionManager::ShouldNoSessionCookieGenerateTmpSession()
{ {
current_ip_ban->no_session_cookie_events += 1; current_ip_ban->no_session_cookie_events += 1;
SetFirstExpirationTime(current_ip_ban); SetFirstExpirationTime(current_ip_ban);
return false;
} }
else else
{ {
log << log2 << "SM: too many times you have not sent a session cookie" << logend; main_log << log2 << "SM: too many times you have not sent a session cookie" << logend;
if( config->no_session_cookie_ban_mode == 1 ) if( config->no_session_cookie_ban_mode == 1 )
IncrementBanLevel(current_ip_ban); IncrementBanLevel(current_ip_ban);
return true;
} }
} }
@ -346,21 +336,21 @@ bool SessionManager::IsSessionCorrect(long id, bool has_index, unsigned int inde
if( id == 0 ) if( id == 0 )
{ {
log << log3 << "SM: id 0 is reserved for the temporary session" << logend; main_log << log3 << "SM: id 0 is reserved for the temporary session" << logend;
IncorrectSessionCheckBan(); IncorrectSessionCheckBan();
return false; return false;
} }
if( s == session_tab.End() ) if( s == session_tab.End() )
{ {
log << log3 << "SM: there is no a session with id: " << id << logend; main_log << log3 << "SM: there is no a session with id: " << id << logend;
IncorrectSessionCheckBan(); IncorrectSessionCheckBan();
return false; return false;
} }
if( s->remove_me ) if( s->remove_me )
{ {
log << log3 << "SM: session: " << id << " is marked for removing" << logend; main_log << log3 << "SM: session: " << id << " is marked for removing" << logend;
return false; return false;
} }
@ -370,7 +360,7 @@ bool SessionManager::IsSessionCorrect(long id, bool has_index, unsigned int inde
if( (size_t)difference > config->session_allow_index_difference ) if( (size_t)difference > config->session_allow_index_difference )
{ {
log << log2 << "SM: an incorrect session index for session: " << id main_log << log2 << "SM: an incorrect session index for session: " << id
<< ", index difference: " << (size_t)difference << logend; << ", index difference: " << (size_t)difference << logend;
IncorrectSessionCheckBan(); IncorrectSessionCheckBan();
@ -423,7 +413,7 @@ bool SessionManager::SetSessionFromCookie(const std::wstring & cookie)
if( !session_id_manager.DecodeToken(cookie, id, index) ) if( !session_id_manager.DecodeToken(cookie, id, index) )
{ {
log << log2 << "SM: an incorrect cookie string was sent" << logend; main_log << log2 << "SM: an incorrect cookie string was sent" << logend;
BrokenCookieCheckBan(); BrokenCookieCheckBan();
return false; return false;
} }
@ -448,14 +438,14 @@ bool SessionManager::IsIPBanned()
if( current_ip_ban->expires != 0 && cur->request->start_time >= current_ip_ban->expires ) if( current_ip_ban->expires != 0 && cur->request->start_time >= current_ip_ban->expires )
{ {
log << log2 << "SM: resetting events counters for this IP" << logend; main_log << log2 << "SM: resetting events counters for this IP" << logend;
current_ip_ban->ResetEventsCounters(); current_ip_ban->ResetEventsCounters();
} }
else else
if( current_ip_ban->IsIPBanned() ) if( current_ip_ban->IsIPBanned() )
{ {
PT::Date date = current_ip_ban->expires; PT::Date date = current_ip_ban->expires;
log << log2 << "SM: this ip is bannned to: " << date << " UTC" << logend; main_log << log2 << "SM: this ip is bannned to: " << date << " UTC" << logend;
return true; return true;
} }
} }
@ -480,11 +470,11 @@ return false;
* so we always have a session * so we always have a session
* *
*/ */
void SessionManager::PrepareSession() Session * SessionManager::PrepareSession()
{ {
session = nullptr; session = nullptr;
if( !IsIPBanned() && cur->request->function ) if( !IsIPBanned() )
{ {
CookieTab::iterator i = cur->request->cookie_tab.find(config->http_session_id_name); CookieTab::iterator i = cur->request->cookie_tab.find(config->http_session_id_name);
@ -493,39 +483,54 @@ void SessionManager::PrepareSession()
if( !SetSessionFromCookie(i->second) ) if( !SetSessionFromCookie(i->second) )
{ {
cur->request->cookie_tab.erase(i); cur->request->cookie_tab.erase(i);
if( cur->request->function->need_session )
{
// IP could be banned in SetSessionFromCookie() when the cookie string was incorrect
if( !current_ip_ban || !current_ip_ban->IsIPBanned() )
{
CreateSession();
}
}
} }
} }
else else
{ {
if( cur->request->function->need_session ) if( cur->request->function && cur->request->function->need_session )
{ {
if( !ShouldNoSessionCookieGenerateTmpSession() ) NoSessionCookieWasSent();
{
CreateSession();
}
} }
} }
} }
if( !session && cur->request->function && cur->request->function->need_session )
{
if( !current_ip_ban || !current_ip_ban->IsIPBanned() )
{
CreateSession();
}
}
if( !session ) if( !session )
{ {
SetTemporarySession(); SetTemporarySession();
} }
session->ip_ban = current_ip_ban; session->ip_ban = current_ip_ban;
return session;
} }
Session * SessionManager::CheckIfFunctionRequireSession()
{
if( cur->request->function && cur->request->function->need_session )
{
if( session == &temporary_session )
{
if( !current_ip_ban || !current_ip_ban->IsIPBanned() )
{
CreateSession();
session->ip_ban = current_ip_ban;
}
}
}
return session;
}
Session * SessionManager::FindSession(long id) Session * SessionManager::FindSession(long id)
{ {
SessionContainer::Iterator i = session_tab.FindById(id); SessionContainer::Iterator i = session_tab.FindById(id);
@ -596,11 +601,11 @@ SessionContainer::Iterator i = session_tab.FindById(old_id);
if( changed ) if( changed )
plugin->Call(&(*i), WINIX_SESSION_CHANGED_ID, old_id, new_id); plugin->Call(&(*i), WINIX_SESSION_CHANGED_ID, old_id, new_id);
else else
log << log1 << "SM: I cannot create a new session id (still uses old one)" << logend; main_log << log1 << "SM: I cannot create a new session id (still uses old one)" << logend;
} }
else else
{ {
log << log2 << "SM: there is no a session with id: " << old_id << logend; main_log << log2 << "SM: there is no a session with id: " << old_id << logend;
} }
return changed; return changed;
@ -611,7 +616,7 @@ void SessionManager::InitTmpSession()
{ {
Session * old_session = cur->session; Session * old_session = cur->session;
log << log4 << "SM: initializing temporary session" << logend; main_log << log4 << "SM: initializing temporary session" << logend;
cur->session = &temporary_session; cur->session = &temporary_session;
plugin->Call(WINIX_SESSION_CREATED); plugin->Call(WINIX_SESSION_CREATED);
@ -624,7 +629,7 @@ void SessionManager::UninitTmpSession()
{ {
Session * old_session = cur->session; Session * old_session = cur->session;
log << log4 << "SM: uninitializing temporary session" << logend; main_log << log4 << "SM: uninitializing temporary session" << logend;
cur->session = &temporary_session; cur->session = &temporary_session;
@ -690,11 +695,11 @@ char file_path[WINIX_OS_PATH_SIZE];
if( !file ) if( !file )
{ {
log << log1 << "SM: cannot open the session file for writing - sessions lost" << logend; main_log << log1 << "SM: cannot open the session file for writing - sessions lost" << logend;
return; return;
} }
log << log2 << "SM: saving sessions" << logend; main_log << log2 << "SM: saving sessions" << logend;
long len = 0; long len = 0;
SessionContainer::Iterator i = session_tab.Begin(); SessionContainer::Iterator i = session_tab.Begin();
@ -714,7 +719,7 @@ char file_path[WINIX_OS_PATH_SIZE];
file.close(); file.close();
chmod(file_path, 0600); chmod(file_path, 0600);
log << log2 << "SM: saved " << len << " session(s)" << logend; main_log << log2 << "SM: saved " << len << " session(s)" << logend;
} }

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (c) 2008-2018, Tomasz Sowa * Copyright (c) 2008-2019, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -62,7 +62,6 @@ public:
SessionManager(); SessionManager();
void SetCur(Cur * pcur); void SetCur(Cur * pcur);
void SetConfig(Config * pconfig);
void SetSystem(System * psystem); void SetSystem(System * psystem);
void SetLastContainer(LastContainer * plast_container); void SetLastContainer(LastContainer * plast_container);
@ -71,7 +70,12 @@ public:
// can return a null pointer // can return a null pointer
Session * FindSession(long id); Session * FindSession(long id);
void PrepareSession(); Session * PrepareSession();
// some functions require a session (need_session flag) so if there was
// a temporary session and a function requires a session then we create a new session
Session * CheckIfFunctionRequireSession();
void DeleteSessions(); // deleting all sessions void DeleteSessions(); // deleting all sessions
bool ChangeSessionId(long old_id); bool ChangeSessionId(long old_id);
@ -108,7 +112,6 @@ public:
private: private:
Config * config;
Cur * cur; Cur * cur;
System * system; System * system;
LastContainer * last_container; LastContainer * last_container;
@ -133,7 +136,7 @@ private:
void SetFirstExpirationTime(IPBan * ip_ban); void SetFirstExpirationTime(IPBan * ip_ban);
void BrokenCookieCheckBan(); void BrokenCookieCheckBan();
void IncorrectSessionCheckBan(); void IncorrectSessionCheckBan();
bool ShouldNoSessionCookieGenerateTmpSession(); void NoSessionCookieWasSent();
/* /*

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (c) 2011-2018, Tomasz Sowa * Copyright (c) 2011-2019, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -72,27 +72,31 @@ sigset_t set;
void ThreadManager::Add(BaseThread * pbase, const wchar_t * thread_name) void ThreadManager::Add(BaseThread * pbase, const wchar_t * thread_name)
{ {
ThreadItem item; thread_tab.emplace_back();
ThreadItem & item = thread_tab.back();
item.thread_item_data = new ThreadItemData();
ThreadItemData & data = *item.thread_item_data;
item.object = pbase; item.object = pbase;
item.name = thread_name; item.name = thread_name;
item.object->set_dependency(this);
thread_tab.push_back(item); // main log buffer (from the main thread)
thread_tab.back().thread_item_data = new ThreadItemData(); item.object->set_main_log_buffer(log.GetLogBuffer());
thread_tab.back().object->set_dependency(this); item.object->set_main_file_log(log.GetFileLog());
// the logger buffer and model_connector are different // the logger buffer and model_connector are different
ThreadItemData & data = *thread_tab.back().thread_item_data; item.object->set_log_buffer(&data.log_buffer);
thread_tab.back().object->set_log_buffer(&data.log_buffer);
//data.postgresql_connector.set_logger(logger); //data.postgresql_connector.set_logger(logger);
data.postgresql_connector.set_conn_param(config->db_database, config->db_user, config->db_pass); data.postgresql_connector.set_conn_param(config->db_database, config->db_user, config->db_pass);
data.postgresql_connector.set_logger(thread_tab.back().object->get_logger()); data.postgresql_connector.set_logger(item.object->get_logger());
data.postgresql_connector.set_log_queries(config->log_db_query); data.postgresql_connector.set_log_queries(config->log_db_query);
data.postgresql_connector.wait_for_connection(); data.postgresql_connector.wait_for_connection();
data.model_connector.set_db_connector(data.postgresql_connector); data.model_connector.set_db_connector(data.postgresql_connector);
data.model_connector.set_flat_connector(data.json_connector); data.model_connector.set_flat_connector(data.json_connector);
thread_tab.back().object->set_model_connector(&data.model_connector);
item.object->set_model_connector(&data.model_connector);
if( were_started ) if( were_started )
{ {
@ -144,8 +148,6 @@ void ThreadManager::StartAll()
void ThreadManager::Start(int id, ThreadItem * item) void ThreadManager::Start(int id, ThreadItem * item)
{ {
item->object->SetSynchro(synchro);
if( item->object->StartThread() ) if( item->object->StartThread() )
{ {
log << log4 << "TM: thread " << id << " (" << item->object->ThreadId() << ", name: " log << log4 << "TM: thread " << id << " (" << item->object->ThreadId() << ", name: "

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (c) 2011-2018, Tomasz Sowa * Copyright (c) 2011-2019, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -183,10 +183,7 @@ void ExportThread::DoMessage()
if( message_work.errors > conn_max_errors ) if( message_work.errors > conn_max_errors )
{ {
message_work.can_remove = true; message_work.can_remove = true;
Lock();
log << log1 << "Export: too many errors for uploading " << message_work.path << " (skipping)" << logend << logsave; log << log1 << "Export: too many errors for uploading " << message_work.path << " (skipping)" << logend << logsave;
Unlock();
} }
} }
} }
@ -206,9 +203,7 @@ bool ExportThread::Fetch(const char * url)
if( !curl ) if( !curl )
{ {
Lock();
log << log1 << "Export: I can't use curl" << logend; log << log1 << "Export: I can't use curl" << logend;
Unlock();
return false; return false;
} }
@ -232,9 +227,7 @@ bool ExportThread::Fetch(const char * url)
if( res != 0 ) if( res != 0 )
{ {
Lock();
log << log1 << "Export: download failed: " << error_buf << logend << logsave; log << log1 << "Export: download failed: " << error_buf << logend << logsave;
Unlock();
return false; return false;
} }
@ -288,9 +281,7 @@ FILE * file = 0;
if( !file ) if( !file )
{ {
Lock();
log << log1 << "Export: I cannot open the file: " << local_path << logend; log << log1 << "Export: I cannot open the file: " << local_path << logend;
Unlock();
return false; return false;
} }
else else
@ -307,9 +298,7 @@ FILE * file = 0;
if( !curl ) if( !curl )
{ {
Lock();
log << log1 << "Export: I can't use curl (sending)" << logend; log << log1 << "Export: I can't use curl (sending)" << logend;
Unlock();
if( file ) if( file )
fclose(file); fclose(file);
@ -355,16 +344,12 @@ FILE * file = 0;
if( res != 0 ) if( res != 0 )
{ {
Lock();
log << log1 << "Export: upload failed: " << error_buf << " (" << ftp_server << ")" << logend << logsave; log << log1 << "Export: upload failed: " << error_buf << " (" << ftp_server << ")" << logend << logsave;
Unlock();
return false; return false;
} }
else else
{ {
Lock();
log << log2 << "Export: uploaded: " << ftp_server << logend << logsave; log << log2 << "Export: uploaded: " << ftp_server << logend << logsave;
Unlock();
} }
return true; return true;