summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x[-rw-r--r--]Bugs.txt311
-rwxr-xr-xChangelog.txt22
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/arabic.lng164
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/bulgarian.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/chinese_simple.lng133
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/chinese_traditional.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/croatian.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/czech.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/danish.lng140
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/dutch.lng140
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/english_uk.lng146
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/french.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/german.lng23
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/greek.lng140
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/hebrew.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/hindi.lng140
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/hungarian.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/italian.lng178
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/japanese.lng134
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/korean.lng58
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/lithuanian.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/norwegian.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/polish.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/portuguese.lng142
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/portuguese_br.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/romanian.lng44
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/russian.lng146
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/slovak.lng42
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/slovenian.lng152
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/spanish.lng140
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/swedish.lng140
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/turkish.lng42
-rwxr-xr-xFreeFileSync/Build/Resources/Languages/ukrainian.lng164
-rwxr-xr-xFreeFileSync/Build/Resources/cacert.pem48
-rwxr-xr-xFreeFileSync/Source/Makefile3
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/Makefile1
-rw-r--r--FreeFileSync/Source/RealTimeSync/gui_generated.cpp31
-rw-r--r--FreeFileSync/Source/RealTimeSync/gui_generated.h4
-rw-r--r--FreeFileSync/Source/RealTimeSync/main_dlg.cpp4
-rw-r--r--FreeFileSync/Source/afs/abstract.cpp15
-rw-r--r--FreeFileSync/Source/afs/abstract.h1
-rw-r--r--FreeFileSync/Source/afs/ftp.cpp184
-rw-r--r--FreeFileSync/Source/afs/gdrive.cpp30
-rw-r--r--FreeFileSync/Source/afs/init_curl_libssh2.cpp23
-rw-r--r--FreeFileSync/Source/afs/libcurl/curl_wrap.h5
-rw-r--r--FreeFileSync/Source/afs/libssh2/init_libssh2.cpp40
-rw-r--r--FreeFileSync/Source/afs/libssh2/init_libssh2.h16
-rw-r--r--FreeFileSync/Source/afs/libssh2/libssh2_wrap.h231
-rw-r--r--FreeFileSync/Source/afs/native.cpp3
-rw-r--r--FreeFileSync/Source/afs/sftp.cpp222
-rw-r--r--FreeFileSync/Source/base/algorithm.cpp43
-rw-r--r--FreeFileSync/Source/base/application.cpp1
-rw-r--r--FreeFileSync/Source/base/db_file.cpp69
-rw-r--r--FreeFileSync/Source/base/file_hierarchy.cpp1
-rw-r--r--FreeFileSync/Source/base/log_file.cpp2
-rw-r--r--FreeFileSync/Source/base/parse_lng.h4
-rw-r--r--FreeFileSync/Source/base/parse_plural.h2
-rw-r--r--FreeFileSync/Source/base/synchronization.cpp5
-rw-r--r--FreeFileSync/Source/base/versioning.cpp8
-rw-r--r--FreeFileSync/Source/ui/gui_generated.cpp53
-rw-r--r--FreeFileSync/Source/ui/gui_generated.h3
-rw-r--r--FreeFileSync/Source/ui/main_dlg.cpp38
-rw-r--r--FreeFileSync/Source/ui/progress_indicator.cpp53
-rw-r--r--FreeFileSync/Source/ui/small_dlgs.cpp12
-rw-r--r--FreeFileSync/Source/ui/version_check.cpp6
-rw-r--r--FreeFileSync/Source/version/version.h2
-rw-r--r--zen/basic_math.h3
-rw-r--r--zen/build_info.h22
-rw-r--r--zen/file_access.cpp4
-rw-r--r--zen/i18n.h5
-rw-r--r--zen/json.h4
-rw-r--r--zen/legacy_compiler.cpp32
-rw-r--r--zen/legacy_compiler.h10
-rw-r--r--zen/open_ssl.cpp458
-rw-r--r--zen/open_ssl.h10
-rw-r--r--zen/shutdown.cpp5
-rw-r--r--zen/socket.h2
-rw-r--r--zen/stl_tools.h1
-rw-r--r--zen/string_base.h11
-rw-r--r--zen/string_tools.h64
-rw-r--r--zen/string_traits.h67
-rw-r--r--zen/sys_error.h4
-rw-r--r--zen/thread.h6
-rw-r--r--zen/time.h2
-rw-r--r--zen/warn_static.h13
-rw-r--r--zen/zlib_wrap.cpp4
-rw-r--r--zen/zlib_wrap.h6
-rw-r--r--zen/zstring.cpp2
-rw-r--r--zenXml/zenxml/parser.h4
89 files changed, 3145 insertions, 1982 deletions
diff --git a/Bugs.txt b/Bugs.txt
index da0fe9b4..8e45ce73 100644..100755
--- a/Bugs.txt
+++ b/Bugs.txt
@@ -1,81 +1,16 @@
-When manually compiling FreeFileSync, you should also fix the following bugs in its dependent libraries.
+When manually compiling FreeFileSync, you should also fix the following bugs in its library dependencies.
FreeFileSync generally uses the latest library versions and works with upstream to get the bugs fixed
that affect FreeFileSync. Therefore it is not recommended to compile against older library versions than
the ones mentioned below. The remaining issues that are yet to be fixed are listed in the following:
------------------
-| libcurl 7.66.0 |
+| libcurl 7.67.0 |
------------------
__________________________________________________________________________________________________________
-/lib/setopt.c
-https://github.com/curl/curl/pull/4321
-
-- if ((arg < CURLFTPMETHOD_DEFAULT) || (arg > CURLFTPMETHOD_SINGLECWD))
-+ if ((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST))
-
-__________________________________________________________________________________________________________
-https://github.com/curl/curl/pull/4331
-
-/include/curl/curl.h
-
- CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */
-+ CURLFTPMETHOD_FULLPATH, //AKA "CURLFTPMETHOD_NOCWD_BUT_THIS_TIME_FOR_REAL"
-
-
-/lib/ftp.h
-
- FTPFILE_SINGLECWD = 3 /* make one CWD, then SIZE / RETR / STOR on the
- file */
-+ FTPFILE_FULLPATH = 4 //AKA "FTPFILE_NOCWD_BUT_THIS_TIME_FOR_REAL"
-
-
/lib/ftp.c
-
-- if ((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
-+ if ((data->set.ftp_filemethod == FTPFILE_NOCWD ||
-+ data->set.ftp_filemethod == FTPFILE_FULLPATH) &&
-
-
-+ if (data->set.ftp_filemethod == FTPFILE_FULLPATH)
-+ {
-+ //no CWDs happened => remember old working dir
-+ //ftpc->prevmethod =
-+ //ftpc->prevpath =
-+ }
-+ else
-+ {
- /* now store a copy of the directory we are in */
- free(ftpc->prevpath);
-
- [...]
-
- else
- {
- ftpc->prevpath = NULL; /* no path */
- free(path);
- }
- }
-
-+ }
-
-
- switch (data->set.ftp_filemethod)
- {
- case FTPFILE_NOCWD:
-+ case FTPFILE_FULLPATH:
-
-
-+ if (data->set.ftp_filemethod == FTPFILE_FULLPATH)
-+ ftpc->cwddone = TRUE;
-+ else
- if (ftpc->prevpath)
-
-__________________________________________________________________________________________________________
https://github.com/curl/curl/issues/1455
-/lib/ftp.c:
-
Add:
static bool is_routable_ip_v4(unsigned int ip[4])
{
@@ -89,29 +24,19 @@ Add:
}
-Remove: if (data->set.ftp_skip_ip)
-
-Replace with:
-
- bool skipIp = data->set.ftp_skip_ip;
- if (!skipIp && !is_routable_ip_v4(ip))
- {
- unsigned int ip_ctrl[4];
- if (4 != sscanf(control_address(conn), "%u.%u.%u.%u",
- &ip_ctrl[0], &ip_ctrl[1], &ip_ctrl[2], &ip_ctrl[3]) ||
- is_routable_ip_v4(ip_ctrl))
- skipIp = true;
- }
-
- if (skipIp)
-
-__________________________________________________________________________________________________________
-/lib/ftp.c
-https://github.com/curl/curl/pull/4332
+- if (data->set.ftp_skip_ip)
-- else if (conn->bits.reuse && ftpc->entrypath)
-+ else if (conn->bits.reuse && ftpc->entrypath &&
-+ !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) //no need to go to entrypath when we have an absolute path
++ bool skipIp = data->set.ftp_skip_ip;
++ if (!skipIp && !is_routable_ip_v4(ip))
++ {
++ unsigned int ip_ctrl[4];
++ if (4 != sscanf(control_address(conn), "%u.%u.%u.%u",
++ &ip_ctrl[0], &ip_ctrl[1], &ip_ctrl[2], &ip_ctrl[3]) ||
++ is_routable_ip_v4(ip_ctrl))
++ skipIp = true;
++ }
++
++ if (skipIp)
__________________________________________________________________________________________________________
/lib/ftp.c
@@ -121,54 +46,181 @@ https://github.com/curl/curl/issues/4342
+ result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_LIST_TYPE);
__________________________________________________________________________________________________________
-/lib/ftp.c
-https://github.com/curl/curl/pull/4348
-
-
-- size_t n = strlen(inpath);
-- /* Check if path does not end with /, as then we cut off the file part */
-- if (inpath[n - 1] != '/')
-- {
-- /* chop off the file part if format is dir/dir/file */
-- slashPos = strrchr(inpath, '/');
-- n = slashPos - inpath;
-- }
-
-+ /* chop off the file part if format is dir/file
-+ otherwise remove the trailing slash for dir/dir/
-+ and full paths like %2f/ except for / */
-+ size_t n = strrchr(inpath, '/') - inpath;
-+ if(n == 0)
-+ ++n;
-__________________________________________________________________________________________________________
-
-/lib/vtls/openssl.c
-https://github.com/curl/curl/issues/4329
-
- case SSL_ERROR_ZERO_RETURN: /* no more data */
- /* close_notify alert */
-+ if(num == FIRSTSOCKET)
- connclose(conn, "TLS close_notify");
-__________________________________________________________________________________________________________
-----------------
| libssh2 1.9.0 |
-----------------
__________________________________________________________________________________________________________
+src/session.c
+memory leak: https://github.com/libssh2/libssh2/issues/28
+
+-if (session->state & LIBSSH2_STATE_NEWKEYS)
++//if (session->state & LIBSSH2_STATE_NEWKEYS)
+
+__________________________________________________________________________________________________________
move the following constants from src/sftp.h to include/libssh2_sftp.h:
#define MAX_SFTP_OUTGOING_SIZE 30000
#define MAX_SFTP_READ_SIZE 30000
+
+__________________________________________________________________________________________________________
+src/comp.c
+https://github.com/libssh2/libssh2/pull/418
+
+_libssh2_comp_methods(LIBSSH2_SESSION* session)
+{
++ #undef compress
+ if (session->flag.compress)
+ return comp_methods;
+ else
+ return no_comp_methods;
+}
+
+__________________________________________________________________________________________________________
+src/openssl.cpp
+properly support ssh-ed25519: https://github.com/libssh2/libssh2/pull/416
+
++static int
++gen_publickey_from_ed_evp(LIBSSH2_SESSION* session,
++ unsigned char** method,
++ size_t* method_len,
++ unsigned char** pubkeydata,
++ size_t* pubkeydata_len,
++ EVP_PKEY* pk)
++{
++ const char methodName[] = "ssh-ed25519";
++ unsigned char* methodBuf = NULL;
++ unsigned char* pubKeyBuf = NULL;
++ size_t pubKeyLen = 0;
++ size_t edKeyLen = 0;
++ unsigned char* bufPos = NULL;
++
++ _libssh2_debug(session,
++ LIBSSH2_TRACE_AUTH,
++ "Computing public key from ED private key envelop");
++
++ methodBuf = LIBSSH2_ALLOC(session, sizeof(methodName) - 1);
++ if (!methodBuf)
++ {
++ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
++ "Unable to allocate memory for private key data");
++ goto cleanup;
++ }
++
++ if (EVP_PKEY_get_raw_public_key(pk, NULL, &edKeyLen) != 1)
++ {
++ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
++ "EVP_PKEY_get_raw_public_key failed");
++ goto cleanup;
++ }
++
++ pubKeyLen = 4 + sizeof(methodName) - 1 + 4 + edKeyLen;
++ bufPos = pubKeyBuf = LIBSSH2_ALLOC(session, pubKeyLen);
++ if (!pubKeyBuf)
++ {
++ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
++ "Unable to allocate memory for private key data");
++ goto cleanup;
++ }
++
++ _libssh2_store_str(&bufPos, methodName, sizeof(methodName) - 1);
++ _libssh2_store_u32(&bufPos, edKeyLen);
++
++ if (EVP_PKEY_get_raw_public_key(pk, bufPos, &edKeyLen) != 1)
++ {
++ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
++ "EVP_PKEY_get_raw_public_key failed");
++ goto cleanup;
++ }
++
++ memcpy(methodBuf, methodName, sizeof(methodName) - 1);
++ *method = methodBuf;
++ *method_len = sizeof(methodName) - 1;
++ *pubkeydata = pubKeyBuf;
++ *pubkeydata_len = pubKeyLen;
++ return 0;
++
++cleanup:
++ if (methodBuf)
++ LIBSSH2_FREE(session, methodBuf);
++ if (pubKeyBuf)
++ LIBSSH2_FREE(session, pubKeyBuf);
++ return -1;
++}
++
+static int
+gen_publickey_from_ed25519_openssh_priv_data(LIBSSH2_SESSION* session,
+ struct string_buf* decrypted,
+ unsigned char** method,
+ size_t* method_len,
+ unsigned char** pubkeydata,
+
+
+
+
+int
+_libssh2_ed25519_new_private_frommemory(libssh2_ed25519_ctx** ed_ctx,
+ LIBSSH2_SESSION* session,
+ const char* filedata,
+ size_t filedata_len,
+ unsigned const char* passphrase)
+{
++ libssh2_ed25519_ctx* ctx = NULL;
++
++ _libssh2_init_if_needed();
++
++ ctx = _libssh2_ed25519_new_ctx();
++ if (!ctx)
++ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
++ "Unable to allocate memory for ed25519 key");
++
++ if (read_private_key_from_memory((void**)&ctx->private_key, (pem_read_bio_func)&PEM_read_bio_PrivateKey,
++ filedata, filedata_len, passphrase) == 0)
++ {
++ if (EVP_PKEY_id(ctx->private_key) != EVP_PKEY_ED25519)
++ {
++ _libssh2_ed25519_free(ctx);
++ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
++ "Private key is not an ed25519 key");
++ }
++
++ *ed_ctx = ctx;
++ return 0;
++ }
++ _libssh2_ed25519_free(ctx);
+
+ return read_openssh_private_key_from_memory((void**)ed_ctx, session,
+ "ssh-ed25519",
+ filedata, filedata_len,
+ passphrase);
+
+
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ pktype = EVP_PKEY_id(pk);
+#else
+ pktype = pk->type;
+#endif
+
+ switch (pktype)
+ {
++#if LIBSSH2_ED25519
++ case EVP_PKEY_ED25519 :
++ st = gen_publickey_from_ed_evp(session, method, method_len,
++ pubkeydata, pubkeydata_len, pk);
++ break;
++#endif /* LIBSSH2_ED25519 */
+ case EVP_PKEY_RSA :
+ st = gen_publickey_from_rsa_evp(session, method, method_len,
__________________________________________________________________________________________________________
-
+
-------------------------------
| wxWidgets master 2019-07-22 |
-------------------------------
__________________________________________________________________________________________________________
-Fix incorrect pane height calculations:
-
/src/aui/framemanager.cpp:
+Fix incorrect pane height calculations:
- // determine the dock's minimum size
- bool plus_border = false;
@@ -202,8 +254,7 @@ Fix incorrect pane height calculations:
- dock_min_size += (caption_size);
-
- dock.min_size = dock_min_size;
-
-
+
+ // determine the dock's minimum size
+ int dock_min_size = 0;
+ for (j = 0; j < dock_pane_count; ++j)
@@ -223,18 +274,17 @@ Fix incorrect pane height calculations:
+ }
+
+ dock.min_size = dock_min_size;
-__________________________________________________________________________________________________________
-/src/gtk/menu.cpp:
+__________________________________________________________________________________________________________
+/src/gtk/menu.cpp
-g_signal_connect(m_menu, "map", G_CALLBACK(menu_map), this);
+g_signal_connect(m_menu, "show", G_CALLBACK(menu_map), this); //"map" is never called on Ubuntu Unity, but "show" is
-__________________________________________________________________________________________________________
+__________________________________________________________________________________________________________
+/src/gtk/window.cpp
Backspace not working in filter dialog: http://www.freefilesync.org/forum/viewtopic.php?t=347
-/src/gtk/window.cpp:
-
void wxWindowGTK::ConnectWidget( GtkWidget *widget )
{
- static bool isSourceAttached;
@@ -265,13 +315,12 @@ Backspace not working in filter dialog: http://www.freefilesync.org/forum/viewto
+// g_source_set_priority(source, GDK_PRIORITY_EVENTS - 1);
+// g_source_attach(source, NULL);
+// }
-__________________________________________________________________________________________________________
+__________________________________________________________________________________________________________
+/include/wx/window.h
wxWidgets/GTK2 on some Linux systems incorrectly detects high DPI: https://freefilesync.org/forum/viewtopic.php?t=6114
=> hack away high-DPI support for GTK2 (= pretend GTK2 has device independent pixels, which it clearly has not!)
-/include/wx/window.h:
-
#include "wx/gtk/window.h"
- #ifdef __WXGTK3__
+ //#ifdef __WXGTK3__
diff --git a/Changelog.txt b/Changelog.txt
index 9d1713f4..7da64263 100755
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -1,20 +1,34 @@
+FreeFileSync 10.17 [2019-10-17]
+-------------------------------
+Support PuTTY private key files for SFTP login
+Enable zlib compression for SFTP servers if supported
+Update last sync time despite differences if nothing to do
+Reduce graph total time update interval
+Remember folder history not just for first folder pair
+Allow unprivileged symlink creation in Windows Developer Mode
+Integrate latest libcurl FTP bug fixes
+Detect common invalid SFTP key file formats
+Fixed startup crash caused by corrupted HDD properties
+Allow SFTP access via Ed25519 key in PKIX format
+
+
FreeFileSync 10.16 [2019-09-16]
-------------------------------
Redesigned progress indicator graphs
Avoid needless HTTP delay prior to Google Drive upload
Skip redundant CWDs during FTP metadata updates
Fixed MLSD 501 syntax error on Serv-U FTP server
-Check FTP server status using home directory instead of root
+Check FTP server status using FEAT/HELP instead of root folder
Avoid redundant TYPE changes during FTP directory listing
Access FTP files by full path and avoid CWDs
Support FTP home paths with non-ASCII chars
-Workaround libcurl bug failing to buffer FTP TLS authentication
+Work around libcurl bug failing to buffer FTP TLS authentication
Skip redundant FTP SIZE check before downloading file
Use ISO 8601 week of the year definition for %week% macro
Show login prompt for disconnected NAS share
Force icon resolution to 96 DPI in GTK2 build (Linux)
-Detect missing full disk access permission (macOS)
-Fixed accessibility issue due to graph color inconsistency
+Notify missing full disk access permission (macOS)
+Fixed accessibility issue with progress graph colors
Use short naming convention when deleting abandoned folder lock
Detect endless folder lock recursion on buggy file systems
Fixed Google Drive parsing error for invalid file time
diff --git a/FreeFileSync/Build/Resources/Languages/arabic.lng b/FreeFileSync/Build/Resources/Languages/arabic.lng
index dea9b522..05e21675 100755
--- a/FreeFileSync/Build/Resources/Languages/arabic.lng
+++ b/FreeFileSync/Build/Resources/Languages/arabic.lng
@@ -399,12 +399,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>مل٠قاعدة البيانات تالÙ:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>لا تحتوي ملÙات قاعدة البيانات حتى الآن على معلومات حول المزامنة الأخيرة.</target>
-
<source>Loading file %x...</source>
<target>جار تحميل المل٠%x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>لا تحتوي ملÙات قاعدة البيانات حتى الآن على معلومات حول المزامنة الأخيرة.</target>
+
<source>Saving file %x...</source>
<target>جاري Ø­Ùظ المل٠%x...</target>
@@ -493,6 +493,27 @@ Actual: %y bytes
<source>Update attributes on right</source>
<target>تحديث السمات على اليسار</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>حدث خطأ أثناء تحليل المل٠%x، الص٠%y، و العمود %z.</target>
+
+<source>Services</source>
+<target>خدمات</target>
+
+<source>Show All</source>
+<target>اظهر الكل</target>
+
+<source>Hide Others</source>
+<target>اخÙاء الاخرين</target>
+
+<source>Hide %x</source>
+<target>اخÙاء %x</target>
+
+<source>Quit %x</source>
+<target>الغاء %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>لا يمكن وضع Ù‚ÙÙ„ المسار للمجلدات الأتية:</target>
+
<source>Errors:</source>
<target>أخطاء:</target>
@@ -517,27 +538,6 @@ Actual: %y bytes
<source>Cleaning up log files:</source>
<target>تنظي٠ملÙات السجل:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>حدث خطأ أثناء تحليل المل٠%x، الص٠%y، و العمود %z.</target>
-
-<source>Services</source>
-<target>خدمات</target>
-
-<source>Show All</source>
-<target>اظهر الكل</target>
-
-<source>Hide Others</source>
-<target>اخÙاء الاخرين</target>
-
-<source>Hide %x</source>
-<target>اخÙاء %x</target>
-
-<source>Quit %x</source>
-<target>الغاء %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>لا يمكن وضع Ù‚ÙÙ„ المسار للمجلدات الأتية:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -734,14 +734,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>الاستخدام:</target>
-<source>1. Select folders to watch.</source>
-<target>1. حدد المجلدات للمتابعة.</target>
+<source>Select folders to watch.</source>
+<target>حدد المجلدات للمتابعة.</target>
-<source>2. Enter a command line.</source>
-<target>2. إدخال سطر أوامر.</target>
+<source>Enter a command line.</source>
+<target>إدخال سطر أوامر.</target>
-<source>3. Press 'Start'.</source>
-<target>3. اضغط على 'ابدأ'.</target>
+<source>Press 'Start'.</source>
+<target>اضغط على 'ابدأ'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>للبدء قم باستيراد مل٠"ffs_batch".</target>
@@ -1448,14 +1448,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>قم بتنشيط FreeFileSync Donation Edition بإحدى الطرق التالية:</target>
-<source>1. Activate via internet now:</source>
-<target>1. تنشيط عبر الإنترنت الآن:</target>
+<source>Activate via internet now:</source>
+<target>تنشيط عبر الإنترنت الآن:</target>
<source>Activate online</source>
<target>التنشيط عبر الإنترنت</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. استرداد Ù…Ùتاح تنشيط دون اتصال من الرابط التالي:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>استرداد Ù…Ùتاح تنشيط دون اتصال من الرابط التالي:</target>
<source>&Copy to clipboard</source>
<target>&نسخ من الذاكرة</target>
@@ -1469,6 +1469,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>ميز التكوينات التى لم يتم تشغيلها لأكثر من عدد الأيام الأتية:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target></target>
+
+<source>Locate the FreeFileSync app</source>
+<target></target>
+
+<source>Open Security && Privacy</source>
+<target></target>
+
+<source>Click the lock to allow changes.</source>
+<target></target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target></target>
+
<source>Synchronization Settings</source>
<target>إعدادات المزامنة</target>
@@ -1493,6 +1508,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>تمييز التكوينات</target>
+<source>Grant Full Disk Access</source>
+<target></target>
+
<source>Info</source>
<target>معلومات</target>
@@ -1557,45 +1575,6 @@ This guarantees a consistent state even in case of a serious error.
<source>&Execute</source>
<target>&تنÙيذ</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>0 مسار</pluralform>
-<pluralform>1 مسار واحد</pluralform>
-<pluralform>2 مساران</pluralform>
-<pluralform>%x مسارات</pluralform>
-<pluralform>%x مساراً</pluralform>
-<pluralform>%x مسار</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>0 ملÙ</pluralform>
-<pluralform>1 مل٠واحد</pluralform>
-<pluralform>2 ملÙان</pluralform>
-<pluralform>%x ملÙات</pluralform>
-<pluralform>%x ملÙاً</pluralform>
-<pluralform>%x ملÙ</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>الظاهر %y من أصل سطر 0</pluralform>
-<pluralform>الظاهر %y من أصل سطر وحيد 1</pluralform>
-<pluralform>الظاهر %y من أصل سطرين اثنين 2</pluralform>
-<pluralform>الظاهر %y من أصل %x سطور</pluralform>
-<pluralform>الظاهر %y من أصل %x سطراً</pluralform>
-<pluralform>الظاهر %y من أصل %x سطر</pluralform>
-</target>
-
<source>Set direction:</source>
<target>تحديد الاتجاه:</target>
@@ -1734,6 +1713,45 @@ This guarantees a consistent state even in case of a serious error.
<source>All files are in sync</source>
<target>جميع الملÙات ÙÙŠ تزامن كامل</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>0 مسار</pluralform>
+<pluralform>1 مسار واحد</pluralform>
+<pluralform>2 مساران</pluralform>
+<pluralform>%x مسارات</pluralform>
+<pluralform>%x مساراً</pluralform>
+<pluralform>%x مسار</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>0 ملÙ</pluralform>
+<pluralform>1 مل٠واحد</pluralform>
+<pluralform>2 ملÙان</pluralform>
+<pluralform>%x ملÙات</pluralform>
+<pluralform>%x ملÙاً</pluralform>
+<pluralform>%x ملÙ</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>الظاهر %y من أصل سطر 0</pluralform>
+<pluralform>الظاهر %y من أصل سطر وحيد 1</pluralform>
+<pluralform>الظاهر %y من أصل سطرين اثنين 2</pluralform>
+<pluralform>الظاهر %y من أصل %x سطور</pluralform>
+<pluralform>الظاهر %y من أصل %x سطراً</pluralform>
+<pluralform>الظاهر %y من أصل %x سطر</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>لا يمكن العثور على %x</target>
diff --git a/FreeFileSync/Build/Resources/Languages/bulgarian.lng b/FreeFileSync/Build/Resources/Languages/bulgarian.lng
index b0ecdafd..9fd5baa6 100755
--- a/FreeFileSync/Build/Resources/Languages/bulgarian.lng
+++ b/FreeFileSync/Build/Resources/Languages/bulgarian.lng
@@ -387,12 +387,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>Базата данни е повредена:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Базите данни още не Ñъдържат Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° поÑледната ÑинхронизациÑ.</target>
-
<source>Loading file %x...</source>
<target>Зарежда файл %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Базите данни още не Ñъдържат Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° поÑледната ÑинхронизациÑ.</target>
+
<source>Saving file %x...</source>
<target>Запазва файл %x...</target>
@@ -714,14 +714,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>Употреба:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Изберете папки за Ñледене.</target>
+<source>Select folders to watch.</source>
+<target>Изберете папки за Ñледене.</target>
-<source>2. Enter a command line.</source>
-<target>2. Въведете команден ред.</target>
+<source>Enter a command line.</source>
+<target>Въведете команден ред.</target>
-<source>3. Press 'Start'.</source>
-<target>3. ÐатиÑнете 'Старт'.</target>
+<source>Press 'Start'.</source>
+<target>ÐатиÑнете 'Старт'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>За Ñтарт проÑто импортирайте файл "ffs_batch".</target>
@@ -1420,14 +1420,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Ðктивирайте дарителÑко издание на FreeFileSync по един от Ñледните методи:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Ðктивирайте по Интернет Ñега:</target>
+<source>Activate via internet now:</source>
+<target>Ðктивирайте по Интернет Ñега:</target>
<source>Activate online</source>
<target>Ðктивирайте онлайн</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Вземете активационен код за офлайн от ÑÐ»ÐµÐ´Ð½Ð¸Ñ Ð°Ð´Ñ€ÐµÑ:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Вземете активационен код за офлайн от ÑÐ»ÐµÐ´Ð½Ð¸Ñ Ð°Ð´Ñ€ÐµÑ:</target>
<source>&Copy to clipboard</source>
<target>&Копирайте в клипборда</target>
@@ -1441,6 +1441,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Маркирай конфигурациите, които не Ñа изпълнÑвани в течение на повече от ÑÐ»ÐµÐ´Ð½Ð¸Ñ Ð±Ñ€Ð¾Ð¹ дни:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync изиÑква права за доÑтъп, за да избегне грешки "ОперациÑта не е разрешена", когато Ñинхронизира данните Ви (напр. поща, ÑъобщениÑ, календари).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Ðамерете приложение FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Отворете СиÑтемниÐаÑтройки/СигурноÑÑ‚</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Щракнете върху Заключване, за да разрешите промени.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Довлечете FreeFileSync в панела.</target>
+
<source>Synchronization Settings</source>
<target>ÐаÑтройки на СинхронизациÑ</target>
@@ -1465,6 +1480,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>Маркирай Конфигурациите</target>
+<source>Grant Full Disk Access</source>
+<target>ОÑигурете пълен доÑтъп до диÑка</target>
+
<source>Info</source>
<target>ИнформациÑ</target>
diff --git a/FreeFileSync/Build/Resources/Languages/chinese_simple.lng b/FreeFileSync/Build/Resources/Languages/chinese_simple.lng
index 7b00b32b..625d4154 100755
--- a/FreeFileSync/Build/Resources/Languages/chinese_simple.lng
+++ b/FreeFileSync/Build/Resources/Languages/chinese_simple.lng
@@ -384,12 +384,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>æ•°æ®åº“文件已æŸå:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>此数æ®åº“æ–‡ä»¶å¹¶æœªåŒ…å« æœ‰å…³æœ€åŽåŒæ­¥çš„ä¿¡æ¯.</target>
-
<source>Loading file %x...</source>
<target>正在加载文件 %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>此数æ®åº“æ–‡ä»¶å¹¶æœªåŒ…å« æœ‰å…³æœ€åŽåŒæ­¥çš„ä¿¡æ¯.</target>
+
<source>Saving file %x...</source>
<target>正在ä¿å­˜æ–‡ä»¶ %x...</target>
@@ -473,6 +473,27 @@ Actual: %y bytes
<source>Update attributes on right</source>
<target>æ›´æ–°å³ä¾§çš„文件属性</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>当分æžæ–‡ä»¶ %x , è¡Œ %y, 列 %z 时出错.</target>
+
+<source>Services</source>
+<target>æœåŠ¡</target>
+
+<source>Show All</source>
+<target>显示所有</target>
+
+<source>Hide Others</source>
+<target>éšè—其他</target>
+
+<source>Hide %x</source>
+<target>éšè— %x</target>
+
+<source>Quit %x</source>
+<target>退出 %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>无法为如下文件夹设置 目录é”定:</target>
+
<source>Errors:</source>
<target>错误:</target>
@@ -497,27 +518,6 @@ Actual: %y bytes
<source>Cleaning up log files:</source>
<target>正在清ç†æ—¥å¿—文件:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>当分æžæ–‡ä»¶ %x , è¡Œ %y, 列 %z 时出错.</target>
-
-<source>Services</source>
-<target>æœåŠ¡</target>
-
-<source>Show All</source>
-<target>显示所有</target>
-
-<source>Hide Others</source>
-<target>éšè—其他</target>
-
-<source>Hide %x</source>
-<target>éšè— %x</target>
-
-<source>Quit %x</source>
-<target>退出 %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>无法为如下文件夹设置 目录é”定:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -709,14 +709,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>用法:</target>
-<source>1. Select folders to watch.</source>
-<target>1. 选择è¦ç›‘视的文件夹.</target>
+<source>Select folders to watch.</source>
+<target>选择è¦ç›‘视的文件夹.</target>
-<source>2. Enter a command line.</source>
-<target>2. 输入一个命令行.</target>
+<source>Enter a command line.</source>
+<target>输入一个命令行.</target>
-<source>3. Press 'Start'.</source>
-<target>3. 点击'开始'.</target>
+<source>Press 'Start'.</source>
+<target>点击'开始'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>è¦å¼€å§‹åªéœ€å¯¼å…¥ä¸€ä¸ª "ffs_batch"文件.</target>
@@ -1410,15 +1410,12 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>激活 FreeFileSync æ赠版å¯ç”¨å¦‚下方å¼ä¹‹ä¸€:</target>
-<source>1. Activate via internet now:</source>
-<target>1. 现在就通过互è”网激活:</target>
+<source>Activate via internet now:</source>
+<target>现在就通过互è”网激活:</target>
<source>Activate online</source>
<target>在线激活</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>从如下网å€æŽ¥æ”¶ä¸€ä¸ªç¦»çº¿æ¿€æ´»ç :</target>
-
<source>&Copy to clipboard</source>
<target>å¤åˆ¶åˆ°å‰ªè´´æ¿(&C)</target>
@@ -1431,6 +1428,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>çªå‡ºæ˜¾ç¤ºé‚£äº›è¶…过如下天数 未è¿è¡Œçš„é…置文件:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync需è¦è®¿é—®æƒé™, 以é¿å…在åŒæ­¥æ•°æ®(例如邮件, 消æ¯, 日历)时出现"æ“作ä¸å…许"错误.</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>定ä½åˆ° FreeFileSync 应用程åº</target>
+
+<source>Open Security && Privacy</source>
+<target>打开安全和éšç§</target>
+
+<source>Click the lock to allow changes.</source>
+<target>点击é”头图标以å…许更改.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>拖动FreeFileSync放到é¢æ¿.</target>
+
<source>Synchronization Settings</source>
<target>åŒæ­¥è®¾ç½®</target>
@@ -1455,6 +1467,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>çªå‡ºæ˜¾ç¤ºé…置文件</target>
+<source>Grant Full Disk Access</source>
+<target>授予全盘访问æƒé™</target>
+
<source>Info</source>
<target>ä¿¡æ¯</target>
@@ -1514,30 +1529,6 @@ This guarantees a consistent state even in case of a serious error.
<source>&Execute</source>
<target>排除(&E)</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>%x 目录</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>%x 文件</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>显示 %x 中的 %y 行</pluralform>
-</target>
-
<source>Set direction:</source>
<target>设置方å‘:</target>
@@ -1676,6 +1667,30 @@ This guarantees a consistent state even in case of a serious error.
<source>All files are in sync</source>
<target>所有文件å‡æ˜¯åŒæ­¥çš„</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>%x 目录</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>%x 文件</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>显示 %x 中的 %y 行</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>找ä¸åˆ° %x</target>
diff --git a/FreeFileSync/Build/Resources/Languages/chinese_traditional.lng b/FreeFileSync/Build/Resources/Languages/chinese_traditional.lng
index 901500b9..670fcfb1 100755
--- a/FreeFileSync/Build/Resources/Languages/chinese_traditional.lng
+++ b/FreeFileSync/Build/Resources/Languages/chinese_traditional.lng
@@ -384,12 +384,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>資料庫檔案已æ壞:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>資料庫檔案未包å«ä¸Šæ¬¡åŒæ­¥è³‡è¨Šã€‚</target>
-
<source>Loading file %x...</source>
<target>正在讀å–檔案 %x…</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>資料庫檔案未包å«ä¸Šæ¬¡åŒæ­¥è³‡è¨Šã€‚</target>
+
<source>Saving file %x...</source>
<target>正在儲存檔案 %x…</target>
@@ -709,14 +709,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>使用方法:</target>
-<source>1. Select folders to watch.</source>
-<target>1. é¸æ“‡è¦ç›£çœ‹çš„資料夾。</target>
+<source>Select folders to watch.</source>
+<target>é¸æ“‡è¦ç›£çœ‹çš„資料夾。</target>
-<source>2. Enter a command line.</source>
-<target>2. 輸入一個命令列。</target>
+<source>Enter a command line.</source>
+<target>輸入一個命令列。</target>
-<source>3. Press 'Start'.</source>
-<target>3. 按下「開始ã€ã€‚</target>
+<source>Press 'Start'.</source>
+<target>按下「開始ã€ã€‚</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>è‹¥è¦é–‹å§‹åªè¦å°Žå…¥ä¸€å€‹"ffs_batch"檔。</target>
@@ -1413,14 +1413,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>é€éŽä»¥ä¸‹å…¶ä¸­ä¸€ç¨®æ–¹æ³•ä¾†å•Ÿå‹•FreeFileSync贊助版:</target>
-<source>1. Activate via internet now:</source>
-<target>1. ç«‹å³ç”±ç¶²éš›ç¶²è·¯å•Ÿå‹•ï¼š</target>
+<source>Activate via internet now:</source>
+<target>ç«‹å³ç”±ç¶²éš›ç¶²è·¯å•Ÿå‹•ï¼š</target>
<source>Activate online</source>
<target>線上啟動</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. 從下é¢ç¶²å€æ“·å–離線啟用金鑰:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>從下é¢ç¶²å€æ“·å–離線啟用金鑰:</target>
<source>&Copy to clipboard</source>
<target>複製到剪貼簿(&C)</target>
@@ -1434,6 +1434,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>çªé¡¯æœªåŸ·è¡Œè¶…éŽä»¥ä¸‹å¤©æ•¸çš„é…置:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSyncè¦æ±‚å–å¾—å­˜å–權é™ï¼Œä»¥é¿å…在åŒæ­¥è³‡æ–™(例如郵件ã€è¨Šæ¯ã€æ—¥æ›†)時出ç¾"ä¸å…許æ“作"錯誤。</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>找到FreeFileSync app</target>
+
+<source>Open Security && Privacy</source>
+<target>開啟安全性與隱ç§æ¬Š</target>
+
+<source>Click the lock to allow changes.</source>
+<target>按鎖頭一下以å…許更改。</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>å°‡FreeFileSync拖移到é¢æ¿ä¸­ã€‚</target>
+
<source>Synchronization Settings</source>
<target>åŒæ­¥è¨­å®š</target>
@@ -1458,6 +1473,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>çªé¡¯é…ç½®</target>
+<source>Grant Full Disk Access</source>
+<target>完全å–用ç£ç¢Ÿ</target>
+
<source>Info</source>
<target>訊æ¯</target>
diff --git a/FreeFileSync/Build/Resources/Languages/croatian.lng b/FreeFileSync/Build/Resources/Languages/croatian.lng
index 1acf95e3..c1fbb9f7 100755
--- a/FreeFileSync/Build/Resources/Languages/croatian.lng
+++ b/FreeFileSync/Build/Resources/Languages/croatian.lng
@@ -390,12 +390,12 @@ Stvarno: %y bajta
<source>Database file is corrupted:</source>
<target>Datoteka baze je oštećena:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Datoteke baze još uvijek ne sadrže informacije o zadnjoj sinkronizaciji.</target>
-
<source>Loading file %x...</source>
<target>UÄitavam datoteku %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Datoteke baze još uvijek ne sadrže informacije o zadnjoj sinkronizaciji.</target>
+
<source>Saving file %x...</source>
<target>Spremanje datoteke %x...</target>
@@ -719,14 +719,14 @@ Stvarno: %y bajta
<source>Usage:</source>
<target>Uporaba:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Odaberite mape za nadziranje.</target>
+<source>Select folders to watch.</source>
+<target>Odaberite mape za nadziranje.</target>
-<source>2. Enter a command line.</source>
-<target>2. Unesite naredbu.</target>
+<source>Enter a command line.</source>
+<target>Unesite naredbu.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Pretisnite 'Start'.</target>
+<source>Press 'Start'.</source>
+<target>Pretisnite 'Start'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Da biste zapoÄeli jednostavno uvezite "ffs_batch" datoteku.</target>
@@ -1427,14 +1427,14 @@ Ovo garantira stabilno stanje Äak u sluÄaju ozbiljne greÅ¡ke.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Aktivirajte FreeFileSync Donatorsku Verziju pomoću sljedećih naÄina:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Aktivacija preko interneta:</target>
+<source>Activate via internet now:</source>
+<target>Aktivacija preko interneta:</target>
<source>Activate online</source>
<target>Aktiviraj online</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Preuzeti offline aktivacijski kljuÄ sa sljedeće URL adrese:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Preuzeti offline aktivacijski kljuÄ sa sljedeće URL adrese:</target>
<source>&Copy to clipboard</source>
<target>&Kopirati u međuspremnik</target>
@@ -1448,6 +1448,21 @@ Ovo garantira stabilno stanje Äak u sluÄaju ozbiljne greÅ¡ke.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Istakni postavke koje nisu pokrenute više od broja dana:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync zahtjeva prava pristupa kako bi izbjegli pogrešku "Naredba nije dopuštena" prilikom sinkronizacije vaših datoteka (npr. Mail, Poruke, Kalendar).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Lociranje FreeFileSync aplikacije</target>
+
+<source>Open Security && Privacy</source>
+<target>Otvoriti Sigurnost i privatnost</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Kliknite na lokot kako bi odobrili promjene.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Povući FreeFileSync u prozor.</target>
+
<source>Synchronization Settings</source>
<target>Postavke Sinkronizacije</target>
@@ -1472,6 +1487,9 @@ Ovo garantira stabilno stanje Äak u sluÄaju ozbiljne greÅ¡ke.
<source>Highlight Configurations</source>
<target>Istakni postavke</target>
+<source>Grant Full Disk Access</source>
+<target>Dopustiti puni pristup disku</target>
+
<source>Info</source>
<target>Info</target>
diff --git a/FreeFileSync/Build/Resources/Languages/czech.lng b/FreeFileSync/Build/Resources/Languages/czech.lng
index 006c8dc8..2594f3aa 100755
--- a/FreeFileSync/Build/Resources/Languages/czech.lng
+++ b/FreeFileSync/Build/Resources/Languages/czech.lng
@@ -390,12 +390,12 @@ Aktuálně: %y b
<source>Database file is corrupted:</source>
<target>Databáze je poškozená:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Databázový soubor ještě neobsahuje informace o poslední synchronizaci.</target>
-
<source>Loading file %x...</source>
<target>NaÄítání souboru %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Databázový soubor ještě neobsahuje informace o poslední synchronizaci.</target>
+
<source>Saving file %x...</source>
<target>Ukládání souboru %x...</target>
@@ -719,14 +719,14 @@ Aktuálně: %y b
<source>Usage:</source>
<target>Použití:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Vyberte složku ke sledování.</target>
+<source>Select folders to watch.</source>
+<target>Vyberte složku ke sledování.</target>
-<source>2. Enter a command line.</source>
-<target>2. Zadejte příkazovou řádku.</target>
+<source>Enter a command line.</source>
+<target>Zadejte příkazovou řádku.</target>
-<source>3. Press 'Start'.</source>
-<target>3. ZmáÄknÄ›te 'Start'.</target>
+<source>Press 'Start'.</source>
+<target>ZmáÄknÄ›te 'Start'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Můžete naÄíst také konfiguraÄní soubor "ffs_batch".</target>
@@ -1424,14 +1424,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Aktivovat placenou verzi FreeFileSync Donation Edition pomocí:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Aktivovat přes internet:</target>
+<source>Activate via internet now:</source>
+<target>Aktivovat přes internet:</target>
<source>Activate online</source>
<target>Aktivace on-line</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Získat aktivaÄní klÃ­Ä z následující adresy:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Získat aktivaÄní klÃ­Ä z následující adresy:</target>
<source>&Copy to clipboard</source>
<target>&Kopírovat do schránky</target>
@@ -1445,6 +1445,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Zvýraznit položky, které nebyly spuÅ¡tÄ›ny za poslední poÄet dní:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync potřebuje povolit přístupová práva, aby při synchronizaci dat (např. pošty, zpráv, kalendáře) nedošlo k odepření přístupu a k chybě zálohování.</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Najděte umístění aplikace FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>JdÄ›te do ZabezpeÄení a soukromí</target>
+
+<source>Click the lock to allow changes.</source>
+<target>OdemÄete zámek pro povolení zmÄ›n.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Přetáhněte FreeFileSync do pravého okna.</target>
+
<source>Synchronization Settings</source>
<target>Nastavení synchronizace</target>
@@ -1469,6 +1484,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>Zvýraznění položek</target>
+<source>Grant Full Disk Access</source>
+<target>Povolení plný přístup k disku</target>
+
<source>Info</source>
<target>Info</target>
diff --git a/FreeFileSync/Build/Resources/Languages/danish.lng b/FreeFileSync/Build/Resources/Languages/danish.lng
index df98938c..1e5c9952 100755
--- a/FreeFileSync/Build/Resources/Languages/danish.lng
+++ b/FreeFileSync/Build/Resources/Languages/danish.lng
@@ -387,12 +387,12 @@ Aktuel: %y byte
<source>Database file is corrupted:</source>
<target>Databasefil i stykker:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Databasefilen indeholder endnu ikke oplysninger om sidste synkronisering.</target>
-
<source>Loading file %x...</source>
<target>Indlæser filen %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Databasefilen indeholder endnu ikke oplysninger om sidste synkronisering.</target>
+
<source>Saving file %x...</source>
<target>Gemmer filen %x...</target>
@@ -477,6 +477,27 @@ Aktuel: %y byte
<source>Update attributes on right</source>
<target>Opdater attributter mod højre</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Behandlingsfejl i filen %x, række %y, kolonne %z.</target>
+
+<source>Services</source>
+<target>Tjenester</target>
+
+<source>Show All</source>
+<target>Vis alle</target>
+
+<source>Hide Others</source>
+<target>Skjul andre</target>
+
+<source>Hide %x</source>
+<target>Skjul %x</target>
+
+<source>Quit %x</source>
+<target>Luk %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>Kan ikke sætte mappelåse for følgende mapper:</target>
+
<source>Errors:</source>
<target>Fejl:</target>
@@ -501,27 +522,6 @@ Aktuel: %y byte
<source>Cleaning up log files:</source>
<target>Rydder logfiler:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Behandlingsfejl i filen %x, række %y, kolonne %z.</target>
-
-<source>Services</source>
-<target>Tjenester</target>
-
-<source>Show All</source>
-<target>Vis alle</target>
-
-<source>Hide Others</source>
-<target>Skjul andre</target>
-
-<source>Hide %x</source>
-<target>Skjul %x</target>
-
-<source>Quit %x</source>
-<target>Luk %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>Kan ikke sætte mappelåse for følgende mapper:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -714,14 +714,14 @@ Aktuel: %y byte
<source>Usage:</source>
<target>Gør sådan:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Vælg mapper til jobbet.</target>
+<source>Select folders to watch.</source>
+<target>Vælg mapper til jobbet.</target>
-<source>2. Enter a command line.</source>
-<target>2. Angiv en kommando.</target>
+<source>Enter a command line.</source>
+<target>Angiv en kommando.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Klik 'Start'.</target>
+<source>Press 'Start'.</source>
+<target>Klik 'Start'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Importer en "ffs_batch"fil (Fil > Ã…ben...) for at komme igang.</target>
@@ -1420,14 +1420,14 @@ Sikrer processen ved alvorlige fejl.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Aktivér FreeFileSync donationsudgave på en af følgende måder:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Aktivér via internet nu:</target>
+<source>Activate via internet now:</source>
+<target>Aktivér via internet nu:</target>
<source>Activate online</source>
<target>Aktivér online</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Hent offline aktiveringsnøgle fra følgende adresse:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Hent offline aktiveringsnøgle fra følgende adresse:</target>
<source>&Copy to clipboard</source>
<target>&Kopiér</target>
@@ -1441,6 +1441,21 @@ Sikrer processen ved alvorlige fejl.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Fremhæv indstillinger der ikke er kørt i det følgende antal dage:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync kræver adgangsrettighedeer for at undgå "Handling ikke tilladt" fejl, ved synkronisering af data (mail, beskeder, kalender...etc.).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Find FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Ã…ben Sikkerhed && anonymitet</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Klik på låsen tillader ændringer.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Træk FreeFileSync ind i panelet.</target>
+
<source>Synchronization Settings</source>
<target>Synkroniseringsindstillinger</target>
@@ -1465,6 +1480,9 @@ Sikrer processen ved alvorlige fejl.
<source>Highlight Configurations</source>
<target>Fremhæv indstillinger</target>
+<source>Grant Full Disk Access</source>
+<target>Tillad fuld adgang til disk</target>
+
<source>Info</source>
<target>Info</target>
@@ -1525,33 +1543,6 @@ Sikrer processen ved alvorlige fejl.
<source>&Execute</source>
<target>&Udfør</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>1 mappe</pluralform>
-<pluralform>%x mapper</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>1 fil</pluralform>
-<pluralform>%x filer</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>Viser %y af 1 række</pluralform>
-<pluralform>Viser %y af %x rækker</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Retning:</target>
@@ -1690,6 +1681,33 @@ Sikrer processen ved alvorlige fejl.
<source>All files are in sync</source>
<target>Alt er synkroniseret</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>1 mappe</pluralform>
+<pluralform>%x mapper</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>1 fil</pluralform>
+<pluralform>%x filer</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>Viser %y af 1 række</pluralform>
+<pluralform>Viser %y af %x rækker</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>Kan ikke finde %x</target>
diff --git a/FreeFileSync/Build/Resources/Languages/dutch.lng b/FreeFileSync/Build/Resources/Languages/dutch.lng
index 62a3ef67..6a7dd34c 100755
--- a/FreeFileSync/Build/Resources/Languages/dutch.lng
+++ b/FreeFileSync/Build/Resources/Languages/dutch.lng
@@ -387,12 +387,12 @@ Werkelijk: %y bytes
<source>Database file is corrupted:</source>
<target>Databasebestand is beschadigd:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>De databasebestanden bevatten nog geen informatie over de laatste synchronisatie.</target>
-
<source>Loading file %x...</source>
<target>Laden van bestand %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>De databasebestanden bevatten nog geen informatie over de laatste synchronisatie.</target>
+
<source>Saving file %x...</source>
<target>Opslaan van bestand %x...</target>
@@ -477,6 +477,27 @@ Werkelijk: %y bytes
<source>Update attributes on right</source>
<target>Update kenmerken aan de rechterzijde</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Fout bij het analyseren van bestand %x, rij %y, kolom %z.</target>
+
+<source>Services</source>
+<target>Diensten</target>
+
+<source>Show All</source>
+<target>Toon alles</target>
+
+<source>Hide Others</source>
+<target>Verberg anderen</target>
+
+<source>Hide %x</source>
+<target>Verberg %x</target>
+
+<source>Quit %x</source>
+<target>Sluit %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>Kan map vergrendelingen niet instellen voor de volgende mappen:</target>
+
<source>Errors:</source>
<target>Fouten:</target>
@@ -501,27 +522,6 @@ Werkelijk: %y bytes
<source>Cleaning up log files:</source>
<target>Logbestanden opschonen:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Fout bij het analyseren van bestand %x, rij %y, kolom %z.</target>
-
-<source>Services</source>
-<target>Diensten</target>
-
-<source>Show All</source>
-<target>Toon alles</target>
-
-<source>Hide Others</source>
-<target>Verberg anderen</target>
-
-<source>Hide %x</source>
-<target>Verberg %x</target>
-
-<source>Quit %x</source>
-<target>Sluit %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>Kan map vergrendelingen niet instellen voor de volgende mappen:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -714,14 +714,14 @@ Werkelijk: %y bytes
<source>Usage:</source>
<target>Gebruik:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Selecteer mappen om te bekijken.</target>
+<source>Select folders to watch.</source>
+<target>Selecteer mappen om te bekijken.</target>
-<source>2. Enter a command line.</source>
-<target>2. Een opdrachtregel invoeren.</target>
+<source>Enter a command line.</source>
+<target>Een opdrachtregel invoeren.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Druk op 'Start'.</target>
+<source>Press 'Start'.</source>
+<target>Druk op 'Start'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Om te beginnen gewoon een "ffs_batch"-bestand importeren.</target>
@@ -1420,14 +1420,14 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Activeer de FreeFileSync Donatie Editie door één van de volgende manieren:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Activeer nu via internet:</target>
+<source>Activate via internet now:</source>
+<target>Activeer nu via internet:</target>
<source>Activate online</source>
<target>Online activeren</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Een offline activatie sleutel ophalen van de volgende URL:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Een offline activatie sleutel ophalen van de volgende URL:</target>
<source>&Copy to clipboard</source>
<target>&Naar klembord kopiëren</target>
@@ -1441,6 +1441,21 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Markeer configuraties die niet meer dan het volgende aantal dagen zijn uitgevoerd:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync heeft toegangsrechten nodig om te voorkomen dat "Bediening niet toegestaan" foutmeldingen bij het synchroniseren van uw gegevens (bijv. Mail, Berichten, Kalenders) ontstaan.</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Zoek de FreeFileSync-app op</target>
+
+<source>Open Security && Privacy</source>
+<target>Open Beveiliging en privacy</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Klik op het slot om wijzigingen toe te staan.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Sleep FreeFileSync naar het paneel.</target>
+
<source>Synchronization Settings</source>
<target>Synchronisatie instellingen</target>
@@ -1465,6 +1480,9 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Highlight Configurations</source>
<target>Markeer Configuraties</target>
+<source>Grant Full Disk Access</source>
+<target>Volledige schijftoegang verlenen</target>
+
<source>Info</source>
<target>Info</target>
@@ -1525,33 +1543,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>&Execute</source>
<target>&Uitvoeren</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>1 map</pluralform>
-<pluralform>%x mappen</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>1 bestand</pluralform>
-<pluralform>%x bestanden</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>%y van 1 rij weergeven</pluralform>
-<pluralform>%y van %x rijen weergeven</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Richting instellen:</target>
@@ -1690,6 +1681,33 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>All files are in sync</source>
<target>Alle bestanden zijn in synchronisatie</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>1 map</pluralform>
+<pluralform>%x mappen</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>1 bestand</pluralform>
+<pluralform>%x bestanden</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>%y van 1 rij weergeven</pluralform>
+<pluralform>%y van %x rijen weergeven</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>Kan %x niet vinden.</target>
diff --git a/FreeFileSync/Build/Resources/Languages/english_uk.lng b/FreeFileSync/Build/Resources/Languages/english_uk.lng
index 29715212..64605033 100755
--- a/FreeFileSync/Build/Resources/Languages/english_uk.lng
+++ b/FreeFileSync/Build/Resources/Languages/english_uk.lng
@@ -7,9 +7,6 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
-<source>Failed to determine file permission support for folder %x.</source>
-<target></target>
-
<source>Cannot read file %x.</source>
<target>Cannot read file %x.</target>
@@ -390,12 +387,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>Database file is corrupted:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>The database files do not yet contain information about the last synchronisation.</target>
-
<source>Loading file %x...</source>
<target>Loading file %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>The database files do not yet contain information about the last synchronisation.</target>
+
<source>Saving file %x...</source>
<target>Saving file %x...</target>
@@ -480,6 +477,27 @@ Actual: %y bytes
<source>Update attributes on right</source>
<target>Update attributes on right</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Error parsing file %x, row %y, column %z.</target>
+
+<source>Services</source>
+<target>Services</target>
+
+<source>Show All</source>
+<target>Show All</target>
+
+<source>Hide Others</source>
+<target>Hide Others</target>
+
+<source>Hide %x</source>
+<target>Hide %x</target>
+
+<source>Quit %x</source>
+<target>Quit %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>Cannot set directory locks for the following folders:</target>
+
<source>Errors:</source>
<target>Errors:</target>
@@ -504,27 +522,6 @@ Actual: %y bytes
<source>Cleaning up log files:</source>
<target>Cleaning up log files:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Error parsing file %x, row %y, column %z.</target>
-
-<source>Services</source>
-<target>Services</target>
-
-<source>Show All</source>
-<target>Show All</target>
-
-<source>Hide Others</source>
-<target>Hide Others</target>
-
-<source>Hide %x</source>
-<target>Hide %x</target>
-
-<source>Quit %x</source>
-<target>Quit %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>Cannot set directory locks for the following folders:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -717,14 +714,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>Usage:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Select folders to watch.</target>
+<source>Select folders to watch.</source>
+<target>Select folders to watch.</target>
-<source>2. Enter a command line.</source>
-<target>2. Enter a command line.</target>
+<source>Enter a command line.</source>
+<target>Enter a command line.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Press 'Start'.</target>
+<source>Press 'Start'.</source>
+<target>Press 'Start'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>To get started just import a "ffs_batch" file.</target>
@@ -1423,14 +1420,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Activate the FreeFileSync Donation Edition by one of the following methods:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Activate via internet now:</target>
+<source>Activate via internet now:</source>
+<target>Activate via internet now:</target>
<source>Activate online</source>
<target>Activate online</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Retrieve an offline activation key from the following URL:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Retrieve an offline activation key from the following URL:</target>
<source>&Copy to clipboard</source>
<target>&Copy to clipboard</target>
@@ -1444,6 +1441,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Highlight configurations that have not been run for more than the following number of days:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Locate the FreeFileSync app</target>
+
+<source>Open Security && Privacy</source>
+<target>Open Security && Privacy</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Click the lock to allow changes.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Drag FreeFileSync into the panel.</target>
+
<source>Synchronization Settings</source>
<target>Synchronisation Settings</target>
@@ -1468,6 +1480,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>Highlight Configurations</target>
+<source>Grant Full Disk Access</source>
+<target>Grant Full Disk Access</target>
+
<source>Info</source>
<target>Info</target>
@@ -1528,33 +1543,6 @@ This guarantees a consistent state even in case of a serious error.
<source>&Execute</source>
<target>&Execute</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Set direction:</target>
@@ -1693,6 +1681,33 @@ This guarantees a consistent state even in case of a serious error.
<source>All files are in sync</source>
<target>All files are in sync</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>Cannot find %x</target>
@@ -1978,6 +1993,9 @@ This guarantees a consistent state even in case of a serious error.
<source>The file is locked by another process:</source>
<target>The file is locked by another process:</target>
+<source>Failed to determine file permission support for folder %x.</source>
+<target>Failed to determine file permission support for folder %x.</target>
+
<source>Cannot read security context of %x.</source>
<target>Cannot read security context of %x.</target>
diff --git a/FreeFileSync/Build/Resources/Languages/french.lng b/FreeFileSync/Build/Resources/Languages/french.lng
index aee43f7c..2e47d567 100755
--- a/FreeFileSync/Build/Resources/Languages/french.lng
+++ b/FreeFileSync/Build/Resources/Languages/french.lng
@@ -387,12 +387,12 @@ Trouvé : %y octets
<source>Database file is corrupted:</source>
<target>La base de données est abîmée :</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>La base de données ne contient plus aujourd'hui d'informations sur la dernière synchronisation.</target>
-
<source>Loading file %x...</source>
<target>Chargement du fichier %x ...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>La base de données ne contient plus aujourd'hui d'informations sur la dernière synchronisation.</target>
+
<source>Saving file %x...</source>
<target>Enregistrement du fichier %x ...</target>
@@ -714,14 +714,14 @@ Trouvé : %y octets
<source>Usage:</source>
<target>Utilisation :</target>
-<source>1. Select folders to watch.</source>
-<target>1. Choisir les dossiers à examiner.</target>
+<source>Select folders to watch.</source>
+<target>Choisir les dossiers à examiner.</target>
-<source>2. Enter a command line.</source>
-<target>2. Entrez une ligne de commande.</target>
+<source>Enter a command line.</source>
+<target>Entrez une ligne de commande.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Cliquez sur 'Démarrer'.</target>
+<source>Press 'Start'.</source>
+<target>Cliquez sur 'Démarrer'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Pour démarrer, il faut importer un fichier "ffs_batch".</target>
@@ -1420,14 +1420,14 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Activez la FreeFileSync Donation Edition par l'une des méthodes suivantes :</target>
-<source>1. Activate via internet now:</source>
-<target>1. Activez par internet maintenant :</target>
+<source>Activate via internet now:</source>
+<target>Activez par internet maintenant :</target>
<source>Activate online</source>
<target>Activez en ligne</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Récupérer une clé d'activation hors ligne à partir de l'URL suivante :</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Récupérer une clé d'activation hors ligne à partir de l'URL suivante :</target>
<source>&Copy to clipboard</source>
<target>& Copie dans le presse-papiers</target>
@@ -1441,6 +1441,21 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Signaler les configurations non exécutées depuis le nombre de jours suivant :</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync requiert les droits d'accès pour éviter les erreurs "Opération non autorisée" lors de la synchronisation de vos données (Courrier, Messages, Calendriers, par exemple).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Trouver l'application FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Ouvrir Sécurité et confidentialité</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Cliquez sur le verrou pour autoriser les modifications.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Déplacer FreeFileSync vers le panneau.</target>
+
<source>Synchronization Settings</source>
<target>Configuration de la Synchronisation</target>
@@ -1465,6 +1480,9 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Highlight Configurations</source>
<target>Signaler les configurations</target>
+<source>Grant Full Disk Access</source>
+<target>Accordez l'accès complet au disque</target>
+
<source>Info</source>
<target>Info</target>
diff --git a/FreeFileSync/Build/Resources/Languages/german.lng b/FreeFileSync/Build/Resources/Languages/german.lng
index 47ae6f4e..f505735a 100755
--- a/FreeFileSync/Build/Resources/Languages/german.lng
+++ b/FreeFileSync/Build/Resources/Languages/german.lng
@@ -30,6 +30,9 @@ Tatsächlich: %y bytes
<source>Operation not supported between different devices.</source>
<target>Der Vorgang wird zwischen unterschiedlichen Geräten nicht unterstützt.</target>
+<source>Temporary access error:</source>
+<target>Vorübergehender Zugriffsfehler:</target>
+
<source>Cannot delete file %x.</source>
<target>Die Datei %x kann nicht gelöscht werden.</target>
@@ -714,14 +717,14 @@ Tatsächlich: %y bytes
<source>Usage:</source>
<target>Verwendung:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Zu überwachende Ordner wählen.</target>
+<source>Select folders to watch.</source>
+<target>Zu überwachende Ordner wählen.</target>
-<source>2. Enter a command line.</source>
-<target>2. Eine Befehlszeile angeben.</target>
+<source>Enter a command line.</source>
+<target>Eine Befehlszeile angeben.</target>
-<source>3. Press 'Start'.</source>
-<target>3. 'Start' drücken.</target>
+<source>Press 'Start'.</source>
+<target>'Start' drücken.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Zum Schnelleinstieg einfach eine "ffs_batch" Datei importieren.</target>
@@ -1420,14 +1423,14 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Aktivieren Sie die FreeFileSync Spendenversion durch eine der folgenden Methoden:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Jetzt über das Internet aktivieren:</target>
+<source>Activate via internet now:</source>
+<target>Jetzt über das Internet aktivieren:</target>
<source>Activate online</source>
<target>Online aktivieren</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Einen Offline-Aktivierungsschlüssel von folgender URL abrufen:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Einen Offline-Aktivierungsschlüssel von folgender URL abrufen:</target>
<source>&Copy to clipboard</source>
<target>In &Zwischenablage kopieren</target>
diff --git a/FreeFileSync/Build/Resources/Languages/greek.lng b/FreeFileSync/Build/Resources/Languages/greek.lng
index 85330175..3cb3c1a8 100755
--- a/FreeFileSync/Build/Resources/Languages/greek.lng
+++ b/FreeFileSync/Build/Resources/Languages/greek.lng
@@ -387,12 +387,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>Το αÏχείο βάσης δεδομένων είναι κατεστÏαμμένο:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Τα αÏχεία βάσης δεδομένων δεν πεÏιέχουν ακόμα πληÏοφοÏίες για τον τελευταίο συγχÏονισμό.</target>
-
<source>Loading file %x...</source>
<target>Λήψη του αÏχείου %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Τα αÏχεία βάσης δεδομένων δεν πεÏιέχουν ακόμα πληÏοφοÏίες για τον τελευταίο συγχÏονισμό.</target>
+
<source>Saving file %x...</source>
<target>Αποθήκευση του αÏχείου %x...</target>
@@ -477,6 +477,27 @@ Actual: %y bytes
<source>Update attributes on right</source>
<target>ΕνημέÏωση των χαÏακτηÏιστικών στα δεξιά</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Σφάλμα κατά την ανάλυση του αÏχείου %x, γÏαμμή %y, στήλη %z.</target>
+
+<source>Services</source>
+<target>ΥπηÏεσίες</target>
+
+<source>Show All</source>
+<target>Εμφάνιση Όλων</target>
+
+<source>Hide Others</source>
+<target>ΑπόκÏυψη Άλλων</target>
+
+<source>Hide %x</source>
+<target>ΑπόκÏυψη %x</target>
+
+<source>Quit %x</source>
+<target>Έξοδος από %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>Δεν μποÏοÏν να κλειδωθοÏν οι ακόλουθοι υποκατάλογοι:</target>
+
<source>Errors:</source>
<target>Σφάλματα:</target>
@@ -501,27 +522,6 @@ Actual: %y bytes
<source>Cleaning up log files:</source>
<target>ΚαθάÏισμα αÏχείων καταγÏαφής:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Σφάλμα κατά την ανάλυση του αÏχείου %x, γÏαμμή %y, στήλη %z.</target>
-
-<source>Services</source>
-<target>ΥπηÏεσίες</target>
-
-<source>Show All</source>
-<target>Εμφάνιση Όλων</target>
-
-<source>Hide Others</source>
-<target>ΑπόκÏυψη Άλλων</target>
-
-<source>Hide %x</source>
-<target>ΑπόκÏυψη %x</target>
-
-<source>Quit %x</source>
-<target>Έξοδος από %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>Δεν μποÏοÏν να κλειδωθοÏν οι ακόλουθοι υποκατάλογοι:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -714,14 +714,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>ΧÏήση:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Επιλέξτε τους φακέλους που θα παÏακολουθοÏνται.</target>
+<source>Select folders to watch.</source>
+<target>Επιλέξτε τους φακέλους που θα παÏακολουθοÏνται.</target>
-<source>2. Enter a command line.</source>
-<target>2. Εισάγετε μια γÏαμμή εντολών.</target>
+<source>Enter a command line.</source>
+<target>Εισάγετε μια γÏαμμή εντολών.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Πατήστε το 'ΈναÏξη'.</target>
+<source>Press 'Start'.</source>
+<target>Πατήστε το 'ΈναÏξη'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Για να ξεκινήσετε μποÏείτε απλά να εισάγετε ένα αÏχείο "ffs_batch".</target>
@@ -1420,14 +1420,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>ΕνεÏγοποίηση της Έκδοσης ΔωÏητή του FreeFileSync μέσω μιας από τις παÏακάτω μεθόδους:</target>
-<source>1. Activate via internet now:</source>
-<target>1. ΕνεÏγοποίηση μέσω internet Ï„ÏŽÏα:</target>
+<source>Activate via internet now:</source>
+<target>ΕνεÏγοποίηση μέσω internet Ï„ÏŽÏα:</target>
<source>Activate online</source>
<target>Απευθείας ενεÏγοποίηση</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Ανάκτηση ενός ÎºÎ»ÎµÎ¹Î´Î¹Î¿Ï ÎµÎ½ÎµÏγοποίησης χωÏίς σÏνδεση από το ακόλουθο URL:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Ανάκτηση ενός ÎºÎ»ÎµÎ¹Î´Î¹Î¿Ï ÎµÎ½ÎµÏγοποίησης χωÏίς σÏνδεση από το ακόλουθο URL:</target>
<source>&Copy to clipboard</source>
<target>&ΑντιγÏαφή στο Ï€ÏόχειÏο</target>
@@ -1441,6 +1441,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Επισήμανση παÏαμέτÏων συγχÏÎ¿Î½Î¹ÏƒÎ¼Î¿Ï Ï€Î¿Ï… δεν έχουν εκτελεστεί για μεγαλÏτεÏο από τον ακόλουθο αÏιθμό ημεÏών:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>Το FreeFileSync χÏειάζεται δικαιώματα Ï€Ïόσβασης για να αποφÏγει λάθη του Ï„Ïπου "Η λειτουÏγία δεν επιτÏέπεται" όταν συγχÏονίζει τα δεδομένα σας (Ï€.χ. στο Mail, στα ΜηνÏματα, στα ΗμεÏολόγια).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Εντοπίστε την εφαÏμογή FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Ανοίξτε την Ασφάλεια και απόÏÏητο</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Κάντε κλικ στην κλειδαÏιά για να επιτÏέπονται οι αλλαγές.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>ΣÏÏετε το FreeFileSync μέσα στο παÏάθυÏο.</target>
+
<source>Synchronization Settings</source>
<target>Ρυθμίσεις συγχÏονισμοÏ</target>
@@ -1465,6 +1480,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>Επισήμανση ΠαÏαμέτÏων</target>
+<source>Grant Full Disk Access</source>
+<target>ΠαÏαχώÏηση πλήÏους Ï€Ïόσβασης στο δίσκο</target>
+
<source>Info</source>
<target>ΠληÏοφοÏίες</target>
@@ -1525,33 +1543,6 @@ This guarantees a consistent state even in case of a serious error.
<source>&Execute</source>
<target>&Εκτέλεση</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>1 φάκελος</pluralform>
-<pluralform>%x φάκελοι</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>1 αÏχείο</pluralform>
-<pluralform>%x αÏχεία</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>Εμφανίζεται %y από 1 γÏαμμή</pluralform>
-<pluralform>Εμφανίζονται %y από %x γÏαμμές</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Επιλογή κατεÏθυνσης:</target>
@@ -1690,6 +1681,33 @@ This guarantees a consistent state even in case of a serious error.
<source>All files are in sync</source>
<target>Όλα τα αÏχεία είναι συγχÏονισμένα</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>1 φάκελος</pluralform>
+<pluralform>%x φάκελοι</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>1 αÏχείο</pluralform>
+<pluralform>%x αÏχεία</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>Εμφανίζεται %y από 1 γÏαμμή</pluralform>
+<pluralform>Εμφανίζονται %y από %x γÏαμμές</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>Δεν μποÏεί να βÏεθεί το %x</target>
diff --git a/FreeFileSync/Build/Resources/Languages/hebrew.lng b/FreeFileSync/Build/Resources/Languages/hebrew.lng
index 0d5d3eb1..7e7678a6 100755
--- a/FreeFileSync/Build/Resources/Languages/hebrew.lng
+++ b/FreeFileSync/Build/Resources/Languages/hebrew.lng
@@ -387,12 +387,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>קובץ בסיס × ×ª×•× ×™× ×¤×’×•×:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>קבצי בסיס ×”× ×ª×•× ×™× ××™× × ×›×•×œ×œ×™× ×¢×“×™×™×Ÿ מידע על סינכרון ×חרון.</target>
-
<source>Loading file %x...</source>
<target>טוען קובץ %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>קבצי בסיס ×”× ×ª×•× ×™× ××™× × ×›×•×œ×œ×™× ×¢×“×™×™×Ÿ מידע על סינכרון ×חרון.</target>
+
<source>Saving file %x...</source>
<target>שומר קובץ %x...</target>
@@ -714,14 +714,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>שימוש:</target>
-<source>1. Select folders to watch.</source>
-<target>1. בחר תיקיות לצפייה.</target>
+<source>Select folders to watch.</source>
+<target>בחר תיקיות לצפייה.</target>
-<source>2. Enter a command line.</source>
-<target>2. הקש שורת פקודות.</target>
+<source>Enter a command line.</source>
+<target>הקש שורת פקודות.</target>
-<source>3. Press 'Start'.</source>
-<target>3. לחץ 'הפעל'.</target>
+<source>Press 'Start'.</source>
+<target>לחץ 'הפעל'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>בכדי להתחיל ×™×‘× ×§×•×‘×¥ "ffs_batch".</target>
@@ -1420,14 +1420,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>הפעל ×ת מהדורת התרומות של FreeFileSync ב×חת מהשיטות הב×ות:</target>
-<source>1. Activate via internet now:</source>
-<target>1. הפעל ב×מצעות ×”×ינטרנט עכשיו:</target>
+<source>Activate via internet now:</source>
+<target>הפעל ב×מצעות ×”×ינטרנט עכשיו:</target>
<source>Activate online</source>
<target>הפעלה מקוונת</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. קבל מפתח הפעלה למערכת ×œ× ×ž×§×•×•× ×ª מן הלינק הב×:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>קבל מפתח הפעלה למערכת ×œ× ×ž×§×•×•× ×ª מן הלינק הב×:</target>
<source>&Copy to clipboard</source>
<target>&העתק ללוח</target>
@@ -1441,6 +1441,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>הדגש תצורות ×©×œ× ×”×•×¤×¢×œ×• למשך יותר ממספר ×”×™×ž×™× ×”×‘×:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync דורש זכויות גישה כדי להימנע משגי×ות "פעולה ×סורה" בעת סנכרון ×”× ×ª×•× ×™× ×©×œ×š (למשל דו×ר, הודעות, לוחות שנה).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>×תר ×ת ×פליקצית FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>פתח ×ת ×בטחה ופרטיות</target>
+
+<source>Click the lock to allow changes.</source>
+<target>הקלק בכדי ל×פשר שינויי×.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>גרור ×ת FreeFileSync ×ל הלוח.</target>
+
<source>Synchronization Settings</source>
<target>הגדרות סנכרון</target>
@@ -1465,6 +1480,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>הדגש תצורות</target>
+<source>Grant Full Disk Access</source>
+<target>הענק גישה מל××” לדיסק</target>
+
<source>Info</source>
<target>מידע</target>
diff --git a/FreeFileSync/Build/Resources/Languages/hindi.lng b/FreeFileSync/Build/Resources/Languages/hindi.lng
index c210fdf6..57917e9d 100755
--- a/FreeFileSync/Build/Resources/Languages/hindi.lng
+++ b/FreeFileSync/Build/Resources/Languages/hindi.lng
@@ -387,12 +387,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>डेटाबेस फ़ाइल दूषित है:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>डेटाबेस फ़ाइलà¥à¤¸ में अभी तक पिछले सिंकà¥à¤°à¤¨à¤¾à¤‡à¥›à¥‡à¤¶à¤¨ की जानकारी नहीं है।</target>
-
<source>Loading file %x...</source>
<target>फ़ाइल %x को लोड किया जा रहा है...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>डेटाबेस फ़ाइलà¥à¤¸ में अभी तक पिछले सिंकà¥à¤°à¤¨à¤¾à¤‡à¥›à¥‡à¤¶à¤¨ की जानकारी नहीं है।</target>
+
<source>Saving file %x...</source>
<target>फ़ाइल %x सहेजी जा रही है...</target>
@@ -477,6 +477,27 @@ Actual: %y bytes
<source>Update attributes on right</source>
<target>दाईं तरफ के गà¥à¤£ अदà¥à¤¯à¤¤à¤¨ करें</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>फ़ाइल %x, पंकà¥à¤¤à¤¿ %y, सà¥à¤¤à¤‚भ %z पदचà¥à¤›à¥‡à¤¦à¤¨ में तà¥à¤°à¥à¤Ÿà¤¿à¥¤</target>
+
+<source>Services</source>
+<target>सेवाà¤à¤</target>
+
+<source>Show All</source>
+<target>सब दिखाओ</target>
+
+<source>Hide Others</source>
+<target>अनà¥à¤¯ छà¥à¤ªà¤¾à¤à¤‚</target>
+
+<source>Hide %x</source>
+<target>%x छà¥à¤ªà¤¾à¤à¤‚</target>
+
+<source>Quit %x</source>
+<target>%x छोड़ें</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>निमà¥à¤¨ निरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾à¤“ं के लिठनिरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾ अवरोध सेट नहीं कर सकते:</target>
+
<source>Errors:</source>
<target>तà¥à¤°à¥à¤Ÿà¤¿à¤¯à¤¾à¤:</target>
@@ -501,27 +522,6 @@ Actual: %y bytes
<source>Cleaning up log files:</source>
<target>लॉग फाइलों की सफ़ाई जारी:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>फ़ाइल %x, पंकà¥à¤¤à¤¿ %y, सà¥à¤¤à¤‚भ %z पदचà¥à¤›à¥‡à¤¦à¤¨ में तà¥à¤°à¥à¤Ÿà¤¿à¥¤</target>
-
-<source>Services</source>
-<target>सेवाà¤à¤</target>
-
-<source>Show All</source>
-<target>सब दिखाओ</target>
-
-<source>Hide Others</source>
-<target>अनà¥à¤¯ छà¥à¤ªà¤¾à¤à¤‚</target>
-
-<source>Hide %x</source>
-<target>%x छà¥à¤ªà¤¾à¤à¤‚</target>
-
-<source>Quit %x</source>
-<target>%x छोड़ें</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>निमà¥à¤¨ निरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾à¤“ं के लिठनिरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾ अवरोध सेट नहीं कर सकते:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -714,14 +714,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>पà¥à¤°à¤¯à¥‹à¤—:</target>
-<source>1. Select folders to watch.</source>
-<target>1. निगरानी के लिठफ़ोलडरà¥à¤¸ चà¥à¤¨à¤¿à¤à¥¤</target>
+<source>Select folders to watch.</source>
+<target>निगरानी के लिठफ़ोलडरà¥à¤¸ चà¥à¤¨à¤¿à¤à¥¤</target>
-<source>2. Enter a command line.</source>
-<target>2. आदेश-पंकà¥à¤¤à¤¿ पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ करें।</target>
+<source>Enter a command line.</source>
+<target>आदेश-पंकà¥à¤¤à¤¿ पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ करें।</target>
-<source>3. Press 'Start'.</source>
-<target>3. 'पà¥à¤°à¤¾à¤°à¤‚भ' दबाà¤à¤‚।</target>
+<source>Press 'Start'.</source>
+<target>'पà¥à¤°à¤¾à¤°à¤‚भ' दबाà¤à¤‚।</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>पà¥à¤°à¤¾à¤°à¤‚भ करने के लिठकेवल कोई "ffs_batch" फ़ाइल आयात करें।</target>
@@ -1420,14 +1420,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>FreeFileSync दान संसà¥â€à¤•à¤°à¤£ को निमà¥à¤¨ विधियों में से à¤à¤• के दà¥à¤µà¤¾à¤°à¤¾ सकà¥à¤°à¤¿à¤¯ करें:</target>
-<source>1. Activate via internet now:</source>
-<target>१. इंटरनेट दà¥à¤µà¤¾à¤°à¤¾ अभी सकà¥à¤°à¤¿à¤¯ करें:</target>
+<source>Activate via internet now:</source>
+<target>इंटरनेट दà¥à¤µà¤¾à¤°à¤¾ अभी सकà¥à¤°à¤¿à¤¯ करें:</target>
<source>Activate online</source>
<target>ऑनलाइन सकà¥à¤°à¤¿à¤¯ करें</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>२. ऑफ़लाइन सकà¥à¤°à¤¿à¤¯à¤£ कà¥à¤‚जी निमà¥à¤¨ URL से पà¥à¤°à¤¾à¤ªà¥à¤¤ करें:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>ऑफ़लाइन सकà¥à¤°à¤¿à¤¯à¤£ कà¥à¤‚जी निमà¥à¤¨ URL से पà¥à¤°à¤¾à¤ªà¥à¤¤ करें:</target>
<source>&Copy to clipboard</source>
<target>कà¥à¤²à¤¿à¤ªà¤¬à¥‹à¤°à¥à¤¡ पर पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ बनाà¤à¤ (&C)</target>
@@ -1441,6 +1441,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>निमà¥à¤¨ दिनों से अधिक समय तक ना चलाठगठकॉनà¥à¤«à¤¼à¤¿à¤—रेशनà¥à¤¸ को हाइलाइट करें:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync को आपके डेटा को सिंकà¥à¤°à¤¨à¤¾à¤‡à¤œà¤¼ करते समय "कारà¥à¤°à¤µà¤¾à¤ˆ की अनà¥à¤®à¤¤à¤¿ नहीं" तà¥à¤°à¥à¤Ÿà¤¿à¤¯à¥‹à¤‚ से बचने के लिठपहà¥à¤à¤š अधिकारों की आवशà¥à¤¯à¤•à¤¤à¤¾ होती है (जैसे मेल, संदेश, कैलेंडर)।</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>FreeFileSync à¤à¤ª की सà¥à¤¥à¤¿à¤¤à¤¿ जानें</target>
+
+<source>Open Security && Privacy</source>
+<target>सà¥à¤°à¤•à¥à¤·à¤¾ और गोपनीयता</target>
+
+<source>Click the lock to allow changes.</source>
+<target>परिवरà¥à¤¤à¤¨à¥‹à¤‚ को अनà¥à¤®à¤¤à¤¿ देने के लिठलॉक पर कà¥à¤²à¤¿à¤• करें।</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>पैनल में FreeFileSync खींचें।</target>
+
<source>Synchronization Settings</source>
<target>सिंकà¥à¤°à¤¨à¤¾à¤‡à¥›à¥‡à¤¶à¤¨ सेटिंगà¥à¤¸</target>
@@ -1465,6 +1480,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>कॉनà¥à¤«à¤¼à¤¿à¤—रेशनà¥à¤¸ हाइलाइट करें</target>
+<source>Grant Full Disk Access</source>
+<target>पूरà¥à¤£ डिसà¥à¤• à¤à¤•à¥à¤¸à¥‡à¤¸ पà¥à¤°à¤¦à¤¾à¤¨ करें</target>
+
<source>Info</source>
<target>जानकारी</target>
@@ -1525,33 +1543,6 @@ This guarantees a consistent state even in case of a serious error.
<source>&Execute</source>
<target>निषà¥à¤ªà¤¾à¤¦à¤¿à¤¤ करें (&E)</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>1 निरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾</pluralform>
-<pluralform>%x निरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾à¤à¤</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>1 फ़ाइल</pluralform>
-<pluralform>%x फ़ाइलà¥à¤¸</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>1 पंकà¥à¤¤à¤¿ में से %y पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤</pluralform>
-<pluralform>%x पंकà¥à¤¤à¤¿à¤¯à¥‹à¤‚ में से %y पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤</pluralform>
-</target>
-
<source>Set direction:</source>
<target>दिशा सेट करें:</target>
@@ -1690,6 +1681,33 @@ This guarantees a consistent state even in case of a serious error.
<source>All files are in sync</source>
<target>सभी फ़ाइलà¥à¤¸ सिंक में हैं</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>1 निरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾</pluralform>
+<pluralform>%x निरà¥à¤¦à¥‡à¤¶à¤¿à¤•à¤¾à¤à¤</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>1 फ़ाइल</pluralform>
+<pluralform>%x फ़ाइलà¥à¤¸</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>1 पंकà¥à¤¤à¤¿ में से %y पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤</pluralform>
+<pluralform>%x पंकà¥à¤¤à¤¿à¤¯à¥‹à¤‚ में से %y पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>%x ढूंढने में असमरà¥à¤¥</target>
diff --git a/FreeFileSync/Build/Resources/Languages/hungarian.lng b/FreeFileSync/Build/Resources/Languages/hungarian.lng
index e7223bbc..a280724a 100755
--- a/FreeFileSync/Build/Resources/Languages/hungarian.lng
+++ b/FreeFileSync/Build/Resources/Languages/hungarian.lng
@@ -387,12 +387,12 @@ Tényleges: %y bájt
<source>Database file is corrupted:</source>
<target>A következő adatbázis állomány sérült:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Az adatbázis állományok nem tartalmaznak információt a legutóbbi szinkronizálásról.</target>
-
<source>Loading file %x...</source>
<target>%x állomány betöltése...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Az adatbázis állományok nem tartalmaznak információt a legutóbbi szinkronizálásról.</target>
+
<source>Saving file %x...</source>
<target>%x állomány mentése...</target>
@@ -714,14 +714,14 @@ Tényleges: %y bájt
<source>Usage:</source>
<target>Használat:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Válaszd ki a figyelendő könyvtárat.</target>
+<source>Select folders to watch.</source>
+<target>Válaszd ki a figyelendő könyvtárat.</target>
-<source>2. Enter a command line.</source>
-<target>2. Add meg a parancssort.</target>
+<source>Enter a command line.</source>
+<target>Add meg a parancssort.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Nyomd meg a Start gombot.</target>
+<source>Press 'Start'.</source>
+<target>Nyomd meg a Start gombot.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Az induláshoz importáljon egy "ffs_batch" állományt.</target>
@@ -1420,14 +1420,14 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Aktiválja a FreeFileSync Támogatói kiadását a következő módok egyikével:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Aktiválja most interneten keresztül:</target>
+<source>Activate via internet now:</source>
+<target>Aktiválja most interneten keresztül:</target>
<source>Activate online</source>
<target>Aktiválja online</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Igényeljen egy offline aktiváló kulcsot a következő URL-ről:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Igényeljen egy offline aktiváló kulcsot a következő URL-ről:</target>
<source>&Copy to clipboard</source>
<target>&Másolja a vágólapra</target>
@@ -1441,6 +1441,21 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Jelölje ki azokat a beállításokat, amelyek nem futottak legalább a következő számú napja:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>A FreeFileSync-nek hozzáférési jogokra van szüksége, hogy elkerüljük a "Nem megengedett művelet" hibákat az adatok (pl. levelek, üzenetek, naptárak) szinkronizálásakor.</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Keresse meg a FreeFileSync app-ot</target>
+
+<source>Open Security && Privacy</source>
+<target>Nyissa meg a "Biztonság és adatvédelem" fület</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Kattintson a lakatra a változások engedélyezéséhez.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Húzza a FreeFileSync-et a panelre.</target>
+
<source>Synchronization Settings</source>
<target>Szinkronizálási beállítások</target>
@@ -1465,6 +1480,9 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Highlight Configurations</source>
<target>Jelölje ki a beállításokat</target>
+<source>Grant Full Disk Access</source>
+<target>Biztosítson teljes hozzáférést a lemezhez</target>
+
<source>Info</source>
<target>Információ</target>
diff --git a/FreeFileSync/Build/Resources/Languages/italian.lng b/FreeFileSync/Build/Resources/Languages/italian.lng
index 444a4486..12fe93a5 100755
--- a/FreeFileSync/Build/Resources/Languages/italian.lng
+++ b/FreeFileSync/Build/Resources/Languages/italian.lng
@@ -387,12 +387,12 @@ Attuale: %y byte
<source>Database file is corrupted:</source>
<target>Il file del database è danneggiato:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>I file del database non contengono ancora informazioni sull'ultima sincronizzazione.</target>
-
<source>Loading file %x...</source>
<target>Caricamento file %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>I file del database non contengono ancora informazioni sull'ultima sincronizzazione.</target>
+
<source>Saving file %x...</source>
<target>Salvare il file %x...</target>
@@ -477,6 +477,27 @@ Attuale: %y byte
<source>Update attributes on right</source>
<target>Aggiorna attributi a destra</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Errore nel parsing del file %x, riga %y, colonna %z.</target>
+
+<source>Services</source>
+<target>Servizi</target>
+
+<source>Show All</source>
+<target>Mostra tutto</target>
+
+<source>Hide Others</source>
+<target>Nascondi gli altri</target>
+
+<source>Hide %x</source>
+<target>Nascondi %x</target>
+
+<source>Quit %x</source>
+<target>Esci da %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>Impossibile impostare i blocchi di directory per le seguenti cartelle:</target>
+
<source>Errors:</source>
<target>Errori:</target>
@@ -501,27 +522,6 @@ Attuale: %y byte
<source>Cleaning up log files:</source>
<target>Pulizia dei file di registro:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Errore nel parsing del file %x, riga %y, colonna %z.</target>
-
-<source>Services</source>
-<target>Servizi</target>
-
-<source>Show All</source>
-<target>Mostra tutto</target>
-
-<source>Hide Others</source>
-<target>Nascondi gli altri</target>
-
-<source>Hide %x</source>
-<target>Nascondi %x</target>
-
-<source>Quit %x</source>
-<target>Esci da %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>Impossibile impostare i blocchi di directory per le seguenti cartelle:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -625,7 +625,7 @@ Attuale: %y byte
<target>Impossibile trovare la cartella %x.</target>
<source>Target folder %x is already existing, but was not available during folder comparison.</source>
-<target></target>
+<target>La cartella di destinazione %x è già esistente, ma non era disponibile durante il confronto delle cartelle.</target>
<source>Target folder input field must not be empty.</source>
<target>Il campo per la cartella di destinazione non può essere vuoto.</target>
@@ -655,7 +655,7 @@ Attuale: %y byte
<target>Alcuni file saranno sincronizzati come parte di più cartelle base.</target>
<source>To avoid conflicts, set up exclude filters so that each updated file is included by only one base folder.</source>
-<target></target>
+<target>Per evitare conflitti, impostare i filtri di esclusione in modo che ogni file aggiornato sia incluso in una sola cartella di base.</target>
<source>Versioning folder:</source>
<target>Cartella versione:</target>
@@ -714,14 +714,14 @@ Attuale: %y byte
<source>Usage:</source>
<target>Uso:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Seleziona cartelle da controllare.</target>
+<source>Select folders to watch.</source>
+<target>Seleziona cartelle da controllare.</target>
-<source>2. Enter a command line.</source>
-<target>2. Inserisci linea di comando.</target>
+<source>Enter a command line.</source>
+<target>Inserisci linea di comando.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Premi 'Avvio'.</target>
+<source>Press 'Start'.</source>
+<target>Premi 'Avvio'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Per iniziare è sufficiente importare un file con estensione "ffs_batch".</target>
@@ -748,7 +748,7 @@ Attuale: %y byte
<target>Riga di comando:</target>
<source>&Hide console window</source>
-<target></target>
+<target>&Nascondi la finestra della console</target>
<source>
The command is triggered if:
@@ -786,7 +786,7 @@ Il comando è attivato se:
<target>In attesa che la directory sia disponibile:</target>
<source>&Configure</source>
-<target></target>
+<target>&Configurazione</target>
<source>&Show error message</source>
<target>&Mostra messaggio di errore</target>
@@ -822,7 +822,7 @@ Il comando è attivato se:
<target>Scansione...</target>
<source>configuration file</source>
-<target></target>
+<target>file di configurazione</target>
<source>System: Sleep</source>
<target>Sistema: In Sonno</target>
@@ -834,7 +834,7 @@ Il comando è attivato se:
<target>Non c'è nulla da sincronizzare</target>
<source>Executing command:</source>
-<target></target>
+<target>Comando di esecuzione:</target>
<source>You can switch to FreeFileSync's main window to resolve this issue.</source>
<target>È possibile passare alla finestra principale di FreeFileSync per risolvere questo problema.</target>
@@ -1035,10 +1035,10 @@ Il comando è attivato se:
<target>Corrispondenza</target>
<source>Processed:</source>
-<target></target>
+<target>Elaborato:</target>
<source>Remaining:</source>
-<target></target>
+<target>Residuo:</target>
<source>New</source>
<target>Nuovo</target>
@@ -1277,10 +1277,10 @@ Il comando è attivato se:
<target>&Non mostrare più questo avviso</target>
<source>Bytes:</source>
-<target></target>
+<target>Byte:</target>
<source>Items:</source>
-<target></target>
+<target>Articoli:</target>
<source>Synchronizing...</source>
<target>Sincronizzazione...</target>
@@ -1370,10 +1370,10 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<target>&Elimina i log dopo x giorni:</target>
<source>Notification sounds:</source>
-<target></target>
+<target>Suoni di notifica:</target>
<source>Synchronization finished:</source>
-<target></target>
+<target>Sincronizzazione terminata:</target>
<source>Customize context menu:</source>
<target>Personalizzare menu contestuale:</target>
@@ -1420,14 +1420,14 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Attivare la FreeFileSync Donazione con uno dei seguenti metodi:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Attivare via internet ora:</target>
+<source>Activate via internet now:</source>
+<target>Attivare via internet ora:</target>
<source>Activate online</source>
<target>Attivazione on-line</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Recuperare una chiave di attivazione non in linea dal seguente URL:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Recuperare una chiave di attivazione non in linea dal seguente URL:</target>
<source>&Copy to clipboard</source>
<target>&Copia negli appunti</target>
@@ -1441,6 +1441,21 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Evidenzia le configurazioni che non sono state eseguite per più del seguente numero di giorni:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync richiede i diritti di accesso per evitare errori di 'Operazione non consentita' durante la sincronizzazione dei dati (ad esempio Mail, Messaggi, Calendari).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Individuare l'app FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Apri Sicurezza e Privacy</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Fare clic sul blocco per consentire le modifiche.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Trascinare FreeFileSync nel pannello.</target>
+
<source>Synchronization Settings</source>
<target>Impostazioni di Sincronizzazione</target>
@@ -1465,6 +1480,9 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Highlight Configurations</source>
<target>Evidenzia le configurazioni</target>
+<source>Grant Full Disk Access</source>
+<target>Concedere l'accesso completo al disco</target>
+
<source>Info</source>
<target>Info</target>
@@ -1525,33 +1543,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>&Execute</source>
<target>&Esecuzione</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directory</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>1 file</pluralform>
-<pluralform>%x file</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>Risultati %y di 1 riga</pluralform>
-<pluralform>Risultati %y di %x righe</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Imposta direzione:</target>
@@ -1616,19 +1607,19 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<target>No&n salvare</target>
<source>%x cannot be renamed.</source>
-<target></target>
+<target>%x non può essere rinominato.</target>
<source>New name:</source>
-<target></target>
+<target>Nuovo nome:</target>
<source>Rename Configuration</source>
-<target></target>
+<target>Rinomina configurazione</target>
<source>Configuration name must not be empty.</source>
-<target></target>
+<target>Il nome della configurazione non deve essere vuoto.</target>
<source>&Rename...</source>
-<target></target>
+<target>&Ridenominazione...</target>
<source>Hide configuration</source>
<target>Nascondi configurazione</target>
@@ -1690,6 +1681,33 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>All files are in sync</source>
<target>Tutti i file sono in sincronia</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directory</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>1 file</pluralform>
+<pluralform>%x file</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>Risultati %y di 1 riga</pluralform>
+<pluralform>Risultati %y di %x righe</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>Impossibile trovare %x</target>
@@ -1742,7 +1760,7 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<target>Password:</target>
<source>Key passphrase:</source>
-<target></target>
+<target>Passphrase chiave:</target>
<source>Please enter a file path.</source>
<target>Si prega di inserire un percorso di file.</target>
@@ -1976,7 +1994,7 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<target>Il file è bloccato da un altro processo:</target>
<source>Failed to determine file permission support for folder %x.</source>
-<target></target>
+<target>Impossibile determinare il supporto delle autorizzazioni per i file per la cartella %x.</target>
<source>Cannot read security context of %x.</source>
<target>Impossibile leggere il contesto di protezione di %x.</target>
diff --git a/FreeFileSync/Build/Resources/Languages/japanese.lng b/FreeFileSync/Build/Resources/Languages/japanese.lng
index 64c078f5..20de640b 100755
--- a/FreeFileSync/Build/Resources/Languages/japanese.lng
+++ b/FreeFileSync/Build/Resources/Languages/japanese.lng
@@ -384,12 +384,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>データベースãŒç ´æã—ã¦ã„ã¾ã™:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>ã“ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ ファイルã«ã¯å‰å›žã®åŒæœŸã«é–¢ã™ã‚‹æƒ…å ±ãŒã¾ã å«ã¾ã‚Œã¦ã„ã¾ã›ã‚“.</target>
-
<source>Loading file %x...</source>
<target>ファイル %x を読ã¿è¾¼ã¿ä¸­...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>ã“ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ ファイルã«ã¯å‰å›žã®åŒæœŸã«é–¢ã™ã‚‹æƒ…å ±ãŒã¾ã å«ã¾ã‚Œã¦ã„ã¾ã›ã‚“.</target>
+
<source>Saving file %x...</source>
<target>ファイル %x ã‚’ä¿å­˜ä¸­...</target>
@@ -473,6 +473,27 @@ Actual: %y bytes
<source>Update attributes on right</source>
<target>å³ã®å±žæ€§ã‚’æ›´æ–°</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>ファイル %x ã®æ§‹æ–‡è§£æžã‚¨ãƒ©ãƒ¼, è¡Œ %y, 列 %z.</target>
+
+<source>Services</source>
+<target>サービス</target>
+
+<source>Show All</source>
+<target>ã™ã¹ã¦è¡¨ç¤º</target>
+
+<source>Hide Others</source>
+<target>ä»–ã‚’éžè¡¨ç¤º</target>
+
+<source>Hide %x</source>
+<target>%x ã‚’éžè¡¨ç¤º</target>
+
+<source>Quit %x</source>
+<target>%x を終了</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>次ã®ãƒ•ã‚©ãƒ«ãƒ€ã«ã‚るディレクトリã¯ãƒ­ãƒƒã‚¯ã§ãã¾ã›ã‚“:</target>
+
<source>Errors:</source>
<target>エラー:</target>
@@ -497,27 +518,6 @@ Actual: %y bytes
<source>Cleaning up log files:</source>
<target>ログファイルã®ã‚¯ãƒªãƒ¼ãƒ³:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>ファイル %x ã®æ§‹æ–‡è§£æžã‚¨ãƒ©ãƒ¼, è¡Œ %y, 列 %z.</target>
-
-<source>Services</source>
-<target>サービス</target>
-
-<source>Show All</source>
-<target>ã™ã¹ã¦è¡¨ç¤º</target>
-
-<source>Hide Others</source>
-<target>ä»–ã‚’éžè¡¨ç¤º</target>
-
-<source>Hide %x</source>
-<target>%x ã‚’éžè¡¨ç¤º</target>
-
-<source>Quit %x</source>
-<target>%x を終了</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>次ã®ãƒ•ã‚©ãƒ«ãƒ€ã«ã‚るディレクトリã¯ãƒ­ãƒƒã‚¯ã§ãã¾ã›ã‚“:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -709,14 +709,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>使用方法:</target>
-<source>1. Select folders to watch.</source>
-<target>1. 監視ã™ã‚‹ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠž.</target>
+<source>Select folders to watch.</source>
+<target>監視ã™ã‚‹ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠž.</target>
-<source>2. Enter a command line.</source>
-<target>2. コマンドラインを入力.</target>
+<source>Enter a command line.</source>
+<target>コマンドラインを入力.</target>
-<source>3. Press 'Start'.</source>
-<target>3. 'スタート'をクリック.</target>
+<source>Press 'Start'.</source>
+<target>'スタート'をクリック.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>"ffs_batch" をインãƒãƒ¼ãƒˆå¾Œã€ã™ãã«é–‹å§‹.</target>
@@ -1413,14 +1413,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>FreeFileSync 寄付版ã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ™ãƒ¼ãƒˆã¯æ¬¡ã®ã„ãšã‚Œã‹ã®æ–¹æ³•ã§è¡Œã„ã¾ã™:</target>
-<source>1. Activate via internet now:</source>
-<target>1. ãƒãƒƒãƒˆçµŒç”±ã§ã‚¢ã‚¯ãƒ†ã‚£ãƒ™ãƒ¼ãƒˆ:</target>
+<source>Activate via internet now:</source>
+<target>ãƒãƒƒãƒˆçµŒç”±ã§ã‚¢ã‚¯ãƒ†ã‚£ãƒ™ãƒ¼ãƒˆ:</target>
<source>Activate online</source>
<target>オンライン アクティベート</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. 次ã®URLã‹ã‚‰ã‚ªãƒ•ãƒ©ã‚¤ãƒ³ アクティベーションキーをå–å¾—:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>次ã®URLã‹ã‚‰ã‚ªãƒ•ãƒ©ã‚¤ãƒ³ アクティベーションキーをå–å¾—:</target>
<source>&Copy to clipboard</source>
<target>クリップボードã«ã‚³ãƒ”ー(&C)</target>
@@ -1434,6 +1434,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>指定ã—ãŸæ—¥æ•°ã‚’超ãˆã¦å®Ÿè¡Œã•ã‚Œã¦ã„ãªã„構æˆã‚’強調表示ã™ã‚‹:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync ã§ã¯åŒæœŸæ™‚ã® "許å¯ã•ã‚Œã¦ã„ãªã„æ“作" エラーを回é¿ã™ã‚‹ãŸã‚ã«ã‚¢ã‚¯ã‚»ã‚¹æ¨©ã‚’å¿…è¦ã¨ã—ã¾ã™ (例. メール, メッセージ, カレンダー).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>FreeFileSync app ã®å ´æ‰€</target>
+
+<source>Open Security && Privacy</source>
+<target>セキュリティ 㨠プライãƒã‚·ãƒ¼ã‚’é–‹ã</target>
+
+<source>Click the lock to allow changes.</source>
+<target>éµã‚’クリックã§å¤‰æ›´ã‚’許å¯.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>パãƒãƒ«ã« FreeFileSync をドラッグ.</target>
+
<source>Synchronization Settings</source>
<target>åŒæœŸã®è¨­å®š</target>
@@ -1458,6 +1473,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>構æˆã®å¼·èª¿è¡¨ç¤º</target>
+<source>Grant Full Disk Access</source>
+<target>フルディスクアクセス</target>
+
<source>Info</source>
<target>情報</target>
@@ -1517,30 +1535,6 @@ This guarantees a consistent state even in case of a serious error.
<source>&Execute</source>
<target>実行(&E)</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>%x ディレクトリ</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>%x 個ã®ãƒ•ã‚¡ã‚¤ãƒ«</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>%x è¡Œã®å†… %y 行を表示</pluralform>
-</target>
-
<source>Set direction:</source>
<target>æ–¹å‘ã®è¨­å®š:</target>
@@ -1679,6 +1673,30 @@ This guarantees a consistent state even in case of a serious error.
<source>All files are in sync</source>
<target>ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’åŒæœŸ</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>%x ディレクトリ</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>%x 個ã®ãƒ•ã‚¡ã‚¤ãƒ«</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>%x è¡Œã®å†… %y 行を表示</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>%x ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</target>
diff --git a/FreeFileSync/Build/Resources/Languages/korean.lng b/FreeFileSync/Build/Resources/Languages/korean.lng
index f8efe8d4..7ea2002d 100755
--- a/FreeFileSync/Build/Resources/Languages/korean.lng
+++ b/FreeFileSync/Build/Resources/Languages/korean.lng
@@ -91,7 +91,7 @@ Actual: %y bytes
<target>ì´ë¦„ %xì´(ê°€) ê°™ì€ í´ë” ë‚´ í•œ ê°œ ì´ìƒì˜ 항목ì—ì„œ 사용ë˜ê³  있습니다.</target>
<source>Please authorize access to user account %x.</source>
-<target>ì‚¬ìš©ìž ê³„ì • %xì— ëŒ€í•œ 액세스 ê¶Œí•œì„ ë¶€ì—¬í•˜ì‹­ì‹œì˜¤.</target>
+<target>ì‚¬ìš©ìž ê³„ì • %xì— ëŒ€í•œ ì ‘ê·¼ ê¶Œí•œì„ ë¶€ì—¬í•˜ì‹­ì‹œì˜¤.</target>
<source>Cannot open file %x.</source>
<target>íŒŒì¼ %xì„(를) ì—´ 수 없습니다.</target>
@@ -266,7 +266,7 @@ Actual: %y bytes
<target>í´ë”는 í•„ìš” ì‹œ ìžë™ìœ¼ë¡œ ìƒì„±ë©ë‹ˆë‹¤.</target>
<source>The following folder paths differ in case. Please use a single form in order to avoid duplicate accesses.</source>
-<target>ë‹¤ìŒ í´ë” 경로는 ê²½ìš°ì— ë”°ë¼ ë‹¤ë¦…ë‹ˆë‹¤. 중복 액세스를 피하기 위해 ë‹¨ì¼ ì–‘ì‹ì„ 사용하십시오.</target>
+<target>ë‹¤ìŒ í´ë” 경로는 ê²½ìš°ì— ë”°ë¼ ë‹¤ë¦…ë‹ˆë‹¤. 중복 ì ‘ê·¼ì„ í”¼í•˜ê¸° 위해 ë‹¨ì¼ ì–‘ì‹ì„ 사용하십시오.</target>
<source>Scanning:</source>
<target>스캔 중:</target>
@@ -384,12 +384,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>ë°ì´í„°ë² ì´ìŠ¤ íŒŒì¼ ì†ìƒ:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>ë°ì´í„°ë² ì´ìŠ¤ 파ì¼ì— ì•„ì§ ë§ˆì§€ë§‰ ë™ê¸°í™”ì— ëŒ€í•œ ì •ë³´ê°€ 없습니다.</target>
-
<source>Loading file %x...</source>
<target>íŒŒì¼ %x 로딩 중...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>ë°ì´í„°ë² ì´ìŠ¤ 파ì¼ì— ì•„ì§ ë§ˆì§€ë§‰ ë™ê¸°í™”ì— ëŒ€í•œ ì •ë³´ê°€ 없습니다.</target>
+
<source>Saving file %x...</source>
<target>íŒŒì¼ %x 저장 중...</target>
@@ -709,14 +709,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>사용:</target>
-<source>1. Select folders to watch.</source>
-<target>1. ê°ì‹œí•  í´ë”를 ì„ íƒí•˜ì„¸ìš”.</target>
+<source>Select folders to watch.</source>
+<target>ê°ì‹œí•  í´ë”를 ì„ íƒí•˜ì„¸ìš”.</target>
-<source>2. Enter a command line.</source>
-<target>2. ëª…ë ¹ì¤„ì„ ìž…ë ¥í•˜ì„¸ìš”.</target>
+<source>Enter a command line.</source>
+<target>ëª…ë ¹ì¤„ì„ ìž…ë ¥í•˜ì„¸ìš”.</target>
-<source>3. Press 'Start'.</source>
-<target>3. '시작'ì„ ëˆ„ë¥´ì„¸ìš”.</target>
+<source>Press 'Start'.</source>
+<target>'시작'ì„ ëˆ„ë¥´ì„¸ìš”.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>시작하려면 "ffs_batch" fileì„ ê°€ì ¸ 오십시오.</target>
@@ -1145,7 +1145,7 @@ The command is triggered if:
<target>최대:</target>
<source>Time span:</source>
-<target>시간 간격:</target>
+<target>시간 범위:</target>
<source>C&lear</source>
<target>제거(&l)</target>
@@ -1252,7 +1252,7 @@ The command is triggered if:
<target>서버 디렉터리:</target>
<source>Access timeout (in seconds):</source>
-<target>액세스 제한 시간(초):</target>
+<target>접근 제한 시간(초):</target>
<source>SFTP channels per connection:</source>
<target>ì—°ê²° 당 SFTP ì±„ë„ ìˆ˜:</target>
@@ -1413,14 +1413,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>ë‹¤ìŒ ì¤‘ í•œ ë°©ë²•ì„ ì‚¬ìš©í•˜ì—¬ FreeFileSync ê¸°ë¶€ìž ì—ë””ì…˜ì„ í™œì„±í™”í•˜ì„¸ìš”:</target>
-<source>1. Activate via internet now:</source>
-<target>1. ì¸í„°ë„·ì„ 통해 지금 바로 활성화:</target>
+<source>Activate via internet now:</source>
+<target>ì¸í„°ë„·ì„ 통해 지금 바로 활성화:</target>
<source>Activate online</source>
<target>온ë¼ì¸ 활성화</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. ë‹¤ìŒ URLì—ì„œ 오프ë¼ì¸ 활성화 키를 검색:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>ë‹¤ìŒ URLì—ì„œ 오프ë¼ì¸ 활성화 키를 검색:</target>
<source>&Copy to clipboard</source>
<target>í´ë¦½ë³´ë“œì— 복사(&C)</target>
@@ -1434,11 +1434,26 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>ë‹¤ìŒ ì¼ ìˆ˜ë³´ë‹¤ 오래 실행ë˜ì§€ ì•Šì€ êµ¬ì„±ì„ ê°•ì¡° 표시:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>ë°ì´í„° ë™ê¸°í™” ì‹œ (예: ë©”ì¼, 메시지, 캘린ë”), "허용ë˜ì§€ ì•Šì€ ìž‘ì—…" 오류를 피하기 위해 FreeFileSync는 ì ‘ê·¼ ê¶Œí•œì´ í•„ìš”í•©ë‹ˆë‹¤.</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>FreeFileSync 앱 찾기</target>
+
+<source>Open Security && Privacy</source>
+<target>보안 ë° ê°œì¸ ì •ë³´ 보호</target>
+
+<source>Click the lock to allow changes.</source>
+<target>변경 í—ˆìš©ì„ í•˜ë ¤ë©´ ìž ê¸ˆì„ í´ë¦­í•˜ì„¸ìš”.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>FreeFileSync를 íŒ¨ë„ ì•ˆìœ¼ë¡œ ëŒì–´ 오세요.</target>
+
<source>Synchronization Settings</source>
<target>ë™ê¸°í™” 설정</target>
<source>Access Online Storage</source>
-<target>온ë¼ì¸ 저장소 액세스</target>
+<target>온ë¼ì¸ 저장소 ì ‘ì†</target>
<source>Save as a Batch Job</source>
<target>ì¼ê´„ 작업으로 저장</target>
@@ -1453,11 +1468,14 @@ This guarantees a consistent state even in case of a serious error.
<target>옵션</target>
<source>Select Time Span</source>
-<target>시간간격(타임스팬) ì„ íƒ</target>
+<target>시간 범위 ì„ íƒ</target>
<source>Highlight Configurations</source>
<target>강조 표시 구성</target>
+<source>Grant Full Disk Access</source>
+<target>ì „ì²´ ë””ìŠ¤í¬ ì ‘ê·¼ 권한 부여</target>
+
<source>Info</source>
<target>ì •ë³´</target>
@@ -1557,7 +1575,7 @@ This guarantees a consistent state even in case of a serious error.
<target>í¬ê²Œ</target>
<source>Select time span...</source>
-<target>시간간격(타임스팬) ì„ íƒ...</target>
+<target>시간 범위 ì„ íƒ...</target>
<source>Donation Edition</source>
<target>ê¸°ë¶€ìž ì—디션</target>
diff --git a/FreeFileSync/Build/Resources/Languages/lithuanian.lng b/FreeFileSync/Build/Resources/Languages/lithuanian.lng
index c2e84a42..649cc05e 100755
--- a/FreeFileSync/Build/Resources/Languages/lithuanian.lng
+++ b/FreeFileSync/Build/Resources/Languages/lithuanian.lng
@@ -390,12 +390,12 @@ Esamas: %y baitai
<source>Database file is corrupted:</source>
<target>Duomenų bazės failas yra sugadintas:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Duomenų bazės failas dar neturi informacijos apie paskutinį suvienodinimą.</target>
-
<source>Loading file %x...</source>
<target>Įkraunamas failas %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Duomenų bazės failas dar neturi informacijos apie paskutinį suvienodinimą.</target>
+
<source>Saving file %x...</source>
<target>IÅ¡saugomas failas %x...</target>
@@ -719,14 +719,14 @@ Esamas: %y baitai
<source>Usage:</source>
<target>Naudojimas:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Pasirinkite stebimus aplankus.</target>
+<source>Select folders to watch.</source>
+<target>Pasirinkite stebimus aplankus.</target>
-<source>2. Enter a command line.</source>
-<target>2. Įvesti komandinę eilutę.</target>
+<source>Enter a command line.</source>
+<target>Įvesti komandinę eilutę.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Spauskite „Pradėti“'.</target>
+<source>Press 'Start'.</source>
+<target>Spauskite „Pradėti“'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Kad pradÄ—ti tiesiog importuokite "ffs_batch" failÄ….</target>
@@ -1427,14 +1427,14 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Aktyvuoti FreeFileSync Donoro VersijÄ…, naudojant vienÄ… iÅ¡ sekanÄių metodų:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Aktyvuoti dabar per internetÄ…:</target>
+<source>Activate via internet now:</source>
+<target>Aktyvuoti dabar per internetÄ…:</target>
<source>Activate online</source>
<target>Actyvuoti per internetÄ…</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Gauti aktyvavimo raktą, kuris veikia neprisijungus, iš šio URL:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Gauti aktyvavimo raktą, kuris veikia neprisijungus, iš šio URL:</target>
<source>&Copy to clipboard</source>
<target>&Kopijuoti į mainų sritį</target>
@@ -1448,6 +1448,21 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Paryškinti parametrus kurie nebuvo vykdomi daugiau nei dienų:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>Kai suvienodina jūsų duomenis, FreeFileSync reikia priegos teisių, (pvz.: paštas, žinutės, kalendorius), kad išvengtų klaidos "Operacija neleidžiama".</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Surasti FreeFileSync programÄ…</target>
+
+<source>Open Security && Privacy</source>
+<target>Atversti Apsuaga && Privatumas</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Įgalinti pakeitimus spausti užraktą.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Tempti FreeFileSync į panelę.</target>
+
<source>Synchronization Settings</source>
<target>Suvienodinimo parametrai</target>
@@ -1472,6 +1487,9 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Highlight Configurations</source>
<target>Pažymėti Nustatymus</target>
+<source>Grant Full Disk Access</source>
+<target>Sutekti PilnÄ… Disko PrieigÄ…</target>
+
<source>Info</source>
<target>Informacija</target>
diff --git a/FreeFileSync/Build/Resources/Languages/norwegian.lng b/FreeFileSync/Build/Resources/Languages/norwegian.lng
index 6817a571..3c2df217 100755
--- a/FreeFileSync/Build/Resources/Languages/norwegian.lng
+++ b/FreeFileSync/Build/Resources/Languages/norwegian.lng
@@ -387,12 +387,12 @@ Faktisk: %y bytes
<source>Database file is corrupted:</source>
<target>Databasefilen er skadet:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Databasefilene inneholder ennå ikke informasjon om siste synkronisering.</target>
-
<source>Loading file %x...</source>
<target>Laster fil %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Databasefilene inneholder ennå ikke informasjon om siste synkronisering.</target>
+
<source>Saving file %x...</source>
<target>Lagrer filen %x...</target>
@@ -714,14 +714,14 @@ Faktisk: %y bytes
<source>Usage:</source>
<target>Bruk:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Velg mapper å se på.</target>
+<source>Select folders to watch.</source>
+<target>Velg mapper å se på.</target>
-<source>2. Enter a command line.</source>
-<target>2. Skriv inn en kommando.</target>
+<source>Enter a command line.</source>
+<target>Skriv inn en kommando.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Klikk 'Start'.</target>
+<source>Press 'Start'.</source>
+<target>Klikk 'Start'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Importer en "ffs_batch"fil for å komme igang.</target>
@@ -1420,14 +1420,14 @@ Sikrer prosessen ved alvorlige feil.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Aktivér FreeFileSync Donation Versjon med en av følgende metoder:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Aktivér via internett nå:</target>
+<source>Activate via internet now:</source>
+<target>Aktivér via internett nå:</target>
<source>Activate online</source>
<target>Aktivér på nett</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Hent en aktiveringsnøkkel fra følgende URL:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Hent en aktiveringsnøkkel fra følgende URL:</target>
<source>&Copy to clipboard</source>
<target>&Kopiér til utklippstavla</target>
@@ -1441,6 +1441,21 @@ Sikrer prosessen ved alvorlige feil.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Uthev konfigurasjoner som ikke har blitt kjørt for mer enn følgende antall dager:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync krever tilgangsrettigheter for å unngå feil "Operasjon ikke tillatt" når du synkroniserer dataene dine (f.eks. E-post, Meldinger, Kalendere).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Finn FreeFileSync-appen</target>
+
+<source>Open Security && Privacy</source>
+<target>Ã…pne sikkerhet og personvern</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Klikk på låsen for å tillate endringer.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Dra FreeFileSync inn i panelet.</target>
+
<source>Synchronization Settings</source>
<target>Synkroniserings-innstillinger</target>
@@ -1465,6 +1480,9 @@ Sikrer prosessen ved alvorlige feil.
<source>Highlight Configurations</source>
<target>Uthev konfigurasjoner</target>
+<source>Grant Full Disk Access</source>
+<target>Gi full disktilgang</target>
+
<source>Info</source>
<target>Info</target>
diff --git a/FreeFileSync/Build/Resources/Languages/polish.lng b/FreeFileSync/Build/Resources/Languages/polish.lng
index eb79f424..9f71bd6f 100755
--- a/FreeFileSync/Build/Resources/Languages/polish.lng
+++ b/FreeFileSync/Build/Resources/Languages/polish.lng
@@ -390,12 +390,12 @@ Przesłany: %y bajtów
<source>Database file is corrupted:</source>
<target>Plik bazy danych jest uszkodzony:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Pliki bazy danych nie posiadają żadnych informacji o ostatniej synchronizacji.</target>
-
<source>Loading file %x...</source>
<target>Wczytywanie pliku %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Pliki bazy danych nie posiadają żadnych informacji o ostatniej synchronizacji.</target>
+
<source>Saving file %x...</source>
<target>Zapisywanie pliku %x...</target>
@@ -719,14 +719,14 @@ Przesłany: %y bajtów
<source>Usage:</source>
<target>Użycie:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Określ obserwowane katalogi.</target>
+<source>Select folders to watch.</source>
+<target>Określ obserwowane katalogi.</target>
-<source>2. Enter a command line.</source>
-<target>2. Wprowadź komendę.</target>
+<source>Enter a command line.</source>
+<target>Wprowadź komendę.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Wciśnij 'Start'.</target>
+<source>Press 'Start'.</source>
+<target>Wciśnij 'Start'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Aby rozpocząć, zaimportuj plik "ffs_batch".</target>
@@ -1427,14 +1427,14 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Aktywuj FreeFileSync Donation Edition jedną z poniższych metod:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Aktywuj przez internet:</target>
+<source>Activate via internet now:</source>
+<target>Aktywuj przez internet:</target>
<source>Activate online</source>
<target>Aktywuj online</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Pobierz offline'owy klucz aktywacyjny z poniższego adresu:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Pobierz offline'owy klucz aktywacyjny z poniższego adresu:</target>
<source>&Copy to clipboard</source>
<target>&Kopiuj do schowka</target>
@@ -1448,6 +1448,21 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Pokaż konfiguracje, które nie były uruchamiane więcej niż następująca liczba dni:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync wymaga uprawnień, aby uniknąć błędów „Operacja niedozwolona†podczas synchronizacji danych (np. Poczta, Wiadomości, Kalendarze).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Znajdź aplikację FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Otwórz Ochrona i prywatność</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Kliknij blokadę, aby zezwolić na zmiany.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>PrzeciÄ…gnij FreeFileSync do panelu.</target>
+
<source>Synchronization Settings</source>
<target>Ustawienia synchronizacji</target>
@@ -1472,6 +1487,9 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Highlight Configurations</source>
<target>Pokaż konfiguracje</target>
+<source>Grant Full Disk Access</source>
+<target>Udziel pełnego dostępu do dysku</target>
+
<source>Info</source>
<target>Info</target>
diff --git a/FreeFileSync/Build/Resources/Languages/portuguese.lng b/FreeFileSync/Build/Resources/Languages/portuguese.lng
index afd9ab8d..48919fec 100755
--- a/FreeFileSync/Build/Resources/Languages/portuguese.lng
+++ b/FreeFileSync/Build/Resources/Languages/portuguese.lng
@@ -387,12 +387,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>O ficheiro da base de dados está corrompido:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Os ficheiros da base de dados ainda não contém informações sobre a última sincronização.</target>
-
<source>Loading file %x...</source>
<target>A carregar arquivo %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Os ficheiros da base de dados ainda não contém informações sobre a última sincronização.</target>
+
<source>Saving file %x...</source>
<target>A guardar ficheiro %x...</target>
@@ -477,6 +477,27 @@ Actual: %y bytes
<source>Update attributes on right</source>
<target>Actualizar atributos à direita</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Erro ao analisar ficheiro %x, linha %y, coluna %z.</target>
+
+<source>Services</source>
+<target>Serviços</target>
+
+<source>Show All</source>
+<target>Mostrar Todos</target>
+
+<source>Hide Others</source>
+<target>Ocultar Outros</target>
+
+<source>Hide %x</source>
+<target>Ocultar %x</target>
+
+<source>Quit %x</source>
+<target>Cerrar o %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>Incapaz de definir bloqueios de directórios para as seguintes pastas:</target>
+
<source>Errors:</source>
<target>Erros:</target>
@@ -501,27 +522,6 @@ Actual: %y bytes
<source>Cleaning up log files:</source>
<target>A limpar ficheiros de registo:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Erro ao analisar ficheiro %x, linha %y, coluna %z.</target>
-
-<source>Services</source>
-<target>Serviços</target>
-
-<source>Show All</source>
-<target>Mostrar Todos</target>
-
-<source>Hide Others</source>
-<target>Ocultar Outros</target>
-
-<source>Hide %x</source>
-<target>Ocultar %x</target>
-
-<source>Quit %x</source>
-<target>Cerrar o %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>Incapaz de definir bloqueios de directórios para as seguintes pastas:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -714,14 +714,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>Uso:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Seleccionar pastas para observar.</target>
+<source>Select folders to watch.</source>
+<target>Seleccionar pastas para observar.</target>
-<source>2. Enter a command line.</source>
-<target>2. Inserir a linha de comando.</target>
+<source>Enter a command line.</source>
+<target>Inserir a linha de comando.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Pressionar 'Iniciar'.</target>
+<source>Press 'Start'.</source>
+<target>Pressionar 'Iniciar'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Para começar basta importar um ficheiro "ffs_batch".</target>
@@ -1420,14 +1420,14 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Active o FreeFileSync Donation Edition por um dos seguintes métodos:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Active via Internet agora:</target>
+<source>Activate via internet now:</source>
+<target>Active via Internet agora:</target>
<source>Activate online</source>
<target>Activar online</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Obter uma chave de activação offline a partir da seguinte URL:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Obter uma chave de activação offline a partir da seguinte URL:</target>
<source>&Copy to clipboard</source>
<target>&Copiar da clipboard</target>
@@ -1441,11 +1441,26 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Realce configurações que não foram executadas pelo seguinte número de dias:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>O FreeFileSync necessita de direitos de acesso para evitar erros de "Operação não permitida" ao sincronizar seus dados (ex.: Mail, Mensagens, Calendários).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Localizar aplicativo FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Abrir Segurança e Privacidade</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Clique no cadeado para permitir as alterações.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Arraste o FreeFileSync para o painel.</target>
+
<source>Synchronization Settings</source>
<target>Definições de Sincronização</target>
<source>Access Online Storage</source>
-<target>Acessar Armazenamento Online</target>
+<target>Aceder Armazenamento Online</target>
<source>Save as a Batch Job</source>
<target>Salvar como Tarefa em Lote</target>
@@ -1465,6 +1480,9 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Highlight Configurations</source>
<target>Realçar Configurações</target>
+<source>Grant Full Disk Access</source>
+<target>Garantir Acesso completo ao disco</target>
+
<source>Info</source>
<target>Info</target>
@@ -1525,33 +1543,6 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>&Execute</source>
<target>&Executar</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>1 directório</pluralform>
-<pluralform>%x directórios</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>1 ficheiro</pluralform>
-<pluralform>%x ficheiros</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>A mostrar %y de 1 linha</pluralform>
-<pluralform>A mostrar %y de %x linhas</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Escolher direcção:</target>
@@ -1690,6 +1681,33 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>All files are in sync</source>
<target>Todos os ficheiros estão sincronizados</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>1 directório</pluralform>
+<pluralform>%x directórios</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>1 ficheiro</pluralform>
+<pluralform>%x ficheiros</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>A mostrar %y de 1 linha</pluralform>
+<pluralform>A mostrar %y de %x linhas</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>Não é possível descobrir %x</target>
diff --git a/FreeFileSync/Build/Resources/Languages/portuguese_br.lng b/FreeFileSync/Build/Resources/Languages/portuguese_br.lng
index d95d04d9..4054b167 100755
--- a/FreeFileSync/Build/Resources/Languages/portuguese_br.lng
+++ b/FreeFileSync/Build/Resources/Languages/portuguese_br.lng
@@ -387,12 +387,12 @@ Atual: %y bytes
<source>Database file is corrupted:</source>
<target>O arquivo de banco de dados está corrompido:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Os arquivos de banco de dados ainda não contêm informação sobre a última sincronização.</target>
-
<source>Loading file %x...</source>
<target>Carregando arquivo %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Os arquivos de banco de dados ainda não contêm informação sobre a última sincronização.</target>
+
<source>Saving file %x...</source>
<target>Salvando arquivo %x...</target>
@@ -714,14 +714,14 @@ Atual: %y bytes
<source>Usage:</source>
<target>Uso:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Selecionar as pastas para monitorar.</target>
+<source>Select folders to watch.</source>
+<target>Selecionar as pastas para monitorar.</target>
-<source>2. Enter a command line.</source>
-<target>2. Inserir uma linha de comando.</target>
+<source>Enter a command line.</source>
+<target>Inserir uma linha de comando.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Pressionar 'Iniciar'.</target>
+<source>Press 'Start'.</source>
+<target>Pressionar 'Iniciar'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Para iniciar importe um arquivo "ffs_batch".</target>
@@ -1420,14 +1420,14 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Ative o FreeFileSync Edição do Doador por algum dos seguintes métodos:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Ativar pela internet agora:</target>
+<source>Activate via internet now:</source>
+<target>Ativar pela internet agora:</target>
<source>Activate online</source>
<target>Ativar online</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Recuperar uma chave de ativação offline a partir da seguinte URL:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Recuperar uma chave de ativação offline a partir da seguinte URL:</target>
<source>&Copy to clipboard</source>
<target>&Copiar para área de transferência</target>
@@ -1441,6 +1441,21 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Realçar configurações que não foram executadas a mais do seguinte número de dias:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync requer direitos de acesso para evitar erros de "Operação não permitida" quando sincronizando seus dados (ex.: Mail, Mensagens, Calendário).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Localize o app FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Abra Segurança e Privacidade</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Clique no cadeado para permitir alterações.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Arraste FreeFileSync para o painel.</target>
+
<source>Synchronization Settings</source>
<target>Configurações de Sincronização</target>
@@ -1465,6 +1480,9 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Highlight Configurations</source>
<target>Realçar Configurações</target>
+<source>Grant Full Disk Access</source>
+<target>Conceder Acesso Total ao Disco</target>
+
<source>Info</source>
<target>Informações</target>
diff --git a/FreeFileSync/Build/Resources/Languages/romanian.lng b/FreeFileSync/Build/Resources/Languages/romanian.lng
index fe270fee..9d6bddfd 100755
--- a/FreeFileSync/Build/Resources/Languages/romanian.lng
+++ b/FreeFileSync/Build/Resources/Languages/romanian.lng
@@ -390,12 +390,12 @@ Actuală: %y baiți
<source>Database file is corrupted:</source>
<target>Fila bazei de date este stricată (coruptă):</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Filele cu baze de date nu conțin încă informații despre ultima sincornizare.</target>
-
<source>Loading file %x...</source>
<target>Deschid fila %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Filele cu baze de date nu conțin încă informații despre ultima sincornizare.</target>
+
<source>Saving file %x...</source>
<target>Salvez fila %x...</target>
@@ -719,14 +719,14 @@ Actuală: %y baiți
<source>Usage:</source>
<target>Utilizare:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Selectează dosarele de monitorizat.</target>
+<source>Select folders to watch.</source>
+<target>Selectează dosarele de monitorizat.</target>
-<source>2. Enter a command line.</source>
-<target>2. Scrie calea.</target>
+<source>Enter a command line.</source>
+<target>Scrie calea.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Apasă pe 'Pornește'.</target>
+<source>Press 'Start'.</source>
+<target>Apasă pe 'Pornește'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Pentru a începe, importă o filă de tipul "ffs_batch".</target>
@@ -1427,14 +1427,14 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Activează FreeFileSync Ediția pentru Donatori prin una din următoarele metode:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Activatează prin internet acum:</target>
+<source>Activate via internet now:</source>
+<target>Activatează prin internet acum:</target>
<source>Activate online</source>
<target>Activatează prin internet [online]</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Preia o cheie de activare neconectată [offline] de la adresa următoare:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Preia o cheie de activare neconectată [offline] de la adresa următoare:</target>
<source>&Copy to clipboard</source>
<target>&Copiază în cliplanșetă [clipboard]</target>
@@ -1448,6 +1448,21 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Evidențiază configurațiile care n-au fost rulate de mai multă vreme decît numărul de zile următor:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync necesită drepturi de acces pentru a evita erorile de tip "Operație nepermisă" la sincronizarea datelor (de ex. la Mail, Mesaje, Calendare).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Localizează aplicația FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Deschide Securitate și intimitate</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Clichează pe lacăt pentru a permite schimbările.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Trage FreeFileSync în panou.</target>
+
<source>Synchronization Settings</source>
<target>Setările Sincronizării</target>
@@ -1472,6 +1487,9 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Highlight Configurations</source>
<target>Evidențiază Configurațiile</target>
+<source>Grant Full Disk Access</source>
+<target>Permite Acces complet la disc</target>
+
<source>Info</source>
<target>Informații</target>
diff --git a/FreeFileSync/Build/Resources/Languages/russian.lng b/FreeFileSync/Build/Resources/Languages/russian.lng
index 1330bb21..6c1836e4 100755
--- a/FreeFileSync/Build/Resources/Languages/russian.lng
+++ b/FreeFileSync/Build/Resources/Languages/russian.lng
@@ -390,12 +390,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>Файл базы данных поврежден:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Файлы базы данных еще не Ñодержат информацию о поÑледней Ñинхронизации.</target>
-
<source>Loading file %x...</source>
<target>Загрузка файла %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Файлы базы данных еще не Ñодержат информацию о поÑледней Ñинхронизации.</target>
+
<source>Saving file %x...</source>
<target>Сохранение файла %x...</target>
@@ -481,6 +481,27 @@ Actual: %y bytes
<source>Update attributes on right</source>
<target>Обновление атрибутов Ñправа</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Ошибка при разборе файла %x, Ñтрока %y, колонка %z.</target>
+
+<source>Services</source>
+<target>Службы</target>
+
+<source>Show All</source>
+<target>Показать вÑе</target>
+
+<source>Hide Others</source>
+<target>Скрыть оÑтальные</target>
+
+<source>Hide %x</source>
+<target>Скрыть %x</target>
+
+<source>Quit %x</source>
+<target>Выйти из %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>Ðевозможно уÑтановить блокировки Ð´Ð»Ñ Ñледующих папок:</target>
+
<source>Errors:</source>
<target>Ошибки:</target>
@@ -505,27 +526,6 @@ Actual: %y bytes
<source>Cleaning up log files:</source>
<target>ОчиÑтка файлов журнала:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Ошибка при разборе файла %x, Ñтрока %y, колонка %z.</target>
-
-<source>Services</source>
-<target>Службы</target>
-
-<source>Show All</source>
-<target>Показать вÑе</target>
-
-<source>Hide Others</source>
-<target>Скрыть оÑтальные</target>
-
-<source>Hide %x</source>
-<target>Скрыть %x</target>
-
-<source>Quit %x</source>
-<target>Выйти из %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>Ðевозможно уÑтановить блокировки Ð´Ð»Ñ Ñледующих папок:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -719,14 +719,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>ИнÑтрукциÑ:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Выберите папки Ð´Ð»Ñ Ð½Ð°Ð±Ð»ÑŽÐ´ÐµÐ½Ð¸Ñ.</target>
+<source>Select folders to watch.</source>
+<target>Выберите папки Ð´Ð»Ñ Ð½Ð°Ð±Ð»ÑŽÐ´ÐµÐ½Ð¸Ñ.</target>
-<source>2. Enter a command line.</source>
-<target>2. Введите командную Ñтроку.</target>
+<source>Enter a command line.</source>
+<target>Введите командную Ñтроку.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Ðажмите 'Старт'.</target>
+<source>Press 'Start'.</source>
+<target>Ðажмите 'Старт'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Ð”Ð»Ñ Ð·Ð°Ð¿ÑƒÑка проÑто импортируйте файл "ffs_batch".</target>
@@ -1427,14 +1427,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Ðктивировать платную верÑию FreeFileSync одним из Ñледующих ÑпоÑобов:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Ðктивировать через интернет ÑейчаÑ:</target>
+<source>Activate via internet now:</source>
+<target>Ðктивировать через интернет ÑейчаÑ:</target>
<source>Activate online</source>
<target>Ðктивировать онлайн</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Получить автономный ключ активации Ñо Ñледующего URL-адреÑа:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Получить автономный ключ активации Ñо Ñледующего URL-адреÑа:</target>
<source>&Copy to clipboard</source>
<target>&Копировать в буфер обмена</target>
@@ -1448,6 +1448,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Выделить конфигурации, которые не запуÑкалиÑÑŒ больше указанного количеÑтва дней:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync требует права доÑтупа, чтобы избежать ошибки "ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½Ðµ разрешена" при Ñинхронизации ваших данных (например, Почты, Сообщений, Календарей).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Ðайдите приложение FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Откройте Защита и безопаÑноÑÑ‚ÑŒ</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Ðажмите на замок, чтобы разрешить изменениÑ.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Перетащите FreeFileSync на панель.</target>
+
<source>Synchronization Settings</source>
<target>ÐаÑтройки Ñинхронизации</target>
@@ -1472,6 +1487,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>Выделение конфигураций</target>
+<source>Grant Full Disk Access</source>
+<target>ПредоÑтавить ДоÑтуп к диÑку</target>
+
<source>Info</source>
<target>ИнформациÑ</target>
@@ -1533,36 +1551,6 @@ This guarantees a consistent state even in case of a serious error.
<source>&Execute</source>
<target>&Выполнить</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>%x папка</pluralform>
-<pluralform>%x папки</pluralform>
-<pluralform>%x папок</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>%x файл</pluralform>
-<pluralform>%x файла</pluralform>
-<pluralform>%x файлов</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>Показано %y из %x Ñтроки</pluralform>
-<pluralform>Показано %y из %x Ñтрок</pluralform>
-<pluralform>Показано %y из %x Ñтрок</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Выберите направление:</target>
@@ -1701,6 +1689,36 @@ This guarantees a consistent state even in case of a serious error.
<source>All files are in sync</source>
<target>Ð’Ñе файлы Ñинхронизированы</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>%x папка</pluralform>
+<pluralform>%x папки</pluralform>
+<pluralform>%x папок</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>%x файл</pluralform>
+<pluralform>%x файла</pluralform>
+<pluralform>%x файлов</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>Показано %y из %x Ñтроки</pluralform>
+<pluralform>Показано %y из %x Ñтрок</pluralform>
+<pluralform>Показано %y из %x Ñтрок</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>Ðевозможно найти %x</target>
diff --git a/FreeFileSync/Build/Resources/Languages/slovak.lng b/FreeFileSync/Build/Resources/Languages/slovak.lng
index 0d896e51..8d24625e 100755
--- a/FreeFileSync/Build/Resources/Languages/slovak.lng
+++ b/FreeFileSync/Build/Resources/Languages/slovak.lng
@@ -390,12 +390,12 @@ Aktuálne: %y b
<source>Database file is corrupted:</source>
<target>Databázový súbor je poškodený:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Databázový súbor ešte neobsahuje informácie o poslednej synchronizácií.</target>
-
<source>Loading file %x...</source>
<target>NaÄítavanie súboru %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Databázový súbor ešte neobsahuje informácie o poslednej synchronizácií.</target>
+
<source>Saving file %x...</source>
<target>Ukládanie súboru %x...</target>
@@ -719,14 +719,14 @@ Aktuálne: %y b
<source>Usage:</source>
<target>Použitie:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Vyberte prieÄinok k sledovaniu.</target>
+<source>Select folders to watch.</source>
+<target>Vyberte prieÄinok k sledovaniu.</target>
-<source>2. Enter a command line.</source>
-<target>2. Zadajte príkazový riadok.</target>
+<source>Enter a command line.</source>
+<target>Zadajte príkazový riadok.</target>
-<source>3. Press 'Start'.</source>
-<target>3. StlaÄte 'Å tart'.</target>
+<source>Press 'Start'.</source>
+<target>StlaÄte 'Å tart'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Môžete naÄítaÅ¥ tiež konfiguraÄný súbor "ffs_batch".</target>
@@ -1424,13 +1424,13 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>aktivovať FreeFileSync Donation Edition pomocou jednej z nasledujúcich metód:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Aktivovať teraz cez internet:</target>
+<source>Activate via internet now:</source>
+<target>Aktivovať teraz cez internet:</target>
<source>Activate online</source>
<target>Aktivovať online</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
+<source>Retrieve an offline activation key from the following URL:</source>
<target>ZískaÅ¥ offline aktivaÄný kÄ¾ÃºÄ z URL adresy:</target>
<source>&Copy to clipboard</source>
@@ -1445,6 +1445,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>ZvýrazniÅ¥ konfigurácie, ktoré neboli vykonané viac ako nasledujúci poÄet dní:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync potrebuje povolenie pre prístup aby prediÅ¡lo "Operácia nie je povolená" chybám poÄas synchronizácie dát (napr. Email, Správy, Kalendár).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Lokalizovať FreeFileSync aplikáciou</target>
+
+<source>Open Security && Privacy</source>
+<target>OtvoriÅ¥ BezpeÄnosÅ¥ a súkromie</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Pre povolenie zmien klikni na zámok.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Potiahni FreeFileSync na panel.</target>
+
<source>Synchronization Settings</source>
<target>Nastavenia synchronizácie</target>
@@ -1469,6 +1484,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>Zvýrazniť konfigurácie</target>
+<source>Grant Full Disk Access</source>
+<target>Povoliť plný prístup k disku</target>
+
<source>Info</source>
<target>Info</target>
diff --git a/FreeFileSync/Build/Resources/Languages/slovenian.lng b/FreeFileSync/Build/Resources/Languages/slovenian.lng
index 6deee301..6e81849b 100755
--- a/FreeFileSync/Build/Resources/Languages/slovenian.lng
+++ b/FreeFileSync/Build/Resources/Languages/slovenian.lng
@@ -393,12 +393,12 @@ Dejansko: %y bajtov
<source>Database file is corrupted:</source>
<target>Datoteka v bazi podatkov je poškodovana:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Datoteke v bazi podatkov Å¡e ne vsebujejo informacije o zadnji sinhronizaciji.</target>
-
<source>Loading file %x...</source>
<target>Nalagam datoteko %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Datoteke v bazi podatkov Å¡e ne vsebujejo informacije o zadnji sinhronizaciji.</target>
+
<source>Saving file %x...</source>
<target>Shranjevanje datoteke %x...</target>
@@ -485,6 +485,27 @@ Dejansko: %y bajtov
<source>Update attributes on right</source>
<target>Posodobi atribute na desni strani</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Napaka pri razÄlenjevanju datoteke %x, vrstica %y, stolpec %z.</target>
+
+<source>Services</source>
+<target>Storitve</target>
+
+<source>Show All</source>
+<target>Pokaži vse</target>
+
+<source>Hide Others</source>
+<target>Skrij druge</target>
+
+<source>Hide %x</source>
+<target>Skrij %x</target>
+
+<source>Quit %x</source>
+<target>Nehaj %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>Ne morem nastaviti zaklepanje imenika za naslednje mape:</target>
+
<source>Errors:</source>
<target>Napake:</target>
@@ -509,27 +530,6 @@ Dejansko: %y bajtov
<source>Cleaning up log files:</source>
<target>ÄŒiÅ¡Äenje datotek dnevnika:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Napaka pri razÄlenjevanju datoteke %x, vrstica %y, stolpec %z.</target>
-
-<source>Services</source>
-<target>Storitve</target>
-
-<source>Show All</source>
-<target>Pokaži vse</target>
-
-<source>Hide Others</source>
-<target>Skrij druge</target>
-
-<source>Hide %x</source>
-<target>Skrij %x</target>
-
-<source>Quit %x</source>
-<target>Nehaj %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>Ne morem nastaviti zaklepanje imenika za naslednje mape:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -724,14 +724,14 @@ Dejansko: %y bajtov
<source>Usage:</source>
<target>Uporaba:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Izberite mape za opazovanje.</target>
+<source>Select folders to watch.</source>
+<target>Izberite mape za opazovanje.</target>
-<source>2. Enter a command line.</source>
-<target>2. Vnesite ukazno vrstico.</target>
+<source>Enter a command line.</source>
+<target>Vnesite ukazno vrstico.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Pritisnite 'ZaÄni'.</target>
+<source>Press 'Start'.</source>
+<target>Pritisnite 'ZaÄni'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>ÄŒe želite zaÄeti, uvozite datoteko "ffs_batch".</target>
@@ -1434,14 +1434,14 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Aktivirajte FreeFileSync Donation Edition na en od naslednjih naÄinov:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Aktivirajte preko interneta zdaj:</target>
+<source>Activate via internet now:</source>
+<target>Aktivirajte preko interneta zdaj:</target>
<source>Activate online</source>
<target>Aktivirajte po spletu</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Pridobite aktivacijski kljuÄ brez povezave na naslednjem URL:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Pridobite aktivacijski kljuÄ brez povezave na naslednjem URL:</target>
<source>&Copy to clipboard</source>
<target>&Kopirajte v odložiÅ¡Äe</target>
@@ -1455,6 +1455,21 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>OznaÄite konfiguracije, ki se ne izvajajo veÄ kot naslednje Å¡tevilo dni:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync zahteva pravice dostopa, da se izognete napakam "Operacija ni dovoljena" pri sinhronizaciji vaÅ¡ih podatkov (npr. PoÅ¡ta, SporoÄila, Koledarji).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>PoiÅ¡Äite aplikacijo FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Odprite Varnost in Zasebnost</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Kliknite kljuÄavnico, da omogoÄite spremembe.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Povlecite FreeFileSync na panel.</target>
+
<source>Synchronization Settings</source>
<target>Nastavitve sinhnorizacije</target>
@@ -1479,6 +1494,9 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Highlight Configurations</source>
<target>OznaÄite konfiguracije</target>
+<source>Grant Full Disk Access</source>
+<target>Dovolite popoln dostop do diska</target>
+
<source>Info</source>
<target>Info</target>
@@ -1541,39 +1559,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>&Execute</source>
<target>&Izvedi</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>%x imenik</pluralform>
-<pluralform>%x imenika</pluralform>
-<pluralform>%x imeniki</pluralform>
-<pluralform>%x imenikov</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>%x datoteka</pluralform>
-<pluralform>%x datoteki</pluralform>
-<pluralform>%x datoteke</pluralform>
-<pluralform>%x datotek</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>Prikazano %y od %x vrstice</pluralform>
-<pluralform>Prikazano %y od %x vrstic</pluralform>
-<pluralform>Prikazano %y od %x vrstic</pluralform>
-<pluralform>Prikazano %y od %x vrstic</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Nastavi smer:</target>
@@ -1712,6 +1697,39 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>All files are in sync</source>
<target>Vse datoteke so sinhronizirane</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>%x imenik</pluralform>
+<pluralform>%x imenika</pluralform>
+<pluralform>%x imeniki</pluralform>
+<pluralform>%x imenikov</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>%x datoteka</pluralform>
+<pluralform>%x datoteki</pluralform>
+<pluralform>%x datoteke</pluralform>
+<pluralform>%x datotek</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>Prikazano %y od %x vrstice</pluralform>
+<pluralform>Prikazano %y od %x vrstic</pluralform>
+<pluralform>Prikazano %y od %x vrstic</pluralform>
+<pluralform>Prikazano %y od %x vrstic</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>Ne najdem %x</target>
diff --git a/FreeFileSync/Build/Resources/Languages/spanish.lng b/FreeFileSync/Build/Resources/Languages/spanish.lng
index 29d5ee3b..94f2eb41 100755
--- a/FreeFileSync/Build/Resources/Languages/spanish.lng
+++ b/FreeFileSync/Build/Resources/Languages/spanish.lng
@@ -387,12 +387,12 @@ Reales: %y bytes
<source>Database file is corrupted:</source>
<target>El archivo de base de datos está dañado:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Los archivos de datos aún no contienen información acerca de una sincronización anterior.</target>
-
<source>Loading file %x...</source>
<target>Cargando el archivo %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Los archivos de datos aún no contienen información acerca de una sincronización anterior.</target>
+
<source>Saving file %x...</source>
<target>Guardando archivo %x...</target>
@@ -477,6 +477,27 @@ Reales: %y bytes
<source>Update attributes on right</source>
<target>Actualizar atributos en la derecha</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Error analizando archivo %x, fila %y, columna %z.</target>
+
+<source>Services</source>
+<target>Servicios</target>
+
+<source>Show All</source>
+<target>Mostrar todo</target>
+
+<source>Hide Others</source>
+<target>Ocultar otros</target>
+
+<source>Hide %x</source>
+<target>Ocultar %x</target>
+
+<source>Quit %x</source>
+<target>Salir de %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>No se pudieron bloquear directorios para las carpetas siguientes:</target>
+
<source>Errors:</source>
<target>Errores:</target>
@@ -501,27 +522,6 @@ Reales: %y bytes
<source>Cleaning up log files:</source>
<target>Limpiando archivos de registro:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Error analizando archivo %x, fila %y, columna %z.</target>
-
-<source>Services</source>
-<target>Servicios</target>
-
-<source>Show All</source>
-<target>Mostrar todo</target>
-
-<source>Hide Others</source>
-<target>Ocultar otros</target>
-
-<source>Hide %x</source>
-<target>Ocultar %x</target>
-
-<source>Quit %x</source>
-<target>Salir de %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>No se pudieron bloquear directorios para las carpetas siguientes:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -714,14 +714,14 @@ Reales: %y bytes
<source>Usage:</source>
<target>Uso:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Seleccionar carpetas para mostrar.</target>
+<source>Select folders to watch.</source>
+<target>Seleccionar carpetas para mostrar.</target>
-<source>2. Enter a command line.</source>
-<target>2. Introduzca una línea de comandos.</target>
+<source>Enter a command line.</source>
+<target>Introduzca una línea de comandos.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Presione 'Inicio'.</target>
+<source>Press 'Start'.</source>
+<target>Haga clic en 'Inicio'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Para comenzar, importe un archivo "ffs_batch".</target>
@@ -1420,14 +1420,14 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Active FreeFileSync Donation Edition por uno de los métodos siguientes:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Activar por internet ahora:</target>
+<source>Activate via internet now:</source>
+<target>Activar por internet ahora:</target>
<source>Activate online</source>
<target>Activar en línea</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Recuperar una clave de activación sin conexión desde la dirección URL siguiente:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Recuperar una clave de activación sin conexión desde la dirección URL siguiente:</target>
<source>&Copy to clipboard</source>
<target>&Copiar al portapapeles</target>
@@ -1441,6 +1441,21 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Resaltar las configuraciones no utilizadas desde hace más del número de días:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync requiere derechos de acceso para evitar errores por "Operaciones no permitidas" al sincronizar sus datos (por ej. Mail, Messages, Calendars).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Localice la app FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Abra Seguridad y privacidad</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Haga clic en el candado para permitir los cambios.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Arrastre FreeFileSync dentro del panel.</target>
+
<source>Synchronization Settings</source>
<target>Opciones de sincronización</target>
@@ -1465,6 +1480,9 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Highlight Configurations</source>
<target>Resaltar configuraciones</target>
+<source>Grant Full Disk Access</source>
+<target>Dar acceso a total al disco</target>
+
<source>Info</source>
<target>Info</target>
@@ -1525,33 +1543,6 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>&Execute</source>
<target>&Ejecutar</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>1 directorio</pluralform>
-<pluralform>%x directorios</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>1 archivo</pluralform>
-<pluralform>%x archivos</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>Mostrando %y de 1 línea</pluralform>
-<pluralform>Mostrando %y de %x líneas</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Usar dirección:</target>
@@ -1690,6 +1681,33 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>All files are in sync</source>
<target>Todos los archivos están sincronizados</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>1 directorio</pluralform>
+<pluralform>%x directorios</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>1 archivo</pluralform>
+<pluralform>%x archivos</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>Mostrando %y de 1 línea</pluralform>
+<pluralform>Mostrando %y de %x líneas</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>No se puede encontrar %x</target>
diff --git a/FreeFileSync/Build/Resources/Languages/swedish.lng b/FreeFileSync/Build/Resources/Languages/swedish.lng
index cb1b2211..9e844a73 100755
--- a/FreeFileSync/Build/Resources/Languages/swedish.lng
+++ b/FreeFileSync/Build/Resources/Languages/swedish.lng
@@ -387,12 +387,12 @@ Aktuell: %y byte
<source>Database file is corrupted:</source>
<target>Databasfilen är skadad:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Datapasfilen innehåller ännu ingen information om senaste synkronisering.</target>
-
<source>Loading file %x...</source>
<target>Läser in filen %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Datapasfilen innehåller ännu ingen information om senaste synkronisering.</target>
+
<source>Saving file %x...</source>
<target>Sparar %x...</target>
@@ -477,6 +477,27 @@ Aktuell: %y byte
<source>Update attributes on right</source>
<target>Uppdatera attribut på höger sida</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Tolkningsfel på filen %x, rad %y, kolumn %z.</target>
+
+<source>Services</source>
+<target>Tjänster</target>
+
+<source>Show All</source>
+<target>Visa all</target>
+
+<source>Hide Others</source>
+<target>Dölj andra</target>
+
+<source>Hide %x</source>
+<target>Dölj %x</target>
+
+<source>Quit %x</source>
+<target>Avsluta %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>Kan inte ange mapplås för följanda mappar:</target>
+
<source>Errors:</source>
<target>Fel:</target>
@@ -501,27 +522,6 @@ Aktuell: %y byte
<source>Cleaning up log files:</source>
<target>Städar upp loggfiler:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Tolkningsfel på filen %x, rad %y, kolumn %z.</target>
-
-<source>Services</source>
-<target>Tjänster</target>
-
-<source>Show All</source>
-<target>Visa all</target>
-
-<source>Hide Others</source>
-<target>Dölj andra</target>
-
-<source>Hide %x</source>
-<target>Dölj %x</target>
-
-<source>Quit %x</source>
-<target>Avsluta %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>Kan inte ange mapplås för följanda mappar:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -714,14 +714,14 @@ Aktuell: %y byte
<source>Usage:</source>
<target>Användning:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Välj mapp att bevaka.</target>
+<source>Select folders to watch.</source>
+<target>Välj mapp att bevaka.</target>
-<source>2. Enter a command line.</source>
-<target>2. Mata in ett kommando.</target>
+<source>Enter a command line.</source>
+<target>Mata in ett kommando.</target>
-<source>3. Press 'Start'.</source>
-<target>3. Tryck 'Start'.</target>
+<source>Press 'Start'.</source>
+<target>Tryck 'Start'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Importera en "ffs_batch"-fil för att komma igång.</target>
@@ -1420,14 +1420,14 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Aktivera FreeFileSync Donation Edition med en av följande metoder:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Aktivera via internet nu:</target>
+<source>Activate via internet now:</source>
+<target>Aktivera via internet nu:</target>
<source>Activate online</source>
<target>Ativera online</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Hämta en offline-aktivering från följande URL:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Hämta en offline-aktivering från följande URL:</target>
<source>&Copy to clipboard</source>
<target>&Kopiera till urklipp</target>
@@ -1441,6 +1441,21 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Färgmarkera konfigurationer som inte har körts på mer än följande antal dagar:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync kräver åtkomsträttigheter för att undvika "Åtgärden tillåts inte", vid synkronisering av din data (t.ex. Mail, Meddelanden och Kalender).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Lokalisera programmet FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Öppna Säkerhet och integritet</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Klicka på låset för att tillåta ändringar.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>Dra och släpp FreeFileSync på panelen.</target>
+
<source>Synchronization Settings</source>
<target>Synkroniseringsinställningar</target>
@@ -1465,6 +1480,9 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Highlight Configurations</source>
<target>Färgmarkera konfigurationer</target>
+<source>Grant Full Disk Access</source>
+<target>Tilldela full skivtillgång</target>
+
<source>Info</source>
<target>Info</target>
@@ -1525,33 +1543,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>&Execute</source>
<target>&Kör</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>1 mapp</pluralform>
-<pluralform>%x mappar</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>1 fil</pluralform>
-<pluralform>%x filer</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>Visar %y av 1 rad</pluralform>
-<pluralform>Visar %y av %x rader</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Ange riktning:</target>
@@ -1690,6 +1681,33 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>All files are in sync</source>
<target>Alla filer är synkroniserade</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>1 mapp</pluralform>
+<pluralform>%x mappar</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>1 fil</pluralform>
+<pluralform>%x filer</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>Visar %y av 1 rad</pluralform>
+<pluralform>Visar %y av %x rader</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>Kan inte hitta %x</target>
diff --git a/FreeFileSync/Build/Resources/Languages/turkish.lng b/FreeFileSync/Build/Resources/Languages/turkish.lng
index 9c0171e3..4ce717ec 100755
--- a/FreeFileSync/Build/Resources/Languages/turkish.lng
+++ b/FreeFileSync/Build/Resources/Languages/turkish.lng
@@ -387,12 +387,12 @@ Gerçekleşen: %y bayt
<source>Database file is corrupted:</source>
<target>Veritabanı dosyası bozulmuş:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Veritabanı dosyalarında henüz son eşitleme bilgileri yok.</target>
-
<source>Loading file %x...</source>
<target>%x dosyaları yükleniyor...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Veritabanı dosyalarında henüz son eşitleme bilgileri yok.</target>
+
<source>Saving file %x...</source>
<target>%x dosyası kaydediliyor...</target>
@@ -714,14 +714,14 @@ Gerçekleşen: %y bayt
<source>Usage:</source>
<target>Kullanım:</target>
-<source>1. Select folders to watch.</source>
-<target>1. İzlenecek klasörleri seçin.</target>
+<source>Select folders to watch.</source>
+<target>İzlenecek klasörleri seçin.</target>
-<source>2. Enter a command line.</source>
-<target>2. Bir satır komutu yazın.</target>
+<source>Enter a command line.</source>
+<target>Bir satır komutu yazın.</target>
-<source>3. Press 'Start'.</source>
-<target>3. 'Başlat' düğmesine tıklayın.</target>
+<source>Press 'Start'.</source>
+<target>'Başlat' düğmesine tıklayın.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>"ffs_batch" dosyasını yükleyerek başlayabilirsiniz.</target>
@@ -1420,13 +1420,13 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>FreeFileSync Bağış Sürümü şu yöntemlerden biri ile etkinleştirilebilir:</target>
-<source>1. Activate via internet now:</source>
-<target>1. İnternet üzerinden etkinleştirme:</target>
+<source>Activate via internet now:</source>
+<target>İnternet üzerinden etkinleştirme:</target>
<source>Activate online</source>
<target>Çevrimiçi Etkinleştir</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
+<source>Retrieve an offline activation key from the following URL:</source>
<target>Şu adresten bir çevrimdışı etkinleştirme anahtarı alarak:</target>
<source>&Copy to clipboard</source>
@@ -1441,6 +1441,21 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Şu kadar gündür çalıştırılmayan yapılandırmalar vurgulansın:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync uygulaması verilerinizi (Posta, İletiler, Takvim gibi) eşitlerken oluşabilecek "İşleme izin verilmiyor" hatalarıyla karşılaşmamak için Tam Disk Erişimi iznine gerek duyuyor.</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>FreeFileSync uygulamasını seçin</target>
+
+<source>Open Security && Privacy</source>
+<target>Güvenlik ve Gizlilik ayarlarını açın</target>
+
+<source>Click the lock to allow changes.</source>
+<target>Değişiklik yapmak için kilit üzerine tıklayın.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>FreeFileSync simgesini panoya sürükleyin.</target>
+
<source>Synchronization Settings</source>
<target>Eşitleme Ayarları</target>
@@ -1465,6 +1480,9 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Highlight Configurations</source>
<target>Yapılandırmalar Vurgulansın</target>
+<source>Grant Full Disk Access</source>
+<target>Tam Disk EriÅŸimi Ä°zni Verin</target>
+
<source>Info</source>
<target>Bilgi</target>
diff --git a/FreeFileSync/Build/Resources/Languages/ukrainian.lng b/FreeFileSync/Build/Resources/Languages/ukrainian.lng
index 5e0c211e..44979449 100755
--- a/FreeFileSync/Build/Resources/Languages/ukrainian.lng
+++ b/FreeFileSync/Build/Resources/Languages/ukrainian.lng
@@ -1,6 +1,6 @@
<header>
<language>УкраїнÑька</language>
- <translator>Lexxai</translator>
+ <translator>lexxai</translator>
<locale>uk_UA</locale>
<image>flag_ukraine.png</image>
<plural_count>3</plural_count>
@@ -390,12 +390,12 @@ Actual: %y bytes
<source>Database file is corrupted:</source>
<target>Файл бази даних пошкоджений:</target>
-<source>The database files do not yet contain information about the last synchronization.</source>
-<target>Файли бази даних не міÑÑ‚ÑÑ‚ÑŒ інформації про оÑтанню Ñинхронізацію.</target>
-
<source>Loading file %x...</source>
<target>ЗавантажуєтьÑÑ Ñ„Ð°Ð¹Ð» %x...</target>
+<source>The database files do not yet contain information about the last synchronization.</source>
+<target>Файли бази даних не міÑÑ‚ÑÑ‚ÑŒ інформації про оÑтанню Ñинхронізацію.</target>
+
<source>Saving file %x...</source>
<target>Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ %x...</target>
@@ -481,6 +481,27 @@ Actual: %y bytes
<source>Update attributes on right</source>
<target>Оновити атрибути праворуч</target>
+<source>Error parsing file %x, row %y, column %z.</source>
+<target>Помилка розбору файлу %x, Ñ€Ñдок %y, колонка %z.</target>
+
+<source>Services</source>
+<target>Служби</target>
+
+<source>Show All</source>
+<target>Показати УÑÑ–</target>
+
+<source>Hide Others</source>
+<target>Сховати Інші</target>
+
+<source>Hide %x</source>
+<target>Сховати %x</target>
+
+<source>Quit %x</source>
+<target>Вихід %x</target>
+
+<source>Cannot set directory locks for the following folders:</source>
+<target>Ðеможливо вÑтановити Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ñ–Ð² Ð´Ð»Ñ Ñ‚Ð°ÐºÐ¸Ñ… папок:</target>
+
<source>Errors:</source>
<target>Помилки:</target>
@@ -505,27 +526,6 @@ Actual: %y bytes
<source>Cleaning up log files:</source>
<target>ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð² журналу:</target>
-<source>Error parsing file %x, row %y, column %z.</source>
-<target>Помилка розбору файлу %x, Ñ€Ñдок %y, колонка %z.</target>
-
-<source>Services</source>
-<target>Служби</target>
-
-<source>Show All</source>
-<target>Показати УÑÑ–</target>
-
-<source>Hide Others</source>
-<target>Сховати Інші</target>
-
-<source>Hide %x</source>
-<target>Сховати %x</target>
-
-<source>Quit %x</source>
-<target>Вихід %x</target>
-
-<source>Cannot set directory locks for the following folders:</source>
-<target>Ðеможливо вÑтановити Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ñ–Ð² Ð´Ð»Ñ Ñ‚Ð°ÐºÐ¸Ñ… папок:</target>
-
<source>
<pluralform>1 thread</pluralform>
<pluralform>%x threads</pluralform>
@@ -660,7 +660,7 @@ Actual: %y bytes
<target>ДеÑкі файли були Ñинхронізовані Ñк чаÑтина декількох оÑновних папок.</target>
<source>To avoid conflicts, set up exclude filters so that each updated file is included by only one base folder.</source>
-<target></target>
+<target>Щоб уникнути конфліктів, вÑтановіть Ð²Ð¸ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ„Ñ–Ð»ÑŒÑ‚Ñ€Ñ–Ð², щоб кожен оновлений файл міÑтив лише одну базову папку.</target>
<source>Versioning folder:</source>
<target>Папка з верÑÑ–Ñми:</target>
@@ -719,14 +719,14 @@ Actual: %y bytes
<source>Usage:</source>
<target>ВикориÑтаннÑ:</target>
-<source>1. Select folders to watch.</source>
-<target>1. Виберіть папки Ð´Ð»Ñ Ð¼Ð¾Ð½Ñ–Ñ‚Ð¾Ñ€Ð¸Ð½Ð³Ñƒ.</target>
+<source>Select folders to watch.</source>
+<target>Виберіть папки Ð´Ð»Ñ Ð¼Ð¾Ð½Ñ–Ñ‚Ð¾Ñ€Ð¸Ð½Ð³Ñƒ.</target>
-<source>2. Enter a command line.</source>
-<target>2. Введіть Ñ€Ñдок команди.</target>
+<source>Enter a command line.</source>
+<target>Введіть Ñ€Ñдок команди.</target>
-<source>3. Press 'Start'.</source>
-<target>3. ÐатиÑніть 'ЗапуÑк'.</target>
+<source>Press 'Start'.</source>
+<target>ÐатиÑніть 'ЗапуÑк'.</target>
<source>To get started just import a "ffs_batch" file.</source>
<target>Щоб запуÑтити імпортуйте "ffs_batch" файл.</target>
@@ -828,7 +828,7 @@ The command is triggered if:
<target>СкануваннÑ...</target>
<source>configuration file</source>
-<target></target>
+<target>файл конфігурації</target>
<source>System: Sleep</source>
<target>СиÑтема: Сон</target>
@@ -1042,10 +1042,10 @@ The command is triggered if:
<target>Враховувати регіÑÑ‚Ñ€</target>
<source>Processed:</source>
-<target></target>
+<target>Оброблено:</target>
<source>Remaining:</source>
-<target></target>
+<target>ЗалишилоÑÑ:</target>
<source>New</source>
<target>Ðова</target>
@@ -1284,10 +1284,10 @@ The command is triggered if:
<target>Більше &не показувати цей діалог</target>
<source>Bytes:</source>
-<target></target>
+<target>Байтів:</target>
<source>Items:</source>
-<target></target>
+<target>Об'єктів:</target>
<source>Synchronizing...</source>
<target>СинхронізаціÑ...</target>
@@ -1427,14 +1427,14 @@ This guarantees a consistent state even in case of a serious error.
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
<target>Ðктивувати FreeFileSync Donation Edition за допомогою одного з наÑтупних методів:</target>
-<source>1. Activate via internet now:</source>
-<target>1. Ðктивувати через інтернет зараз:</target>
+<source>Activate via internet now:</source>
+<target>Ðктивувати через інтернет зараз:</target>
<source>Activate online</source>
<target>Ðктивувати online</target>
-<source>2. Retrieve an offline activation key from the following URL:</source>
-<target>2. Отримати ключ Ð´Ð»Ñ offline активації за допомогою наÑтупного поÑиланнÑ:</target>
+<source>Retrieve an offline activation key from the following URL:</source>
+<target>Отримати ключ Ð´Ð»Ñ offline активації за допомогою наÑтупного поÑиланнÑ:</target>
<source>&Copy to clipboard</source>
<target>&Копіювати в буфер обміну</target>
@@ -1448,6 +1448,21 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight configurations that have not been run for more than the following number of days:</source>
<target>Виділити конфігурації що не запуÑкалиÑÑŒ більше ніж наÑтупну кількіÑÑ‚ÑŒ днів:</target>
+<source>FreeFileSync requires access rights to avoid "Operation not permitted" errors when synchronizing your data (e.g. Mail, Messages, Calendars).</source>
+<target>FreeFileSync вимагає прав доÑтупу, щоб уникнути помилок "ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ дозволена" під Ñ‡Ð°Ñ Ñинхронізації ваших даних (наприклад, Пошти, Повідомлень, Календарів).</target>
+
+<source>Locate the FreeFileSync app</source>
+<target>Ð Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸ FreeFileSync</target>
+
+<source>Open Security && Privacy</source>
+<target>Відкрити ЗахиÑÑ‚ Ñ– безпека</target>
+
+<source>Click the lock to allow changes.</source>
+<target>ÐатиÑніть на замок, щоб дозволити зміни.</target>
+
+<source>Drag FreeFileSync into the panel.</source>
+<target>ПеретÑгніть FreeFileSync до панелі.</target>
+
<source>Synchronization Settings</source>
<target>ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ—</target>
@@ -1472,6 +1487,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Highlight Configurations</source>
<target>ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð´Ñ–Ð»ÐµÐ½Ð½Ñ</target>
+<source>Grant Full Disk Access</source>
+<target>ÐÐ°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ð½Ð¸Ð¹ доÑтуп до диÑку</target>
+
<source>Info</source>
<target>ІнформаціÑ</target>
@@ -1533,36 +1551,6 @@ This guarantees a consistent state even in case of a serious error.
<source>&Execute</source>
<target>&Виконати</target>
-<source>
-<pluralform>1 directory</pluralform>
-<pluralform>%x directories</pluralform>
-</source>
-<target>
-<pluralform>%x папка</pluralform>
-<pluralform>%x папки</pluralform>
-<pluralform>%x папок</pluralform>
-</target>
-
-<source>
-<pluralform>1 file</pluralform>
-<pluralform>%x files</pluralform>
-</source>
-<target>
-<pluralform>%x файл</pluralform>
-<pluralform>%x файли</pluralform>
-<pluralform>%x файлів</pluralform>
-</target>
-
-<source>
-<pluralform>Showing %y of 1 row</pluralform>
-<pluralform>Showing %y of %x rows</pluralform>
-</source>
-<target>
-<pluralform>Показано %y з %x Ñ€Ñдка</pluralform>
-<pluralform>Показано %y з %x Ñ€Ñдків</pluralform>
-<pluralform>Показано %y з %x Ñ€Ñдків</pluralform>
-</target>
-
<source>Set direction:</source>
<target>Виберіть напрÑм:</target>
@@ -1701,6 +1689,36 @@ This guarantees a consistent state even in case of a serious error.
<source>All files are in sync</source>
<target>Ð’ÑÑ– файли Ñинхронні</target>
+<source>
+<pluralform>1 directory</pluralform>
+<pluralform>%x directories</pluralform>
+</source>
+<target>
+<pluralform>%x папка</pluralform>
+<pluralform>%x папки</pluralform>
+<pluralform>%x папок</pluralform>
+</target>
+
+<source>
+<pluralform>1 file</pluralform>
+<pluralform>%x files</pluralform>
+</source>
+<target>
+<pluralform>%x файл</pluralform>
+<pluralform>%x файли</pluralform>
+<pluralform>%x файлів</pluralform>
+</target>
+
+<source>
+<pluralform>Showing %y of 1 row</pluralform>
+<pluralform>Showing %y of %x rows</pluralform>
+</source>
+<target>
+<pluralform>Показано %y з %x Ñ€Ñдка</pluralform>
+<pluralform>Показано %y з %x Ñ€Ñдків</pluralform>
+<pluralform>Показано %y з %x Ñ€Ñдків</pluralform>
+</target>
+
<source>Cannot find %x</source>
<target>Ðеможливо знайти %x</target>
@@ -1753,7 +1771,7 @@ This guarantees a consistent state even in case of a serious error.
<target>Пароль:</target>
<source>Key passphrase:</source>
-<target></target>
+<target>Ключова парольна фраза:</target>
<source>Please enter a file path.</source>
<target>Будь-лаÑка, введіть шлÑÑ… до файлу.</target>
@@ -1990,7 +2008,7 @@ This guarantees a consistent state even in case of a serious error.
<target>Файл заблоковано іншим процеÑом:</target>
<source>Failed to determine file permission support for folder %x.</source>
-<target></target>
+<target>Ðе вдалоÑÑ Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð¸Ñ‚Ð¸ підтримку дозволу на файл Ð´Ð»Ñ Ð¿Ð°Ð¿ÐºÐ¸ %x.</target>
<source>Cannot read security context of %x.</source>
<target>Ðе вдаєтьÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ контекÑÑ‚ безпеки %x.</target>
diff --git a/FreeFileSync/Build/Resources/cacert.pem b/FreeFileSync/Build/Resources/cacert.pem
index 65be2181..edc5090c 100755
--- a/FreeFileSync/Build/Resources/cacert.pem
+++ b/FreeFileSync/Build/Resources/cacert.pem
@@ -1,7 +1,7 @@
##
## Bundle of CA Root Certificates
##
-## Certificate data from Mozilla as of: Wed Aug 28 03:12:10 2019 GMT
+## Certificate data from Mozilla as of: Wed Oct 16 03:12:09 2019 GMT
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
@@ -14,7 +14,7 @@
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl version 1.27.
-## SHA256: fffa309937c3be940649293f749b8207fabc6eb224e50e4bb3f2c5e44e0d6a6b
+## SHA256: c979c6f35714a0fedb17d9e5ba37adecbbc91a8faf4186b4e23d6f9ca44fd6cb
##
@@ -592,28 +592,6 @@ mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K
-----END CERTIFICATE-----
-Certplus Class 2 Primary CA
-===========================
------BEGIN CERTIFICATE-----
-MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE
-BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN
-OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy
-dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR
-5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ
-Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO
-YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e
-e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME
-CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ
-YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t
-L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD
-P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R
-TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+
-7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW
-//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
-l7+ijrRU
------END CERTIFICATE-----
-
DST Root CA X3
==============
-----BEGIN CERTIFICATE-----
@@ -921,28 +899,6 @@ PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY
WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
-----END CERTIFICATE-----
-Deutsche Telekom Root CA 2
-==========================
------BEGIN CERTIFICATE-----
-MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT
-RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG
-A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5
-MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G
-A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS
-b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5
-bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI
-KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY
-AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK
-Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV
-jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV
-HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr
-E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy
-zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8
-rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G
-dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
-Cm26OWMohpLzGITY+9HPBVZkVw==
------END CERTIFICATE-----
-
Cybertrust Global Root
======================
-----BEGIN CERTIFICATE-----
diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile
index 856117c1..4ecc5f79 100755
--- a/FreeFileSync/Source/Makefile
+++ b/FreeFileSync/Source/Makefile
@@ -1,6 +1,6 @@
EXENAME = FreeFileSync_$(shell arch)
-CXXFLAGS = -std=c++2a -pipe -DWXINTL_NO_GETTEXT_MACRO -DLIBSSH2_OPENSSL -I../.. -I../../zenXml -include "zen/i18n.h" -include "zen/warn_static.h" \
+CXXFLAGS = -std=c++2a -pipe -DWXINTL_NO_GETTEXT_MACRO -I../.. -I../../zenXml -include "zen/i18n.h" -include "zen/warn_static.h" \
-Wall -Wfatal-errors -Wmissing-include-dirs -Wswitch-enum -Wcast-align -Wshadow -Wnon-virtual-dtor \
-O3 -DNDEBUG `wx-config --cxxflags --debug=no` -pthread
@@ -86,6 +86,7 @@ CPP_FILES+=../../zen/file_traverser.cpp
CPP_FILES+=../../zen/http.cpp
CPP_FILES+=../../zen/zstring.cpp
CPP_FILES+=../../zen/format_unit.cpp
+CPP_FILES+=../../zen/legacy_compiler.cpp
CPP_FILES+=../../zen/open_ssl.cpp
CPP_FILES+=../../zen/process_priority.cpp
CPP_FILES+=../../zen/shutdown.cpp
diff --git a/FreeFileSync/Source/RealTimeSync/Makefile b/FreeFileSync/Source/RealTimeSync/Makefile
index ef318cfc..3d0e98ec 100755
--- a/FreeFileSync/Source/RealTimeSync/Makefile
+++ b/FreeFileSync/Source/RealTimeSync/Makefile
@@ -30,6 +30,7 @@ CPP_FILES+=../../../zen/file_access.cpp
CPP_FILES+=../../../zen/file_io.cpp
CPP_FILES+=../../../zen/file_traverser.cpp
CPP_FILES+=../../../zen/format_unit.cpp
+CPP_FILES+=../../../zen/legacy_compiler.cpp
CPP_FILES+=../../../zen/shutdown.cpp
CPP_FILES+=../../../zen/thread.cpp
CPP_FILES+=../../../zen/zstring.cpp
diff --git a/FreeFileSync/Source/RealTimeSync/gui_generated.cpp b/FreeFileSync/Source/RealTimeSync/gui_generated.cpp
index 2bdb9a56..e2cb067b 100644
--- a/FreeFileSync/Source/RealTimeSync/gui_generated.cpp
+++ b/FreeFileSync/Source/RealTimeSync/gui_generated.cpp
@@ -66,23 +66,36 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr
bSizer16->Add( m_staticText9, 0, wxALL, 5 );
- wxBoxSizer* bSizer15;
- bSizer15 = new wxBoxSizer( wxVERTICAL );
+ ffgSizer111 = new wxFlexGridSizer( 0, 2, 5, 5 );
+ ffgSizer111->SetFlexibleDirection( wxBOTH );
+ ffgSizer111->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
- m_staticText3 = new wxStaticText( this, wxID_ANY, _("1. Select folders to watch."), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText16 = new wxStaticText( this, wxID_ANY, _("1."), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText16->Wrap( -1 );
+ ffgSizer111->Add( m_staticText16, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+ m_staticText3 = new wxStaticText( this, wxID_ANY, _("Select folders to watch."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText3->Wrap( -1 );
- bSizer15->Add( m_staticText3, 0, 0, 5 );
+ ffgSizer111->Add( m_staticText3, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_staticText17 = new wxStaticText( this, wxID_ANY, _("2."), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText17->Wrap( -1 );
+ ffgSizer111->Add( m_staticText17, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
- m_staticText4 = new wxStaticText( this, wxID_ANY, _("2. Enter a command line."), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText4 = new wxStaticText( this, wxID_ANY, _("Enter a command line."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText4->Wrap( -1 );
- bSizer15->Add( m_staticText4, 0, 0, 5 );
+ ffgSizer111->Add( m_staticText4, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_staticText18 = new wxStaticText( this, wxID_ANY, _("3."), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText18->Wrap( -1 );
+ ffgSizer111->Add( m_staticText18, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
- m_staticText5 = new wxStaticText( this, wxID_ANY, _("3. Press 'Start'."), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText5 = new wxStaticText( this, wxID_ANY, _("Press 'Start'."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText5->Wrap( -1 );
- bSizer15->Add( m_staticText5, 0, 0, 5 );
+ ffgSizer111->Add( m_staticText5, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer16->Add( bSizer15, 0, wxALL, 5 );
+ bSizer16->Add( ffgSizer111, 0, wxALL, 5 );
bSizer161->Add( bSizer16, 0, 0, 5 );
diff --git a/FreeFileSync/Source/RealTimeSync/gui_generated.h b/FreeFileSync/Source/RealTimeSync/gui_generated.h
index c51491f8..323db79d 100644
--- a/FreeFileSync/Source/RealTimeSync/gui_generated.h
+++ b/FreeFileSync/Source/RealTimeSync/gui_generated.h
@@ -53,8 +53,12 @@ protected:
wxMenuItem* m_menuItemAbout;
wxBoxSizer* bSizerMain;
wxStaticText* m_staticText9;
+ wxFlexGridSizer* ffgSizer111;
+ wxStaticText* m_staticText16;
wxStaticText* m_staticText3;
+ wxStaticText* m_staticText17;
wxStaticText* m_staticText4;
+ wxStaticText* m_staticText18;
wxStaticText* m_staticText5;
wxStaticText* m_staticText811;
wxStaticText* m_staticText10;
diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
index 3aab7438..6ddbc215 100644
--- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
+++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
@@ -190,9 +190,9 @@ void MainDialog::OnMenuAbout(wxCommandEvent& event)
#endif
build +=
-#ifdef ZEN_BUILD_32BIT
+#if ZEN_BUILD_ARCH == ZEN_ARCH_32BIT
L" x86";
-#elif defined ZEN_BUILD_64BIT
+#else
L" x64";
#endif
diff --git a/FreeFileSync/Source/afs/abstract.cpp b/FreeFileSync/Source/afs/abstract.cpp
index 7a537b7d..0b3c85d5 100644
--- a/FreeFileSync/Source/afs/abstract.cpp
+++ b/FreeFileSync/Source/afs/abstract.cpp
@@ -131,18 +131,19 @@ AFS::FileCopyResult AFS::copyFileAsStream(const AfsPath& afsPathSource, const St
bufferedStreamCopy(*streamIn, *streamOut); //throw FileError, ErrorFileLocked, X
- const AFS::FinalizeResult finResult = streamOut->finalize(); //throw FileError, X
-
- //catch file I/O bugs + read/write conflicts: (note: different check than inside AbstractFileSystem::OutputStream::finalize() => checks notifyUnbufferedIO()!)
- ZEN_ON_SCOPE_FAIL(try { removeFilePlain(apTarget); /*throw FileError*/ }
- catch (FileError&) {}); //after finalize(): not guarded by ~AFS::OutputStream() anymore!
-
+ //check incomplete input *before* failing with (slightly) misleading error message in OutputStream::finalize()
if (totalBytesRead != makeSigned(attrSourceNew.fileSize))
throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getDisplayPath(afsPathSource))),
replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"),
L"%x", numberTo<std::wstring>(attrSourceNew.fileSize)),
L"%y", numberTo<std::wstring>(totalBytesRead)) + L" [notifyUnbufferedRead]");
+ const AFS::FinalizeResult finResult = streamOut->finalize(); //throw FileError, X
+
+ //catch file I/O bugs + read/write conflicts: (note: different check than inside AbstractFileSystem::OutputStream::finalize() => checks notifyUnbufferedIO()!)
+ ZEN_ON_SCOPE_FAIL(try { removeFilePlain(apTarget); /*throw FileError*/ }
+ catch (FileError&) {}); //after finalize(): not guarded by ~AFS::OutputStream() anymore!
+
if (totalBytesWritten != totalBytesRead)
throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getDisplayPath(apTarget))),
replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"),
@@ -314,7 +315,7 @@ std::optional<AFS::ItemType> AFS::itemStillExists(const AfsPath& afsPath) const
}
catch (const ItemType&) //finding the item after getItemType() previously failed is exceptional
{
- throw e; //yes, slicing
+ throw FileError(_("Temporary access error:") + L' ' + e.toString());
}
return {};
}
diff --git a/FreeFileSync/Source/afs/abstract.h b/FreeFileSync/Source/afs/abstract.h
index 85916fcd..535d4114 100644
--- a/FreeFileSync/Source/afs/abstract.h
+++ b/FreeFileSync/Source/afs/abstract.h
@@ -468,7 +468,6 @@ AbstractFileSystem::FinalizeResult AbstractFileSystem::OutputStream::finalize()
replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"),
L"%x", numberTo<std::wstring>(*bytesExpected_)),
L"%y", numberTo<std::wstring>(bytesWrittenTotal_)));
- warn_static("somehow indicate that this is about source, and not the file name presented")
const FinalizeResult result = outStream_->finalize(); //throw FileError, X
finalizeSucceeded_ = true;
diff --git a/FreeFileSync/Source/afs/ftp.cpp b/FreeFileSync/Source/afs/ftp.cpp
index af3605e4..8b5f65dd 100644
--- a/FreeFileSync/Source/afs/ftp.cpp
+++ b/FreeFileSync/Source/afs/ftp.cpp
@@ -186,23 +186,17 @@ std::vector<std::string> splitFtpResponse(const std::string& buf)
{
std::vector<std::string> lines;
- std::string lineBuf;
- auto flushLineBuf = [&]
+ const auto isLb = [](char c) { return isLineBreak(c) || c == '\0'; };
+ auto it = buf.begin();
+ for (;;)
{
- if (!lineBuf.empty())
- {
- lines.push_back(lineBuf);
- lineBuf.clear();
- }
- };
- for (const char c : buf)
- if (c == '\r' || c == '\n' || c == '\0')
- flushLineBuf();
- else
- lineBuf += c;
+ auto itLineBegin = std::find_if_not(it, buf.end(), isLb);
+ if (itLineBegin == buf.end())
+ return lines;
- flushLineBuf();
- return lines;
+ it = std::find_if(itLineBegin + 1, buf.end(), isLb);
+ lines.emplace_back(itLineBegin, it);
+ }
}
@@ -343,7 +337,6 @@ public:
else
::curl_easy_reset(easyHandle_);
-
std::vector<Option> options;
curlErrorBuf_[0] = '\0';
@@ -364,25 +357,13 @@ public:
const std::string curlPath = getCurlUrlPath(afsPath, isDir, timeoutSec); //throw SysError
options.emplace_back(CURLOPT_URL, curlPath.c_str());
- options.emplace_back(CURLOPT_FTP_FILEMETHOD, pathMethod);
-
assert(pathMethod != CURLFTPMETHOD_MULTICWD); //too slow!
- assert(pathMethod != CURLFTPMETHOD_NOCWD); //too buggy!
- /* "wrong dir listing because libcurl remembers wrong CWD": https://github.com/curl/curl/issues/1782
-
- => "fixed" by adding only the "if (data->set.ftp_filemethod == FTPFILE_NOCWD)" below: https://github.com/curl/curl/issues/1811
- => this is NOT enough! consider what happens for a reused connection that first used CURLFTPMETHOD_MULTICWD, now CURLFTPMETHOD_NOCWD:
-
- the code in ftp_state_cwd() will issue a CWD sequence that ends with "ftpc->cwdcount == 1"!!! See "if (++ftpc->cwdcount <= ftpc->dirdepth)"
- => this skips the previous "fix" in https://github.com/curl/curl/issues/1718 with
- if ((conn->data->set.ftp_filemethod == FTPFILE_NOCWD) && !ftpc->cwdcount)
- ------------------------------------------------------------
- workaround => use absolute paths only!
- ------------------------------------------------------------
- CURLFTPMETHOD_NOCWD doesn't work as advertized: "CWD is sent despite CURLOPT_QUOTE/CURLOPT_NOBODY" https://github.com/curl/curl/issues/1443
- */
+ options.emplace_back(CURLOPT_FTP_FILEMETHOD, pathMethod);
- warn_static("what if server uses ansii encoding")
+ //ANSI or UTF encoding?
+ // "modern" FTP servers (implementing RFC 2640) have UTF8 enabled by default => pray and hope for the best.
+ // What about ANSI-FTP servers and "Microsoft FTP Service" which requires "OPTS UTF8 ON"? => *psh*
+ // CURLOPT_PREQUOTE to the rescue? Nope, issued long after USER/PASS
const auto username = utfTo<std::string>(sessionId_.username);
const auto password = utfTo<std::string>(sessionId_.password);
if (!username.empty()) //else: libcurl handles anonymous login for us (including fake email as password)
@@ -391,15 +372,6 @@ public:
options.emplace_back(CURLOPT_PASSWORD, password.c_str());
}
-
- warn_static("remove after test")
- //const auto username2 = utfToAnsiEncoding(sessionId_.username);
- //options.emplace_back(CURLOPT_USERNAME, username2.c_str());
-
-
-
-
-
if (sessionId_.port > 0)
options.emplace_back(CURLOPT_PORT, static_cast<long>(sessionId_.port));
@@ -514,7 +486,7 @@ public:
{
const CURLcode rc = ::curl_easy_setopt(easyHandle_, opt.option, opt.value);
if (rc != CURLE_OK)
- throw SysError(formatSystemError(L"curl_easy_setopt " + numberTo<std::wstring>(opt.option),
+ throw SysError(formatSystemError(L"curl_easy_setopt " + numberTo<std::wstring>(static_cast<int>(opt.option)),
formatCurlStatusCode(rc), utfTo<std::wstring>(::curl_easy_strerror(rc))));
}
@@ -543,7 +515,7 @@ public:
ZEN_ON_SCOPE_EXIT(::curl_slist_free_all(quote));
quote = ::curl_slist_append(quote, ftpCmd.c_str());
- return perform(AfsPath(), true /*isDir*/, CURLFTPMETHOD_FULLPATH, //really avoid needless CWDs unlike buggy(!) CURLFTPMETHOD_NOCWD
+ return perform(AfsPath(), true /*isDir*/, CURLFTPMETHOD_NOCWD, //avoid needless CWDs
{
{ CURLOPT_NOBODY, 1L },
{ CURLOPT_QUOTE, quote },
@@ -569,22 +541,18 @@ public:
if (!homePathCached_)
homePathCached_ = [&]
{
- if (!easyHandle_)
- testConnection(timeoutSec); //throw SysError
- assert(easyHandle_);
-
- const char* homePathCurl = nullptr; //not owned
- /*CURLcode rc =*/ ::curl_easy_getinfo(easyHandle_, CURLINFO_FTP_ENTRY_PATH, &homePathCurl);
-
- if (homePathCurl && isAsciiString(homePathCurl))
- return sanitizeRootRelativePath(utfTo<Zstring>(homePathCurl));
-
- //home path with non-ASCII chars: libcurl issues PWD right after login *before* server was set up for UTF8
- //=> CURLINFO_FTP_ENTRY_PATH could be in any encoding => useless!
- // Test case: Windows 10 IIS FTP with non-Ascii entry path
- //=> start new FTP session and parse PWD *after* UTF8 is enabled:
if (easyHandle_)
{
+ const char* homePathCurl = nullptr; //not owned
+ /*CURLcode rc =*/ ::curl_easy_getinfo(easyHandle_, CURLINFO_FTP_ENTRY_PATH, &homePathCurl);
+
+ if (homePathCurl && isAsciiString(homePathCurl))
+ return sanitizeRootRelativePath(utfTo<Zstring>(homePathCurl));
+
+ //home path with non-ASCII chars: libcurl issues PWD right after login *before* server was set up for UTF8
+ //=> CURLINFO_FTP_ENTRY_PATH could be in any encoding => useless!
+ // Test case: Windows 10 IIS FTP with non-Ascii entry path
+ //=> start new FTP session and parse PWD *after* UTF8 is enabled:
::curl_easy_cleanup(easyHandle_);
easyHandle_ = nullptr;
}
@@ -632,16 +600,16 @@ public:
return numeric::dist(std::chrono::steady_clock::now(), lastSuccessfulUseTime_) <= FTP_SESSION_MAX_IDLE_TIME;
}
- std::string getServerRelPathInternal(const AfsPath& afsPath, int timeoutSec) //throw SysError
+ std::string getServerPathInternal(const AfsPath& afsPath, int timeoutSec) //throw SysError
{
- const Zstring serverRelPath = getServerRelPath(afsPath);
+ const Zstring serverPath = getServerRelPath(afsPath);
- if (afsPath.value.empty()) //endless recursion caveat!! getServerEncoding() transitively depends on getServerRelPathInternal()
- return utfTo<std::string>(serverRelPath);
+ if (afsPath.value.empty()) //endless recursion caveat!! getServerEncoding() transitively depends on getServerPathInternal()
+ return utfTo<std::string>(serverPath);
const ServerEncoding encoding = getServerEncoding(timeoutSec); //throw SysError
- return utfToServerEncoding(serverRelPath, encoding); //throw SysError
+ return utfToServerEncoding(serverPath, encoding); //throw SysError
}
private:
@@ -650,9 +618,9 @@ private:
std::string getCurlUrlPath(const AfsPath& afsPath /*optional*/, bool isDir, int timeoutSec) //throw SysError
{
- std::string curlRelPath; //libcurl expects encoded paths (except for '/' char!!!)
+ std::string curlRelPath; //libcurl expects encoded paths (except for '/' char!!!) => bug: https://github.com/curl/curl/pull/4423
- for (const std::string& comp : split(getServerRelPathInternal(afsPath, timeoutSec), '/', SplitType::SKIP_EMPTY)) //throw SysError
+ for (const std::string& comp : split(getServerPathInternal(afsPath, timeoutSec), '/', SplitType::SKIP_EMPTY)) //throw SysError
{
char* compFmt = ::curl_easy_escape(easyHandle_, comp.c_str(), static_cast<int>(comp.size()));
if (!compFmt)
@@ -664,12 +632,11 @@ private:
curlRelPath += compFmt;
}
- /* 1. FFS CURLFTPMETHOD_NOCWD is buggy (see comment FtpSession::perform()) => must use absolute, not home-relative paths!
- 2. Support CURLFTPMETHOD_FULLPATH => must use absolute, not home-relative paths!
- 3. Some FTP servers distinguish between user-home- and root-relative paths! e.g. FreeNAS: https://freefilesync.org/forum/viewtopic.php?t=6129
- => use root-relative paths (= same as expected by CURLOPT_QUOTE) https://curl.haxx.se/docs/faq.html#How_do_I_list_the_root_dir_of_an
- => use // because /%2f had bugs (but they should be fixed: https://github.com/curl/curl/pull/4348)
- */
+ static_assert(LIBCURL_VERSION_MAJOR > 7 || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 67));
+ /* 1. CURLFTPMETHOD_NOCWD requires absolute paths to unconditionally skip CWDs: https://github.com/curl/curl/pull/4382
+ 2. CURLFTPMETHOD_SINGLECWD requires absolute paths to skip one needless "CWD entry path": https://github.com/curl/curl/pull/4332
+ => https://curl.haxx.se/docs/faq.html#How_do_I_list_the_root_dir_of_an
+ => use // because /%2f had bugs (but they should be fixed: https://github.com/curl/curl/pull/4348) */
std::string path = utfTo<std::string>(Zstring(ftpPrefix) + Zstr("//") + sessionId_.server) + "//" + curlRelPath;
if (isDir && !endsWith(path, '/')) //curl-FTP needs directory paths to end with a slash
@@ -697,6 +664,7 @@ private:
//"prefix the command with an asterisk to make libcurl continue even if the command fails"
//-> ignore if server does not know this legacy command (but report all *other* issues; else getActiveSocket() below won't return value and hide real error!)
+ //"If an RFC 2640 compliant client sends OPTS UTF-8 ON, it has to use UTF-8 regardless whether OPTS UTF-8 ON succeeds or not. "
runSingleFtpCommand("*OPTS UTF8 ON", false /*requiresUtf8*/, timeoutSec); //throw SysError
//make sure our unicode-enabled session is still there (== libcurl behaves as we expect)
@@ -1021,7 +989,7 @@ public:
}();
if (!pathHasWildcards)
- pathMethod = CURLFTPMETHOD_FULLPATH; //16% faster traversal compared to CURLFTPMETHOD_SINGLECWD (35% faster than CURLFTPMETHOD_MULTICWD)
+ pathMethod = CURLFTPMETHOD_NOCWD; //16% faster traversal compared to CURLFTPMETHOD_SINGLECWD (35% faster than CURLFTPMETHOD_MULTICWD)
}
//else: use "LIST" + CURLFTPMETHOD_SINGLECWD
//caveat: let's better not use LIST parameters: https://cr.yp.to/ftp/list.html
@@ -1052,11 +1020,9 @@ private:
for (const std::string& line : splitFtpResponse(buf))
{
const FtpItem item = parseMlstLine(line, enc); //throw SysError
- if (item.itemName == Zstr(".") ||
- item.itemName == Zstr(".."))
- continue;
-
- output.push_back(item);
+ if (item.itemName != Zstr(".") &&
+ item.itemName != Zstr(".."))
+ output.push_back(item);
}
return output;
}
@@ -1189,11 +1155,9 @@ private:
}();
const FtpItem item = parseUnixLine(*it, utcTimeNow, utcCurrentYear, *unixListingHaveGroup_, enc); //throw SysError
- if (item.itemName == Zstr(".") ||
- item.itemName == Zstr(".."))
- continue;
-
- output.push_back(item);
+ if (item.itemName != Zstr(".") &&
+ item.itemName != Zstr(".."))
+ output.push_back(item);
}
return output;
@@ -1466,17 +1430,19 @@ private:
if (itemName.empty())
throw SysError(L"Folder contains child item without a name.");
- if (itemName == "." || itemName == "..")
- continue;
//------------------------------------------------------------------------------------
- FtpItem item;
- if (isDir)
- item.type = AFS::ItemType::FOLDER;
- item.itemName = serverToUtfEncoding(itemName, enc); //throw SysError
- item.fileSize = fileSize;
- item.modTime = utcTime;
-
- output.push_back(item);
+ if (itemName != "." &&
+ itemName != "..")
+ {
+ FtpItem item;
+ if (isDir)
+ item.type = AFS::ItemType::FOLDER;
+ item.itemName = serverToUtfEncoding(itemName, enc); //throw SysError
+ item.fileSize = fileSize;
+ item.modTime = utcTime;
+
+ output.push_back(item);
+ }
}
catch (const SysError& e)
{
@@ -1580,18 +1546,17 @@ void ftpFileDownload(const FtpLoginInfo& login, const AfsPath& afsFilePath, //th
};
using CbType = decltype(onBytesReceived);
- using CbWrapperType = size_t (*)(const void* buffer, size_t size, size_t nitems, void* callbackData); //needed for cdecl function pointer cast
- CbWrapperType onBytesReceivedWrapper = [](const void* buffer, size_t size, size_t nitems, void* callbackData)
+ using CbWrapperType = size_t (*)(const void* buffer, size_t size, size_t nitems, CbType* callbackData); //needed for cdecl function pointer cast
+ CbWrapperType onBytesReceivedWrapper = [](const void* buffer, size_t size, size_t nitems, CbType* callbackData)
{
- auto cb = static_cast<CbType*>(callbackData); //free this poor little C-API from its shackles and redirect to a proper lambda
- return (*cb)(buffer, size * nitems);
+ return (*callbackData)(buffer, size * nitems); //free this poor little C-API from its shackles and redirect to a proper lambda
};
try
{
accessFtpSession(login, [&](FtpSession& session) //throw SysError
{
- session.perform(afsFilePath, false /*isDir*/, CURLFTPMETHOD_FULLPATH, //are there any servers that require CURLFTPMETHOD_SINGLECWD? let's find out
+ session.perform(afsFilePath, false /*isDir*/, CURLFTPMETHOD_NOCWD, //are there any servers that require CURLFTPMETHOD_SINGLECWD? let's find out
{
{ CURLOPT_WRITEDATA, &onBytesReceived },
{ CURLOPT_WRITEFUNCTION, onBytesReceivedWrapper },
@@ -1637,11 +1602,10 @@ void ftpFileUpload(const FtpLoginInfo& login, const AfsPath& afsFilePath, //thro
};
using CbType = decltype(getBytesToSend);
- using CbWrapperType = size_t (*)(void* buffer, size_t size, size_t nitems, void* callbackData);
- CbWrapperType getBytesToSendWrapper = [](void* buffer, size_t size, size_t nitems, void* callbackData)
+ using CbWrapperType = size_t (*)(void* buffer, size_t size, size_t nitems, CbType* callbackData);
+ CbWrapperType getBytesToSendWrapper = [](void* buffer, size_t size, size_t nitems, CbType* callbackData)
{
- auto cb = static_cast<CbType*>(callbackData); //free this poor little C-API from its shackles and redirect to a proper lambda
- return (*cb)(buffer, size * nitems);
+ return (*callbackData)(buffer, size * nitems); //free this poor little C-API from its shackles and redirect to a proper lambda
};
try
@@ -1653,11 +1617,11 @@ void ftpFileUpload(const FtpLoginInfo& login, const AfsPath& afsFilePath, //thro
ZEN_ON_SCOPE_EXIT(::curl_slist_free_all(quote));
//"prefix the command with an asterisk to make libcurl continue even if the command fails"
- quote = ::curl_slist_append(quote, ("*DELE " + session.getServerRelPathInternal(afsFilePath)).c_str()); //throw SysError
+ quote = ::curl_slist_append(quote, ("*DELE " + session.getServerPathInternal(afsFilePath)).c_str()); //throw SysError
//optimize fail-safe copy with RNFR/RNTO as CURLOPT_POSTQUOTE? -> even slightly *slower* than RNFR/RNTO as additional curl_easy_perform()
*/
- session.perform(afsFilePath, false /*isDir*/, CURLFTPMETHOD_FULLPATH, //are there any servers that require CURLFTPMETHOD_SINGLECWD? let's find out
+ session.perform(afsFilePath, false /*isDir*/, CURLFTPMETHOD_NOCWD, //are there any servers that require CURLFTPMETHOD_SINGLECWD? let's find out
{
{ CURLOPT_UPLOAD, 1L },
{ CURLOPT_READDATA, &getBytesToSend },
@@ -1836,7 +1800,7 @@ private:
if (!session.supportsMfmt(login_.timeoutSec)) //throw SysError
throw SysError(L"Server does not support the MFMT command.");
- session.runSingleFtpCommand("MFMT " + isoTime + " " + session.getServerRelPathInternal(afsPath_, login_.timeoutSec),
+ session.runSingleFtpCommand("MFMT " + isoTime + " " + session.getServerPathInternal(afsPath_, login_.timeoutSec),
true /*requiresUtf8*/, login_.timeoutSec); //throw SysError
//Does MFMT follow symlinks?? Anyway, our FTP implementation supports folder symlinks only
});
@@ -1958,7 +1922,7 @@ private:
{
accessFtpSession(login_, [&](FtpSession& session) //throw SysError
{
- session.runSingleFtpCommand("MKD " + session.getServerRelPathInternal(afsPath, login_.timeoutSec),
+ session.runSingleFtpCommand("MKD " + session.getServerPathInternal(afsPath, login_.timeoutSec),
true /*requiresUtf8*/, login_.timeoutSec); //throw SysError
});
}
@@ -1974,7 +1938,7 @@ private:
{
accessFtpSession(login_, [&](FtpSession& session) //throw SysError
{
- session.runSingleFtpCommand("DELE " + session.getServerRelPathInternal(afsPath, login_.timeoutSec),
+ session.runSingleFtpCommand("DELE " + session.getServerPathInternal(afsPath, login_.timeoutSec),
true /*requiresUtf8*/, login_.timeoutSec); //throw SysError
});
}
@@ -2001,7 +1965,7 @@ private:
{
try
{
- session.runSingleFtpCommand("RMD " + session.getServerRelPathInternal(afsPath, login_.timeoutSec),
+ session.runSingleFtpCommand("RMD " + session.getServerPathInternal(afsPath, login_.timeoutSec),
true /*requiresUtf8*/, login_.timeoutSec); //throw SysError
}
catch (const SysError& e) { delError = e; }
@@ -2123,10 +2087,10 @@ private:
{
struct curl_slist* quote = nullptr;
ZEN_ON_SCOPE_EXIT(::curl_slist_free_all(quote));
- quote = ::curl_slist_append(quote, ("RNFR " + session.getServerRelPathInternal(pathFrom, login_.timeoutSec)).c_str()); //throw SysError
- quote = ::curl_slist_append(quote, ("RNTO " + session.getServerRelPathInternal(pathTo.afsPath, login_.timeoutSec)).c_str()); //
+ quote = ::curl_slist_append(quote, ("RNFR " + session.getServerPathInternal(pathFrom, login_.timeoutSec)).c_str()); //throw SysError
+ quote = ::curl_slist_append(quote, ("RNTO " + session.getServerPathInternal(pathTo.afsPath, login_.timeoutSec)).c_str()); //
- session.perform(AfsPath(), true /*isDir*/, CURLFTPMETHOD_FULLPATH, //really avoid needless CWDs unlike buggy(!) CURLFTPMETHOD_NOCWD
+ session.perform(AfsPath(), true /*isDir*/, CURLFTPMETHOD_NOCWD, //avoid needless CWDs
{
{ CURLOPT_NOBODY, 1L },
{ CURLOPT_QUOTE, quote },
diff --git a/FreeFileSync/Source/afs/gdrive.cpp b/FreeFileSync/Source/afs/gdrive.cpp
index 9b885036..bcbed675 100644
--- a/FreeFileSync/Source/afs/gdrive.cpp
+++ b/FreeFileSync/Source/afs/gdrive.cpp
@@ -232,11 +232,10 @@ public:
}
};
using ReadCbType = decltype(onBytesReceived);
- using ReadCbWrapperType = size_t (*)(const void* buffer, size_t size, size_t nitems, void* callbackData); //needed for cdecl function pointer cast
- ReadCbWrapperType onBytesReceivedWrapper = [](const void* buffer, size_t size, size_t nitems, void* callbackData)
+ using ReadCbWrapperType = size_t (*)(const void* buffer, size_t size, size_t nitems, ReadCbType* callbackData); //needed for cdecl function pointer cast
+ ReadCbWrapperType onBytesReceivedWrapper = [](const void* buffer, size_t size, size_t nitems, ReadCbType* callbackData)
{
- auto cb = static_cast<ReadCbType*>(callbackData); //free this poor little C-API from its shackles and redirect to a proper lambda
- return (*cb)(buffer, size * nitems);
+ return (*callbackData)(buffer, size * nitems); //free this poor little C-API from its shackles and redirect to a proper lambda
};
//---------------------------------------------------
auto getBytesToSend = [&](void* buffer, size_t len) -> size_t
@@ -255,11 +254,10 @@ public:
}
};
using WriteCbType = decltype(getBytesToSend);
- using WriteCbWrapperType = size_t (*)(void* buffer, size_t size, size_t nitems, void* callbackData);
- WriteCbWrapperType getBytesToSendWrapper = [](void* buffer, size_t size, size_t nitems, void* callbackData)
+ using WriteCbWrapperType = size_t (*)(void* buffer, size_t size, size_t nitems, WriteCbType* callbackData);
+ WriteCbWrapperType getBytesToSendWrapper = [](void* buffer, size_t size, size_t nitems, WriteCbType* callbackData)
{
- auto cb = static_cast<WriteCbType*>(callbackData); //free this poor little C-API from its shackles and redirect to a proper lambda
- return (*cb)(buffer, size * nitems);
+ return (*callbackData)(buffer, size * nitems); //free this poor little C-API from its shackles and redirect to a proper lambda
};
//---------------------------------------------------
if (writeResponse)
@@ -301,7 +299,7 @@ public:
{
const CURLcode rc = ::curl_easy_setopt(easyHandle_, opt.option, opt.value);
if (rc != CURLE_OK)
- throw SysError(formatSystemError(L"curl_easy_setopt " + numberTo<std::wstring>(opt.option),
+ throw SysError(formatSystemError(L"curl_easy_setopt " + numberTo<std::wstring>(static_cast<int>(opt.option)),
formatCurlStatusCode(rc), utfTo<std::wstring>(::curl_easy_strerror(rc))));
}
@@ -1473,25 +1471,23 @@ std::string /*itemId*/ gdriveUploadFile(const Zstring& fileName, const std::stri
std::string uploadUrl;
- auto onBytesReceived = [&](const void* buffer, size_t len)
+ auto onBytesReceived = [&](const char* buffer, size_t len)
{
//inside libcurl's C callstack => better not throw exceptions here!!!
//"The callback will be called once for each header and only complete header lines are passed on to the callback" (including \r\n at the end)
- const auto strBegin = static_cast<const char*>(buffer);
- if (startsWithAsciiNoCase(StringRef<const char>(strBegin, strBegin + len), "Location:"))
+ if (startsWithAsciiNoCase(std::string_view(buffer, len), "Location:"))
{
- uploadUrl.assign(strBegin, len); //not null-terminated!
+ uploadUrl.assign(buffer, len); //not null-terminated!
uploadUrl = afterFirst(uploadUrl, ':', IF_MISSING_RETURN_NONE);
trim(uploadUrl);
}
return len;
};
using ReadCbType = decltype(onBytesReceived);
- using ReadCbWrapperType = size_t (*)(const void* buffer, size_t size, size_t nitems, void* callbackData); //needed for cdecl function pointer cast
- ReadCbWrapperType onBytesReceivedWrapper = [](const void* buffer, size_t size, size_t nitems, void* callbackData)
+ using ReadCbWrapperType = size_t (*)(const char* buffer, size_t size, size_t nitems, ReadCbType* callbackData); //needed for cdecl function pointer cast
+ ReadCbWrapperType onBytesReceivedWrapper = [](const char* buffer, size_t size, size_t nitems, ReadCbType* callbackData)
{
- auto cb = static_cast<ReadCbType*>(callbackData); //free this poor little C-API from its shackles and redirect to a proper lambda
- return (*cb)(buffer, size * nitems);
+ return (*callbackData)(buffer, size * nitems); //free this poor little C-API from its shackles and redirect to a proper lambda
};
std::string response;
diff --git a/FreeFileSync/Source/afs/init_curl_libssh2.cpp b/FreeFileSync/Source/afs/init_curl_libssh2.cpp
index dd956343..76556b43 100644
--- a/FreeFileSync/Source/afs/init_curl_libssh2.cpp
+++ b/FreeFileSync/Source/afs/init_curl_libssh2.cpp
@@ -9,8 +9,8 @@
#include <zen/thread.h>
#include <zen/file_error.h>
#include <zen/open_ssl.h>
-#include "libssh2/init_libssh2.h"
-#include "libcurl/curl_wrap.h" //DON'T include <curl/curl.h> directly!
+#include "libssh2/libssh2_wrap.h" //DON'T include <libssh2_sftp.h> directly!
+#include "libcurl/curl_wrap.h" //DON'T include <curl/curl.h> directly!
using namespace zen;
@@ -30,10 +30,21 @@ void libsshCurlUnifiedInit()
openSslInit();
- libssh2Init();
- [[maybe_unused]] const CURLcode rc2 = ::curl_global_init(CURL_GLOBAL_NOTHING /*CURL_GLOBAL_DEFAULT = CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32*/);
- assert(rc2 == CURLE_OK);
+ [[maybe_unused]] const int rc2 = ::libssh2_init(0);
+ /*
+ we need libssh2's crypto init:
+ - there is other OpenSSL-related initialization which might be needed (and hopefully won't hurt...)
+
+ 2019-02-26: following reasons are obsolete due to HAVE_EVP_AES_128_CTR:
+ // - initializes a few statically allocated constants => avoid (minor) race condition if these were initialized by worker threads
+ // - enable proper clean up of these variables in libssh2_exit() (otherwise: memory leaks!)
+ */
+ assert(rc2 == 0); //libssh2 unconditionally returns 0 => why then have a return value in first place???
+
+
+ [[maybe_unused]] const CURLcode rc3 = ::curl_global_init(CURL_GLOBAL_NOTHING /*CURL_GLOBAL_DEFAULT = CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32*/);
+ assert(rc3 == CURLE_OK);
}
@@ -45,7 +56,7 @@ void libsshCurlUnifiedTearDown()
return;
::curl_global_cleanup();
- libssh2TearDown();
+ ::libssh2_exit();
openSslTearDown();
}
diff --git a/FreeFileSync/Source/afs/libcurl/curl_wrap.h b/FreeFileSync/Source/afs/libcurl/curl_wrap.h
index 670ad6f5..e4878743 100644
--- a/FreeFileSync/Source/afs/libcurl/curl_wrap.h
+++ b/FreeFileSync/Source/afs/libcurl/curl_wrap.h
@@ -15,6 +15,9 @@
#include <curl/curl.h>
//-------------------------------------------------
+#ifndef CURLINC_CURL_H
+ #error curl.h header guard changed
+#endif
namespace zen
{
@@ -123,7 +126,7 @@ std::wstring formatCurlStatusCode(CURLcode sc)
}
static_assert(CURL_LAST == CURLE_AUTH_ERROR + 1);
- return replaceCpy<std::wstring>(L"Curl status %x.", L"%x", numberTo<std::wstring>(sc));
+ return replaceCpy<std::wstring>(L"Curl status %x.", L"%x", numberTo<std::wstring>(static_cast<int>(sc)));
}
}
}
diff --git a/FreeFileSync/Source/afs/libssh2/init_libssh2.cpp b/FreeFileSync/Source/afs/libssh2/init_libssh2.cpp
deleted file mode 100644
index 4db53458..00000000
--- a/FreeFileSync/Source/afs/libssh2/init_libssh2.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "init_libssh2.h"
-#include <cassert>
-#include <libssh2_sftp.h>
-
-
-#ifndef LIBSSH2_OPENSSL
- #error check code when/if-ever the OpenSSL libssh2 backend is changed
-#endif
-
-
-#if defined LIBSSH2_WIN32 && !defined HAVE_EVP_AES_128_CTR
- #error OpenSSL already supports EVP_aes_128_ctr(), etc. => no need for the libssh2 fallbacks!
-#endif
-
-
-void zen::libssh2Init()
-{
- [[maybe_unused]] const int rc = ::libssh2_init(0);
- /*
- we need libssh2's crypto init:
- - there is other OpenSSL-related initialization which might be needed (and hopefully won't hurt...)
-
- 2019-02-26: following reasons are obsolete due to HAVE_EVP_AES_128_CTR:
- // - initializes a few statically allocated constants => avoid (minor) race condition if these were initialized by worker threads
- // - enable proper clean up of these variables in libssh2_exit() (otherwise: memory leaks!)
- */
- assert(rc == 0); //libssh2 unconditionally returns 0 => why then have a return value in first place???
-}
-
-
-void zen::libssh2TearDown()
-{
- ::libssh2_exit();
-}
diff --git a/FreeFileSync/Source/afs/libssh2/init_libssh2.h b/FreeFileSync/Source/afs/libssh2/init_libssh2.h
deleted file mode 100644
index a159850a..00000000
--- a/FreeFileSync/Source/afs/libssh2/init_libssh2.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef INIT_LIBSSH2_H_42578934275823624556
-#define INIT_LIBSSH2_H_42578934275823624556
-
-namespace zen
-{
-void libssh2Init(); //WITHOUT OpenSSL initialization!
-void libssh2TearDown();
-}
-
-#endif //INIT_LIBSSH2_H_42578934275823624556
diff --git a/FreeFileSync/Source/afs/libssh2/libssh2_wrap.h b/FreeFileSync/Source/afs/libssh2/libssh2_wrap.h
new file mode 100644
index 00000000..37a62a24
--- /dev/null
+++ b/FreeFileSync/Source/afs/libssh2/libssh2_wrap.h
@@ -0,0 +1,231 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef LIBSSH2_WRAP_H_087280957180967346572465
+#define LIBSSH2_WRAP_H_087280957180967346572465
+
+#include <zen/scope_guard.h>
+#include <zen/string_tools.h>
+
+
+
+//-------------------------------------------------
+#include <libssh2_sftp.h>
+//-------------------------------------------------
+
+#ifndef LIBSSH2_SFTP_H
+ #error libssh2_sftp.h header guard changed
+#endif
+
+//fix libssh2 64-bit warning mess: https://github.com/libssh2/libssh2/pull/96
+#undef libssh2_userauth_password
+inline int libssh2_userauth_password(LIBSSH2_SESSION* session, const std::string& username, const std::string& password)
+{
+ return libssh2_userauth_password_ex(session,
+ username.c_str(), static_cast<unsigned int>(username.size()),
+ password.c_str(), static_cast<unsigned int>(password.size()), nullptr);
+}
+
+#undef libssh2_userauth_keyboard_interactive
+inline int libssh2_userauth_keyboard_interactive(LIBSSH2_SESSION* session, const std::string& username, LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback)))
+{
+ return libssh2_userauth_keyboard_interactive_ex(session, username.c_str(), static_cast<unsigned int>(username.size()), response_callback);
+}
+
+inline char* libssh2_userauth_list(LIBSSH2_SESSION* session, const std::string& username)
+{
+ return libssh2_userauth_list(session, username.c_str(), static_cast<unsigned int>(username.size()));
+}
+
+
+inline int libssh2_userauth_publickey_frommemory(LIBSSH2_SESSION* session, const std::string& username, const std::string& privateKeyStream, const std::string& passphrase)
+{
+ return libssh2_userauth_publickey_frommemory(session, username.c_str(), username.size(), nullptr, 0,
+ privateKeyStream.c_str(), privateKeyStream.size(), passphrase.c_str());
+}
+
+#undef libssh2_sftp_opendir
+inline LIBSSH2_SFTP_HANDLE* libssh2_sftp_opendir(LIBSSH2_SFTP* sftp, const std::string& path)
+{
+ return libssh2_sftp_open_ex(sftp, path.c_str(), static_cast<unsigned int>(path.size()), 0, 0, LIBSSH2_SFTP_OPENDIR);
+}
+
+#undef libssh2_sftp_stat
+inline int libssh2_sftp_stat(LIBSSH2_SFTP* sftp, const std::string& path, LIBSSH2_SFTP_ATTRIBUTES* attrs)
+{
+ return libssh2_sftp_stat_ex(sftp, path.c_str(), static_cast<unsigned int>(path.size()), LIBSSH2_SFTP_STAT, attrs);
+}
+
+#undef libssh2_sftp_open
+inline LIBSSH2_SFTP_HANDLE* libssh2_sftp_open(LIBSSH2_SFTP* sftp, const std::string& path, unsigned long flags, long mode)
+{
+ return libssh2_sftp_open_ex(sftp, path.c_str(), static_cast<unsigned int>(path.size()), flags, mode, LIBSSH2_SFTP_OPENFILE);
+}
+
+#undef libssh2_sftp_setstat
+inline int libssh2_sftp_setstat(LIBSSH2_SFTP* sftp, const std::string& path, LIBSSH2_SFTP_ATTRIBUTES* attrs)
+{
+ return libssh2_sftp_stat_ex(sftp, path.c_str(), static_cast<unsigned int>(path.size()), LIBSSH2_SFTP_SETSTAT, attrs);
+}
+
+#undef libssh2_sftp_lstat
+inline int libssh2_sftp_lstat(LIBSSH2_SFTP* sftp, const std::string& path, LIBSSH2_SFTP_ATTRIBUTES* attrs)
+{
+ return libssh2_sftp_stat_ex(sftp, path.c_str(), static_cast<unsigned int>(path.size()), LIBSSH2_SFTP_LSTAT, attrs);
+}
+
+#undef libssh2_sftp_mkdir
+inline int libssh2_sftp_mkdir(LIBSSH2_SFTP* sftp, const std::string& path, long mode)
+{
+ return libssh2_sftp_mkdir_ex(sftp, path.c_str(), static_cast<unsigned int>(path.size()), mode);
+}
+
+#undef libssh2_sftp_unlink
+inline int libssh2_sftp_unlink(LIBSSH2_SFTP* sftp, const std::string& path)
+{
+ return libssh2_sftp_unlink_ex(sftp, path.c_str(), static_cast<unsigned int>(path.size()));
+}
+
+#undef libssh2_sftp_rmdir
+inline int libssh2_sftp_rmdir(LIBSSH2_SFTP* sftp, const std::string& path)
+{
+ return libssh2_sftp_rmdir_ex(sftp, path.c_str(), static_cast<unsigned int>(path.size()));
+}
+
+#undef libssh2_sftp_realpath
+inline int libssh2_sftp_realpath(LIBSSH2_SFTP* sftp, const std::string& path, char* buf, size_t bufSize)
+{
+ return libssh2_sftp_symlink_ex(sftp, path.c_str(), static_cast<unsigned int>(path.size()), buf, static_cast<unsigned int>(bufSize), LIBSSH2_SFTP_REALPATH);
+}
+
+#undef libssh2_sftp_readlink
+inline int libssh2_sftp_readlink(LIBSSH2_SFTP* sftp, const std::string& path, char* buf, size_t bufSize)
+{
+ return libssh2_sftp_symlink_ex(sftp, path.c_str(), static_cast<unsigned int>(path.size()), buf, static_cast<unsigned int>(bufSize), LIBSSH2_SFTP_READLINK);
+}
+
+#undef libssh2_sftp_rename
+inline int libssh2_sftp_rename(LIBSSH2_SFTP* sftp, const std::string& pathFrom, const std::string& pathTo, long flags)
+{
+ return libssh2_sftp_rename_ex(sftp,
+ pathFrom.c_str(), static_cast<unsigned int>(pathFrom.size()),
+ pathTo.c_str(), static_cast<unsigned int>(pathTo.size()), flags);
+}
+
+
+
+namespace zen
+{
+namespace
+{
+std::wstring formatSshStatusCode(int sc)
+{
+ switch (sc)
+ {
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_NONE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SOCKET_NONE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_BANNER_RECV);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_BANNER_SEND);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_INVALID_MAC);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_KEX_FAILURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_ALLOC);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SOCKET_SEND);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_TIMEOUT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_HOSTKEY_INIT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_HOSTKEY_SIGN);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_DECRYPT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SOCKET_DISCONNECT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_PROTO);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_PASSWORD_EXPIRED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_FILE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_METHOD_NONE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_AUTHENTICATION_FAILED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_OUTOFORDER);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_FAILURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_UNKNOWN);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_CLOSED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_EOF_SENT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SCP_PROTOCOL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_ZLIB);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SOCKET_TIMEOUT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SFTP_PROTOCOL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_REQUEST_DENIED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_METHOD_NOT_SUPPORTED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_INVAL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_INVALID_POLL_TYPE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_PUBLICKEY_PROTOCOL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_EAGAIN);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_BUFFER_TOO_SMALL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_BAD_USE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_COMPRESS);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_OUT_OF_BOUNDARY);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_AGENT_PROTOCOL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SOCKET_RECV);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_ENCRYPT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_BAD_SOCKET);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_KNOWN_HOSTS);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_WINDOW_FULL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_KEYFILE_AUTH_FAILED);
+ }
+ return replaceCpy<std::wstring>(L"SSH status %x.", L"%x", numberTo<std::wstring>(sc));
+}
+
+
+std::wstring formatSftpStatusCode(unsigned long sc)
+{
+ switch (sc)
+ {
+ //*INDENT-OFF*
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_OK);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_EOF);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NO_SUCH_FILE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_PERMISSION_DENIED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_FAILURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_BAD_MESSAGE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NO_CONNECTION);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_CONNECTION_LOST);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_OP_UNSUPPORTED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_INVALID_HANDLE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NO_SUCH_PATH);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_FILE_ALREADY_EXISTS);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_WRITE_PROTECT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NO_MEDIA);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_QUOTA_EXCEEDED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_UNKNOWN_PRINCIPAL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_LOCK_CONFLICT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_DIR_NOT_EMPTY);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NOT_A_DIRECTORY);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_INVALID_FILENAME);
+ ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_LINK_LOOP);
+
+ //SFTP error codes missing from libssh2: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
+ case 22: return L"SSH_FX_CANNOT_DELETE";
+ case 23: return L"SSH_FX_INVALID_PARAMETER";
+ case 24: return L"SSH_FX_FILE_IS_A_DIRECTORY";
+ case 25: return L"SSH_FX_BYTE_RANGE_LOCK_CONFLICT";
+ case 26: return L"SSH_FX_BYTE_RANGE_LOCK_REFUSED";
+ case 27: return L"SSH_FX_DELETE_PENDING";
+ case 28: return L"SSH_FX_FILE_CORRUPT";
+ case 29: return L"SSH_FX_OWNER_INVALID";
+ case 30: return L"SSH_FX_GROUP_INVALID";
+ case 31: return L"SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK";
+
+ default: return replaceCpy<std::wstring>(L"SFTP status %x.", L"%x", numberTo<std::wstring>(sc));
+ //*INDENT-ON*
+ }
+}
+}
+}
+
+#else
+#error Why is this header already defined? Do not include in other headers: encapsulate the gory details!
+#endif //LIBSSH2_WRAP_H_087280957180967346572465
diff --git a/FreeFileSync/Source/afs/native.cpp b/FreeFileSync/Source/afs/native.cpp
index 266c88d2..937523fc 100644
--- a/FreeFileSync/Source/afs/native.cpp
+++ b/FreeFileSync/Source/afs/native.cpp
@@ -301,8 +301,7 @@ struct InputStreamNative : public AbstractFileSystem::InputStream
try
{
const NativeFileInfo fileInfo = getFileAttributes(fi_.getHandle()); //throw SysError
- return
- AFS::StreamAttributes(
+ return AFS::StreamAttributes(
{
fileInfo.modTime,
fileInfo.fileSize,
diff --git a/FreeFileSync/Source/afs/sftp.cpp b/FreeFileSync/Source/afs/sftp.cpp
index aa5de7eb..ffddae13 100644
--- a/FreeFileSync/Source/afs/sftp.cpp
+++ b/FreeFileSync/Source/afs/sftp.cpp
@@ -11,7 +11,8 @@
#include <zen/file_io.h>
#include <zen/basic_math.h>
#include <zen/socket.h>
-#include <libssh2_sftp.h>
+#include <zen/open_ssl.h>
+#include "libssh2/libssh2_wrap.h" //DON'T include <libssh2_sftp.h> directly!
#include "init_curl_libssh2.h"
#include "ftp_common.h"
#include "abstract_impl.h"
@@ -167,109 +168,6 @@ std::wstring getSftpDisplayPath(const Zstring& serverName, const AfsPath& afsPat
//===========================================================================================================================
-std::wstring formatSshStatusCode(int sc)
-{
- switch (sc)
- {
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_NONE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SOCKET_NONE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_BANNER_RECV);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_BANNER_SEND);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_INVALID_MAC);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_KEX_FAILURE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_ALLOC);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SOCKET_SEND);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_TIMEOUT);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_HOSTKEY_INIT);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_HOSTKEY_SIGN);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_DECRYPT);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SOCKET_DISCONNECT);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_PROTO);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_PASSWORD_EXPIRED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_FILE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_METHOD_NONE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_AUTHENTICATION_FAILED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_OUTOFORDER);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_FAILURE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_UNKNOWN);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_CLOSED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_EOF_SENT);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SCP_PROTOCOL);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_ZLIB);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SOCKET_TIMEOUT);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SFTP_PROTOCOL);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_REQUEST_DENIED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_METHOD_NOT_SUPPORTED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_INVAL);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_INVALID_POLL_TYPE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_PUBLICKEY_PROTOCOL);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_EAGAIN);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_BUFFER_TOO_SMALL);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_BAD_USE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_COMPRESS);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_OUT_OF_BOUNDARY);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_AGENT_PROTOCOL);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_SOCKET_RECV);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_ENCRYPT);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_BAD_SOCKET);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_KNOWN_HOSTS);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_CHANNEL_WINDOW_FULL);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_ERROR_KEYFILE_AUTH_FAILED);
- }
- return replaceCpy<std::wstring>(L"SSH status %x.", L"%x", numberTo<std::wstring>(sc));
-}
-
-std::wstring formatSftpStatusCode(unsigned long sc)
-{
- switch (sc)
- {
- //*INDENT-OFF*
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_OK);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_EOF);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NO_SUCH_FILE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_PERMISSION_DENIED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_FAILURE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_BAD_MESSAGE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NO_CONNECTION);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_CONNECTION_LOST);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_OP_UNSUPPORTED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_INVALID_HANDLE);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NO_SUCH_PATH);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_FILE_ALREADY_EXISTS);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_WRITE_PROTECT);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NO_MEDIA);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_QUOTA_EXCEEDED);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_UNKNOWN_PRINCIPAL);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_LOCK_CONFLICT);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_DIR_NOT_EMPTY);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_NOT_A_DIRECTORY);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_INVALID_FILENAME);
- ZEN_CHECK_CASE_FOR_CONSTANT(LIBSSH2_FX_LINK_LOOP);
-
- //SFTP error codes missing from libssh2: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
- case 22: return L"SSH_FX_CANNOT_DELETE";
- case 23: return L"SSH_FX_INVALID_PARAMETER";
- case 24: return L"SSH_FX_FILE_IS_A_DIRECTORY";
- case 25: return L"SSH_FX_BYTE_RANGE_LOCK_CONFLICT";
- case 26: return L"SSH_FX_BYTE_RANGE_LOCK_REFUSED";
- case 27: return L"SSH_FX_DELETE_PENDING";
- case 28: return L"SSH_FX_FILE_CORRUPT";
- case 29: return L"SSH_FX_OWNER_INVALID";
- case 30: return L"SSH_FX_GROUP_INVALID";
- case 31: return L"SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK";
-
- default: return replaceCpy<std::wstring>(L"SFTP status %x.", L"%x", numberTo<std::wstring>(sc));
- //*INDENT-ON*
- }
-}
-
-
std::wstring formatLastSshError(const std::wstring& functionName, LIBSSH2_SESSION* sshSession, LIBSSH2_SFTP* sftpChannel /*optional*/)
{
char* lastErrorMsg = nullptr; //owned by "sshSession"
@@ -322,14 +220,9 @@ public:
if (!sshSession_) //does not set ssh last error; source: only memory allocation may fail
throw SysError(formatSystemError(L"libssh2_session_init", formatSshStatusCode(LIBSSH2_ERROR_ALLOC), std::wstring()));
- /*
- => libssh2 using zlib crashes for Bitvise Servers: https://freefilesync.org/forum/viewtopic.php?t=2825
- => Don't enable zlib compression: libssh2 also recommends this option disabled: http://comments.gmane.org/gmane.network.ssh.libssh2.devel/6203
- const int rc = ::libssh2_session_flag(sshSession_, LIBSSH2_FLAG_COMPRESS, 1); //does not set ssh last error
- if (rc != 0)
+ //if zlib compression causes trouble, make it a user setting: https://freefilesync.org/forum/viewtopic.php?t=6663
+ if (const int rc = ::libssh2_session_flag(sshSession_, LIBSSH2_FLAG_COMPRESS, 1); rc != 0) //does not set ssh last error
throw SysError(formatSystemError(L"libssh2_session_flag", formatSshStatusCode(rc), std::wstring()));
- => build libssh2 without LIBSSH2_HAVE_ZLIB
- */
::libssh2_session_set_blocking(sshSession_, 1);
@@ -337,7 +230,6 @@ public:
::libssh2_session_set_timeout(sshSession_, timeoutSec * 1000 /*ms*/);
-
if (::libssh2_session_handshake(sshSession_, socket_->get()) != 0)
throw SysError(formatLastSshError(L"libssh2_session_handshake", sshSession_, nullptr));
@@ -346,7 +238,7 @@ public:
const auto usernameUtf8 = utfTo<std::string>(sessionId_.username);
const auto passwordUtf8 = utfTo<std::string>(sessionId_.password);
- const char* authList = ::libssh2_userauth_list(sshSession_, usernameUtf8.c_str(), static_cast<unsigned int>(usernameUtf8.length()));
+ const char* authList = ::libssh2_userauth_list(sshSession_, usernameUtf8);
if (!authList)
{
if (::libssh2_userauth_authenticated(sshSession_) == 0)
@@ -375,7 +267,7 @@ public:
{
if (supportAuthPassword)
{
- if (::libssh2_userauth_password(sshSession_, usernameUtf8.c_str(), passwordUtf8.c_str()) != 0)
+ if (::libssh2_userauth_password(sshSession_, usernameUtf8, passwordUtf8) != 0)
throw SysError(formatLastSshError(L"libssh2_userauth_password", sshSession_, nullptr));
}
else if (supportAuthInteractive) //some servers, e.g. web.sourceforge.net, support "keyboard-interactive", but not "password"
@@ -415,7 +307,7 @@ public:
*reinterpret_cast<AuthCbType**>(::libssh2_session_abstract(sshSession_)) = &authCallback;
ZEN_ON_SCOPE_EXIT(*::libssh2_session_abstract(sshSession_) = nullptr);
- if (::libssh2_userauth_keyboard_interactive(sshSession_, usernameUtf8.c_str(), authCallbackWrapper) != 0)
+ if (::libssh2_userauth_keyboard_interactive(sshSession_, usernameUtf8, authCallbackWrapper) != 0)
throw SysError(formatLastSshError(L"libssh2_userauth_keyboard_interactive", sshSession_, nullptr) +
(unexpectedPrompts.empty() ? L"" : L"\nUnexpected prompts: " + unexpectedPrompts));
}
@@ -431,22 +323,65 @@ public:
throw SysError(replaceCpy(_("The server does not support authentication via %x."), L"%x", L"\"key file\"") +
L"\n" +_("Required:") + L" " + utfTo<std::wstring>(authList));
+ std::string passphrase = passwordUtf8;
std::string pkStream;
try
{
pkStream = loadBinContainer<std::string>(sessionId_.privateKeyFilePath, nullptr /*notifyUnbufferedIO*/); //throw FileError
+ trim(pkStream);
}
catch (const FileError& e) { throw SysError(e.toString()); } //errors should be further enriched by context info => SysError
- if (::libssh2_userauth_publickey_frommemory(sshSession_, //LIBSSH2_SESSION *session,
- usernameUtf8.c_str(), //const char *username,
- usernameUtf8.size(), //size_t username_len,
- nullptr, //const char *publickeydata,
- 0, //size_t publickeydata_len,
- pkStream.c_str(), //const char *privatekeydata,
- pkStream.size(), //size_t privatekeydata_len,
- passwordUtf8.c_str()) != 0) //const char *passphrase
+ //libssh2 doesn't support the PuTTY key file format, but we do!
+ if (isPuttyKeyStream(pkStream))
+ {
+ pkStream = convertPuttyKeyToPkix(pkStream, passphrase); //throw SysError
+ passphrase.clear();
+ }
+
+ if (::libssh2_userauth_publickey_frommemory(sshSession_, usernameUtf8, pkStream, passphrase) != 0) //const char* passphrase
+ {
+ //libssh2_userauth_publickey_frommemory()'s "Unable to extract public key from private key" isn't exactly *helpful*
+ //=> detect invalid key files and give better error message:
+ const wchar_t* invalidKeyFormat = [&]() -> const wchar_t*
+ {
+ std::string firstLine(pkStream.begin(), std::find_if(pkStream.begin(), pkStream.end(), isLineBreak<char>));
+ trim(firstLine);
+
+ //"-----BEGIN PUBLIC KEY-----" OpenSSH SSH-2 public key (X.509 SubjectPublicKeyInfo) = PKIX
+ //"-----BEGIN RSA PUBLIC KEY-----" OpenSSH SSH-2 public key (PKCS#1 RSAPublicKey)
+ //"---- BEGIN SSH2 PUBLIC KEY ----" SSH-2 public key (RFC 4716 format)
+ if (contains(firstLine, "PUBLIC KEY"))
+ return L"OpenSSH public key";
+
+ if (startsWith(firstLine, "ssh-") || //ssh-rsa, ssh-dss, ssh-ed25519
+ startsWith(firstLine, "ecdsa-")) //ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521
+ return L"OpenSSH public key"; //OpenSSH SSH-2 public key
+
+ if (std::count(pkStream.begin(), pkStream.end(), ' ') == 2 &&
+ std::all_of(pkStream.begin(), pkStream.end(), [](char c) { return isDigit(c) || c == ' '; }))
+ return L"SSH-1 public key";
+
+ if (startsWith(firstLine, "PuTTY-User-Key-File-1")) //PuTTY SSH-2 private key
+ return L"Old PuTTY v1 key"; //we only support v2!
+
+ //"-----BEGIN PRIVATE KEY-----" => OpenSSH SSH-2 private key (PKCS#8 PrivateKeyInfo) => should work
+ //"-----BEGIN ENCRYPTED PRIVATE KEY-----" => OpenSSH SSH-2 private key (PKCS#8 EncryptedPrivateKeyInfo) => should work
+ //"-----BEGIN RSA PRIVATE KEY-----" => OpenSSH SSH-2 private key (PKCS#1 RSAPrivateKey) => should work
+ //"-----BEGIN DSA PRIVATE KEY-----" => OpenSSH SSH-2 private key (PKCS#1 DSAPrivateKey) => should work
+ //"-----BEGIN EC PRIVATE KEY-----" => OpenSSH SSH-2 private key (PKCS#1 ECPrivateKey) => should work
+ //"-----BEGIN OPENSSH PRIVATE KEY-----" => OpenSSH SSH-2 private key (new format) => should work
+ //"---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" => ssh.com SSH-2 private key => unclear
+ //"SSH PRIVATE KEY FILE FORMAT 1.1" => SSH-1 private key => unclear
+ return nullptr; //other: maybe invalid, maybe not
+ }();
+ if (invalidKeyFormat)
+ throw SysError(_("Authentication failed.") + L" " +
+ replaceCpy<std::wstring>(L"%x is not an OpenSSH or PuTTY private key file.", L"%x",
+ fmtPath(sessionId_.privateKeyFilePath) + L" [" + invalidKeyFormat + L"]"));
+
throw SysError(formatLastSshError(L"libssh2_userauth_publickey_frommemory", sshSession_, nullptr));
+ }
}
break;
@@ -1102,7 +1037,7 @@ std::vector<SftpItem> getDirContentFlat(const SftpLoginInfo& login, const AfsPat
runSftpCommand(login, L"libssh2_sftp_opendir", //throw SysError
[&](const SshSession::Details& sd) //noexcept!
{
- dirHandle = ::libssh2_sftp_opendir(sd.sftpChannel, getLibssh2Path(dirPath).c_str());
+ dirHandle = ::libssh2_sftp_opendir(sd.sftpChannel, getLibssh2Path(dirPath));
if (!dirHandle)
return std::min(::libssh2_session_last_errno(sd.sshSession), LIBSSH2_ERROR_SOCKET_NONE);
return LIBSSH2_ERROR_NONE;
@@ -1170,7 +1105,7 @@ SftpItemDetails getSymlinkTargetDetails(const SftpLoginInfo& login, const AfsPat
try
{
runSftpCommand(login, L"libssh2_sftp_stat", //throw SysError
- [&](const SshSession::Details& sd) { return ::libssh2_sftp_stat(sd.sftpChannel, getLibssh2Path(linkPath).c_str(), &attribsTrg); }); //noexcept!
+ [&](const SshSession::Details& sd) { return ::libssh2_sftp_stat(sd.sftpChannel, getLibssh2Path(linkPath), &attribsTrg); }); //noexcept!
}
catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(getSftpDisplayPath(login.server, linkPath))), e.toString()); }
@@ -1288,7 +1223,7 @@ struct InputStreamSftp : public AbstractFileSystem::InputStream
session_->executeBlocking(L"libssh2_sftp_open", //throw SysError, FatalSshError
[&](const SshSession::Details& sd) //noexcept!
{
- fileHandle_ = ::libssh2_sftp_open(sd.sftpChannel, getLibssh2Path(filePath).c_str(), LIBSSH2_FXF_READ, 0);
+ fileHandle_ = ::libssh2_sftp_open(sd.sftpChannel, getLibssh2Path(filePath), LIBSSH2_FXF_READ, 0);
if (!fileHandle_)
return std::min(::libssh2_session_last_errno(sd.sshSession), LIBSSH2_ERROR_SOCKET_NONE);
return LIBSSH2_ERROR_NONE;
@@ -1405,7 +1340,7 @@ struct OutputStreamSftp : public AbstractFileSystem::OutputStreamImpl
session_->executeBlocking(L"libssh2_sftp_open", //throw SysError, FatalSshError
[&](const SshSession::Details& sd) //noexcept!
{
- fileHandle_ = ::libssh2_sftp_open(sd.sftpChannel, getLibssh2Path(filePath).c_str(),
+ fileHandle_ = ::libssh2_sftp_open(sd.sftpChannel, getLibssh2Path(filePath),
LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_EXCL,
LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | //
LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IWGRP | //0666
@@ -1551,7 +1486,7 @@ private:
try
{
session_->executeBlocking(L"libssh2_sftp_setstat", //throw SysError, FatalSshError
- [&](const SshSession::Details& sd) { return ::libssh2_sftp_setstat(sd.sftpChannel, getLibssh2Path(filePath_).c_str(), &attribNew); }); //noexcept!
+ [&](const SshSession::Details& sd) { return ::libssh2_sftp_setstat(sd.sftpChannel, getLibssh2Path(filePath_), &attribNew); }); //noexcept!
}
catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(displayPath_)), e.toString()); }
catch (const FatalSshError& e) { throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(displayPath_)), e.toString()); } //SSH session corrupted! => caller (will/should) stop using session
@@ -1581,12 +1516,11 @@ public:
{
try
{
- warn_static("should we use ~ instead???") //https://curl.haxx.se/docs/faq.html#How_to_SFTP_from_my_user_s_home
-
//we never ever change the SFTP working directory, right? ...right?
return getServerRealPath("."); //throw SysError
+ //use "~" instead? NO: libssh2_sftp_realpath() fails with LIBSSH2_FX_NO_SUCH_FILE
}
- catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(getDisplayPath(AfsPath(Zstr("."))))), e.toString()); }
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(getDisplayPath(AfsPath(Zstr("~"))))), e.toString()); }
}
private:
@@ -1620,7 +1554,7 @@ private:
{
LIBSSH2_SFTP_ATTRIBUTES attr = {};
runSftpCommand(login_, L"libssh2_sftp_lstat", //throw SysError
- [&](const SshSession::Details& sd) { return ::libssh2_sftp_lstat(sd.sftpChannel, getLibssh2Path(afsPath).c_str(), &attr); }); //noexcept!
+ [&](const SshSession::Details& sd) { return ::libssh2_sftp_lstat(sd.sftpChannel, getLibssh2Path(afsPath), &attr); }); //noexcept!
if ((attr.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) == 0)
throw SysError(L"File attributes not available.");
@@ -1654,11 +1588,8 @@ private:
runSftpCommand(login_, L"libssh2_sftp_mkdir", //throw SysError
[&](const SshSession::Details& sd) //noexcept!
{
-#if 1 //let's see how LIBSSH2_SFTP_DEFAULT_MODE works out:
- return ::libssh2_sftp_mkdir(sd.sftpChannel, getLibssh2Path(afsPath).c_str(), LIBSSH2_SFTP_DEFAULT_MODE);
-#else //default for newly created directories: 0777
- return ::libssh2_sftp_mkdir(sd.sftpChannel, getLibssh2Path(afsPath).c_str(), LIBSSH2_SFTP_S_IRWXU | LIBSSH2_SFTP_S_IRWXG | LIBSSH2_SFTP_S_IRWXO);
-#endif
+ return ::libssh2_sftp_mkdir(sd.sftpChannel, getLibssh2Path(afsPath), LIBSSH2_SFTP_DEFAULT_MODE);
+ //default for newly created directories: 0777 (LIBSSH2_SFTP_S_IRWXU | LIBSSH2_SFTP_S_IRWXG | LIBSSH2_SFTP_S_IRWXO)
});
}
catch (const SysError& e) //libssh2_sftp_mkdir reports generic LIBSSH2_FX_FAILURE if existing
@@ -1672,7 +1603,7 @@ private:
try
{
runSftpCommand(login_, L"libssh2_sftp_unlink", //throw SysError
- [&](const SshSession::Details& sd) { return ::libssh2_sftp_unlink(sd.sftpChannel, getLibssh2Path(afsPath).c_str()); }); //noexcept!
+ [&](const SshSession::Details& sd) { return ::libssh2_sftp_unlink(sd.sftpChannel, getLibssh2Path(afsPath)); }); //noexcept!
}
catch (const SysError& e)
{
@@ -1691,7 +1622,7 @@ private:
try
{
runSftpCommand(login_, L"libssh2_sftp_rmdir", //throw SysError
- [&](const SshSession::Details& sd) { return delResult = ::libssh2_sftp_rmdir(sd.sftpChannel, getLibssh2Path(afsPath).c_str()); }); //noexcept!
+ [&](const SshSession::Details& sd) { return delResult = ::libssh2_sftp_rmdir(sd.sftpChannel, getLibssh2Path(afsPath)); }); //noexcept!
}
catch (const SysError& e)
{
@@ -1720,11 +1651,11 @@ private:
//----------------------------------------------------------------------------------------------------------------
AfsPath getServerRealPath(const std::string& sftpPath) const //throw SysError
{
- const unsigned int bufSize = 10000;
+ const size_t bufSize = 10000;
std::vector<char> buf(bufSize + 1); //ensure buffer is always null-terminated since we don't evaluate the byte count returned by libssh2_sftp_realpath()!
runSftpCommand(login_, L"libssh2_sftp_realpath", //throw SysError
- [&](const SshSession::Details& sd) { return ::libssh2_sftp_realpath(sd.sftpChannel, sftpPath.c_str(), &buf[0], bufSize); }); //noexcept!
+ [&](const SshSession::Details& sd) { return ::libssh2_sftp_realpath(sd.sftpChannel, sftpPath, &buf[0], bufSize); }); //noexcept!
const std::string sftpPathTrg = &buf[0];
if (!startsWith(sftpPathTrg, '/'))
@@ -1750,7 +1681,7 @@ private:
try
{
runSftpCommand(login_, L"libssh2_sftp_readlink", //throw SysError
- [&](const SshSession::Details& sd) { return ::libssh2_sftp_readlink(sd.sftpChannel, getLibssh2Path(afsPath).c_str(), &buf[0], bufSize); }); //noexcept!
+ [&](const SshSession::Details& sd) { return ::libssh2_sftp_readlink(sd.sftpChannel, getLibssh2Path(afsPath), &buf[0], bufSize); }); //noexcept!
}
catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(getDisplayPath(afsPath))), e.toString()); }
@@ -1824,7 +1755,7 @@ private:
try
{
- runSftpCommand(login_, L"libssh2_sftp_rename_ex", //throw SysError
+ runSftpCommand(login_, L"libssh2_sftp_rename", //throw SysError
[&](const SshSession::Details& sd) //noexcept!
{
/*
@@ -1840,10 +1771,7 @@ private:
const std::string sftpPathOld = getLibssh2Path(pathFrom);
const std::string sftpPathNew = getLibssh2Path(pathTo.afsPath);
- return ::libssh2_sftp_rename_ex(sd.sftpChannel,
- sftpPathOld.c_str(), static_cast<unsigned int>(sftpPathOld.size()),
- sftpPathNew.c_str(), static_cast<unsigned int>(sftpPathNew.size()),
- LIBSSH2_SFTP_RENAME_ATOMIC);
+ return ::libssh2_sftp_rename(sd.sftpChannel, sftpPathOld, sftpPathNew, LIBSSH2_SFTP_RENAME_ATOMIC);
});
}
catch (const SysError& e) //libssh2_sftp_rename_ex reports generic LIBSSH2_FX_FAILURE if target is already existing!
diff --git a/FreeFileSync/Source/base/algorithm.cpp b/FreeFileSync/Source/base/algorithm.cpp
index 820b1bdd..edb70cd1 100644
--- a/FreeFileSync/Source/base/algorithm.cpp
+++ b/FreeFileSync/Source/base/algorithm.cpp
@@ -102,7 +102,7 @@ public:
static void execute(const DirectionSet& dirCfgIn, ContainerObject& hierObj) { Redetermine(dirCfgIn).recurse(hierObj); }
private:
- Redetermine(const DirectionSet& dirCfgIn) : dirCfg(dirCfgIn) {}
+ Redetermine(const DirectionSet& dirCfgIn) : dirCfg_(dirCfgIn) {}
void recurse(ContainerObject& hierObj) const
{
@@ -128,26 +128,26 @@ private:
switch (cat)
{
case FILE_LEFT_SIDE_ONLY:
- file.setSyncDir(dirCfg.exLeftSideOnly);
+ file.setSyncDir(dirCfg_.exLeftSideOnly);
break;
case FILE_RIGHT_SIDE_ONLY:
- file.setSyncDir(dirCfg.exRightSideOnly);
+ file.setSyncDir(dirCfg_.exRightSideOnly);
break;
case FILE_RIGHT_NEWER:
- file.setSyncDir(dirCfg.rightNewer);
+ file.setSyncDir(dirCfg_.rightNewer);
break;
case FILE_LEFT_NEWER:
- file.setSyncDir(dirCfg.leftNewer);
+ file.setSyncDir(dirCfg_.leftNewer);
break;
case FILE_DIFFERENT_CONTENT:
- file.setSyncDir(dirCfg.different);
+ file.setSyncDir(dirCfg_.different);
break;
case FILE_CONFLICT:
case FILE_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize"
- if (dirCfg.conflict == SyncDirection::NONE)
+ if (dirCfg_.conflict == SyncDirection::NONE)
file.setSyncDirConflict(file.getCatExtraDescription()); //take over category conflict
else
- file.setSyncDir(dirCfg.conflict);
+ file.setSyncDir(dirCfg_.conflict);
break;
case FILE_EQUAL:
file.setSyncDir(SyncDirection::NONE);
@@ -160,26 +160,26 @@ private:
switch (symlink.getLinkCategory())
{
case SYMLINK_LEFT_SIDE_ONLY:
- symlink.setSyncDir(dirCfg.exLeftSideOnly);
+ symlink.setSyncDir(dirCfg_.exLeftSideOnly);
break;
case SYMLINK_RIGHT_SIDE_ONLY:
- symlink.setSyncDir(dirCfg.exRightSideOnly);
+ symlink.setSyncDir(dirCfg_.exRightSideOnly);
break;
case SYMLINK_LEFT_NEWER:
- symlink.setSyncDir(dirCfg.leftNewer);
+ symlink.setSyncDir(dirCfg_.leftNewer);
break;
case SYMLINK_RIGHT_NEWER:
- symlink.setSyncDir(dirCfg.rightNewer);
+ symlink.setSyncDir(dirCfg_.rightNewer);
break;
case SYMLINK_CONFLICT:
case SYMLINK_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize"
- if (dirCfg.conflict == SyncDirection::NONE)
+ if (dirCfg_.conflict == SyncDirection::NONE)
symlink.setSyncDirConflict(symlink.getCatExtraDescription()); //take over category conflict
else
- symlink.setSyncDir(dirCfg.conflict);
+ symlink.setSyncDir(dirCfg_.conflict);
break;
case SYMLINK_DIFFERENT_CONTENT:
- symlink.setSyncDir(dirCfg.different);
+ symlink.setSyncDir(dirCfg_.different);
break;
case SYMLINK_EQUAL:
symlink.setSyncDir(SyncDirection::NONE);
@@ -201,27 +201,27 @@ private:
switch (cat)
{
case DIR_LEFT_SIDE_ONLY:
- folder.setSyncDir(dirCfg.exLeftSideOnly);
+ folder.setSyncDir(dirCfg_.exLeftSideOnly);
break;
case DIR_RIGHT_SIDE_ONLY:
- folder.setSyncDir(dirCfg.exRightSideOnly);
+ folder.setSyncDir(dirCfg_.exRightSideOnly);
break;
case DIR_EQUAL:
folder.setSyncDir(SyncDirection::NONE);
break;
case DIR_CONFLICT:
case DIR_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize"
- if (dirCfg.conflict == SyncDirection::NONE)
+ if (dirCfg_.conflict == SyncDirection::NONE)
folder.setSyncDirConflict(folder.getCatExtraDescription()); //take over category conflict
else
- folder.setSyncDir(dirCfg.conflict);
+ folder.setSyncDir(dirCfg_.conflict);
break;
}
recurse(folder);
}
- const DirectionSet dirCfg;
+ const DirectionSet dirCfg_;
};
//---------------------------------------------------------------------------------------------------------------
@@ -235,8 +235,7 @@ bool allItemsCategoryEqual(const ContainerObject& hierObj)
std::all_of(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(),
[](const SymlinkPair& link) { return link.getLinkCategory() == SYMLINK_EQUAL; })&&
- std::all_of(hierObj.refSubFolders().begin(), hierObj.refSubFolders().end(),
- [](const FolderPair& folder)
+ std::all_of(hierObj.refSubFolders().begin(), hierObj.refSubFolders().end(), [](const FolderPair& folder)
{
return folder.getDirCategory() == DIR_EQUAL && allItemsCategoryEqual(folder); //short-circuit behavior!
});
diff --git a/FreeFileSync/Source/base/application.cpp b/FreeFileSync/Source/base/application.cpp
index 8007ee5e..3e347866 100644
--- a/FreeFileSync/Source/base/application.cpp
+++ b/FreeFileSync/Source/base/application.cpp
@@ -29,7 +29,6 @@
#include <gtk/gtk.h>
-
using namespace zen;
using namespace fff;
diff --git a/FreeFileSync/Source/base/db_file.cpp b/FreeFileSync/Source/base/db_file.cpp
index d27313bc..4e16c885 100644
--- a/FreeFileSync/Source/base/db_file.cpp
+++ b/FreeFileSync/Source/base/db_file.cpp
@@ -550,17 +550,6 @@ private:
process(hierObj.refSubFolders(), hierObj.getRelativePathAny(), dbFolder.folders);
}
- template <class M, class V>
- static V& mapAddOrUpdate(M& map, const Zstring& key, V&& value)
- {
- //C++17's map::try_emplace() is faster than map::emplace() if key is already existing
- const auto [it, inserted] = map.try_emplace(key, std::forward<V>(value)); //and does NOT MOVE r-value arguments unlike map::emplace()!
- if (!inserted)
- it->second = std::forward<V>(value);
-
- return it->second;
- }
-
void process(const ContainerObject::FileList& currentFiles, const Zstring& parentRelPath, InSyncFolder::FileList& dbFiles)
{
std::set<Zstring, LessUnicodeNormal> toPreserve;
@@ -573,17 +562,16 @@ private:
//Caveat: If FILE_EQUAL, we *implicitly* assume equal left and right short names matching case: InSyncFolder's mapping tables use short name as a key!
//This makes us silently dependent from code in algorithm.h!!!
assert(getUnicodeNormalForm(file.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(file.getItemName<RIGHT_SIDE>()));
- //this should be taken for granted:
assert(file.getFileSize<LEFT_SIDE>() == file.getFileSize<RIGHT_SIDE>());
//create or update new "in-sync" state
- mapAddOrUpdate(dbFiles, file.getItemNameAny(),
- InSyncFile(InSyncDescrFile(file.getLastWriteTime< LEFT_SIDE>(),
- file.getFileId < LEFT_SIDE>()),
- InSyncDescrFile(file.getLastWriteTime<RIGHT_SIDE>(),
- file.getFileId <RIGHT_SIDE>()),
- activeCmpVar_,
- file.getFileSize<LEFT_SIDE>()));
+ dbFiles.insert_or_assign(file.getItemNameAny(),
+ InSyncFile(InSyncDescrFile(file.getLastWriteTime< LEFT_SIDE>(),
+ file.getFileId < LEFT_SIDE>()),
+ InSyncDescrFile(file.getLastWriteTime<RIGHT_SIDE>(),
+ file.getFileId <RIGHT_SIDE>()),
+ activeCmpVar_,
+ file.getFileSize<LEFT_SIDE>()));
toPreserve.insert(file.getItemNameAny());
}
else //not in sync: preserve last synchronous state
@@ -594,7 +582,7 @@ private:
}
//delete removed items (= "in-sync") from database
- eraseIf(dbFiles, [&](const InSyncFolder::FileList::value_type& v) -> bool
+ eraseIf(dbFiles, [&](const InSyncFolder::FileList::value_type& v)
{
if (toPreserve.find(v.first) != toPreserve.end())
return false;
@@ -617,10 +605,10 @@ private:
assert(getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>()));
//create or update new "in-sync" state
- mapAddOrUpdate(dbSymlinks, symlink.getItemNameAny(),
- InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime< LEFT_SIDE>()),
- InSyncDescrLink(symlink.getLastWriteTime<RIGHT_SIDE>()),
- activeCmpVar_));
+ dbSymlinks.insert_or_assign(symlink.getItemNameAny(),
+ InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime< LEFT_SIDE>()),
+ InSyncDescrLink(symlink.getLastWriteTime<RIGHT_SIDE>()),
+ activeCmpVar_));
toPreserve.insert(symlink.getItemNameAny());
}
else //not in sync: preserve last synchronous state
@@ -631,7 +619,7 @@ private:
}
//delete removed items (= "in-sync") from database
- eraseIf(dbSymlinks, [&](const InSyncFolder::SymlinkList::value_type& v) -> bool
+ eraseIf(dbSymlinks, [&](const InSyncFolder::SymlinkList::value_type& v)
{
if (toPreserve.find(v.first) != toPreserve.end())
return false;
@@ -643,7 +631,7 @@ private:
void process(const ContainerObject::FolderList& currentFolders, const Zstring& parentRelPath, InSyncFolder::FolderList& dbFolders)
{
- std::unordered_set<const InSyncFolder*> toPreserve;
+ std::map<Zstring, const FolderPair*, LessUnicodeNormal> toPreserve;
for (const FolderPair& folder : currentFolders)
if (!folder.isPairEmpty())
@@ -654,35 +642,26 @@ private:
//update directory entry only (shallow), but do *not touch* existing child elements!!!
InSyncFolder& dbFolder = dbFolders.emplace(folder.getItemNameAny(), InSyncFolder(InSyncFolder::DIR_STATUS_IN_SYNC)).first->second; //get or create
- dbFolder.status = InSyncFolder::DIR_STATUS_IN_SYNC; //update immediate directory entry
+ dbFolder.status = InSyncFolder::DIR_STATUS_IN_SYNC;
- toPreserve.insert(&dbFolder);
- recurse(folder, dbFolder);
+ toPreserve.emplace(folder.getItemNameAny(), &folder);
}
else //not in sync: preserve last synchronous state
{
- auto preserveDbEntry = [&](const Zstring& folderName)
- {
- auto it = dbFolders.find(folderName);
- if (it != dbFolders.end())
- {
- toPreserve.insert(&it->second);
- recurse(folder, it->second); //required: existing child-items may not be in sync, but items deleted on both sides *are* in-sync!!!
- }
- };
- preserveDbEntry(folder.getItemName<LEFT_SIDE>());
-
- //folder match with names differing in case? => treat like any other folder rename => no *new* database entries even if child items are in sync
- if (getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>()))
- preserveDbEntry(folder.getItemName<RIGHT_SIDE>());
+ toPreserve.emplace(folder.getItemName< LEFT_SIDE>(), &folder); //names differing in case? => treat like any other folder rename
+ toPreserve.emplace(folder.getItemName<RIGHT_SIDE>(), &folder); //=> no *new* database entries even if child items are in sync
}
}
//delete removed items (= "in-sync") from database
- eraseIf(dbFolders, [&](InSyncFolder::FolderList::value_type& v) -> bool
+ eraseIf(dbFolders, [&](InSyncFolder::FolderList::value_type& v)
{
- if (toPreserve.find(&v.second) != toPreserve.end())
+ if (auto it = toPreserve.find(v.first); it != toPreserve.end())
+ {
+ recurse(*(it->second), v.second); //required even if e.g. DIR_LEFT_SIDE_ONLY:
+ //existing child-items may not be in sync, but items deleted on both sides *are* in-sync!!!
return false;
+ }
const Zstring& itemRelPath = nativeAppendPaths(parentRelPath, v.first);
//if folder is not included in "current folders", it is either not existing anymore, in which case it should be deleted from database
diff --git a/FreeFileSync/Source/base/file_hierarchy.cpp b/FreeFileSync/Source/base/file_hierarchy.cpp
index 56e65abc..40087637 100644
--- a/FreeFileSync/Source/base/file_hierarchy.cpp
+++ b/FreeFileSync/Source/base/file_hierarchy.cpp
@@ -215,6 +215,7 @@ SyncOperation FolderPair::getSyncOperation() const
case SO_MOVE_RIGHT_FROM:
case SO_MOVE_RIGHT_TO:
assert(false);
+ [[fallthrough]];
case SO_CREATE_NEW_LEFT:
case SO_CREATE_NEW_RIGHT:
case SO_OVERWRITE_LEFT:
diff --git a/FreeFileSync/Source/base/log_file.cpp b/FreeFileSync/Source/base/log_file.cpp
index 5538880f..8a2489f9 100644
--- a/FreeFileSync/Source/base/log_file.cpp
+++ b/FreeFileSync/Source/base/log_file.cpp
@@ -213,7 +213,7 @@ std::vector<LogFileInfo> getLogFiles(const AbstractPath& logFolderPath) //throw
isdigit(tsEnd[-1]))
{
tsBegin = tsEnd - TIME_STAMP_LENGTH;
- const TimeComp tc = parseTime(Zstr("%Y-%m-%d %H%M%S"), StringRef<const Zchar>(tsBegin, tsBegin + 17)); //returns TimeComp() on error
+ const TimeComp tc = parseTime(Zstr("%Y-%m-%d %H%M%S"), makeStringView(tsBegin, 17)); //returns TimeComp() on error
const time_t t = localToTimeT(tc); //returns -1 on error
if (t != -1)
{
diff --git a/FreeFileSync/Source/base/parse_lng.h b/FreeFileSync/Source/base/parse_lng.h
index e675864b..4e6cb6c6 100644
--- a/FreeFileSync/Source/base/parse_lng.h
+++ b/FreeFileSync/Source/base/parse_lng.h
@@ -293,7 +293,7 @@ public:
for (auto it = pos_; it != stream_.begin(); )
{
--it;
- if (*it == '\r' || *it == '\n')
+ if (zen::isLineBreak(*it))
return pos_ - it - 1;
}
return pos_ - stream_.begin();
@@ -308,7 +308,7 @@ private:
bool startsWith(const std::string& prefix) const
{
- return zen::startsWith(zen::StringRef<const char>(pos_, stream_.end()), prefix);
+ return zen::startsWith(zen::makeStringView(pos_, stream_.end()), prefix);
}
static void normalize(std::string& text)
diff --git a/FreeFileSync/Source/base/parse_plural.h b/FreeFileSync/Source/base/parse_plural.h
index 770e4947..242dd4a6 100644
--- a/FreeFileSync/Source/base/parse_plural.h
+++ b/FreeFileSync/Source/base/parse_plural.h
@@ -235,7 +235,7 @@ public:
private:
bool startsWith(const std::string& prefix) const
{
- return zen::startsWith(zen::StringRef<const char>(pos_, stream_.end()), prefix);
+ return zen::startsWith(zen::makeStringView(pos_, stream_.end()), prefix);
}
using TokenList = std::vector<std::pair<std::string, Token::Type>>;
diff --git a/FreeFileSync/Source/base/synchronization.cpp b/FreeFileSync/Source/base/synchronization.cpp
index 33923cc6..24272eb1 100644
--- a/FreeFileSync/Source/base/synchronization.cpp
+++ b/FreeFileSync/Source/base/synchronization.cpp
@@ -186,6 +186,7 @@ void SyncStatistics::processLink(const SymlinkPair& link)
case SO_MOVE_LEFT_TO:
case SO_MOVE_RIGHT_TO:
assert(false);
+ [[fallthrough]];
case SO_DO_NOTHING:
case SO_EQUAL:
break;
@@ -235,6 +236,7 @@ void SyncStatistics::processFolder(const FolderPair& folder)
case SO_MOVE_LEFT_TO:
case SO_MOVE_RIGHT_TO:
assert(false);
+ [[fallthrough]];
case SO_DO_NOTHING:
case SO_EQUAL:
break;
@@ -333,6 +335,7 @@ private:
case SO_MOVE_LEFT_TO:
case SO_MOVE_RIGHT_TO:
assert(false);
+ [[fallthrough]];
case SO_CREATE_NEW_LEFT:
case SO_CREATE_NEW_RIGHT:
case SO_OVERWRITE_LEFT:
@@ -1424,6 +1427,7 @@ FolderPairSyncer::PassNo FolderPairSyncer::getPass(const SymlinkPair& link)
case SO_MOVE_LEFT_TO:
case SO_MOVE_RIGHT_TO:
assert(false);
+ [[fallthrough]];
case SO_DO_NOTHING:
case SO_EQUAL:
case SO_UNRESOLVED_CONFLICT:
@@ -1456,6 +1460,7 @@ FolderPairSyncer::PassNo FolderPairSyncer::getPass(const FolderPair& folder)
case SO_MOVE_LEFT_TO:
case SO_MOVE_RIGHT_TO:
assert(false);
+ [[fallthrough]];
case SO_DO_NOTHING:
case SO_EQUAL:
case SO_UNRESOLVED_CONFLICT:
diff --git a/FreeFileSync/Source/base/versioning.cpp b/FreeFileSync/Source/base/versioning.cpp
index 1a2bf050..37dd35e4 100644
--- a/FreeFileSync/Source/base/versioning.cpp
+++ b/FreeFileSync/Source/base/versioning.cpp
@@ -36,17 +36,17 @@ Zstring getDotExtension(const Zstring& filePath) //including "." if extension is
//or "Sample 2012-05-15 131513"
std::pair<time_t, Zstring> fff::impl::parseVersionedFileName(const Zstring& fileName)
{
- const StringRef<const Zchar> ext(findLast(fileName.begin(), fileName.end(), Zstr('.')), fileName.end());
+ const auto ext = makeStringView(findLast(fileName.begin(), fileName.end(), Zstr('.')), fileName.end());
if (fileName.size() < 2 * ext.length() + 18)
return {};
const auto itExt1 = fileName.end() - (2 * ext.length() + 18);
- const auto itTs = itExt1 + ext.length();
- if (!equalString(ext, StringRef<const Zchar>(itExt1, itTs)))
+ if (!equalString(ext, makeStringView(itExt1, ext.length())))
return {};
- const TimeComp tc = parseTime(Zstr(" %Y-%m-%d %H%M%S"), StringRef<const Zchar>(itTs, itTs + 18)); //returns TimeComp() on error
+ const auto itTs = itExt1 + ext.length();
+ const TimeComp tc = parseTime(Zstr(" %Y-%m-%d %H%M%S"), makeStringView(itTs, 18)); //returns TimeComp() on error
const time_t t = localToTimeT(tc); //returns -1 on error
if (t == -1)
return {};
diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp
index 4a7d822a..d384e0bb 100644
--- a/FreeFileSync/Source/ui/gui_generated.cpp
+++ b/FreeFileSync/Source/ui/gui_generated.cpp
@@ -4870,9 +4870,9 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer171->Add( m_hyperlink11, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
- m_hyperlink7 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxWidgets"), wxT("http://www.wxwidgets.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink7 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("wxWidgets"), wxT("https://www.wxwidgets.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
m_hyperlink7->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink7->SetToolTip( _("http://www.wxwidgets.org") );
+ m_hyperlink7->SetToolTip( _("https://www.wxwidgets.org") );
bSizer171->Add( m_hyperlink7, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
@@ -4924,11 +4924,16 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer172->Add( m_hyperlink18, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
- m_hyperlink9 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Inno Setup"), wxT("http://www.jrsoftware.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink9 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Inno Setup"), wxT("http://www.jrsoftware.org/isinfo.php"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
m_hyperlink9->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink9->SetToolTip( _("http://www.jrsoftware.org") );
+ m_hyperlink9->SetToolTip( _("http://www.jrsoftware.org/isinfo.php") );
- bSizer172->Add( m_hyperlink9, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer172->Add( m_hyperlink9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+
+ m_hyperlink25 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("xBRZ"), wxT("https://sourceforge.net/projects/xbrz/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink25->SetToolTip( _("https://sourceforge.net/projects/xbrz/") );
+
+ bSizer172->Add( m_hyperlink25, 0, wxALIGN_CENTER_VERTICAL, 5 );
bSizer187->Add( bSizer172, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 );
@@ -5142,7 +5147,7 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id,
m_staticTextMain = new wxStaticText( m_panel35, wxID_ANY, _("Activate the FreeFileSync Donation Edition by one of the following methods:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextMain->Wrap( -1 );
- bSizer172->Add( m_staticTextMain, 0, wxBOTTOM|wxRIGHT|wxLEFT, 10 );
+ bSizer172->Add( m_staticTextMain, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 10 );
m_panel35->SetSizer( bSizer172 );
@@ -5165,18 +5170,22 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id,
wxBoxSizer* bSizer234;
bSizer234 = new wxBoxSizer( wxHORIZONTAL );
- m_staticText136 = new wxStaticText( m_panel3511, wxID_ANY, _("1. Activate via internet now:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextMain1 = new wxStaticText( m_panel3511, wxID_ANY, _("1."), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextMain1->Wrap( -1 );
+ bSizer234->Add( m_staticTextMain1, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_staticText136 = new wxStaticText( m_panel3511, wxID_ANY, _("Activate via internet now:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText136->Wrap( -1 );
- bSizer234->Add( m_staticText136, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
+ bSizer234->Add( m_staticText136, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
m_buttonActivateOnline = new wxButton( m_panel3511, wxID_ANY, _("Activate online"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_buttonActivateOnline->SetDefault();
m_buttonActivateOnline->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
- bSizer234->Add( m_buttonActivateOnline, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+ bSizer234->Add( m_buttonActivateOnline, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer263->Add( bSizer234, 0, wxEXPAND|wxALL, 5 );
+ bSizer263->Add( bSizer234, 0, wxEXPAND|wxALL, 10 );
m_panel3511->SetSizer( bSizer263 );
@@ -5202,41 +5211,45 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id,
wxBoxSizer* bSizer236;
bSizer236 = new wxBoxSizer( wxHORIZONTAL );
- m_staticText1361 = new wxStaticText( m_panel351, wxID_ANY, _("2. Retrieve an offline activation key from the following URL:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText175 = new wxStaticText( m_panel351, wxID_ANY, _("2."), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText175->Wrap( -1 );
+ bSizer236->Add( m_staticText175, 0, wxRIGHT|wxALIGN_BOTTOM, 5 );
+
+ m_staticText1361 = new wxStaticText( m_panel351, wxID_ANY, _("Retrieve an offline activation key from the following URL:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText1361->Wrap( -1 );
- bSizer236->Add( m_staticText1361, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
+ bSizer236->Add( m_staticText1361, 1, wxRIGHT|wxALIGN_BOTTOM, 5 );
m_buttonCopyUrl = new wxButton( m_panel351, wxID_ANY, _("&Copy to clipboard"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_buttonCopyUrl->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
- bSizer236->Add( m_buttonCopyUrl, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+ bSizer236->Add( m_buttonCopyUrl, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer237->Add( bSizer236, 0, wxEXPAND, 5 );
+ bSizer237->Add( bSizer236, 0, wxEXPAND|wxBOTTOM, 5 );
m_textCtrlManualActivationUrl = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, 55 ), wxTE_MULTILINE|wxTE_READONLY );
- bSizer237->Add( m_textCtrlManualActivationUrl, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 );
+ bSizer237->Add( m_textCtrlManualActivationUrl, 0, wxEXPAND|wxBOTTOM, 5 );
wxBoxSizer* bSizer235;
bSizer235 = new wxBoxSizer( wxHORIZONTAL );
m_staticText13611 = new wxStaticText( m_panel351, wxID_ANY, _("Enter activation key:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText13611->Wrap( -1 );
- bSizer235->Add( m_staticText13611, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
+ bSizer235->Add( m_staticText13611, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
m_textCtrlOfflineActivationKey = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 250, -1 ), wxTE_PROCESS_ENTER );
- bSizer235->Add( m_textCtrlOfflineActivationKey, 1, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer235->Add( m_textCtrlOfflineActivationKey, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
m_buttonActivateOffline = new wxButton( m_panel351, wxID_ANY, _("Activate offline"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_buttonActivateOffline->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
- bSizer235->Add( m_buttonActivateOffline, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer235->Add( m_buttonActivateOffline, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer237->Add( bSizer235, 0, wxEXPAND, 5 );
+ bSizer237->Add( bSizer235, 0, wxEXPAND|wxTOP, 5 );
- bSizer266->Add( bSizer237, 0, wxALL|wxEXPAND, 5 );
+ bSizer266->Add( bSizer237, 0, wxALL|wxEXPAND, 10 );
m_panel351->SetSizer( bSizer266 );
diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h
index 41febc65..44a4df3f 100644
--- a/FreeFileSync/Source/ui/gui_generated.h
+++ b/FreeFileSync/Source/ui/gui_generated.h
@@ -1166,6 +1166,7 @@ protected:
wxHyperlinkCtrl* m_hyperlink101;
wxHyperlinkCtrl* m_hyperlink18;
wxHyperlinkCtrl* m_hyperlink9;
+ wxHyperlinkCtrl* m_hyperlink25;
wxStaticLine* m_staticline34;
wxStaticText* m_staticText93;
wxStaticBitmap* m_bitmapGpl;
@@ -1235,11 +1236,13 @@ protected:
wxStaticLine* m_staticline181;
wxStaticLine* m_staticline18111;
wxPanel* m_panel3511;
+ wxStaticText* m_staticTextMain1;
wxStaticText* m_staticText136;
wxButton* m_buttonActivateOnline;
wxStaticLine* m_staticline181111;
wxStaticLine* m_staticline181112;
wxPanel* m_panel351;
+ wxStaticText* m_staticText175;
wxStaticText* m_staticText1361;
wxButton* m_buttonCopyUrl;
wxTextCtrl* m_textCtrlManualActivationUrl;
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp
index 1721245a..1c327b25 100644
--- a/FreeFileSync/Source/ui/main_dlg.cpp
+++ b/FreeFileSync/Source/ui/main_dlg.cpp
@@ -1300,7 +1300,7 @@ void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& sel
}
catch (AbortProcess&) {}
- StatusHandlerTemporaryPanel::Result r = statusHandler.reportFinalStatus(); //noexcept
+ const StatusHandlerTemporaryPanel::Result r = statusHandler.reportFinalStatus(); //noexcept
setLastOperationLog(r.summary, r.errorLog);
@@ -1345,7 +1345,7 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec
}
catch (AbortProcess&) {}
- StatusHandlerTemporaryPanel::Result r = statusHandler.reportFinalStatus(); //noexcept
+ const StatusHandlerTemporaryPanel::Result r = statusHandler.reportFinalStatus(); //noexcept
setLastOperationLog(r.summary, r.errorLog);
@@ -1553,7 +1553,7 @@ void MainDialog::openExternalApplication(const Zstring& commandLinePhrase, bool
}
catch (AbortProcess&) {}
- StatusHandlerTemporaryPanel::Result r = statusHandler.reportFinalStatus(); //noexcept
+ const StatusHandlerTemporaryPanel::Result r = statusHandler.reportFinalStatus(); //noexcept
setLastOperationLog(r.summary, r.errorLog);
@@ -3786,6 +3786,8 @@ void MainDialog::OnCompare(wxCommandEvent& event)
const std::chrono::system_clock::time_point startTime = std::chrono::system_clock::now();
+ const std::vector<FolderPairCfg>& fpCfgList = extractCompareCfg(guiCfg.mainCfg);
+
//handle status display and error messages
StatusHandlerTemporaryPanel statusHandler(*this, startTime,
guiCfg.mainCfg.ignoreErrors,
@@ -3803,12 +3805,12 @@ void MainDialog::OnCompare(wxCommandEvent& event)
globalCfg_.runWithBackgroundPriority,
globalCfg_.createLockFile,
dirLocks,
- extractCompareCfg(guiCfg.mainCfg),
+ fpCfgList,
statusHandler); //throw AbortProcess
}
catch (AbortProcess&) {}
- StatusHandlerTemporaryPanel::Result r = statusHandler.reportFinalStatus(); //noexcept
+ const StatusHandlerTemporaryPanel::Result r = statusHandler.reportFinalStatus(); //noexcept
//---------------------------------------------------------------------------
setLastOperationLog(r.summary, r.errorLog);
@@ -3840,22 +3842,28 @@ void MainDialog::OnCompare(wxCommandEvent& event)
if (!IsActive())
RequestUserAttention();
- //add to folder history after successful comparison only
- folderHistoryLeft_ .ref().addItem(utfTo<Zstring>(m_folderPathLeft ->GetValue()));
- folderHistoryRight_.ref().addItem(utfTo<Zstring>(m_folderPathRight->GetValue()));
+ //remember folder history (unless cancelled by user)
+ for (const FolderPairCfg& fpCfg : fpCfgList)
+ {
+ folderHistoryLeft_ .ref().addItem(fpCfg.folderPathPhraseLeft_);
+ folderHistoryRight_.ref().addItem(fpCfg.folderPathPhraseRight_);
+ }
assert(m_buttonCompare->GetId() != wxID_ANY);
if (fp.getFocusId() == m_buttonCompare->GetId())
fp.setFocus(m_buttonSync);
- //prepare status information
- if (allElementsEqual(folderCmp_))
+ //mark selected cfg files as "in sync" when there is nothing to do: https://freefilesync.org/forum/viewtopic.php?t=4991
+ if (r.summary.finalStatus == SyncResult::finishedSuccess)
{
- flashStatusInformation(_("All files are in sync"));
-
- //update last sync date for selected cfg files https://freefilesync.org/forum/viewtopic.php?t=4991
- if (r.summary.finalStatus == SyncResult::finishedSuccess)
+ const SyncStatistics st(folderCmp_);
+ if (st.createCount() +
+ st.updateCount() +
+ st.deleteCount() == 0)
+ {
+ flashStatusInformation(_("All files are in sync"));
updateConfigLastRunStats(std::chrono::system_clock::to_time_t(startTime), r.summary.finalStatus, getNullPath() /*logFilePath*/);
+ }
}
}
@@ -4228,7 +4236,7 @@ void MainDialog::startSyncForSelecction(const std::vector<FileSystemObject*>& se
}
catch (AbortProcess&) {}
- StatusHandlerTemporaryPanel::Result r = statusHandler.reportFinalStatus(); //noexcept
+ const StatusHandlerTemporaryPanel::Result r = statusHandler.reportFinalStatus(); //noexcept
setLastOperationLog(r.summary, r.errorLog);
} //run updateGui() *after* reverting our temporary exclusions
diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp
index 394fb60e..87b75607 100644
--- a/FreeFileSync/Source/ui/progress_indicator.cpp
+++ b/FreeFileSync/Source/ui/progress_indicator.cpp
@@ -39,11 +39,11 @@ using namespace fff;
namespace
{
-//window size used for statistics
-const std::chrono::seconds WINDOW_REMAINING_TIME(60); //USB memory stick scenario can have drop outs of 40 seconds => 60 sec. window size handles it
-const std::chrono::seconds WINDOW_BYTES_PER_SEC (5); //
-const std::chrono::milliseconds SPEED_ESTIMATE_UPDATE_INTERVAL(500);
-const std::chrono::seconds SPEED_ESTIMATE_SAMPLE_INTERVAL(1);
+constexpr std::chrono::seconds WINDOW_BYTES_PER_SEC (5); //window size used for statistics
+constexpr std::chrono::seconds WINDOW_REMAINING_TIME(60); //USB memory stick scenario can have drop outs of 40 seconds => 60 sec. window size handles it
+constexpr std::chrono::seconds SPEED_ESTIMATE_SAMPLE_SKIP(1);
+constexpr std::chrono::milliseconds SPEED_ESTIMATE_UPDATE_INTERVAL(500);
+constexpr std::chrono::seconds GRAPH_TOTAL_TIME_UPDATE_INTERVAL(2);
const size_t PROGRESS_GRAPH_SAMPLE_SIZE_MAX = 2500000; //sizeof(single node) worst case ~ 3 * 8 byte ptr + 16 byte key/value = 40 byte
@@ -351,13 +351,12 @@ void CompareProgressDialog::Impl::updateProgressGui()
wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") :
wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged);
- if (numeric::dist(timeLastSpeedEstimate_, timeElapsed) >= SPEED_ESTIMATE_UPDATE_INTERVAL)
- {
- if (haveTotalStats) //remaining time and speed: only visible during binary comparison
+ if (haveTotalStats) //remaining time and speed: only visible during binary comparison
+ if (numeric::dist(timeLastSpeedEstimate_, timeElapsed) >= SPEED_ESTIMATE_UPDATE_INTERVAL)
{
timeLastSpeedEstimate_ = timeElapsed;
- if (numeric::dist(phaseStart_, timeElapsed) >= SPEED_ESTIMATE_SAMPLE_INTERVAL) //discard stats for first second: probably messy
+ if (numeric::dist(phaseStart_, timeElapsed) >= SPEED_ESTIMATE_SAMPLE_SKIP) //discard stats for first second: probably messy
perf_.addSample(timeElapsed, itemsCurrent, bytesCurrent);
//current speed -> Win 7 copy uses 1 sec update interval instead
@@ -371,7 +370,6 @@ void CompareProgressDialog::Impl::updateProgressGui()
std::optional<double> remTimeSec = perf_.getRemainingTimeSec(bytesTotal - bytesCurrent);
setText(*m_staticTextTimeRemaining, remTimeSec ? formatRemainingTime(*remTimeSec) : std::wstring(1, EM_DASH), &layoutChanged);
}
- }
if (haveTotalStats)
m_panelProgressGraph->Refresh();
@@ -495,7 +493,7 @@ class CurveDataTotalBlock : public CurveData
{
public:
void setValue(double x1, double x2, double y) { x1_ = x1; x2_ = x2; y_ = y; }
- void setTimes(double x1, double x2) { x1_ = x1; x2_ = x2; }
+ void setTotalTime(double x2) { x2_ = x2; }
double getTotalTime() const { return x2_; }
private:
@@ -522,6 +520,7 @@ class CurveDataProcessedBlock : public CurveData
{
public:
void setValue(double x1, double x2, double y1, double y2) { x1_ = x1; x2_ = x2; y1_ = y1; y2_ = y2; }
+ void setTotalTime(double x2) { x2_ = x2; }
private:
std::pair<double, double> getRangeX() const override { return { x1_, x2_ }; }
@@ -718,7 +717,8 @@ private:
//remaining time
PerfCheck perf_{ WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC };
- std::chrono::nanoseconds timeLastSpeedEstimate_ = std::chrono::seconds(-100); //used for calculating intervals between collecting perf samples
+ std::chrono::nanoseconds timeLastSpeedEstimate_ = std::chrono::seconds(-100); //used for calculating intervals between collecting perf samples
+ std::chrono::nanoseconds timeLastGraphTotalUpdate_ = std::chrono::seconds(-100);
//help calculate total speed
std::chrono::nanoseconds phaseStart_{}; //begin of current phase
@@ -942,8 +942,8 @@ void SyncProgressDialogImpl<TopLevelDialog>::initNewPhase()
updateStaticGui(); //evaluates "syncStat_->currentPhase()"
//reset graphs (e.g. after binary comparison)
- curveDataBytesTotal_ ->setTimes(0, 0);
- curveDataItemsTotal_ ->setTimes(0, 0);
+ curveDataBytesTotal_ ->setValue(0, 0, 0);
+ curveDataItemsTotal_ ->setValue(0, 0, 0);
curveDataBytesCurrent_->setValue(0, 0, 0, 0);
curveDataItemsCurrent_->setValue(0, 0, 0, 0);
curveDataBytes_ ->clear();
@@ -953,7 +953,7 @@ void SyncProgressDialogImpl<TopLevelDialog>::initNewPhase()
//start new measurement
perf_ = PerfCheck(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC);
- timeLastSpeedEstimate_ = std::chrono::seconds(-100); //make sure estimate is updated upon next check
+ timeLastGraphTotalUpdate_ = timeLastSpeedEstimate_ = std::chrono::seconds(-100); //make sure estimate is updated upon next check
phaseStart_ = stopWatch_.elapsed();
updateProgressGui(false /*allowYield*/);
@@ -1106,7 +1106,7 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield)
{
timeLastSpeedEstimate_ = timeElapsed;
- if (numeric::dist(phaseStart_, timeElapsed) >= SPEED_ESTIMATE_SAMPLE_INTERVAL) //discard stats for first second: probably messy
+ if (numeric::dist(phaseStart_, timeElapsed) >= SPEED_ESTIMATE_SAMPLE_SKIP) //discard stats for first second: probably messy
perf_.addSample(timeElapsed, itemsCurrent, bytesCurrent);
//current speed -> Win 7 copy uses 1 sec update interval instead
@@ -1128,16 +1128,23 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield)
std::optional<double> remTimeSec = perf_.getRemainingTimeSec(bytesTotal - bytesCurrent);
setText(*pnl_.m_staticTextTimeRemaining, remTimeSec ? formatRemainingTime(*remTimeSec) : std::wstring(1, EM_DASH), &layoutChanged);
- //update estimated total time marker with precision of "10% remaining time" only to avoid needless jumping around:
const double timeRemainingSec = remTimeSec ? *remTimeSec : 0;
const double timeTotalSec = timeElapsedDouble + timeRemainingSec;
- if (numeric::dist(curveDataBytesTotal_->getTotalTime(), timeTotalSec) > 0.1 * timeRemainingSec)
+ //update estimated total time marker only with precision of "15% remaining time" to avoid needless jumping around:
+ if (numeric::dist(curveDataBytesTotal_->getTotalTime(), timeTotalSec) > 0.15 * timeRemainingSec)
{
- curveDataBytesTotal_->setTimes(timeElapsedDouble, timeTotalSec);
- curveDataItemsTotal_->setTimes(timeElapsedDouble, timeTotalSec);
- //don't forget to update these, too:
- curveDataBytesCurrent_->setValue(timeElapsedDouble, timeTotalSec, bytesCurrent, bytesTotal);
- curveDataItemsCurrent_->setValue(timeElapsedDouble, timeTotalSec, itemsCurrent, itemsTotal);
+ //avoid needless flicker and don't update total time graph too often:
+ static_assert(std::chrono::duration_cast<std::chrono::milliseconds>(GRAPH_TOTAL_TIME_UPDATE_INTERVAL).count() % SPEED_ESTIMATE_UPDATE_INTERVAL.count() == 0);
+ if (numeric::dist(timeLastGraphTotalUpdate_, timeElapsed) >= GRAPH_TOTAL_TIME_UPDATE_INTERVAL)
+ {
+ timeLastGraphTotalUpdate_ = timeElapsed;
+
+ curveDataBytesTotal_->setTotalTime(timeTotalSec);
+ curveDataItemsTotal_->setTotalTime(timeTotalSec);
+ //don't forget to update these, too:
+ curveDataBytesCurrent_->setTotalTime(timeTotalSec);
+ curveDataItemsCurrent_->setTotalTime(timeTotalSec);
+ }
}
}
}
diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp
index 44a4d7c2..aeb36d6a 100644
--- a/FreeFileSync/Source/ui/small_dlgs.cpp
+++ b/FreeFileSync/Source/ui/small_dlgs.cpp
@@ -111,9 +111,9 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent)
#endif
build +=
-#ifdef ZEN_BUILD_32BIT
+#if ZEN_BUILD_ARCH == ZEN_ARCH_32BIT
L" x86";
-#elif defined ZEN_BUILD_64BIT
+#else
L" x64";
#endif
@@ -478,7 +478,9 @@ bool CloudSetupDlg::acceptFileDrop(const std::vector<Zstring>& shellItemPaths)
if (shellItemPaths.empty())
return false;
const Zstring ext = getFileExtension(shellItemPaths[0]);
- return ext.empty() || equalAsciiNoCase(ext, Zstr("pem"));
+ return ext.empty() ||
+ equalAsciiNoCase(ext, Zstr("pem")) ||
+ equalAsciiNoCase(ext, Zstr("ppk"));
}
@@ -503,7 +505,9 @@ void CloudSetupDlg::OnSelectKeyfile(wxCommandEvent& event)
wxString(), //message
beforeLast(m_textCtrlKeyfilePath->GetValue(), utfTo<wxString>(FILE_NAME_SEPARATOR), IF_MISSING_RETURN_NONE), //default folder
wxString(), //default file name
- _("All files") + L" (*.*)|*" + L"|" + L"OpenSSL PEM (*.pem)|*.pem",
+ _("All files") + L" (*.*)|*" +
+ L"|" + L"OpenSSL PEM (*.pem)|*.pem" +
+ L"|" + L"PuTTY Private Key (*.ppk)|*.ppk",
wxFD_OPEN);
if (filePicker.ShowModal() == wxID_OK)
m_textCtrlKeyfilePath->ChangeValue(filePicker.GetPath());
diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp
index 9d4ae8ef..fffe1dc0 100644
--- a/FreeFileSync/Source/ui/version_check.cpp
+++ b/FreeFileSync/Source/ui/version_check.cpp
@@ -133,11 +133,7 @@ std::vector<std::pair<std::string, std::string>> geHttpPostParameters()
params.emplace_back("os_version", numberTo<std::string>(osvMajor) + "." + numberTo<std::string>(osvMinor));
-#ifdef ZEN_BUILD_32BIT
- const char* osArch = "32";
-#elif defined ZEN_BUILD_64BIT
- const char* osArch = "64";
-#endif
+ const char* osArch = ZEN_STRINGIZE_NUMBER(ZEN_BUILD_ARCH);
params.emplace_back("os_arch", osArch);
params.emplace_back("language", utfTo<std::string>(getIso639Language()));
diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h
index aee4ad4d..662c0ea3 100644
--- a/FreeFileSync/Source/version/version.h
+++ b/FreeFileSync/Source/version/version.h
@@ -3,7 +3,7 @@
namespace fff
{
-const char ffsVersion[] = "10.16"; //internal linkage!
+const char ffsVersion[] = "10.17"; //internal linkage!
const char FFS_VERSION_SEPARATOR = '.';
}
diff --git a/zen/basic_math.h b/zen/basic_math.h
index b9be28be..8a32ee69 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -59,6 +59,9 @@ const double e = 2.71828182845904523536;
const double sqrt2 = 1.41421356237309504880;
const double ln2 = 0.693147180559945309417;
+#if __cpp_lib_math_constants //C++20
+ #error implement math constants from <numbers> header
+#endif
//static_assert(pi + e + sqrt2 + ln2 == 7.9672352249818781, "whoopsie");
//----------------------------------------------------------------------------------
diff --git a/zen/build_info.h b/zen/build_info.h
index e80f3721..01f1aeb8 100644
--- a/zen/build_info.h
+++ b/zen/build_info.h
@@ -7,20 +7,24 @@
#ifndef BUILD_INFO_H_5928539285603428657
#define BUILD_INFO_H_5928539285603428657
-//determine build info: defines ZEN_BUILD_32BIT or ZEN_BUILD_64BIT
+ #include <bit> //std::endian
+
+#define ZEN_ARCH_32BIT 32
+#define ZEN_ARCH_64BIT 64
#ifdef __LP64__
- #define ZEN_BUILD_64BIT
+ #define ZEN_BUILD_ARCH ZEN_ARCH_64BIT
#else
- #define ZEN_BUILD_32BIT
+ #define ZEN_BUILD_ARCH ZEN_ARCH_32BIT
#endif
-#ifdef ZEN_BUILD_32BIT
- static_assert(sizeof(void*) == 4);
-#endif
+static_assert(ZEN_BUILD_ARCH == sizeof(void*) * 8);
+
+//--------------------------------------------------------------------
-#ifdef ZEN_BUILD_64BIT
- static_assert(sizeof(void*) == 8);
-#endif
+constexpr bool usingLittleEndian()
+{
+ return std::endian::native == std::endian::little;
+}
#endif //BUILD_INFO_H_5928539285603428657
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 483d4b01..e23d48be 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -138,7 +138,7 @@ std::optional<ItemType> zen::itemStillExists(const Zstring& itemPath) //throw Fi
}
catch (const ItemType&) //finding the item after getItemType() previously failed is exceptional
{
- throw e; //yes, slicing
+ throw FileError(_("Temporary access error:") + L' ' + e.toString());
}
return {};
}
@@ -577,7 +577,7 @@ void zen::copySymlink(const Zstring& sourcePath, const Zstring& targetPath, bool
if (::symlink(linkPath.c_str(), targetPath.c_str()) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourcePath)), L"%y", L"\n" + fmtPath(targetPath)), L"symlink");
- //allow only consistent objects to be created -> don't place before ::symlink, targetPath may already exist!
+ //allow only consistent objects to be created -> don't place before ::symlink(); targetPath may already exist!
ZEN_ON_SCOPE_FAIL(try { removeSymlinkPlain(targetPath); /*throw FileError*/ }
catch (FileError&) {});
diff --git a/zen/i18n.h b/zen/i18n.h
index 2ecee45a..a70649fe 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -22,6 +22,8 @@
//source and translation are required to use %x as number placeholder
//for plural form, which will be substituted automatically!!!
+ static_assert(WXINTL_NO_GETTEXT_MACRO, "...must be defined to deactivate wxWidgets underscore macro");
+
namespace zen
{
//implement handler to enable program-wide localizations:
@@ -52,9 +54,6 @@ std::shared_ptr<const TranslationHandler> getTranslator();
-
-
-
//######################## implementation ##############################
namespace impl
{
diff --git a/zen/json.h b/zen/json.h
index 15157cf7..e6464286 100644
--- a/zen/json.h
+++ b/zen/json.h
@@ -403,7 +403,7 @@ public:
for (auto it = pos_; it != stream_.begin(); )
{
--it;
- if (*it == '\r' || *it == '\n')
+ if (isLineBreak(*it))
return pos_ - it - 1;
}
return pos_ - stream_.begin();
@@ -418,7 +418,7 @@ private:
bool startsWith(const std::string& prefix) const
{
- return zen::startsWith(StringRef<const char>(pos_, stream_.end()), prefix);
+ return zen::startsWith(makeStringView(pos_, stream_.end()), prefix);
}
const std::string stream_;
diff --git a/zen/legacy_compiler.cpp b/zen/legacy_compiler.cpp
new file mode 100644
index 00000000..3e3b7ba7
--- /dev/null
+++ b/zen/legacy_compiler.cpp
@@ -0,0 +1,32 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "legacy_compiler.h"
+#include <charconv>
+//1. including this one in string_tools.h blows up VC++:
+// "An internal error has occurred in the compiler. (compiler file 'd:\agent\_work\1\s\src\vctools\Compiler\Utc\src\p2\p2symtab.c', line 2618)"
+//2. using inside PCH: "fatal error C1076: compiler limit: internal heap limit reached"
+
+
+#if __cpp_lib_to_chars
+ #error get rid of workarounds
+#endif
+
+double zen::from_chars(const char* first, const char* last)
+{
+ return std::strtod(std::string(first, last).c_str(), nullptr);
+}
+
+
+const char* zen::to_chars(char* first, char* last, double num)
+{
+ const size_t bufSize = last - first;
+ const int charsWritten = std::snprintf(first, bufSize, "%g", num);
+ //C99: returns number of chars written if successful, < 0 or >= bufferSize on failure
+
+ return 0 <= charsWritten && charsWritten < static_cast<int>(bufSize) ?
+ first + charsWritten : first;
+}
diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h
index 5b69ed94..d0b4d3fe 100644
--- a/zen/legacy_compiler.h
+++ b/zen/legacy_compiler.h
@@ -16,6 +16,10 @@ namespace std
+#if __cpp_lib_span
+ #error get rid of workarounds
+#endif
+
//requires C++20! until then, this should suffice...
template <class T>
class span
@@ -49,4 +53,10 @@ private:
};
}
+namespace zen
+{
+double from_chars(const char* first, const char* last);
+const char* to_chars(char* first, char* last, double num);
+}
+
#endif //LEGACY_COMPILER_H_839567308565656789
diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp
index f3fd7219..ce05de53 100644
--- a/zen/open_ssl.cpp
+++ b/zen/open_ssl.cpp
@@ -5,6 +5,8 @@
// *****************************************************************************
#include "open_ssl.h"
+#include "base64.h"
+#include "build_info.h"
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
@@ -16,9 +18,7 @@ using namespace zen;
#error FFS, we are royally screwed!
#endif
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- #error OpenSSL version too old
-#endif
+static_assert(OPENSSL_VERSION_NUMBER >= 0x10100000L, "OpenSSL version too old");
void zen::openSslInit()
@@ -108,11 +108,11 @@ std::shared_ptr<EVP_PKEY> streamToEvpKey(const std::string& keyStream, BioToEvpF
throw SysError(formatLastOpenSSLError(L"BIO_new_mem_buf"));
ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
- if (EVP_PKEY* evpKey = bioToEvp(bio, //BIO* bp,
- nullptr, //EVP_PKEY** x,
- nullptr, //pem_password_cb* cb,
- nullptr)) //void* u
- return std::shared_ptr<EVP_PKEY>(evpKey, ::EVP_PKEY_free);
+ if (EVP_PKEY* evp = bioToEvp(bio, //BIO* bp,
+ nullptr, //EVP_PKEY** x,
+ nullptr, //pem_password_cb* cb,
+ nullptr)) //void* u
+ return std::shared_ptr<EVP_PKEY>(evp, ::EVP_PKEY_free);
throw SysError(formatLastOpenSSLError(functionName));
}
@@ -134,12 +134,12 @@ std::shared_ptr<EVP_PKEY> streamToEvpKey(const std::string& keyStream, BioToRsaF
throw SysError(formatLastOpenSSLError(functionName));
ZEN_ON_SCOPE_EXIT(::RSA_free(rsa));
- EVP_PKEY* evpKey = ::EVP_PKEY_new();
- if (!evpKey)
+ EVP_PKEY* evp = ::EVP_PKEY_new();
+ if (!evp)
throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new"));
- std::shared_ptr<EVP_PKEY> sharedKey(evpKey, ::EVP_PKEY_free);
+ std::shared_ptr<EVP_PKEY> sharedKey(evp, ::EVP_PKEY_free);
- if (::EVP_PKEY_set1_RSA(evpKey, rsa) != 1) //calls RSA_up_ref() + transfers ownership to evpKey
+ if (::EVP_PKEY_set1_RSA(evp, rsa) != 1) //no ownership transfer (internally ref-counted)
throw SysError(formatLastOpenSSLError(L"EVP_PKEY_set1_RSA"));
return sharedKey;
@@ -161,32 +161,32 @@ std::shared_ptr<EVP_PKEY> streamToKey(const std::string& keyStream, RsaStreamTyp
streamToEvpKey(keyStream, ::PEM_read_bio_RSAPublicKey, L"PEM_read_bio_RSAPublicKey") : //throw SysError
streamToEvpKey(keyStream, ::PEM_read_bio_RSAPrivateKey, L"PEM_read_bio_RSAPrivateKey"); //
- case RsaStreamType::pkcs1_raw:
+ case RsaStreamType::raw:
break;
}
auto tmp = reinterpret_cast<const unsigned char*>(keyStream.c_str());
- EVP_PKEY* evpKey = (publicKey ? ::d2i_PublicKey : ::d2i_PrivateKey)(EVP_PKEY_RSA, //int type,
- nullptr, //EVP_PKEY** a,
- &tmp, /*changes tmp pointer itself!*/ //const unsigned char** pp,
- static_cast<long>(keyStream.size())); //long length
- if (!evpKey)
+ EVP_PKEY* evp = (publicKey ? ::d2i_PublicKey : ::d2i_PrivateKey)(EVP_PKEY_RSA, //int type,
+ nullptr, //EVP_PKEY** a,
+ &tmp, /*changes tmp pointer itself!*/ //const unsigned char** pp,
+ static_cast<long>(keyStream.size())); //long length
+ if (!evp)
throw SysError(formatLastOpenSSLError(publicKey ? L"d2i_PublicKey" : L"d2i_PrivateKey"));
- return std::shared_ptr<EVP_PKEY>(evpKey, ::EVP_PKEY_free);
+ return std::shared_ptr<EVP_PKEY>(evp, ::EVP_PKEY_free);
}
//================================================================================
-using EvpToBioFunc = int (*)(BIO* bio, EVP_PKEY* evpKey);
+using EvpToBioFunc = int (*)(BIO* bio, EVP_PKEY* evp);
-std::string evpKeyToStream(EVP_PKEY* evpKey, EvpToBioFunc evpToBio, const wchar_t* functionName) //throw SysError
+std::string evpKeyToStream(EVP_PKEY* evp, EvpToBioFunc evpToBio, const wchar_t* functionName) //throw SysError
{
BIO* bio = ::BIO_new(BIO_s_mem());
if (!bio)
throw SysError(formatLastOpenSSLError(L"BIO_new"));
ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
- if (evpToBio(bio, evpKey) != 1)
+ if (evpToBio(bio, evp) != 1)
throw SysError(formatLastOpenSSLError(functionName));
//---------------------------------------------
const int keyLen = BIO_pending(bio);
@@ -205,14 +205,14 @@ std::string evpKeyToStream(EVP_PKEY* evpKey, EvpToBioFunc evpToBio, const wchar_
using RsaToBioFunc = int (*)(BIO* bp, RSA* x);
-std::string evpKeyToStream(EVP_PKEY* evpKey, RsaToBioFunc rsaToBio, const wchar_t* functionName) //throw SysError
+std::string evpKeyToStream(EVP_PKEY* evp, RsaToBioFunc rsaToBio, const wchar_t* functionName) //throw SysError
{
BIO* bio = ::BIO_new(BIO_s_mem());
if (!bio)
throw SysError(formatLastOpenSSLError(L"BIO_new"));
ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
- RSA* rsa = ::EVP_PKEY_get0_RSA(evpKey); //unowned reference!
+ RSA* rsa = ::EVP_PKEY_get0_RSA(evp); //unowned reference!
if (!rsa)
throw SysError(formatLastOpenSSLError(L"EVP_PKEY_get0_RSA"));
@@ -260,26 +260,26 @@ int PEM_write_bio_RSAPublicKey2(BIO* bio, RSA* rsa) { return ::PEM_write_bio_RSA
//--------------------------------------------------------------------------------
-std::string keyToStream(EVP_PKEY* evpKey, RsaStreamType streamType, bool publicKey) //throw SysError
+std::string keyToStream(EVP_PKEY* evp, RsaStreamType streamType, bool publicKey) //throw SysError
{
switch (streamType)
{
case RsaStreamType::pkix:
return publicKey ?
- evpKeyToStream(evpKey, ::PEM_write_bio_PUBKEY, L"PEM_write_bio_PUBKEY") : //throw SysError
- evpKeyToStream(evpKey, ::PEM_write_bio_PrivateKey2, L"PEM_write_bio_PrivateKey"); //
+ evpKeyToStream(evp, ::PEM_write_bio_PUBKEY, L"PEM_write_bio_PUBKEY") : //throw SysError
+ evpKeyToStream(evp, ::PEM_write_bio_PrivateKey2, L"PEM_write_bio_PrivateKey"); //
case RsaStreamType::pkcs1:
return publicKey ?
- evpKeyToStream(evpKey, ::PEM_write_bio_RSAPublicKey2, L"PEM_write_bio_RSAPublicKey") : //throw SysError
- evpKeyToStream(evpKey, ::PEM_write_bio_RSAPrivateKey2, L"PEM_write_bio_RSAPrivateKey"); //
+ evpKeyToStream(evp, ::PEM_write_bio_RSAPublicKey2, L"PEM_write_bio_RSAPublicKey") : //throw SysError
+ evpKeyToStream(evp, ::PEM_write_bio_RSAPrivateKey2, L"PEM_write_bio_RSAPrivateKey"); //
- case RsaStreamType::pkcs1_raw:
+ case RsaStreamType::raw:
break;
}
unsigned char* buf = nullptr;
- const int bufSize = (publicKey ? ::i2d_PublicKey : ::i2d_PrivateKey)(evpKey, &buf);
+ const int bufSize = (publicKey ? ::i2d_PublicKey : ::i2d_PrivateKey)(evp, &buf);
if (bufSize <= 0)
throw SysError(formatLastOpenSSLError(publicKey ? L"i2d_PublicKey" : L"i2d_PrivateKey"));
ZEN_ON_SCOPE_EXIT(::OPENSSL_free(buf)); //memory is only allocated for bufSize > 0
@@ -359,8 +359,8 @@ void verifySignature(const std::string& message, const std::string& signature, E
std::string zen::convertRsaKey(const std::string& keyStream, RsaStreamType typeFrom, RsaStreamType typeTo, bool publicKey) //throw SysError
{
assert(typeFrom != typeTo);
- std::shared_ptr<EVP_PKEY> evpKey = streamToKey(keyStream, typeFrom, publicKey); //throw SysError
- return keyToStream(evpKey.get(), typeTo, publicKey); //throw SysError
+ std::shared_ptr<EVP_PKEY> evp = streamToKey(keyStream, typeFrom, publicKey); //throw SysError
+ return keyToStream(evp.get(), typeTo, publicKey); //throw SysError
}
@@ -520,7 +520,7 @@ public:
::SSL_set_verify(ssl_, SSL_VERIFY_PEER, nullptr);
//2. enable check that the certificate matches our host: see SSL_get_verify_result()
- if (::SSL_set1_host(ssl_, server.c_str()) != 1)
+ if (::SSL_set1_host(ssl_, server.c_str()) != 1) //no ownership transfer
throw SysError(L"SSL_set1_host failed."); //no more error details
}
@@ -615,3 +615,393 @@ zen::TlsContext::TlsContext(int socket, const Zstring& server, const Zstring* ca
zen::TlsContext::~TlsContext() {}
size_t zen::TlsContext::tryRead ( void* buffer, size_t bytesToRead ) { return pimpl_->tryRead(buffer, bytesToRead); } //throw SysError
size_t zen::TlsContext::tryWrite(const void* buffer, size_t bytesToWrite) { return pimpl_->tryWrite(buffer, bytesToWrite); } //throw SysError
+
+
+bool zen::isPuttyKeyStream(const std::string& keyStream)
+{
+ std::string firstLine(keyStream.begin(), std::find_if(keyStream.begin(), keyStream.end(), isLineBreak<char>));
+ trim(firstLine);
+ return startsWith(firstLine, "PuTTY-User-Key-File-2:");
+}
+
+
+std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::string& passphrase) //throw SysError
+{
+ std::vector<std::string> lines;
+
+ for (auto it = keyStream.begin();;) //=> keep local: "warning: declaration of ‘it’ shadows a previous local"
+ {
+ auto itLineBegin = std::find_if_not(it, keyStream.end(), isLineBreak<char>);
+ if (itLineBegin == keyStream.end())
+ break;
+
+ it = std::find_if(itLineBegin + 1, keyStream.end(), isLineBreak<char>);
+ lines.emplace_back(itLineBegin, it);
+ }
+ //----------- parse PuTTY ppk structure ----------------------------------
+ auto itLine = lines.begin();
+ if (itLine == lines.end() || !startsWith(*itLine, "PuTTY-User-Key-File-2: "))
+ throw SysError(L"Unknown key file format");
+ const std::string algorithm = afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE);
+ ++itLine;
+
+ if (itLine == lines.end() || !startsWith(*itLine, "Encryption: "))
+ throw SysError(L"Unknown key encryption");
+ const std::string keyEncryption = afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE);
+ ++itLine;
+
+ if (itLine == lines.end() || !startsWith(*itLine, "Comment: "))
+ throw SysError(L"Invalid key comment");
+ const std::string comment = afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE);
+ ++itLine;
+
+ if (itLine == lines.end() || !startsWith(*itLine, "Public-Lines: "))
+ throw SysError(L"Invalid key: invalid public lines");
+ size_t pubLineCount = stringTo<size_t>(afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE));
+ ++itLine;
+
+ std::string publicBlob64;
+ while (pubLineCount-- != 0)
+ if (itLine != lines.end())
+ publicBlob64 += *itLine++;
+ else
+ throw SysError(L"Invalid key: incomplete public lines");
+
+ if (itLine == lines.end() || !startsWith(*itLine, "Private-Lines: "))
+ throw SysError(L"Invalid key: invalid private lines");
+ size_t privLineCount = stringTo<size_t>(afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE));
+ ++itLine;
+
+ std::string privateBlob64;
+ while (privLineCount-- != 0)
+ if (itLine != lines.end())
+ privateBlob64 += *itLine++;
+ else
+ throw SysError(L"Invalid key: incomplete private lines");
+
+ if (itLine == lines.end() || !startsWith(*itLine, "Private-MAC: "))
+ throw SysError(L"Invalid key: MAC missing");
+ const std::string macHex = afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE);
+ ++itLine;
+
+ //----------- unpack key file elements ---------------------
+ const bool keyEncrypted = keyEncryption == "aes256-cbc";
+ if (!keyEncrypted && keyEncryption != "none")
+ throw SysError(L"Unknown key encryption");
+
+ if (macHex.size() % 2 != 0 || !std::all_of(macHex.begin(), macHex.end(), isHexDigit<char>))
+ throw SysError(L"Invalid key: invalid MAC");
+
+ std::string mac;
+ for (size_t i = 0; i < macHex.size(); i += 2)
+ mac += unhexify(macHex[i], macHex[i + 1]);
+
+ const std::string publicBlob = stringDecodeBase64(publicBlob64);
+ const std::string privateBlobEnc = stringDecodeBase64(privateBlob64);
+
+ std::string privateBlob;
+ if (!keyEncrypted)
+ privateBlob = privateBlobEnc;
+ else
+ {
+ if (passphrase.empty())
+ throw SysError(L"Passphrase required to access private key");
+
+ const auto block1 = std::string("\0\0\0\0", 4) + passphrase;
+ const auto block2 = std::string("\0\0\0\1", 4) + passphrase;
+
+ unsigned char key[2 * SHA_DIGEST_LENGTH] = {};
+ SHA1(reinterpret_cast<const unsigned char*>(block1.c_str()), block1.size(), &key[0]); //no-fail
+ SHA1(reinterpret_cast<const unsigned char*>(block2.c_str()), block2.size(), &key[SHA_DIGEST_LENGTH]); //
+
+ EVP_CIPHER_CTX* cipCtx = ::EVP_CIPHER_CTX_new();
+ if (!cipCtx)
+ throw SysError(L"EVP_CIPHER_CTX_new failed."); //no more error details
+ ZEN_ON_SCOPE_EXIT(::EVP_CIPHER_CTX_free(cipCtx));
+
+ if (::EVP_DecryptInit_ex(cipCtx, //EVP_CIPHER_CTX* ctx,
+ EVP_aes_256_cbc(), //const EVP_CIPHER* type,
+ nullptr, //ENGINE* impl,
+ key, //const unsigned char* key, => implied length of 256 bit!
+ nullptr) != 1) //const unsigned char* iv
+ throw SysError(formatLastOpenSSLError(L"EVP_DecryptInit_ex"));
+
+ if (::EVP_CIPHER_CTX_set_padding(cipCtx, 0 /*padding*/) != 1)
+ throw SysError(L"EVP_CIPHER_CTX_set_padding failed."); //no more error details
+
+ privateBlob.resize(privateBlobEnc.size() + ::EVP_CIPHER_block_size(EVP_aes_256_cbc()));
+ //"EVP_DecryptUpdate() should have room for (inl + cipher_block_size) bytes"
+
+ int decLen1 = 0;
+ if (::EVP_DecryptUpdate(cipCtx, //EVP_CIPHER_CTX* ctx,
+ reinterpret_cast<unsigned char*>(&privateBlob[0]), //unsigned char* out,
+ &decLen1, //int* outl,
+ reinterpret_cast<const unsigned char*>(privateBlobEnc.c_str()), //const unsigned char* in,
+ static_cast<int>(privateBlobEnc.size())) != 1) //int inl
+ throw SysError(formatLastOpenSSLError(L"EVP_DecryptUpdate"));
+
+ int decLen2 = 0;
+ if (::EVP_DecryptFinal_ex(cipCtx, //EVP_CIPHER_CTX* ctx,
+ reinterpret_cast<unsigned char*>(&privateBlob[decLen1]), //unsigned char* outm,
+ &decLen2) != 1) //int* outl
+ throw SysError(formatLastOpenSSLError(L"EVP_DecryptFinal_ex"));
+
+ privateBlob.resize(decLen1 + decLen2);
+ }
+
+ //----------- verify key consistency ---------------------
+ std::string macKeyBlob = "putty-private-key-file-mac-key";
+ if (keyEncrypted)
+ macKeyBlob += passphrase;
+
+ unsigned char macKey[SHA_DIGEST_LENGTH] = {};
+ SHA1(reinterpret_cast<const unsigned char*>(macKeyBlob.c_str()), macKeyBlob.size(), &macKey[0]); //no-fail
+
+ auto numToBeString = [](size_t n) -> std::string
+ {
+ static_assert(usingLittleEndian()&& sizeof(n) >= 4);
+ const char* numStr = reinterpret_cast<const char*>(&n);
+ return { numStr[3], numStr[2], numStr[1], numStr[0] }; //big endian!
+ };
+
+ const std::string macData = numToBeString(algorithm .size()) + algorithm +
+ numToBeString(keyEncryption.size()) + keyEncryption +
+ numToBeString(comment .size()) + comment +
+ numToBeString(publicBlob .size()) + publicBlob +
+ numToBeString(privateBlob .size()) + privateBlob;
+ char md[EVP_MAX_MD_SIZE] = {};
+ unsigned int mdLen = 0;
+ if (!::HMAC(EVP_sha1(), //const EVP_MD* evp_md,
+ macKey, //const void* key,
+ sizeof(macKey), //int key_len,
+ reinterpret_cast<const unsigned char*>(macData.c_str()), //const unsigned char* d,
+ static_cast<int>(macData.size()), //int n,
+ reinterpret_cast<unsigned char*>(md), //unsigned char* md,
+ &mdLen)) //unsigned int* md_len
+ throw SysError(L"HMAC failed."); //no more error details
+
+ const bool hashValid = mac == std::string_view(md, mdLen);
+ if (!hashValid)
+ throw SysError(keyEncrypted ? L"MAC validation failed: wrong passphrase or corrupted key" : L"MAC validation failed: corrupted key");
+ //----------------------------------------------------------
+
+ auto extractString = [](auto& it, auto itEnd)
+ {
+ uint32_t byteCount = 0;
+ if (itEnd - it < makeSigned(sizeof(byteCount)))
+ throw SysError(L"String extraction failed: unexpected end of stream");
+
+ static_assert(usingLittleEndian());
+ char* numStr = reinterpret_cast<char*>(&byteCount);
+ numStr[3] = *it++; //
+ numStr[2] = *it++; //Putty uses big endian!
+ numStr[1] = *it++; //
+ numStr[0] = *it++; //
+
+ if (makeUnsigned(itEnd - it) < byteCount)
+ throw SysError(L"String extraction failed: unexpected end of stream(2)");
+
+ std::string str(it, it + byteCount);
+ it += byteCount;
+ return str;
+ };
+
+ struct BnFree { void operator()(BIGNUM* num) const { ::BN_free(num); } };
+ auto createBigNum = []
+ {
+ BIGNUM* bn = ::BN_new();
+ if (!bn)
+ throw SysError(formatLastOpenSSLError(L"BN_new"));
+ return std::unique_ptr<BIGNUM, BnFree>(bn);
+ };
+
+ auto extractBigNum = [&extractString](auto& it, auto itEnd)
+ {
+ const std::string bytes = extractString(it, itEnd);
+
+ BIGNUM* bn = ::BN_bin2bn(reinterpret_cast<const unsigned char*>(&bytes[0]), static_cast<int>(bytes.size()), nullptr);
+ if (!bn)
+ throw SysError(formatLastOpenSSLError(L"BN_bin2bn"));
+ return std::unique_ptr<BIGNUM, BnFree>(bn);
+ };
+
+ auto itPub = publicBlob .begin();
+ auto itPriv = privateBlob.begin();
+
+ auto extractStringPub = [&] { return extractString(itPub, publicBlob .end()); };
+ auto extractStringPriv = [&] { return extractString(itPriv, privateBlob.end()); };
+
+ auto extractBigNumPub = [&] { return extractBigNum(itPub, publicBlob .end()); };
+ auto extractBigNumPriv = [&] { return extractBigNum(itPriv, privateBlob.end()); };
+
+ //----------- parse public/private key blobs ----------------
+ if (extractStringPub() != algorithm)
+ throw SysError(L"Invalid public key stream (header)");
+
+ if (algorithm == "ssh-rsa")
+ {
+ std::unique_ptr<BIGNUM, BnFree> e = extractBigNumPub (); //
+ std::unique_ptr<BIGNUM, BnFree> n = extractBigNumPub (); //
+ std::unique_ptr<BIGNUM, BnFree> d = extractBigNumPriv(); //throw SysError
+ std::unique_ptr<BIGNUM, BnFree> p = extractBigNumPriv(); //
+ std::unique_ptr<BIGNUM, BnFree> q = extractBigNumPriv(); //
+ std::unique_ptr<BIGNUM, BnFree> iqmp = extractBigNumPriv(); //
+
+ //------ calculate missing numbers: dmp1, dmq1 -------------
+ std::unique_ptr<BIGNUM, BnFree> dmp1 = createBigNum(); //
+ std::unique_ptr<BIGNUM, BnFree> dmq1 = createBigNum(); //throw SysError
+ std::unique_ptr<BIGNUM, BnFree> tmp = createBigNum(); //
+
+ BN_CTX* bnCtx = BN_CTX_new();
+ if (!bnCtx)
+ throw SysError(formatLastOpenSSLError(L"BN_CTX_new"));
+ ZEN_ON_SCOPE_EXIT(::BN_CTX_free(bnCtx));
+
+ if (::BN_sub(tmp.get(), p.get(), BN_value_one()) != 1)
+ throw SysError(formatLastOpenSSLError(L"BN_sub"));
+
+ if (::BN_mod(dmp1.get(), d.get(), tmp.get(), bnCtx) != 1)
+ throw SysError(formatLastOpenSSLError(L"BN_mod"));
+
+ if (::BN_sub(tmp.get(), q.get(), BN_value_one()) != 1)
+ throw SysError(formatLastOpenSSLError(L"BN_sub"));
+
+ if (::BN_mod(dmq1.get(), d.get(), tmp.get(), bnCtx) != 1)
+ throw SysError(formatLastOpenSSLError(L"BN_mod"));
+ //----------------------------------------------------------
+
+ RSA* rsa = ::RSA_new();
+ if (!rsa)
+ throw SysError(formatLastOpenSSLError(L"RSA_new"));
+ ZEN_ON_SCOPE_EXIT(::RSA_free(rsa));
+
+ if (::RSA_set0_key(rsa, n.release(), e.release(), d.release()) != 1) //pass BIGNUM ownership
+ throw SysError(formatLastOpenSSLError(L"RSA_set0_key"));
+
+ if (::RSA_set0_factors(rsa, p.release(), q.release()) != 1)
+ throw SysError(formatLastOpenSSLError(L"RSA_set0_factors"));
+
+ if (::RSA_set0_crt_params(rsa, dmp1.release(), dmq1.release(), iqmp.release()) != 1)
+ throw SysError(formatLastOpenSSLError(L"RSA_set0_crt_params"));
+
+ EVP_PKEY* evp = ::EVP_PKEY_new();
+ if (!evp)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new"));
+ ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp));
+
+ if (::EVP_PKEY_set1_RSA(evp, rsa) != 1) //no ownership transfer (internally ref-counted)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_set1_RSA"));
+
+ return keyToStream(evp, RsaStreamType::pkix, false /*publicKey*/); //throw SysError
+ }
+ //----------------------------------------------------------
+ else if (algorithm == "ssh-dss")
+ {
+ std::unique_ptr<BIGNUM, BnFree> p = extractBigNumPub (); //
+ std::unique_ptr<BIGNUM, BnFree> q = extractBigNumPub (); //
+ std::unique_ptr<BIGNUM, BnFree> g = extractBigNumPub (); //throw SysError
+ std::unique_ptr<BIGNUM, BnFree> pub = extractBigNumPub (); //
+ std::unique_ptr<BIGNUM, BnFree> pri = extractBigNumPriv(); //
+ //----------------------------------------------------------
+
+ DSA* dsa = ::DSA_new();
+ if (!dsa)
+ throw SysError(formatLastOpenSSLError(L"DSA_new"));
+ ZEN_ON_SCOPE_EXIT(::DSA_free(dsa));
+
+ if (::DSA_set0_pqg(dsa, p.release(), q.release(), g.release()) != 1) //pass BIGNUM ownership
+ throw SysError(formatLastOpenSSLError(L"DSA_set0_pqg"));
+
+ if (::DSA_set0_key(dsa, pub.release(), pri.release()) != 1)
+ throw SysError(formatLastOpenSSLError(L"DSA_set0_key"));
+
+ EVP_PKEY* evp = ::EVP_PKEY_new();
+ if (!evp)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new"));
+ ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp));
+
+ if (::EVP_PKEY_set1_DSA(evp, dsa) != 1) //no ownership transfer (internally ref-counted)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_set1_DSA"));
+
+ return keyToStream(evp, RsaStreamType::pkix, false /*publicKey*/); //throw SysError
+ }
+ //----------------------------------------------------------
+ else if (algorithm == "ecdsa-sha2-nistp256" ||
+ algorithm == "ecdsa-sha2-nistp384" ||
+ algorithm == "ecdsa-sha2-nistp521")
+ {
+ const std::string algoShort = afterLast(algorithm, '-', IF_MISSING_RETURN_NONE);
+ if (extractStringPub() != algoShort)
+ throw SysError(L"Invalid public key stream (header)");
+
+ const std::string pointStream = extractStringPub();
+ std::unique_ptr<BIGNUM, BnFree> pri = extractBigNumPriv(); //throw SysError
+ //----------------------------------------------------------
+
+ const int curveNid = [&]
+ {
+ if (algoShort == "nistp256")
+ return NID_X9_62_prime256v1; //same as SECG secp256r1
+ if (algoShort == "nistp384")
+ return NID_secp384r1;
+ if (algoShort == "nistp521")
+ return NID_secp521r1;
+ throw SysError(L"Unknown elliptic curve: " + utfTo<std::wstring>(algorithm));
+ }();
+
+ EC_KEY* ecKey = ::EC_KEY_new_by_curve_name(curveNid);
+ if (!ecKey)
+ throw SysError(formatLastOpenSSLError(L"EC_KEY_new_by_curve_name"));
+ ZEN_ON_SCOPE_EXIT(::EC_KEY_free(ecKey));
+
+ const EC_GROUP* ecGroup = ::EC_KEY_get0_group(ecKey);
+ if (!ecGroup)
+ throw SysError(formatLastOpenSSLError(L"EC_KEY_get0_group"));
+
+ EC_POINT* ecPoint = ::EC_POINT_new(ecGroup);
+ if (!ecPoint)
+ throw SysError(formatLastOpenSSLError(L"EC_POINT_new"));
+ ZEN_ON_SCOPE_EXIT(::EC_POINT_free(ecPoint));
+
+ if (::EC_POINT_oct2point(ecGroup, //const EC_GROUP* group,
+ ecPoint, //EC_POINT* p,
+ reinterpret_cast<const unsigned char*>(&pointStream[0]), //const unsigned char* buf,
+ pointStream.size(), //size_t len,
+ nullptr) != 1) //BN_CTX* ctx
+ throw SysError(formatLastOpenSSLError(L"EC_POINT_oct2point"));
+
+ if (::EC_KEY_set_public_key(ecKey, ecPoint) != 1) //no ownership transfer (internally ref-counted)
+ throw SysError(formatLastOpenSSLError(L"EC_KEY_set_public_key"));
+
+ if (::EC_KEY_set_private_key(ecKey, pri.get()) != 1) //no ownership transfer (internally ref-counted)
+ throw SysError(formatLastOpenSSLError(L"EC_KEY_set_private_key"));
+
+ EVP_PKEY* evp = ::EVP_PKEY_new();
+ if (!evp)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new"));
+ ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp));
+
+ if (::EVP_PKEY_set1_EC_KEY(evp, ecKey) != 1) //no ownership transfer (internally ref-counted)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_set1_EC_KEY"));
+
+ return keyToStream(evp, RsaStreamType::pkix, false /*publicKey*/); //throw SysError
+ }
+ //----------------------------------------------------------
+ else if (algorithm == "ssh-ed25519")
+ {
+ //const std::string pubStream = extractStringPub(); -> we don't need the public key
+ const std::string priStream = extractStringPriv();
+
+ EVP_PKEY* evpPriv = ::EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, //int type,
+ nullptr, //ENGINE* e,
+ reinterpret_cast<const unsigned char*>(&priStream[0]), //const unsigned char* priv,
+ priStream.size()); //size_t len
+ if (!evpPriv)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new_raw_private_key"));
+ ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evpPriv));
+
+ return keyToStream(evpPriv, RsaStreamType::pkix, false /*publicKey*/); //throw SysError
+ }
+ else
+ throw SysError(L"Unknown key algorithm: " + utfTo<std::wstring>(algorithm));
+}
diff --git a/zen/open_ssl.h b/zen/open_ssl.h
index 350e3776..2b0c7245 100644
--- a/zen/open_ssl.h
+++ b/zen/open_ssl.h
@@ -20,9 +20,9 @@ void openSslTearDown();
enum class RsaStreamType
{
- pkix, //base-64-encoded SubjectPublicKeyInfo structure ("BEGIN PUBLIC KEY")
- pkcs1, //base-64-encoded RSA number and exponent ("BEGIN RSA PUBLIC KEY")
- pkcs1_raw
+ pkix, //base-64-encoded X.509 SubjectPublicKeyInfo structure ("BEGIN PUBLIC KEY")
+ pkcs1, //base-64-encoded PKCS#1 RSAPublicKey: RSA number and exponent ("BEGIN RSA PUBLIC KEY")
+ raw //raw bytes: DER-encoded PKCS#1
};
//verify signatures produced with: "openssl dgst -sha256 -sign private.pem -out file.sig file.txt"
@@ -34,6 +34,10 @@ void verifySignature(const std::string& message,
std::string convertRsaKey(const std::string& keyStream, RsaStreamType typeFrom, RsaStreamType typeTo, bool publicKey); //throw SysError
+bool isPuttyKeyStream(const std::string& keyStream);
+std::string convertPuttyKeyToPkix(const std::string& keyStream, const std::string& passphrase); //throw SysError
+
+
class TlsContext
{
public:
diff --git a/zen/shutdown.cpp b/zen/shutdown.cpp
index 4fc687d6..5ce586f0 100644
--- a/zen/shutdown.cpp
+++ b/zen/shutdown.cpp
@@ -33,8 +33,9 @@ void zen::suspendSystem() //throw FileError
void zen::terminateProcess(int exitCode)
{
- std::exit(exitCode); //[[noreturn]]; "Stack is not unwound: destructors of variables with automatic storage duration are not called." => perfect
- //don't use std::abort() => crashes process with "EXC_CRASH (SIGABRT)" on macOS
+ std::quick_exit(exitCode); //[[noreturn]]; "Causes normal program termination to occur without completely cleaning the resources." => perfect
+
+
for (;;) //why still here?? => crash deliberately!
*reinterpret_cast<volatile int*>(0) = 0; //crude but at least we'll get crash dumps if it ever happens
}
diff --git a/zen/socket.h b/zen/socket.h
index 7ca0c93f..827d446b 100644
--- a/zen/socket.h
+++ b/zen/socket.h
@@ -142,7 +142,7 @@ size_t tryWriteSocket(SocketType socket, const void* buffer, size_t bytesToWrite
inline
void shutdownSocketSend(SocketType socket) //throw SysError
{
- if (::shutdown(socket, SHUT_WR) != 0)
+ if (::shutdown(socket, SHUT_WR) != 0)
THROW_LAST_SYS_ERROR_WSA(L"shutdown");
}
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 15e7f7ca..9014b0f7 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -15,7 +15,6 @@
#include <algorithm>
#include <optional>
#include "string_traits.h"
-//#include "build_info.h"
//enhancements for <algorithm>
diff --git a/zen/string_base.h b/zen/string_base.h
index 2247f93a..a417b7f6 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -189,8 +189,8 @@ private:
length (static_cast<uint32_t>(len)),
capacity(static_cast<uint32_t>(cap))
{
- static_assert(ATOMIC_INT_LOCK_FREE == 2); //2: "The atomic type is always lock-free"
- //static_assert(decltype(refCount)::is_always_lock_free); //C++17 variant (not yet supported on GCC 6.3)
+ //static_assert(ATOMIC_INT_LOCK_FREE == 2); //2: "The atomic type is always lock-free"
+ static_assert(decltype(refCount)::is_always_lock_free); //C++17 variant (not yet supported on GCC 6.3)
}
std::atomic<unsigned int> refCount { 1 }; //std:atomic is uninitialized by default!
@@ -313,6 +313,11 @@ template <class Char, template <class> class SP> inline Zbase<Char, SP> operator
template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+( Char lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(&lhs, 1) += rhs; }
template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Char* lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs ) += rhs; }
+#if __cpp_impl_three_way_comparison
+#error implement:
+std::strong_ordering operator<=>(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs)
+bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs);
+#endif
@@ -329,7 +334,7 @@ template <class Char, template <class> class SP> inline Zbase<Char, SP> operator
template <class Char, template <class> class SP> inline
Zbase<Char, SP>::Zbase()
{
- //resist the temptation to avoid this allocation by referening a static global: NO performance advantage, MT issues!
+ //resist the temptation to avoid this allocation by referencing a static global: NO performance advantage, MT issues!
rawStr_ = this->create(0);
rawStr_[0] = 0;
}
diff --git a/zen/string_tools.h b/zen/string_tools.h
index c3970d05..dcb5a54a 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -14,15 +14,16 @@
#include <algorithm>
#include <cassert>
#include <vector>
-#include <sstream> //std::basic_ostringstream
#include "stl_tools.h"
#include "string_traits.h"
+#include "legacy_compiler.h" //<charconv> (without compiler crashes)
//enhance arbitray string class with useful non-member functions:
namespace zen
{
template <class Char> bool isWhiteSpace(Char c);
+template <class Char> bool isLineBreak (Char c);
template <class Char> bool isDigit (Char c); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only!
template <class Char> bool isHexDigit (Char c);
template <class Char> bool isAsciiAlpha(Char c);
@@ -116,6 +117,14 @@ bool isWhiteSpace(Char c)
}
template <class Char> inline
+bool isLineBreak(Char c)
+{
+ static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
+ return c == static_cast<Char>('\r') || c == static_cast<Char>('\n');
+}
+
+
+template <class Char> inline
bool isDigit(Char c) //similar to implementation of std::isdigit()!
{
static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
@@ -552,6 +561,10 @@ int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const
template <class S, class T, class Num> inline
S printNumber(const T& format, const Num& number) //format a single number using ::sprintf
{
+#if __cpp_lib_format
+#error refactor
+#endif
+
static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
const int BUFFER_SIZE = 128;
@@ -573,22 +586,31 @@ enum class NumberType
};
+template <class S, class Num> S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::OTHER>) = delete;
+#if 0 //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20)
template <class S, class Num> inline
-S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20)
+S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::OTHER>)
{
std::basic_ostringstream<GetCharTypeT<S>> ss;
ss << number;
return copyStringTo<S>(ss.str());
}
+#endif
-template <class S, class Num> inline S floatToString(const Num& number, char ) { return printNumber<S>( "%g", static_cast<double>(number)); }
-template <class S, class Num> inline S floatToString(const Num& number, wchar_t) { return printNumber<S>(L"%g", static_cast<double>(number)); }
-
template <class S, class Num> inline
S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::FLOATING_POINT>)
{
- return floatToString<S>(number, GetCharTypeT<S>());
+ //don't use sprintf("%g"): way SLOWWWWWWER than std::to_chars()
+
+ char buffer[128]; //zero-initialize?
+ //let's give some leeway, but 24 chars should suffice: https://www.reddit.com/r/cpp/comments/dgj89g/cppcon_2019_stephan_t_lavavej_floatingpoint/f3j7d3q/
+ const char* strEnd = zen::to_chars(std::begin(buffer), std::end(buffer), number);
+
+ S output;
+ std::for_each(static_cast<const char*>(buffer), strEnd,
+ [&](char c) { output += static_cast<GetCharTypeT<S>>(c); });
+ return output;
}
@@ -665,25 +687,46 @@ S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::UNS
//--------------------------------------------------------------------------------
+template <class Num, class S> Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::OTHER>) = delete;
+#if 0 //default string to number conversion using streams: convenient, but SLOW
template <class Num, class S> inline
-Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::OTHER>) //default string to number conversion using streams: convenient, but SLOW
+Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::OTHER>)
{
using CharType = GetCharTypeT<S>;
Num number = 0;
std::basic_istringstream<CharType>(copyStringTo<std::basic_string<CharType>>(str)) >> number;
return number;
}
+#endif
-template <class Num> inline Num stringToFloat(const char* str) { return std::strtod(str, nullptr); }
-template <class Num> inline Num stringToFloat(const wchar_t* str) { return std::wcstod(str, nullptr); }
+inline
+double stringToFloat(const char* first, const char* last)
+{
+ //don't use std::strtod(): 1. requires null-terminated string 2. SLOWER than std::from_chars()
+ return zen::from_chars(first, last);
+}
+
+
+inline
+double stringToFloat(const wchar_t* first, const wchar_t* last)
+{
+ std::string buf(last - first, '\0');
+ std::transform(first, last, buf.begin(), [](wchar_t c) { return static_cast<char>(c); });
+
+ return zen::from_chars(buf.c_str(), buf.c_str() + buf.size());
+}
+
template <class Num, class S> inline
Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::FLOATING_POINT>)
{
- return stringToFloat<Num>(strBegin(str));
+ const auto* const first = strBegin(str);
+ const auto* const last = first + strLength(str);
+ return static_cast<Num>(stringToFloat(first, last));
}
+
template <class Num, class S>
Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
{
@@ -695,7 +738,6 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i
while (first != last && isWhiteSpace(*first)) //skip leading whitespace
++first;
- //handle minus sign
hasMinusSign = false;
if (first != last)
{
diff --git a/zen/string_traits.h b/zen/string_traits.h
index 93cfd81c..d0f34d54 100644
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -7,6 +7,7 @@
#ifndef STRING_TRAITS_H_813274321443234
#define STRING_TRAITS_H_813274321443234
+#include <string_view>
#include <cstring> //strlen
#include "type_traits.h"
@@ -36,27 +37,9 @@ strBegin(): -> not null-terminated! -> may be nullptr if length is 0!
//reference a sub-string for consumption by zen string_tools
-template <class Char>
-class StringRef
-{
-public:
- template <class Iterator>
- StringRef(Iterator first, Iterator last) : len_(last - first),
- str_(first != last ? &*first : reinterpret_cast<Char*>(this) /*Win32 APIs like CompareStringOrdinal() choke on nullptr!*/)
- {
- static_assert(alignof(StringRef) % alignof(Char) == 0); //even though str_ is never dereferenced, make sure the pointer value respects alignment (why? because we can)
- }
- //StringRef(const Char* str, size_t len) : str_(str), len_(len) {} -> needless constraint! Char* not available for empty range!
-
- Char* data () const { return str_; } //no null-termination!
- size_t length() const { return len_; }
-
-private:
- const size_t len_;
- Char* const str_;
-};
-
-
+//=> std::string_view seems decent, but of course fucks up in one regard: construction
+template <class Iterator> auto makeStringView(Iterator first, Iterator last);
+template <class Iterator> auto makeStringView(Iterator first, size_t len);
@@ -84,7 +67,7 @@ public:
};
-template <class S, bool isStringClass> struct GetCharTypeImpl { using Type = void; };
+template <class S, bool isStringClass> struct GetCharTypeImpl { using Type = void; };
template <class S>
struct GetCharTypeImpl<S, true>
@@ -103,10 +86,10 @@ struct GetCharTypeImpl<S, true>
template <> struct GetCharTypeImpl<char, false> { using Type = char; };
template <> struct GetCharTypeImpl<wchar_t, false> { using Type = wchar_t; };
-template <> struct GetCharTypeImpl<StringRef<char >, false> { using Type = char; };
-template <> struct GetCharTypeImpl<StringRef<wchar_t >, false> { using Type = wchar_t; };
-template <> struct GetCharTypeImpl<StringRef<const char >, false> { using Type = char; };
-template <> struct GetCharTypeImpl<StringRef<const wchar_t>, false> { using Type = wchar_t; };
+template <> struct GetCharTypeImpl<std::basic_string_view<char >, false> { using Type = char; };
+template <> struct GetCharTypeImpl<std::basic_string_view<wchar_t >, false> { using Type = wchar_t; };
+template <> struct GetCharTypeImpl<std::basic_string_view<const char >, false> { using Type = char; };
+template <> struct GetCharTypeImpl<std::basic_string_view<const wchar_t>, false> { using Type = wchar_t; };
ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
@@ -184,11 +167,10 @@ inline const wchar_t* strBegin(const wchar_t* str) { return str; }
inline const char* strBegin(const char& ch) { return &ch; }
inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; }
-inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); }
-inline const wchar_t* strBegin(const StringRef<wchar_t >& ref) { return ref.data(); }
-inline const char* strBegin(const StringRef<const char >& ref) { return ref.data(); }
-inline const wchar_t* strBegin(const StringRef<const wchar_t>& ref) { return ref.data(); }
-
+inline const char* strBegin(const std::basic_string_view<char >& ref) { return ref.data(); }
+inline const wchar_t* strBegin(const std::basic_string_view<wchar_t >& ref) { return ref.data(); }
+inline const char* strBegin(const std::basic_string_view<const char >& ref) { return ref.data(); }
+inline const wchar_t* strBegin(const std::basic_string_view<const wchar_t>& ref) { return ref.data(); }
template <class S, typename = std::enable_if_t<StringTraits<S>::isStringClass>> inline
size_t strLength(const S& str) //SFINAE: T must be a "string"
@@ -201,15 +183,15 @@ inline size_t strLength(const wchar_t* str) { return cStringLength(str); }
inline size_t strLength(char) { return 1; }
inline size_t strLength(wchar_t) { return 1; }
-inline size_t strLength(const StringRef<char >& ref) { return ref.length(); }
-inline size_t strLength(const StringRef<wchar_t >& ref) { return ref.length(); }
-inline size_t strLength(const StringRef<const char >& ref) { return ref.length(); }
-inline size_t strLength(const StringRef<const wchar_t>& ref) { return ref.length(); }
+inline size_t strLength(const std::basic_string_view<char >& ref) { return ref.length(); }
+inline size_t strLength(const std::basic_string_view<wchar_t >& ref) { return ref.length(); }
+inline size_t strLength(const std::basic_string_view<const char >& ref) { return ref.length(); }
+inline size_t strLength(const std::basic_string_view<const wchar_t>& ref) { return ref.length(); }
}
template <class S> inline
-auto strBegin(S&& str) -> const GetCharTypeT<S>*
+auto strBegin(S&& str)
{
static_assert(IsStringLikeV<S>);
return impl::strBegin(std::forward<S>(str));
@@ -222,6 +204,19 @@ size_t strLength(S&& str)
static_assert(IsStringLikeV<S>);
return impl::strLength(std::forward<S>(str));
}
+
+
+template <class Iterator> inline
+auto makeStringView(Iterator first, Iterator last)
+{
+ using CharType = GetCharTypeT<decltype(&*first)>;
+
+ return std::basic_string_view<CharType>(first != last ? &*first :
+ reinterpret_cast<CharType*>(0x1000), /*Win32 APIs like CompareStringOrdinal() choke on nullptr!*/
+ last - first);
+}
+
+template <class Iterator> inline auto makeStringView(Iterator first, size_t len) { return makeStringView(first, first + len); }
}
#endif //STRING_TRAITS_H_813274321443234
diff --git a/zen/sys_error.h b/zen/sys_error.h
index a087172f..7c746258 100644
--- a/zen/sys_error.h
+++ b/zen/sys_error.h
@@ -47,6 +47,10 @@ private:
do { const ErrorCode ecInternal = getLastError(); throw SysError(formatSystemError(functionName, ecInternal)); } while (false)
+//helper for error checking macros:
+inline bool validatBool(bool b) { return b; }
+inline bool validatBool(void* b) { return b != nullptr; }
+bool validatBool(int) = delete; //catch unintended bool conversions, e.g. HRESULT
diff --git a/zen/thread.h b/zen/thread.h
index 791aec67..d6cafab7 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -18,6 +18,9 @@ namespace zen
{
class InterruptionStatus;
+#if __cpp_lib_jthread
+ #error refactor!
+#endif
class InterruptibleThread
{
public:
@@ -81,6 +84,7 @@ template <class Function>
auto runAsync(Function&& fun);
//wait for all with a time limit: return true if *all* results are available!
+//TODO: use std::when_all when available
template<class InputIterator, class Duration>
bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration);
@@ -89,6 +93,7 @@ bool isReady(const std::future<T>& f) { return f.wait_for(std::chrono::seconds(0
//------------------------------------------------------------------------------------------
//wait until first job is successful or all failed: substitute until std::when_any is available
+//TODO: use std::when_any when available
template <class T>
class AsyncFirstResult
{
@@ -113,6 +118,7 @@ private:
//------------------------------------------------------------------------------------------
//value associated with mutex and guaranteed protected access:
+//TODO: use std::synchronized_value when available
template <class T>
class Protected
{
diff --git a/zen/time.h b/zen/time.h
index a32e28e3..27ce518f 100644
--- a/zen/time.h
+++ b/zen/time.h
@@ -335,7 +335,7 @@ TimeComp parseTime(const String& format, const String2& str, UserDefinedFormatTa
if (!std::all_of(itStr, itStr + digitCount, isDigit<CharType>))
return false;
- result = zen::stringTo<int>(StringRef<const CharType>(itStr, itStr + digitCount));
+ result = zen::stringTo<int>(makeStringView(itStr, digitCount));
itStr += digitCount;
return true;
};
diff --git a/zen/warn_static.h b/zen/warn_static.h
index 17e7cf25..d5f78b5d 100644
--- a/zen/warn_static.h
+++ b/zen/warn_static.h
@@ -8,17 +8,18 @@
#define WARN_STATIC_H_08724567834560832745
/*
- Portable Compile-Time Warning
- -----------------------------
- Usage:
- warn_static("my message")
+ Portable Compile-Time Warning
+ -----------------------------
+ Usage:
+ warn_static("my message")
*/
-#define ZEN_STATIC_WARNING_STRINGIZE(NUM) #NUM
+#define ZEN_STRINGIZE_STRING(NUM) #NUM
+#define ZEN_STRINGIZE_NUMBER(NUM) ZEN_STRINGIZE_STRING(NUM)
#if defined __GNUC__ //Clang also defines __GNUC__!
#define warn_static(MSG) \
- _Pragma(ZEN_STATIC_WARNING_STRINGIZE(GCC warning MSG))
+ _Pragma(ZEN_STRINGIZE_STRING(GCC warning MSG))
#endif
#endif //WARN_STATIC_H_08724567834560832745
diff --git a/zen/zlib_wrap.cpp b/zen/zlib_wrap.cpp
index 8979efa6..f7418b88 100644
--- a/zen/zlib_wrap.cpp
+++ b/zen/zlib_wrap.cpp
@@ -6,7 +6,7 @@
#include "zlib_wrap.h"
//Windows: use the SAME zlib version that wxWidgets is linking against! //C:\Data\Projects\wxWidgets\Source\src\zlib\zlib.h
-//Linux/macOS: use zlib system header for both wxWidgets and libcurl (zlib is required for HTTP)
+//Linux/macOS: use zlib system header for both wxWidgets and libcurl (zlib is required for HTTP, SFTP)
// => don't compile wxWidgets with: --with-zlib=builtin
#include <zlib.h> //https://www.zlib.net/manual.html
#include <zen/scope_guard.h>
@@ -53,7 +53,7 @@ size_t zen::impl::zlib_compress(const void* src, size_t srcLen, void* trg, size_
// Z_MEM_ERROR: not enough memory
// Z_BUF_ERROR: not enough room in the output buffer
if (rv != Z_OK || bufferSize > trgLen)
- throw SysError(formatSystemError(L"compress2", formatZlibStatusCode(rv), L"zlib error"));
+ throw SysError(formatSystemError(L"compress2", formatZlibStatusCode(rv), L"zlib error"));
return bufferSize;
}
diff --git a/zen/zlib_wrap.h b/zen/zlib_wrap.h
index fbe26193..9d9229ac 100644
--- a/zen/zlib_wrap.h
+++ b/zen/zlib_wrap.h
@@ -90,14 +90,14 @@ BinContainer decompress(const BinContainer& stream) //throw SysError
//retrieve size of uncompressed data
uint64_t uncompressedSize = 0; //use portable number type!
if (stream.size() < sizeof(uncompressedSize))
- throw SysError(L"zlib error: stream size < 8");
+ throw SysError(L"zlib error: stream size < 8");
std::memcpy(&uncompressedSize, &*stream.begin(), sizeof(uncompressedSize));
//attention: contOut MUST NOT be empty! Else it will pass a nullptr to zlib_decompress() => Z_STREAM_ERROR although "uncompressedSize == 0"!!!
//secondary bug: don't dereference iterator into empty container!
if (uncompressedSize == 0) //cannot be 0: compress() directly maps empty -> empty container skipping zlib!
- throw SysError(L"zlib error: uncompressed size == 0");
+ throw SysError(L"zlib error: uncompressed size == 0");
try
{
@@ -105,7 +105,7 @@ BinContainer decompress(const BinContainer& stream) //throw SysError
}
catch (const std::bad_alloc& e) //most likely due to data corruption!
{
- throw SysError(L"zlib error: " + _("Out of memory.") + L" " + utfTo<std::wstring>(e.what()));
+ throw SysError(L"zlib error: " + _("Out of memory.") + L" " + utfTo<std::wstring>(e.what()));
}
const size_t bytesWritten = impl::zlib_decompress(&*stream.begin() + sizeof(uncompressedSize),
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index ad736d04..f018b14f 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -8,7 +8,7 @@
#include <stdexcept>
#include "utf.h"
-#include <glib.h>
+ #include <glib.h>
#include "sys_error.h"
using namespace zen;
diff --git a/zenXml/zenxml/parser.h b/zenXml/zenxml/parser.h
index dbf5a8c4..f0c70b66 100644
--- a/zenXml/zenxml/parser.h
+++ b/zenXml/zenxml/parser.h
@@ -401,7 +401,7 @@ public:
for (auto it = pos_; it != stream_.begin(); )
{
--it;
- if (*it == '\r' || *it == '\n')
+ if (isLineBreak(*it))
return pos_ - it - 1;
}
return pos_ - stream_.begin();
@@ -413,7 +413,7 @@ private:
bool startsWith(const std::string& prefix) const
{
- return zen::startsWith(StringRef<const char>(pos_, stream_.end()), prefix);
+ return zen::startsWith(makeStringView(pos_, stream_.end()), prefix);
}
using TokenList = std::vector<std::pair<std::string, Token::Type>>;
bgstack15