mirror of
https://github.com/ONLYOFFICE/desktop-sdk.git
synced 2026-03-31 10:23:12 +08:00
Merge branch hotfix/v9.0.4 into master
This commit is contained in:
@ -126,6 +126,18 @@ SOURCES += \
|
||||
$$CORE_ROOT_DIR/Common/3dParty/pole/pole.cpp \
|
||||
$$CORE_ROOT_DIR/OOXML/Base/unicode_util.cpp
|
||||
|
||||
AI_TOOLS_PATH = $$PWD/tools
|
||||
HEADERS += \
|
||||
$$AI_TOOLS_PATH/tools.h
|
||||
|
||||
SOURCES += \
|
||||
$$AI_TOOLS_PATH/tools.cpp
|
||||
|
||||
AI_TOOLS_FUNCS_PATH = $$/AI_TOOLS_PATH/functions
|
||||
HEADERS += \
|
||||
AI_TOOLS_FUNCS_PATH/internal/base.h \
|
||||
AI_TOOLS_FUNCS_PATH/internal/funcs.h
|
||||
|
||||
# crypto ----------------------------------
|
||||
LIBS += -L$$CORE_BUILDS_LIBRARIES_PATH -lCryptoPPLib
|
||||
LIBS += -L$$CORE_BUILDS_LIBRARIES_PATH -lCompoundFileLib
|
||||
@ -177,4 +189,4 @@ core_linux {
|
||||
LIBS += -lX11 -lX11-xcb -lxkbcommon-x11 -lxkbcommon
|
||||
}
|
||||
|
||||
ADD_DEPENDENCY(graphics, kernel, UnicodeConverter, kernel_network, PdfFile, XpsFile, DjVuFile, hunspell, ooxmlsignature)
|
||||
ADD_DEPENDENCY(graphics, kernel, UnicodeConverter, kernel_network, PdfFile, XpsFile, DjVuFile, hunspell, ooxmlsignature, doctrenderer)
|
||||
|
||||
@ -293,12 +293,14 @@ namespace NSRequest
|
||||
CefRefPtr<CefRequest> m_request;
|
||||
int m_requestId;
|
||||
int_64_type m_frameId;
|
||||
bool m_isProgress;
|
||||
|
||||
CCefView_Private* m_view;
|
||||
|
||||
public:
|
||||
CSimpleRequestClient(CefRefPtr<CefListValue>& args)
|
||||
{
|
||||
m_isProgress = false;
|
||||
m_request = CefRequest::Create();
|
||||
m_frameId = NSArgumentList::GetInt64(args, 0);
|
||||
m_requestId = args->GetInt(1);
|
||||
@ -307,6 +309,18 @@ namespace NSRequest
|
||||
std::string sMethod = args->GetString(3).ToString();
|
||||
m_request->SetMethod(sMethod);
|
||||
|
||||
std::string::size_type posMethodParams = sMethod.find(":");
|
||||
std::string sParams = "";
|
||||
|
||||
if (posMethodParams != std::string::npos)
|
||||
{
|
||||
sParams = sMethod.substr(posMethodParams + 1);
|
||||
sMethod = sMethod.substr(0, posMethodParams);
|
||||
|
||||
if (std::string::npos != sParams.find("stream"))
|
||||
m_isProgress = true;
|
||||
}
|
||||
|
||||
if ("POST" == sMethod)
|
||||
{
|
||||
std::string sPostData = args->GetString(4).ToString();
|
||||
@ -422,6 +436,28 @@ namespace NSRequest
|
||||
size_t data_length) override
|
||||
{
|
||||
m_download_data += std::string(static_cast<const char*>(data), data_length);
|
||||
|
||||
if (m_isProgress)
|
||||
{
|
||||
CefURLRequest::Status status = request->GetRequestStatus();
|
||||
CefURLRequest::ErrorCode error_code = request->GetRequestError();
|
||||
|
||||
std::string sReturnObject = "{ status: " + std::to_string((int)status) + ", statusCode: " + std::to_string((int)error_code) + ", responseText : \"";
|
||||
|
||||
char* base64 = NULL;
|
||||
int base64Len = 0;
|
||||
NSFile::CBase64Converter::Encode(static_cast<const BYTE*>(data), (int)data_length, base64, base64Len, NSBase64::B64_BASE64_FLAG_NOCRLF);
|
||||
|
||||
sReturnObject += std::string(base64, base64Len);
|
||||
RELEASEARRAYOBJECTS(base64);
|
||||
|
||||
sReturnObject += "\"}";
|
||||
|
||||
std::string sCode = "try { window.AscSimpleRequest._onProgress(" + std::to_string(m_requestId) + ", " +
|
||||
sReturnObject + "); } catch (err) { window.AscSimpleRequest._onError(" +
|
||||
std::to_string(m_requestId) + ", { status : \"error\", statusCode : 404, responseText : \"\" }); }";
|
||||
this->SendToRenderer(m_frameId, sCode);
|
||||
}
|
||||
}
|
||||
|
||||
bool GetAuthCredentials(bool isProxy,
|
||||
@ -558,6 +594,7 @@ namespace NSSystem
|
||||
|
||||
bool bRes = true;
|
||||
|
||||
m_pLocker->StartWrite();
|
||||
m_pLocker->SeekFile(0);
|
||||
|
||||
DWORD dwRead = 0;
|
||||
@ -1510,6 +1547,8 @@ public:
|
||||
// показывать ли консоль для дебага
|
||||
bool m_bDebugInfoSupport;
|
||||
|
||||
bool m_bSupportMultiplugins;
|
||||
|
||||
// экспериментальные возможности
|
||||
bool m_bExperimentalFeatures;
|
||||
|
||||
@ -1642,6 +1681,7 @@ public:
|
||||
m_sLogFile = "";
|
||||
m_bDebugInfoSupport = false;
|
||||
m_bExperimentalFeatures = false;
|
||||
m_bSupportMultiplugins = false;
|
||||
|
||||
m_bIsUseExternalMessageLoop = false;
|
||||
m_pExternalMessageLoop = NULL;
|
||||
@ -1898,6 +1938,10 @@ public:
|
||||
if (pairDEBUG != _map->end() && "1" == pairDEBUG->second)
|
||||
m_bDebugInfoSupport = true;
|
||||
|
||||
std::map<std::string, std::string>::iterator pairComplexPlugins = _map->find("support-complex-plugins");
|
||||
if (pairComplexPlugins != _map->end() && "1" == pairComplexPlugins->second)
|
||||
m_bSupportMultiplugins = true;
|
||||
|
||||
if (!NSCommon::CSystemWindowScale::IsInit())
|
||||
{
|
||||
std::map<std::string, std::string>::iterator pairUseSystemScale = _map->find("system-scale");
|
||||
|
||||
@ -501,6 +501,8 @@ int CApplicationCEF::Init_CEF(CAscApplicationManager* pManager, int argc, char*
|
||||
CPluginsManager oPlugins;
|
||||
oPlugins.m_strDirectory = pManager->m_oSettings.system_plugins_path;
|
||||
oPlugins.m_strUserDirectory = pManager->m_oSettings.user_plugins_path;
|
||||
oPlugins.m_bIsSupportMultiplugins = pManager->m_pInternal->m_bSupportMultiplugins;
|
||||
|
||||
oPlugins.GetInstalledPlugins();
|
||||
|
||||
bool bIsCurrentCryptoPresent = false;
|
||||
|
||||
@ -4689,8 +4689,10 @@ virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser,
|
||||
sGuidA = sGuidA.substr(4);
|
||||
std::wstring sGuid = UTF8_TO_U(sGuidA);
|
||||
|
||||
std::wstring sSrc = (iterExt->isUser ? sUserPluginsPath : sSystemPluginsPath) + L"/" + sGuid + L"/index.html" + m_pParent->m_pInternal->m_pManager->m_pInternal->m_mainPostFix;
|
||||
std::wstring sSrc = (iterExt->isUser ? sUserPluginsPath : sSystemPluginsPath) + L"/" + sGuid + L"/" + UTF8_TO_U(iterExt->sUrl) + m_pParent->m_pInternal->m_pManager->m_pInternal->m_mainPostFix;
|
||||
NSCommon::url_correct(sSrc);
|
||||
if (iterExt->isOnlyofficeScheme)
|
||||
sSrc = L"onlyoffice://plugin/" + sSrc;
|
||||
|
||||
std::wstring sNameG = UTF8_TO_U((iterExt->sName));
|
||||
std::wstring sNameLocal = UTF8_TO_U((iterExt->sNameObject));
|
||||
|
||||
@ -60,6 +60,8 @@
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "../../tools/tools.h"
|
||||
|
||||
std::wstring GetTmpFileFromBase64(const std::string& sData, const std::wstring& sTmpFolder)
|
||||
{
|
||||
if (sData.empty())
|
||||
@ -2394,11 +2396,13 @@ window.AscDesktopEditor._convertFile(path, format);\n\
|
||||
window.AscDesktopEditor.getPortalsList = function() { var ret = []; try { var portals = JSON.parse(localStorage.getItem(\"portals\")); for (var i = 0, len = portals.length; i < len; i++) { ret.push(portals[i].portal); ret.push(portals[i].provider); } } catch(err) { ret = []; } console.log(ret);window.AscDesktopEditor.setPortalsList(ret); };\n\
|
||||
";
|
||||
#ifdef CEF_VERSION_ABOVE_102
|
||||
sCodeInitJS += "!function(){window.AscSimpleRequest=window.AscSimpleRequest||{};var r=0,o={};window.AscSimpleRequest.createRequest=function(e){var "
|
||||
"t;o[++r]={id:r,complete:e.complete,error:e.error},e.timeout&&(o[t=r].timer=setTimeout(function(){o[t]&&(o[t].error&&o[t].error({status:\"error\",statusCode:404},"
|
||||
"\"error\"),delete o[t])},e.timeout)),window.AscDesktopEditor.sendSimpleRequest(r,e)},window.AscSimpleRequest._onSuccess=function(e,t){let "
|
||||
"r=o[e];r&&(r.timer&&clearTimeout(r.timer),r.complete&&r.complete(t,t.status),delete o[e])},window.AscSimpleRequest._onError=function(e,t){let "
|
||||
"r=o[e];r&&(r.timer&&clearTimeout(r.timer),r.error&&r.error(t,t.status),delete o[e])}}();\n";
|
||||
sCodeInitJS += "!function(){window.AscSimpleRequest=window.AscSimpleRequest||{};var r=0,o={};window.AscSimpleRequest.createRequest=function(e)"
|
||||
"{var t;o[++r]={id:r,complete:e.complete,error:e.error,progress:e.progress},e.timeout&&(o[t=r].timer=setTimeout(function()"
|
||||
"{o[t]&&(o[t].error&&o[t].error({status:\"error\",statusCode:404},\"error\"),delete o[t])},e.timeout)),"
|
||||
"window.AscDesktopEditor.sendSimpleRequest(r,e)},window.AscSimpleRequest._onSuccess=function(e,t){"
|
||||
"let r=o[e];r&&(r.timer&&clearTimeout(r.timer),r.complete&&r.complete(t,t.status),delete o[e])},"
|
||||
"window.AscSimpleRequest._onError=function(e,t){let r=o[e];r&&(r.timer&&clearTimeout(r.timer),r.error&&r.error(t,t.status),delete o[e])},"
|
||||
"window.AscSimpleRequest._onProgress=function(e,t){let r=o[e];r&&(r.timer&&clearTimeout(r.timer),r.progress&&r.progress(t,t.status))}}();";
|
||||
#endif
|
||||
sCodeInitJS += "\
|
||||
window.AscDesktopEditor.getViewportSettings=function(){return JSON.parse(window.AscDesktopEditor._getViewportSettings());};\
|
||||
@ -4408,6 +4412,32 @@ window.AscDesktopEditor.CallInFrame(\"" +
|
||||
retval = CefV8Value::CreateInt(nVersion);
|
||||
return true;
|
||||
}
|
||||
else if (name == "getToolFunctions")
|
||||
{
|
||||
CAITools& tools = CAITools::getInstance();
|
||||
if (tools.getWorkDirectory().empty())
|
||||
{
|
||||
tools.setFontsDirectory(m_sFontsData);
|
||||
tools.setWorkDirectory(m_sSystemPlugins + L"/../../converter");
|
||||
}
|
||||
|
||||
retval = CefV8Value::CreateString(tools.getFunctions());
|
||||
return true;
|
||||
}
|
||||
else if (name == "callToolFunction")
|
||||
{
|
||||
CAITools& tools = CAITools::getInstance();
|
||||
if (tools.getWorkDirectory().empty())
|
||||
{
|
||||
tools.setFontsDirectory(m_sFontsData);
|
||||
tools.setWorkDirectory(m_sSystemPlugins + L"/../../converter");
|
||||
}
|
||||
|
||||
std::string name = arguments[0]->GetStringValue().ToString();
|
||||
std::string param = arguments[1]->GetStringValue().ToString();
|
||||
retval = CefV8Value::CreateString(tools.callFunc(name, param));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function does not exist.
|
||||
return false;
|
||||
@ -4983,7 +5013,7 @@ if (targetElem) { targetElem.dispatchEvent(event); }})();";
|
||||
|
||||
CefRefPtr<CefV8Handler> handler = pWrapper;
|
||||
|
||||
#define EXTEND_METHODS_COUNT 186
|
||||
#define EXTEND_METHODS_COUNT 188
|
||||
const char* methods[EXTEND_METHODS_COUNT] = {
|
||||
"Copy",
|
||||
"Paste",
|
||||
@ -5242,6 +5272,9 @@ if (targetElem) { targetElem.dispatchEvent(event); }})();";
|
||||
|
||||
"getEngineVersion",
|
||||
|
||||
"getToolFunctions",
|
||||
"callToolFunction",
|
||||
|
||||
NULL};
|
||||
|
||||
ExtendObject(obj, handler, methods);
|
||||
|
||||
@ -299,8 +299,7 @@ public:
|
||||
virtual bool ReadResponse(void* data_out,
|
||||
int bytes_to_read,
|
||||
int& bytes_read,
|
||||
CefRefPtr<CefCallback> callback)
|
||||
OVERRIDE
|
||||
CefRefPtr<CefCallback> callback) OVERRIDE
|
||||
{
|
||||
CEF_REQUIRE_IO_THREAD();
|
||||
|
||||
@ -374,9 +373,163 @@ private:
|
||||
IMPLEMENT_REFCOUNTING(ClientSchemeHandler);
|
||||
};
|
||||
|
||||
class ClientSchemeHandlerOO : public CefResourceHandler
|
||||
{
|
||||
public:
|
||||
ClientSchemeHandlerOO(CAscApplicationManager* pManager) : offset_(0)
|
||||
{
|
||||
offset_ = 0;
|
||||
data_binary_ = NULL;
|
||||
data_binary_len_ = 0;
|
||||
|
||||
m_pManager = pManager;
|
||||
}
|
||||
virtual ~ClientSchemeHandlerOO()
|
||||
{
|
||||
if (NULL != data_binary_)
|
||||
delete [] data_binary_;
|
||||
}
|
||||
|
||||
virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
|
||||
CefRefPtr<CefCallback> callback) OVERRIDE
|
||||
{
|
||||
CEF_REQUIRE_IO_THREAD();
|
||||
|
||||
std::string url = request->GetURL().ToString();
|
||||
std::string::size_type posFind = url.find("onlyoffice://plugin/");
|
||||
if (posFind != std::string::npos)
|
||||
{
|
||||
std::wstring sFile = read_file_path(request).substr(20);
|
||||
if (!m_pManager->IsResolveLocalFile(sFile))
|
||||
return false;
|
||||
|
||||
std::wstring::size_type posSearch = sFile.find(L"?lang=");
|
||||
if (std::wstring::npos == posSearch)
|
||||
{
|
||||
posSearch = sFile.find(L".html?");
|
||||
if (std::wstring::npos != posSearch)
|
||||
posSearch += 5;
|
||||
}
|
||||
|
||||
if (std::wstring::npos != posSearch)
|
||||
sFile = sFile.substr(0, posSearch);
|
||||
|
||||
if (0 == sFile.find(L"file:///"))
|
||||
{
|
||||
sFile = sFile.substr(7);
|
||||
if (!NSFile::CFileBinary::Exists(sFile))
|
||||
sFile = sFile.substr(1);
|
||||
}
|
||||
|
||||
read_binary_file(sFile, true);
|
||||
|
||||
callback->Continue();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void GetResponseHeaders(CefRefPtr<CefResponse> response,
|
||||
int64& response_length,
|
||||
CefString& redirectUrl) OVERRIDE
|
||||
{
|
||||
CEF_REQUIRE_IO_THREAD();
|
||||
|
||||
DCHECK(!data_.empty() || !data_binary_);
|
||||
|
||||
response->SetMimeType(mime_type_);
|
||||
response->SetStatus(200);
|
||||
|
||||
CefResponse::HeaderMap headers;
|
||||
response->GetHeaderMap(headers);
|
||||
headers.insert(std::make_pair("Access-Control-Allow-Origin", "*"));
|
||||
response->SetHeaderMap(headers);
|
||||
|
||||
// Set the resulting response length
|
||||
response_length = (NULL == data_binary_) ? data_.length() : data_binary_len_;
|
||||
}
|
||||
|
||||
virtual void Cancel() OVERRIDE
|
||||
{
|
||||
CEF_REQUIRE_IO_THREAD();
|
||||
}
|
||||
|
||||
virtual bool ReadResponse(void* data_out,
|
||||
int bytes_to_read,
|
||||
int& bytes_read,
|
||||
CefRefPtr<CefCallback> callback) OVERRIDE
|
||||
{
|
||||
CEF_REQUIRE_IO_THREAD();
|
||||
bytes_read = 0;
|
||||
|
||||
BYTE* read_data = (BYTE*)data_binary_;
|
||||
size_t read_data_len = data_binary_len_;
|
||||
|
||||
if (!read_data)
|
||||
{
|
||||
read_data = (BYTE*)data_.c_str();
|
||||
read_data_len = data_.length();
|
||||
}
|
||||
|
||||
if (offset_ < read_data_len)
|
||||
{
|
||||
// Copy the next block of data into the buffer.
|
||||
int transfer_size = std::min(bytes_to_read, static_cast<int>(read_data_len - offset_));
|
||||
memcpy(data_out, read_data + offset_, transfer_size);
|
||||
offset_ += transfer_size;
|
||||
|
||||
bytes_read = transfer_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::wstring read_file_path(CefRefPtr<CefRequest>& request)
|
||||
{
|
||||
int nFlag = UU_SPACES | UU_REPLACE_PLUS_WITH_SPACE;
|
||||
#if defined (_LINUX) && !defined(_MAC)
|
||||
nFlag |= UU_URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS;
|
||||
#else
|
||||
#ifndef CEF_2623
|
||||
nFlag |= UU_URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
CefString cefUrl = request->GetURL();
|
||||
CefString cefFile = CefURIDecode(cefUrl, false, static_cast<cef_uri_unescape_rule_t>(nFlag));
|
||||
|
||||
return cefFile.ToWString();
|
||||
}
|
||||
|
||||
void read_binary_file(std::wstring& sFile, const bool& isCheckExt = false)
|
||||
{
|
||||
data_binary_len_ = (size_t)asc_scheme::read_file_with_urls(sFile, data_binary_);
|
||||
|
||||
if (isCheckExt)
|
||||
mime_type_ = asc_scheme::GetMimeTypeFromExt(sFile);
|
||||
|
||||
if (mime_type_.empty())
|
||||
mime_type_ = "*/*";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string data_;
|
||||
std::string mime_type_;
|
||||
size_t offset_;
|
||||
|
||||
BYTE* data_binary_;
|
||||
size_t data_binary_len_;
|
||||
|
||||
CAscApplicationManager* m_pManager;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(ClientSchemeHandlerOO);
|
||||
};
|
||||
|
||||
namespace asc_scheme
|
||||
{
|
||||
|
||||
// Implementation of the factory for for creating schema handlers.
|
||||
class ClientSchemeHandlerFactory : public CefSchemeHandlerFactory
|
||||
{
|
||||
@ -394,13 +547,18 @@ namespace asc_scheme
|
||||
virtual CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
const CefString& scheme_name,
|
||||
CefRefPtr<CefRequest> request)
|
||||
OVERRIDE
|
||||
CefRefPtr<CefRequest> request) OVERRIDE
|
||||
{
|
||||
CEF_REQUIRE_IO_THREAD();
|
||||
std::string schemeName = scheme_name.ToString();
|
||||
|
||||
if (schemeName == "onlyoffice")
|
||||
return new ClientSchemeHandlerOO(m_pManager);
|
||||
|
||||
std::wstring sMainUrl = L"";
|
||||
if (browser && browser->GetMainFrame())
|
||||
sMainUrl = browser->GetMainFrame()->GetURL().ToWString();
|
||||
|
||||
return new ClientSchemeHandler(m_pManager, sMainUrl);
|
||||
}
|
||||
|
||||
@ -420,13 +578,17 @@ namespace asc_scheme
|
||||
#endif
|
||||
)
|
||||
{
|
||||
registrar->AddCustomScheme("ascdesktop", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_SECURE | CEF_SCHEME_OPTION_CORS_ENABLED/* | CEF_SCHEME_OPTION_FETCH_ENABLED*/);
|
||||
registrar->AddCustomScheme("ascdesktop", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_SECURE | CEF_SCHEME_OPTION_CORS_ENABLED | CEF_SCHEME_OPTION_FETCH_ENABLED);
|
||||
registrar->AddCustomScheme("onlyoffice", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_SECURE | CEF_SCHEME_OPTION_CORS_ENABLED | CEF_SCHEME_OPTION_FETCH_ENABLED);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool InitScheme(CAscApplicationManager* pManager)
|
||||
{
|
||||
return CefRegisterSchemeHandlerFactory("ascdesktop", "", new ClientSchemeHandlerFactory(pManager));
|
||||
bool res = true;
|
||||
res = res && CefRegisterSchemeHandlerFactory("ascdesktop", "", new ClientSchemeHandlerFactory(pManager));
|
||||
res = res && CefRegisterSchemeHandlerFactory("onlyoffice", "", new ClientSchemeHandlerFactory(pManager));
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace asc_scheme
|
||||
|
||||
@ -26,7 +26,8 @@
|
||||
simpleRequestCallback[simpleRequestCounter] = {
|
||||
id : simpleRequestCounter,
|
||||
complete : obj.complete,
|
||||
error : obj.error
|
||||
error : obj.error,
|
||||
progress : obj.progress
|
||||
};
|
||||
|
||||
if (obj.timeout)
|
||||
@ -82,4 +83,17 @@
|
||||
}
|
||||
};
|
||||
|
||||
window.AscSimpleRequest._onProgress = function(counter, e)
|
||||
{
|
||||
let obj = simpleRequestCallback[counter];
|
||||
if (obj)
|
||||
{
|
||||
if (obj.timer)
|
||||
clearTimeout(obj.timer);
|
||||
|
||||
if (obj.progress)
|
||||
obj.progress(e, e.status);
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
@ -32,6 +32,264 @@
|
||||
|
||||
#include "./filelocker.h"
|
||||
#include <set>
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
#if defined(CreateFile)
|
||||
#undef CreateFile
|
||||
#endif
|
||||
|
||||
#if defined(CopyFile)
|
||||
#undef CopyFile
|
||||
#endif
|
||||
|
||||
#if defined(DeleteFile)
|
||||
#undef DeleteFile
|
||||
#endif
|
||||
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef HOST_NAME_MAX
|
||||
#define HOST_NAME_MAX 1000
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "../../../../core/DesktopEditor/common/SystemUtils.h"
|
||||
#include "../../../../core/DesktopEditor/common/StringBuilder.h"
|
||||
|
||||
#if defined(_LINUX) && !defined(_MAC)
|
||||
#include <gio/gio.h>
|
||||
#define LOCKER_USE_GIO
|
||||
#endif
|
||||
|
||||
namespace NSSystem
|
||||
{
|
||||
CLockFileTemp::CLockFileTemp(const std::wstring& file)
|
||||
{
|
||||
m_file = file;
|
||||
}
|
||||
CLockFileTemp::~CLockFileTemp()
|
||||
{
|
||||
}
|
||||
|
||||
CLockFileTemp::CLockFileTemp(const CLockFileTemp& src)
|
||||
{
|
||||
m_file = src.m_file;
|
||||
m_user = src.m_user;
|
||||
m_host = src.m_host;
|
||||
m_date = src.m_date;
|
||||
m_user_dir = src.m_user_dir;
|
||||
}
|
||||
|
||||
CLockFileTemp& CLockFileTemp::operator=(const CLockFileTemp& src)
|
||||
{
|
||||
m_file = src.m_file;
|
||||
m_user = src.m_user;
|
||||
m_host = src.m_host;
|
||||
m_date = src.m_date;
|
||||
m_user_dir = src.m_user_dir;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void CLockFileTemp::Generate()
|
||||
{
|
||||
if (true)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wchar_t user_name[1000];
|
||||
DWORD user_name_len = 1000 + 1;
|
||||
GetUserNameW(user_name, &user_name_len);
|
||||
m_user = NSFile::CUtf8Converter::GetUtf8StringFromUnicode2(user_name, user_name_len - 1);
|
||||
#else
|
||||
m_user = NSSystemUtils::GetEnvVariableA(L"USER");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (true)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wchar_t hostname[MAX_COMPUTERNAME_LENGTH + 1];
|
||||
DWORD hostname_len = MAX_COMPUTERNAME_LENGTH + 1;
|
||||
|
||||
if (GetComputerNameExW(ComputerNamePhysicalDnsFullyQualified, hostname, &hostname_len))
|
||||
{
|
||||
m_host = NSFile::CUtf8Converter::GetUtf8StringFromUnicode2(hostname, hostname_len);
|
||||
}
|
||||
else if (GetComputerNameW(hostname, &hostname_len))
|
||||
{
|
||||
m_host = NSFile::CUtf8Converter::GetUtf8StringFromUnicode2(hostname, hostname_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_host = "Unknown";
|
||||
}
|
||||
#else
|
||||
char hostname[HOST_NAME_MAX + 1];
|
||||
|
||||
if (gethostname(hostname, sizeof(hostname)) == 0)
|
||||
{
|
||||
hostname[HOST_NAME_MAX] = '\0';
|
||||
m_host = std::string(hostname);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_host = "Unknown";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (true)
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
std::time_t time_t = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm utc_tm = {};
|
||||
|
||||
#ifdef _WIN32
|
||||
gmtime_s(&utc_tm, &time_t);
|
||||
#else
|
||||
gmtime_r(&time_t, &utc_tm);
|
||||
|
||||
#endif
|
||||
std::ostringstream stream;
|
||||
stream << std::setfill('0')
|
||||
<< std::setw(2) << utc_tm.tm_mday << "."
|
||||
<< std::setw(2) << (utc_tm.tm_mon + 1) << "."
|
||||
<< (utc_tm.tm_year + 1900) << " "
|
||||
<< std::setw(2) << utc_tm.tm_hour << ":"
|
||||
<< std::setw(2) << utc_tm.tm_min;
|
||||
|
||||
m_date = stream.str();
|
||||
}
|
||||
|
||||
if (true)
|
||||
{
|
||||
std::wstring user_dir = NSSystemUtils::GetAppDataDir();
|
||||
m_user_dir = U_TO_UTF8(user_dir);
|
||||
|
||||
#ifdef _WIN32
|
||||
NSStringUtils::string_replaceA(m_user_dir, "\\", "/");
|
||||
#endif
|
||||
}
|
||||
|
||||
NSStringUtils::string_replaceA(m_user, ",", "'");
|
||||
NSStringUtils::string_replaceA(m_host, ",", "'");
|
||||
NSStringUtils::string_replaceA(m_date, ",", "'");
|
||||
NSStringUtils::string_replaceA(m_user_dir, ",", "'");
|
||||
}
|
||||
|
||||
void CLockFileTemp::Save(int type)
|
||||
{
|
||||
std::string content = "," + m_user + "," + m_host + "," + m_date + "," + m_user_dir + ";";
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
NSFile::CFileBinary oFile;
|
||||
if (oFile.CreateFile(m_file))
|
||||
{
|
||||
oFile.WriteFile((BYTE*)content.c_str(), (DWORD)content.length());
|
||||
oFile.CloseFile();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
#ifdef LOCKER_USE_GIO
|
||||
GError* error = NULL;
|
||||
std::string sFileA = U_TO_UTF8(m_file);
|
||||
GFile* file = g_file_new_for_commandline_arg(sFileA.c_str());
|
||||
|
||||
GFileOutputStream* outputStream = g_file_create(file, G_FILE_CREATE_NONE, NULL, &error);
|
||||
if (!outputStream)
|
||||
{
|
||||
g_error_free(error);
|
||||
g_object_unref(file);
|
||||
return;
|
||||
}
|
||||
|
||||
gsize bytesWritten = 0;
|
||||
gboolean writeResult = g_output_stream_write_all(G_OUTPUT_STREAM(outputStream), content.c_str(), content.length(), &bytesWritten, NULL, &error);
|
||||
|
||||
if (!writeResult)
|
||||
{
|
||||
g_error_free(error);
|
||||
}
|
||||
|
||||
g_output_stream_close(G_OUTPUT_STREAM(outputStream), NULL, NULL);
|
||||
g_object_unref(outputStream);
|
||||
g_object_unref(file);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CLockFileTemp::Load()
|
||||
{
|
||||
std::string content;
|
||||
if (NSFile::CFileBinary::ReadAllTextUtf8A(m_file, content))
|
||||
{
|
||||
std::string::size_type pos1 = 1;
|
||||
|
||||
std::string::size_type pos2 = content.find(',', pos1);
|
||||
if (std::string::npos != pos2)
|
||||
{
|
||||
m_user = content.substr(pos1, pos2 - pos1);
|
||||
pos1 = pos2 + 1;
|
||||
}
|
||||
|
||||
pos2 = content.find(',', pos1);
|
||||
if (std::string::npos != pos2)
|
||||
{
|
||||
m_host = content.substr(pos1, pos2 - pos1);
|
||||
pos1 = pos2 + 1;
|
||||
}
|
||||
|
||||
pos2 = content.find(',', pos1);
|
||||
if (std::string::npos != pos2)
|
||||
{
|
||||
m_date = content.substr(pos1, pos2 - pos1);
|
||||
pos1 = pos2 + 1;
|
||||
}
|
||||
|
||||
pos2 = content.find(';', pos1);
|
||||
if (std::string::npos != pos2)
|
||||
{
|
||||
m_user_dir = content.substr(pos1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CLockFileTemp::IsEqual(const CLockFileTemp& lock)
|
||||
{
|
||||
if (m_user != lock.m_user)
|
||||
return false;
|
||||
|
||||
if (m_host != lock.m_host)
|
||||
return false;
|
||||
|
||||
if (m_user_dir != lock.m_user_dir)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring CLockFileTemp::GetPath()
|
||||
{
|
||||
return m_file;
|
||||
}
|
||||
}
|
||||
|
||||
class CHandlesMonitor
|
||||
{
|
||||
@ -91,6 +349,14 @@ namespace NSSystem
|
||||
|
||||
virtual bool Lock()
|
||||
{
|
||||
CLockFileTemp lockFile(L"");
|
||||
if (IsUseLockFile())
|
||||
{
|
||||
lockFile = CheckLockFilePath(m_sFile);
|
||||
if (lockFile.GetPath().empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bResult = true;
|
||||
std::wstring sFileFull = CorrectPathW(m_sFile);
|
||||
DWORD dwFileAttributes = 0; //GetFileAttributesW(sFileFull.c_str());
|
||||
@ -107,6 +373,12 @@ namespace NSSystem
|
||||
m_nDescriptor = INVALID_HANDLE_VALUE;
|
||||
bResult = false;
|
||||
}
|
||||
|
||||
if (bResult && !lockFile.GetPath().empty())
|
||||
{
|
||||
m_sLockFilePath = lockFile.GetPath();
|
||||
lockFile.Save();
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
|
||||
@ -125,6 +397,7 @@ namespace NSSystem
|
||||
bResult = true;
|
||||
}
|
||||
|
||||
DeleteLockFile();
|
||||
return bResult;
|
||||
}
|
||||
|
||||
@ -153,6 +426,11 @@ namespace NSSystem
|
||||
}
|
||||
|
||||
public:
|
||||
static bool IsUseLockFile()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static LockType IsLockedInternal(const std::wstring& file)
|
||||
{
|
||||
LockType lockType = LockType::ltNone;
|
||||
@ -175,6 +453,14 @@ namespace NSSystem
|
||||
}
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
|
||||
if (lockType == LockType::ltNone && IsUseLockFile())
|
||||
{
|
||||
CLockFileTemp tmp = CheckLockFilePath(file);
|
||||
if (tmp.GetPath().empty())
|
||||
return LockType::ltLocked;
|
||||
}
|
||||
|
||||
return lockType;
|
||||
}
|
||||
};
|
||||
@ -207,15 +493,31 @@ namespace NSSystem
|
||||
|
||||
virtual bool Lock()
|
||||
{
|
||||
CLockFileTemp lockFile(L"");
|
||||
if (IsUseLockFile())
|
||||
{
|
||||
lockFile = CheckLockFilePath(m_sFile);
|
||||
if (lockFile.GetPath().empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lockFile.GetPath().empty())
|
||||
{
|
||||
m_sLockFilePath = lockFile.GetPath();
|
||||
lockFile.Save();
|
||||
}
|
||||
|
||||
std::string sFileA = U_TO_UTF8(m_sFile);
|
||||
m_nDescriptor = open(sFileA.c_str(), O_RDWR | O_EXCL);
|
||||
if (-1 == m_nDescriptor)
|
||||
return true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool Unlock()
|
||||
{
|
||||
DeleteLockFile();
|
||||
if (-1 == m_nDescriptor)
|
||||
return true;
|
||||
close(m_nDescriptor);
|
||||
@ -266,6 +568,25 @@ namespace NSSystem
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsUseLockFile()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static LockType IsLockedInternal(const std::wstring& file)
|
||||
{
|
||||
LockType lockType = LockType::ltNone;
|
||||
|
||||
if (lockType == LockType::ltNone && IsUseLockFile())
|
||||
{
|
||||
CLockFileTemp tmp = CheckLockFilePath(file);
|
||||
if (tmp.GetPath().empty())
|
||||
return LockType::ltLocked;
|
||||
}
|
||||
|
||||
return lockType;
|
||||
}
|
||||
};
|
||||
|
||||
class CFileLockerFCNTL : public CFileLockerEmpty
|
||||
@ -281,6 +602,14 @@ namespace NSSystem
|
||||
|
||||
virtual bool Lock()
|
||||
{
|
||||
CLockFileTemp lockFile(L"");
|
||||
if (IsUseLockFile())
|
||||
{
|
||||
lockFile = CheckLockFilePath(m_sFile);
|
||||
if (lockFile.GetPath().empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bResult = true;
|
||||
std::string sFileA = U_TO_UTF8(m_sFile);
|
||||
|
||||
@ -298,12 +627,19 @@ namespace NSSystem
|
||||
|
||||
bResult = (0 == fcntl(m_nDescriptor, F_SETLKW, &_lock));
|
||||
|
||||
if (bResult && !lockFile.GetPath().empty())
|
||||
{
|
||||
m_sLockFilePath = lockFile.GetPath();
|
||||
lockFile.Save();
|
||||
}
|
||||
|
||||
CHandlesMonitor::Instance().Add(m_sFile);
|
||||
return bResult;
|
||||
}
|
||||
|
||||
virtual bool Unlock()
|
||||
{
|
||||
DeleteLockFile();
|
||||
if (-1 == m_nDescriptor)
|
||||
return true;
|
||||
|
||||
@ -324,6 +660,11 @@ namespace NSSystem
|
||||
}
|
||||
|
||||
public:
|
||||
static bool IsUseLockFile()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static LockType IsLockedInternal(const std::wstring& file)
|
||||
{
|
||||
LockType lockType = LockType::ltNone;
|
||||
@ -345,12 +686,20 @@ namespace NSSystem
|
||||
if (F_WRLCK == (_lock.l_type & F_WRLCK))
|
||||
lockType = LockType::ltLocked;
|
||||
close(nDescriptor);
|
||||
|
||||
if (lockType == LockType::ltNone && IsUseLockFile())
|
||||
{
|
||||
CLockFileTemp tmp = CheckLockFilePath(file);
|
||||
if (tmp.GetPath().empty())
|
||||
return LockType::ltLocked;
|
||||
}
|
||||
|
||||
return lockType;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#if defined(_LINUX) && !defined(_MAC)
|
||||
#ifdef LOCKER_USE_GIO
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
@ -361,6 +710,7 @@ namespace NSSystem
|
||||
GFile* m_pFile;
|
||||
char* m_pFileUri;
|
||||
GOutputStream* m_pOutputStream;
|
||||
bool m_bIsReplace;
|
||||
|
||||
public:
|
||||
CFileLockerGIO(const std::wstring& file) : CFileLocker(file)
|
||||
@ -368,6 +718,7 @@ namespace NSSystem
|
||||
m_pFile = NULL;
|
||||
m_pFileUri = NULL;
|
||||
m_pOutputStream = NULL;
|
||||
m_bIsReplace = false;
|
||||
}
|
||||
virtual ~CFileLockerGIO()
|
||||
{
|
||||
@ -376,6 +727,14 @@ namespace NSSystem
|
||||
|
||||
virtual bool Lock()
|
||||
{
|
||||
CLockFileTemp lockFile(L"");
|
||||
if (IsUseLockFile())
|
||||
{
|
||||
lockFile = CheckLockFilePath(m_sFile, 1);
|
||||
if (lockFile.GetPath().empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bResult = true;
|
||||
std::string sFileA = U_TO_UTF8(m_sFile);
|
||||
|
||||
@ -385,12 +744,31 @@ namespace NSSystem
|
||||
m_pFileUri = g_file_get_uri(m_pFile);
|
||||
GError* err = NULL;
|
||||
m_pOutputStream = G_OUTPUT_STREAM(g_file_append_to(m_pFile, G_FILE_CREATE_PRIVATE, NULL, &err));
|
||||
if (err && err->code == G_IO_ERROR_NOT_SUPPORTED)
|
||||
{
|
||||
g_error_free(err);
|
||||
err = NULL;
|
||||
|
||||
if (m_pOutputStream)
|
||||
{
|
||||
g_output_stream_close(m_pOutputStream, NULL, NULL);
|
||||
g_object_unref(m_pOutputStream);
|
||||
m_pOutputStream = NULL;
|
||||
}
|
||||
m_bIsReplace = true;
|
||||
}
|
||||
bResult = !err;
|
||||
|
||||
if (err)
|
||||
g_error_free (err);
|
||||
}
|
||||
|
||||
if (bResult && !lockFile.GetPath().empty())
|
||||
{
|
||||
m_sLockFilePath = lockFile.GetPath();
|
||||
lockFile.Save(1);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
@ -405,6 +783,7 @@ namespace NSSystem
|
||||
GError* err = NULL;
|
||||
g_output_stream_close(m_pOutputStream, NULL, &err);
|
||||
g_object_unref(m_pOutputStream);
|
||||
m_pOutputStream = NULL;
|
||||
|
||||
if (err)
|
||||
g_error_free (err);
|
||||
@ -422,6 +801,7 @@ namespace NSSystem
|
||||
bResult = true;
|
||||
}
|
||||
|
||||
DeleteLockFile();
|
||||
return bResult;
|
||||
}
|
||||
|
||||
@ -448,19 +828,50 @@ namespace NSSystem
|
||||
bool bResult = false;
|
||||
if (m_pFile && m_pOutputStream)
|
||||
{
|
||||
if (g_seekable_can_truncate((GSeekable*)m_pOutputStream))
|
||||
if (!m_bIsReplace)
|
||||
{
|
||||
GError* err = NULL;
|
||||
if (g_seekable_truncate((GSeekable*)m_pOutputStream, dwPosition, NULL, &err))
|
||||
bResult = !err;
|
||||
if (g_seekable_can_truncate((GSeekable*)m_pOutputStream))
|
||||
{
|
||||
GError* err = NULL;
|
||||
if (g_seekable_truncate((GSeekable*)m_pOutputStream, dwPosition, NULL, &err))
|
||||
bResult = !err;
|
||||
|
||||
if (err)
|
||||
g_error_free (err);
|
||||
if (err)
|
||||
g_error_free (err);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_output_stream_close(m_pOutputStream, NULL, NULL);
|
||||
g_object_unref(m_pOutputStream);
|
||||
m_pOutputStream = NULL;
|
||||
}
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
|
||||
virtual bool StartWrite()
|
||||
{
|
||||
if (!m_pOutputStream && m_bIsReplace)
|
||||
{
|
||||
GError* err = NULL;
|
||||
m_pOutputStream = G_OUTPUT_STREAM(g_file_replace(m_pFile, nullptr, false, G_FILE_CREATE_PRIVATE, nullptr, &err));
|
||||
if (err)
|
||||
{
|
||||
if (m_pOutputStream)
|
||||
{
|
||||
g_output_stream_close(m_pOutputStream, NULL, NULL);
|
||||
g_object_unref(m_pOutputStream);
|
||||
m_pOutputStream = NULL;
|
||||
}
|
||||
|
||||
g_error_free(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool WriteFile(const void* pData, DWORD dwBytesToWrite, DWORD& dwSizeWrite)
|
||||
{
|
||||
bool bResult = false;
|
||||
@ -474,6 +885,11 @@ namespace NSSystem
|
||||
return bResult;
|
||||
}
|
||||
|
||||
static bool IsUseLockFile()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static LockType IsLockedInternal(const std::wstring& file)
|
||||
{
|
||||
LockType lockType = LockType::ltNone;
|
||||
@ -481,6 +897,14 @@ namespace NSSystem
|
||||
if (!pLocker->Lock())
|
||||
lockType = LockType::ltReadOnly; // ltLocked
|
||||
delete pLocker;
|
||||
|
||||
if (lockType == LockType::ltNone && IsUseLockFile())
|
||||
{
|
||||
CLockFileTemp tmp = CheckLockFilePath(file, 1);
|
||||
if (tmp.GetPath().empty())
|
||||
return LockType::ltLocked;
|
||||
}
|
||||
|
||||
return lockType;
|
||||
}
|
||||
};
|
||||
@ -530,6 +954,8 @@ namespace NSSystem
|
||||
lockType = NSSystem::CFileLockerFCNTL::IsLockedInternal(file);
|
||||
else
|
||||
lockType = NSSystem::CFileLockerGIO::IsLockedInternal(file);
|
||||
#else
|
||||
lockType = NSSystem::CFileLockerEmpty::IsLockedInternal(file);
|
||||
#endif
|
||||
#endif
|
||||
return lockType;
|
||||
@ -559,11 +985,72 @@ namespace NSSystem
|
||||
CFileLocker::CFileLocker(const std::wstring& file)
|
||||
{
|
||||
m_sFile = file;
|
||||
m_sLockFilePath = L"";
|
||||
}
|
||||
CFileLocker::~CFileLocker()
|
||||
{
|
||||
}
|
||||
|
||||
CLockFileTemp CFileLocker::CheckLockFilePath(const std::wstring& file, const int& flags)
|
||||
{
|
||||
std::wstring sDirectory = NSFile::GetDirectoryName(file);
|
||||
std::wstring sFilename = NSFile::GetFileName(file);
|
||||
|
||||
#ifdef LOCKER_USE_GIO
|
||||
if (flags == 1)
|
||||
{
|
||||
std::string fileA = U_TO_UTF8(file);
|
||||
GFile* file = g_file_new_for_path(fileA.c_str());
|
||||
if (file)
|
||||
{
|
||||
GError* error = NULL;
|
||||
GFileInfo* info = g_file_query_info(file,
|
||||
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
|
||||
G_FILE_ATTRIBUTE_STANDARD_NAME,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
NULL, &error);
|
||||
|
||||
if (info && !error)
|
||||
{
|
||||
const char* displayName = g_file_info_get_display_name(info);
|
||||
if (displayName)
|
||||
{
|
||||
std::string result = displayName;
|
||||
sFilename = UTF8_TO_U(result);
|
||||
}
|
||||
g_object_unref(info);
|
||||
}
|
||||
|
||||
if (error)
|
||||
g_error_free(error);
|
||||
|
||||
g_object_unref(file);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::wstring sLockFile = sDirectory + L"/.~lock." + sFilename + L"#";
|
||||
|
||||
CLockFileTemp tempCur(sLockFile);
|
||||
tempCur.Generate();
|
||||
|
||||
if (NSFile::CFileBinary::Exists(sLockFile))
|
||||
{
|
||||
CLockFileTemp tempSaved(sLockFile);
|
||||
tempSaved.Load();
|
||||
|
||||
if (!tempCur.IsEqual(tempSaved))
|
||||
return CLockFileTemp(L"");
|
||||
}
|
||||
return tempCur;
|
||||
}
|
||||
void CFileLocker::DeleteLockFile()
|
||||
{
|
||||
if (!m_sLockFilePath.empty())
|
||||
NSFile::CFileBinary::Remove(m_sLockFilePath);
|
||||
m_sLockFilePath = L"";
|
||||
}
|
||||
|
||||
bool CFileLocker::IsEmpty()
|
||||
{
|
||||
return false;
|
||||
@ -611,4 +1098,9 @@ namespace NSSystem
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool CFileLocker::StartWrite()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,17 +47,47 @@ namespace NSSystem
|
||||
ltNosafe = 0x04
|
||||
};
|
||||
|
||||
class CLockFileTemp
|
||||
{
|
||||
private:
|
||||
std::string m_user;
|
||||
std::string m_host;
|
||||
std::string m_date;
|
||||
std::string m_user_dir;
|
||||
|
||||
std::wstring m_file;
|
||||
|
||||
public:
|
||||
CLockFileTemp(const std::wstring& file);
|
||||
~CLockFileTemp();
|
||||
CLockFileTemp(const CLockFileTemp& file);
|
||||
CLockFileTemp& operator=(const CLockFileTemp& file);
|
||||
|
||||
public:
|
||||
void Generate();
|
||||
void Save(int type = 0);
|
||||
void Load();
|
||||
bool IsEqual(const CLockFileTemp& lock);
|
||||
|
||||
std::wstring GetPath();
|
||||
};
|
||||
|
||||
class CFileLocker
|
||||
{
|
||||
public:
|
||||
std::wstring m_sFile;
|
||||
std::wstring m_sLockFilePath;
|
||||
|
||||
CFileLocker(const std::wstring& file);
|
||||
virtual ~CFileLocker();
|
||||
|
||||
static CLockFileTemp CheckLockFilePath(const std::wstring& file, const int& flags = 0);
|
||||
virtual void DeleteLockFile();
|
||||
|
||||
virtual bool Lock() = 0;
|
||||
virtual bool Unlock() = 0;
|
||||
|
||||
virtual bool StartWrite();
|
||||
virtual bool SeekFile(DWORD dwPosition) = 0;
|
||||
virtual bool Truncate(DWORD dwPosition) = 0;
|
||||
virtual bool WriteFile(const void* pData, DWORD dwBytesToWrite, DWORD& dwSizeWrite) = 0;
|
||||
|
||||
@ -51,6 +51,9 @@ public:
|
||||
std::string sGuid;
|
||||
std::string sName;
|
||||
std::string sNameObject;
|
||||
std::string sConfigName;
|
||||
std::string sUrl;
|
||||
bool isOnlyofficeScheme;
|
||||
bool isUser;
|
||||
};
|
||||
|
||||
@ -74,6 +77,7 @@ public:
|
||||
|
||||
// плагин не для редактора, а для главной страницы (для системных сообщенией)
|
||||
std::vector<CExternalPluginInfo> m_arExternals;
|
||||
bool m_bIsSupportMultiplugins;
|
||||
|
||||
public:
|
||||
CPluginsManager()
|
||||
@ -88,6 +92,8 @@ public:
|
||||
// Возможность псевдо-обновления системных плагинов
|
||||
// По факту подмена системного на более новый из папки пользователя
|
||||
m_isSupportSystemUpdate = true;
|
||||
|
||||
m_bIsSupportMultiplugins = false;
|
||||
}
|
||||
|
||||
std::string GetPluginsJson(const bool& bActivePlugins = true, const bool& bCheckCrypto = false,
|
||||
@ -142,12 +148,13 @@ public:
|
||||
for (size_t j = 0; j < _arPlugins.size(); j++)
|
||||
{
|
||||
std::string sJson;
|
||||
bool bIsExternal = false;
|
||||
if (NSFile::CFileBinary::ReadAllTextUtf8A(_arPlugins[j] + L"/config.json", sJson))
|
||||
{
|
||||
CheckEncryption(sJson, false);
|
||||
|
||||
// !!! это надо обсудить, т.к. возможны такие плагины в папке пользователя
|
||||
CheckExternal(sJson, i == 0);
|
||||
bIsExternal = CheckExternal(sJson, i == 0);
|
||||
|
||||
std::string::size_type pos1 = sJson.find("asc.{");
|
||||
std::string::size_type pos2 = sJson.find('}', pos1);
|
||||
@ -164,6 +171,12 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for desktop
|
||||
if (m_bIsSupportMultiplugins && !bIsExternal && NSFile::CFileBinary::ReadAllTextUtf8A(_arPlugins[j] + L"/configDesktop.json", sJson))
|
||||
{
|
||||
CheckExternal(sJson, i == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -555,6 +568,8 @@ private:
|
||||
info.sName = GetStringValue(sJson, "name");
|
||||
info.sNameObject = GetObjectValue(sJson, "nameLocale");
|
||||
info.isUser = isSystem ? false : true;
|
||||
info.sUrl = GetStringValue(sJson, "url");
|
||||
info.isOnlyofficeScheme = (std::string::npos != sJson.find("onlyofficeScheme")) ? true : false;
|
||||
|
||||
m_arExternals.push_back(info);
|
||||
}
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
#include "./internal/base.h"
|
||||
|
||||
namespace form_field_analyser
|
||||
{
|
||||
std::string description()
|
||||
{
|
||||
return "\n\
|
||||
- form_field_analyser\n\
|
||||
Description: \n\
|
||||
Use this function if you are asked to analyze document or forms.\n\
|
||||
Parameters:\n\
|
||||
- document (string): path or link value to the document you were asked to analyze\n\
|
||||
Examples:\n\
|
||||
If you need to analyze document /home/user/document.docx, respond with: \
|
||||
[functionCalling (form_field_analyser)]: { \"document\" : \"/home/user/document.docx\" }\n";
|
||||
}
|
||||
|
||||
std::string main(const std::string& arg)
|
||||
{
|
||||
json param = json::parse(arg);
|
||||
if (!param.contains("document") || !param["document"].is_string())
|
||||
return "";
|
||||
|
||||
std::string pathA = param["document"];
|
||||
std::wstring path = UTF8_TO_U(pathA);
|
||||
|
||||
CDocBuilder builder;
|
||||
initBuilder(&builder);
|
||||
int res = builder.OpenFile(path.c_str(), L"");
|
||||
if (0 != res)
|
||||
return "";
|
||||
|
||||
CContext context = builder.GetContext();
|
||||
CValue global = context.GetGlobal();
|
||||
|
||||
CValue api = global["Api"];
|
||||
CValue document = api.Call("GetDocument");
|
||||
|
||||
json keys = json::array();
|
||||
|
||||
CValue allForms = document.Call("GetAllForms");
|
||||
int count = allForms.GetLength();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
CValue field = allForms.Get(i);
|
||||
|
||||
std::wstring keyW(field.Call("GetFormKey").ToString().c_str());
|
||||
std::string key = U_TO_UTF8(keyW);
|
||||
|
||||
keys.push_back(key);
|
||||
}
|
||||
|
||||
std::string result = keys.dump();
|
||||
builder.CloseFile();
|
||||
|
||||
std::string prompt = "[prompt]Match the document fields by meaning with the values in the array in javascript format. \
|
||||
And when you are asked to fill out a form, send not invented field values to the function, but only those that are in the passed list: " + result;
|
||||
|
||||
return prompt;
|
||||
}
|
||||
}
|
||||
114
ChromiumBasedEditors/lib/tools/functions/form_field_filler.cpp
Normal file
114
ChromiumBasedEditors/lib/tools/functions/form_field_filler.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
#include "./internal/base.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define timegm _mkgmtime
|
||||
#endif
|
||||
|
||||
namespace form_field_filler
|
||||
{
|
||||
long long parseDateToUTCMilliseconds(const std::string& date)
|
||||
{
|
||||
long long default_value = 946684800000;
|
||||
|
||||
std::tm tm = {};
|
||||
std::istringstream ss(date);
|
||||
ss >> std::get_time(&tm, "%d.%m.%Y");
|
||||
|
||||
if (ss.fail())
|
||||
return default_value;
|
||||
|
||||
tm.tm_hour = 0;
|
||||
tm.tm_min = 0;
|
||||
tm.tm_sec = 0;
|
||||
|
||||
time_t timeUtc = timegm(&tm);
|
||||
if (timeUtc == -1) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
return static_cast<long long>(timeUtc) * 1000;
|
||||
}
|
||||
|
||||
std::string description()
|
||||
{
|
||||
return "\n\
|
||||
- form_field_filler\n\
|
||||
Description: \n\
|
||||
Use this function if you are asked to fill out a document or form.\n\
|
||||
Use only those keys that were obtained during the analysis of the document or form, unless a specific key was specified in the request.\n\
|
||||
Parameters:\n\
|
||||
- document (string): path or link value to the document you were asked to fill\n\
|
||||
- fields (object): path or link value to the document you were asked to fill (\
|
||||
If you are asked for a date, then give it in the format dd.mm.yyyy. \
|
||||
Convert any given date range or time period (e.g. \"summer 2025\", \"fall 2024\", \"January 2023\", \"second half 2026\", \"first quarter 2022\") to two dates: \
|
||||
start and end, represented as timestamp in format dd.mm.yyyy. Use exact calendar dates to define period boundaries (taking into account leap years, number of days in months, etc.)).\n\
|
||||
Examples:\n\
|
||||
[functionCalling (form_field_filler)] { \"document\" : \"/home/user/document.docx\", \"fields\" : { \"key1\" : \"value1\", \"key2\" : \"value2\" } }\n";
|
||||
}
|
||||
|
||||
std::string main(const std::string& arg)
|
||||
{
|
||||
json param = json::parse(arg);
|
||||
if (!param.contains("document") || !param["document"].is_string())
|
||||
return "";
|
||||
|
||||
if (!param.contains("fields") || !param["fields"].is_object())
|
||||
return "";
|
||||
|
||||
std::string pathA = param["document"];
|
||||
std::wstring path = UTF8_TO_U(pathA);
|
||||
|
||||
CDocBuilder builder;
|
||||
initBuilder(&builder);
|
||||
int res = builder.OpenFile(path.c_str(), L"");
|
||||
if (0 != res)
|
||||
return "";
|
||||
|
||||
json inputFields = param["fields"];
|
||||
|
||||
CContext context = builder.GetContext();
|
||||
CValue global = context.GetGlobal();
|
||||
|
||||
CValue api = global["Api"];
|
||||
CValue document = api.Call("GetDocument");
|
||||
|
||||
CValue allForms = document.Call("GetAllForms");
|
||||
int count = allForms.GetLength();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
CValue field = allForms.Get(i);
|
||||
|
||||
std::wstring keyW(field.Call("GetFormKey").ToString().c_str());
|
||||
std::string key = U_TO_UTF8(keyW);
|
||||
|
||||
if (!inputFields.contains(key))
|
||||
continue;
|
||||
|
||||
std::wstring typeW(field.Call("GetFormType").ToString().c_str());
|
||||
std::string type = U_TO_UTF8(typeW);
|
||||
|
||||
if ("textForm" == type && inputFields[key].is_string())
|
||||
{
|
||||
std::string fieldValue = inputFields[key];
|
||||
field.Call("SetText", fieldValue.c_str());
|
||||
}
|
||||
if ("dateForm" == type && inputFields[key].is_string())
|
||||
{
|
||||
std::string fieldValue = inputFields[key];
|
||||
field.Call("SetTime", (double)parseDateToUTCMilliseconds(fieldValue));
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring filePath = CAITools::getInstance().getTempFile() + L".pdf";
|
||||
builder.SaveFile(AVS_OFFICESTUDIO_FILE_DOCUMENT_OFORM_PDF, filePath.c_str());
|
||||
|
||||
builder.CloseFile();
|
||||
|
||||
return "[file]" + U_TO_UTF8(filePath);
|
||||
}
|
||||
}
|
||||
20
ChromiumBasedEditors/lib/tools/functions/internal/base.h
Normal file
20
ChromiumBasedEditors/lib/tools/functions/internal/base.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "../../tools.h"
|
||||
|
||||
#ifndef JSON_HAS_CPP_11
|
||||
#define JSON_HAS_CPP_11
|
||||
#endif
|
||||
|
||||
#include "./json.hpp"
|
||||
using namespace nlohmann;
|
||||
#include "../../../../../../core/DesktopEditor/doctrenderer/docbuilder.h"
|
||||
using namespace NSDoctRenderer;
|
||||
|
||||
#include "../../../../../../core/DesktopEditor/common/File.h"
|
||||
#include "../../../../../../core/Common/OfficeFileFormats.h"
|
||||
|
||||
static void initBuilder(CDocBuilder* builder)
|
||||
{
|
||||
std::wstring allFontsJS = CAITools::getInstance().getFontsDirectory() + L"/AllFonts.js";
|
||||
builder->SetProperty("--all-fonts-path", allFontsJS.c_str());
|
||||
}
|
||||
23
ChromiumBasedEditors/lib/tools/functions/internal/funcs.h
Normal file
23
ChromiumBasedEditors/lib/tools/functions/internal/funcs.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "./base.h"
|
||||
#include "../form_field_analyser.cpp"
|
||||
#include "../form_field_filler.cpp"
|
||||
|
||||
struct TFuncInstance
|
||||
{
|
||||
std::string name;
|
||||
std::function<std::string(std::string)> func;
|
||||
TFuncInstance(const std::string& n, std::function<std::string(const std::string&)> f)
|
||||
: name(n), func(f) {}
|
||||
};
|
||||
|
||||
class CFunctions
|
||||
{
|
||||
public:
|
||||
std::map<std::string, TFuncInstance> m_funcs;
|
||||
CFunctions()
|
||||
{
|
||||
m_funcs.insert(std::make_pair("form_field_analyser", TFuncInstance(form_field_analyser::description(), form_field_analyser::main)));
|
||||
m_funcs.insert(std::make_pair("form_field_filler", TFuncInstance(form_field_filler::description(), form_field_filler::main)));
|
||||
}
|
||||
};
|
||||
57
ChromiumBasedEditors/lib/tools/functions/internal/funcs.py
Normal file
57
ChromiumBasedEditors/lib/tools/functions/internal/funcs.py
Normal file
@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def generate_header_file(folder_path, output_path="./funcs.h"):
|
||||
if not os.path.exists(folder_path):
|
||||
return False
|
||||
|
||||
cpp_files = []
|
||||
for file in os.listdir(folder_path):
|
||||
if file.endswith('.cpp'):
|
||||
filename_without_ext = os.path.splitext(file)[0]
|
||||
cpp_files.append(filename_without_ext)
|
||||
|
||||
header_content = generate_header_content(cpp_files)
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
f.write(header_content)
|
||||
return
|
||||
|
||||
def generate_header_content(cpp_files):
|
||||
content = "#pragma once\n"
|
||||
content += "#include \"./base.h\"\n"
|
||||
|
||||
for cpp_file in cpp_files:
|
||||
content += f"#include \"../{cpp_file}.cpp\"\n"
|
||||
|
||||
content += """
|
||||
struct TFuncInstance
|
||||
{
|
||||
\tstd::string name;
|
||||
\tstd::function<std::string(std::string)> func;
|
||||
\tTFuncInstance(const std::string& n, std::function<std::string(const std::string&)> f)
|
||||
\t\t: name(n), func(f) {}
|
||||
};
|
||||
|
||||
class CFunctions
|
||||
{
|
||||
public:
|
||||
\tstd::map<std::string, TFuncInstance> m_funcs;
|
||||
\tCFunctions()
|
||||
\t{
|
||||
"""
|
||||
|
||||
for cpp_file in cpp_files:
|
||||
content += f'\t\tm_funcs.insert(std::make_pair("{cpp_file}", TFuncInstance({cpp_file}::description(), {cpp_file}::main)));\n'
|
||||
|
||||
content += "\t}\n};\n"
|
||||
return content
|
||||
|
||||
def main():
|
||||
generate_header_file("./..", "./funcs.h")
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
25601
ChromiumBasedEditors/lib/tools/functions/internal/json.hpp
Normal file
25601
ChromiumBasedEditors/lib/tools/functions/internal/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
102
ChromiumBasedEditors/lib/tools/tools.cpp
Normal file
102
ChromiumBasedEditors/lib/tools/tools.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* (c) Copyright Ascensio System SIA 2010-2019
|
||||
*
|
||||
* This program is a free software product. You can redistribute it and/or
|
||||
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
||||
* version 3 as published by the Free Software Foundation. In accordance with
|
||||
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
||||
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
||||
* of any third-party rights.
|
||||
*
|
||||
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
||||
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
*
|
||||
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
|
||||
* street, Riga, Latvia, EU, LV-1050.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of the Program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU AGPL version 3.
|
||||
*
|
||||
* Pursuant to Section 7(b) of the License you must retain the original Product
|
||||
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
||||
* grant you any rights under trademark law for use of our trademarks.
|
||||
*
|
||||
* All the Product's GUI elements, including illustrations and icon sets, as
|
||||
* well as technical writing content are licensed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
||||
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
*
|
||||
*/
|
||||
|
||||
#include "./tools.h"
|
||||
#include "../../../../core/DesktopEditor/doctrenderer/docbuilder.h"
|
||||
#include "../../../../core/DesktopEditor/common/File.h"
|
||||
#include "./functions/internal/funcs.h"
|
||||
|
||||
CAITools::CAITools()
|
||||
{
|
||||
m_funcs = new CFunctions();
|
||||
}
|
||||
|
||||
CAITools::~CAITools()
|
||||
{
|
||||
delete m_funcs;
|
||||
}
|
||||
|
||||
CAITools& CAITools::getInstance()
|
||||
{
|
||||
static CAITools tools;
|
||||
return tools;
|
||||
}
|
||||
|
||||
void CAITools::setWorkDirectory(const std::wstring& dir)
|
||||
{
|
||||
m_workDirectory = dir;
|
||||
NSDoctRenderer::CDocBuilder::Initialize(m_workDirectory.c_str());
|
||||
}
|
||||
std::wstring CAITools::getWorkDirectory()
|
||||
{
|
||||
return m_workDirectory;
|
||||
}
|
||||
void CAITools::setFontsDirectory(const std::wstring& dir)
|
||||
{
|
||||
m_fontsDirectory = dir;
|
||||
}
|
||||
std::wstring CAITools::getFontsDirectory()
|
||||
{
|
||||
return m_fontsDirectory;
|
||||
}
|
||||
|
||||
std::wstring CAITools::getTempFile()
|
||||
{
|
||||
std::wstring sDirTmp = NSFile::CFileBinary::GetTempPath();
|
||||
|
||||
std::wstring sFileTmp = NSFile::CFileBinary::CreateTempFileWithUniqueName(sDirTmp, L"IMG");
|
||||
if (NSFile::CFileBinary::Exists(sFileTmp))
|
||||
NSFile::CFileBinary::Remove(sFileTmp);
|
||||
|
||||
return sFileTmp;
|
||||
}
|
||||
|
||||
std::string CAITools::getFunctions()
|
||||
{
|
||||
std::string sResult = "";
|
||||
|
||||
for (std::map<std::string, TFuncInstance>::iterator iter = m_funcs->m_funcs.begin(); iter != m_funcs->m_funcs.end(); iter++)
|
||||
{
|
||||
sResult += iter->second.name;
|
||||
sResult += "\n";
|
||||
}
|
||||
|
||||
return sResult;
|
||||
}
|
||||
|
||||
std::string CAITools::callFunc(const std::string& name, const std::string& arg)
|
||||
{
|
||||
auto find = m_funcs->m_funcs.find(name);
|
||||
if (find !=m_funcs->m_funcs.end())
|
||||
return find->second.func(arg);
|
||||
return "";
|
||||
}
|
||||
63
ChromiumBasedEditors/lib/tools/tools.h
Normal file
63
ChromiumBasedEditors/lib/tools/tools.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* (c) Copyright Ascensio System SIA 2010-2019
|
||||
*
|
||||
* This program is a free software product. You can redistribute it and/or
|
||||
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
||||
* version 3 as published by the Free Software Foundation. In accordance with
|
||||
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
||||
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
||||
* of any third-party rights.
|
||||
*
|
||||
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
||||
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
*
|
||||
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
|
||||
* street, Riga, Latvia, EU, LV-1050.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of the Program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU AGPL version 3.
|
||||
*
|
||||
* Pursuant to Section 7(b) of the License you must retain the original Product
|
||||
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
||||
* grant you any rights under trademark law for use of our trademarks.
|
||||
*
|
||||
* All the Product's GUI elements, including illustrations and icon sets, as
|
||||
* well as technical writing content are licensed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
||||
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
class CFunctions;
|
||||
class CAITools
|
||||
{
|
||||
private:
|
||||
CAITools();
|
||||
~CAITools();
|
||||
|
||||
std::wstring m_workDirectory;
|
||||
std::wstring m_fontsDirectory;
|
||||
|
||||
CFunctions* m_funcs;
|
||||
|
||||
public:
|
||||
static CAITools& getInstance();
|
||||
|
||||
void setWorkDirectory(const std::wstring& dir);
|
||||
std::wstring getWorkDirectory();
|
||||
|
||||
void setFontsDirectory(const std::wstring& dir);
|
||||
std::wstring getFontsDirectory();
|
||||
|
||||
std::wstring getTempFile();
|
||||
|
||||
std::string callFunc(const std::string& name, const std::string& arg);
|
||||
std::string getFunctions();
|
||||
};
|
||||
Reference in New Issue
Block a user