/* * This file is a part of Winix * and is not publicly distributed * * Copyright (c) 2011-2014, Tomasz Sowa * All rights reserved. * */ #include #include #include #include #include "exportthread.h" #include "core/log.h" #include "core/misc.h" #include "utf8/utf8.h" namespace Winix { namespace Export { ExportThread * ExportThread::exp_thread; ExportThread::ExportThread() { exp_thread = 0; utf8 = false; browser_name = "Winix Export"; conn_timeout = 5; conn_max_errors = 3; } void ExportThread::SetUTF8(bool use_utf8) { utf8 = use_utf8; } void ExportThread::SetBaseUrl(const std::wstring & url) { // SetBaseUrl() is called before the this thread // is started so we can use 'base_url' later without locking // the same is for 'utf8' base_url = url; } void ExportThread::AddMessage(const Message & message) { log << log1 << "export thread: a" << logsave << logend; message_tab.insert(message_tab.end(), message); WakeUpThread(); log << log1 << "export thread: b" << logsave << logend; } // first thread (objects locked) void ExportThread::AddMessage(int type, const std::wstring & url, const std::wstring & path) { message_add_temp.type = type; message_add_temp.url = url; message_add_temp.path = path; AddMessage(message_add_temp); } // second thread // objects are locked bool ExportThread::SignalReceived() { return !message_tab.empty(); } // second thread // objects are not locked void ExportThread::Do() { MessageTab::iterator i; bool end; Lock(); i = message_tab.begin(); Unlock(); do { Lock(); if( i != message_tab.end() ) { message_work = *i; Unlock(); DoMessage(); Lock(); // although there was Unlock() used we can use the same iterator 'i' here // it will *not* be invalidated (MessageTab is a std::list) // and we are deleting only here i->errors = message_work.errors; if( message_work.can_remove ) message_tab.erase(i++); end = false; } else { end = true; } WaitForSignalSleep(1); Unlock(); } while( !end && !IsExitSignal() ); } // second thread // objects are not locked // current message we have in 'message_work' void ExportThread::DoMessage() { bool sent_ok = false; Convert(message_work.url, url_a); if( message_work.type == WINIX_PL_EXPORT_TYPE_CREATE_FILE ) { if( Fetch(url_a.c_str()) ) { ChangeAdresses(buffer); if( Put() ) sent_ok = true; } } else { if( Put() ) sent_ok = true; } if( sent_ok ) { message_work.can_remove = true; } else { message_work.errors += 1; message_work.can_remove = false; if( message_work.errors > conn_max_errors ) { message_work.can_remove = true; Lock(); log << log1 << "Export: too many errors for uploading " << message_work.path << " (skipping)" << logend << logsave; Unlock(); } } } // second thread // objects are not locked bool ExportThread::Fetch(const char * url) { Lock(); CURL * curl = curl_easy_init(); Unlock(); if( !curl ) { Lock(); log << log1 << "Export: I can't use curl" << logend; Unlock(); return false; } error_buf[0] = 0; exp_thread = this; buffer.clear(); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, StaticSaveFunction); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_USERAGENT, browser_name.c_str()); curl_easy_setopt(curl, CURLOPT_TIMEOUT, conn_timeout); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, conn_timeout); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buf); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 20); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); CURLcode res = curl_easy_perform(curl); curl_easy_cleanup(curl); if( res != 0 ) { Lock(); log << log1 << "Export: download failed: " << error_buf << logend << logsave; Unlock(); return false; } return true; } size_t ExportThread::StaticSaveFunction(char * ptr, size_t size, size_t nmemb, void *userdata) { if( exp_thread ) return exp_thread->SaveFunction(ptr, size, nmemb, userdata); return 0; } size_t ExportThread::SaveFunction(char * ptr, size_t size, size_t nmemb, void *userdata) { size_t len = size * nmemb; if( len > 0 ) buffer.append(ptr, len); return len; } // second thread // objects are not locked void ExportThread::Convert(const std::wstring & in, std::string & out, bool clear) { Lock(); if( utf8 ) PT::WideToUTF8(in, out, clear); else AssignString(in, out, clear); Unlock(); } // second thread // objects are not locked bool ExportThread::Put() { FILE * file = 0; if( message_work.type == WINIX_PL_EXPORT_TYPE_CREATE_FILE_STATIC ) { Convert(message_work.url, local_path); file = fopen(local_path.c_str(), "r"); if( !file ) { Lock(); log << log1 << "Export: I cannot open the file: " << local_path << logend; Unlock(); return false; } else { log << log3 << "Export: sending a static file: " << local_path << logend; } } Lock(); CURL * curl = curl_easy_init(); Unlock(); if( !curl ) { Lock(); log << log1 << "Export: I can't use curl (sending)" << logend; Unlock(); if( file ) fclose(file); return false; } exp_thread = this; error_buf[0] = 0; buffer_read_index = 0; ftp_server = "ftp://"; Convert(message_work.ftp_server, ftp_server, false); Convert(message_work.path, ftp_server, false); Convert(message_work.ftp_login, ftp_login); Convert(message_work.ftp_pass, ftp_pass); curl_easy_setopt(curl, CURLOPT_URL, ftp_server.c_str()); curl_easy_setopt(curl, CURLOPT_USERNAME, ftp_login.c_str()); curl_easy_setopt(curl, CURLOPT_PASSWORD, ftp_pass.c_str()); curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 2); curl_easy_setopt(curl, CURLOPT_USERAGENT, browser_name.c_str()); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); curl_easy_setopt(curl, CURLOPT_FTP_RESPONSE_TIMEOUT, conn_timeout); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buf); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); if( file ) { curl_easy_setopt(curl, CURLOPT_READDATA, file); } else { curl_easy_setopt(curl, CURLOPT_READFUNCTION, StaticReadFunction); curl_easy_setopt(curl, CURLOPT_INFILESIZE, buffer.size()); } CURLcode res = curl_easy_perform(curl); curl_easy_cleanup(curl); if( file ) fclose(file); if( res != 0 ) { Lock(); log << log1 << "Export: upload failed: " << error_buf << " (" << ftp_server << ")" << logend << logsave; Unlock(); return false; } else { Lock(); log << log2 << "Export: uploaded: " << ftp_server << logend << logsave; Unlock(); } return true; } size_t ExportThread::StaticReadFunction(char * ptr, size_t size, size_t nmemb, void *userdata) { if( exp_thread ) return exp_thread->ReadFunction(ptr, size, nmemb, userdata); return 0; } size_t ExportThread::ReadFunction(char * ptr, size_t size, size_t nmemb, void *userdata) { size_t max_len = size * nmemb; size_t i; for(i=0 ; i' ||buf[i] == '<' ) { return false; } } return false; } // second thread // objects locked void ExportThread::ChangeAdressesThumb(std::string & buf, const char * http_prefix) { look_for_url = http_prefix; Convert(base_url, look_for_url, false); Convert(message_work.src_dir, look_for_url, false); for(size_t i=0 ; i / Convert(message_work.src_dir, look_for_url); repl_url = '/'; ChangeLogStrings(); ReplaceString(buf, look_for_url, repl_url); // changing: // /sitename" -> /" NoLastSlash(look_for_url); look_for_url += '\"'; repl_url = "/\""; ChangeLogStrings(); ReplaceString(buf, look_for_url, repl_url); Unlock(); } // second thread // objects not locked void ExportThread::ChangeAdresses(std::string & buf) { // http:// in sources here means either http:// or https:// // changing: // http://domain.tld/sitename/[...]/file.jpg/-/thumb -> http://otherdomain.tld/download/[...]/file.jpg // http://domain.tld/sitename/[...]/file.jpg/download/thumb -> http://otherdomain.tld/download/[...]/file.jpg ChangeAdressesThumb(buf); // changing: // http://domain.tld/static/sitename/ -> http://otherdomain.tld/static/ ChangeBaseAdress(buf, "/static", "", "/static/"); // changing: // http://domain.tld/common/sitename/ -> http://otherdomain.tld/common/ //ChangeAdresss(buf, "/common", "", "/common/"); // changing: // http://domain.tld/sitename/ -> http://otherdomain.tld/ ChangeBaseAdress(buf, "", "", "/"); // changing: // http://domain.tld/sitename" -> http://otherdomain.tld" ChangeBaseAdress(buf, "", "\"", "\"", true); // changing: // /sitename/ -> / // /sitename" -> /" ChangeSiteNames(buf); } } // namespace } // namespace Winix