mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-27 10:07:19 +02:00
Sync against taskchampion-sync-server (#3118)
This removes use of gnutls and the TLS implementation, which is no longer needed (task synchronization is handled via Taskchampion, which uses `reqwest`, which handles TLS via other Rust dependencies). This incidentally removes the following config options: * `debug.tls` * `taskd.ca` * `taskd.certificate` * `taskd.ciphers` * `taskd.credentials` * `taskd.key` * `taskd.server` * `taskd.trust`
This commit is contained in:
parent
771977aa69
commit
31105c2ba3
57 changed files with 403 additions and 1615 deletions
|
@ -17,7 +17,6 @@ add_library (task STATIC CLI2.cpp CLI2.h
|
|||
Lexer.cpp Lexer.h
|
||||
TDB2.cpp TDB2.h
|
||||
Task.cpp Task.h
|
||||
TLSClient.cpp TLSClient.h
|
||||
Variant.cpp Variant.h
|
||||
ViewTask.cpp ViewTask.h
|
||||
dependency.cpp
|
||||
|
@ -52,9 +51,10 @@ add_executable (calc_executable calc.cpp)
|
|||
add_executable (lex_executable lex.cpp)
|
||||
|
||||
# Yes, 'task' (and hence libshared) is included twice, otherwise linking fails on assorted OSes.
|
||||
target_link_libraries (task_executable task tc tc-rust commands columns libshared task libshared ${TASK_LIBRARIES})
|
||||
target_link_libraries (calc_executable task tc tc-rust commands columns libshared task libshared ${TASK_LIBRARIES})
|
||||
target_link_libraries (lex_executable task tc tc-rust commands columns libshared task libshared ${TASK_LIBRARIES})
|
||||
# Similarly for `tc`.
|
||||
target_link_libraries (task_executable task tc tc-rust commands tc columns libshared task libshared ${TASK_LIBRARIES})
|
||||
target_link_libraries (calc_executable task tc tc-rust commands tc columns libshared task libshared ${TASK_LIBRARIES})
|
||||
target_link_libraries (lex_executable task tc tc-rust commands tc columns libshared task libshared ${TASK_LIBRARIES})
|
||||
|
||||
set_property (TARGET task_executable PROPERTY OUTPUT_NAME "task")
|
||||
|
||||
|
|
|
@ -272,21 +272,17 @@ std::string configurationDefaults =
|
|||
"list.all.tags=0 # Include old tag names in 'tags' command\n"
|
||||
"print.empty.columns=0 # Print columns which have no data for any task\n"
|
||||
"debug=0 # Display diagnostics\n"
|
||||
"debug.tls=0 # Sync diagnostics\n"
|
||||
"sugar=1 # Syntactic sugar\n"
|
||||
"obfuscate=0 # Obfuscate data for error reporting\n"
|
||||
"fontunderline=1 # Uses underlines rather than -------\n"
|
||||
"\n"
|
||||
"# WARNING: Please read the documentation (man task-sync) before setting up\n"
|
||||
"# Taskwarrior for Taskserver synchronization.\n"
|
||||
"#taskd.ca=<certificate file>\n"
|
||||
"#taskd.certificate=<certificate file>\n"
|
||||
"#taskd.credentials=<organization>/<name>/<password>\n"
|
||||
"#taskd.server=<server>:<port>\n"
|
||||
"taskd.trust=strict\n"
|
||||
"#taskd.trust=ignore hostname\n"
|
||||
"#taskd.trust=allow all\n"
|
||||
"taskd.ciphers=NORMAL\n"
|
||||
"\n"
|
||||
"#sync.server.client_key # Client key for sync to a server\n"
|
||||
"#sync.server.encryption_secret # Encryption secret for sync to a server\n"
|
||||
"#sync.server.origin # Origin of the sync server\n"
|
||||
"#sync.local.server_dir # Directory for local sync\n"
|
||||
"\n"
|
||||
"# Aliases - alternate names for commands\n"
|
||||
"alias.rm=delete # Alias for the delete command\n"
|
||||
|
@ -1354,16 +1350,13 @@ void Context::loadAliases ()
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Using the general rc.debug setting automaticalls sets debug.tls, debug.hooks
|
||||
// Using the general rc.debug setting automaticalls sets debug.hooks
|
||||
// and debug.parser, unless they already have values, which by default they do
|
||||
// not.
|
||||
void Context::propagateDebug ()
|
||||
{
|
||||
if (config.getBoolean ("debug"))
|
||||
{
|
||||
if (! config.has ("debug.tls"))
|
||||
config.set ("debug.tls", 2);
|
||||
|
||||
if (! config.has ("debug.hooks"))
|
||||
config.set ("debug.hooks", 1);
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <format.h>
|
||||
#include <main.h>
|
||||
#include <util.h>
|
||||
#include "tc/Server.h"
|
||||
|
||||
bool TDB2::debug_mode = false;
|
||||
static void dependency_scan (std::vector<Task> &);
|
||||
|
@ -469,6 +470,12 @@ int TDB2::num_reverts_possible ()
|
|||
return (int)replica.num_undo_points ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TDB2::sync (tc::Server server, bool avoid_snapshots)
|
||||
{
|
||||
replica.sync(std::move(server), avoid_snapshots);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TDB2::dump ()
|
||||
{
|
||||
|
|
|
@ -38,6 +38,10 @@
|
|||
#include <tc/WorkingSet.h>
|
||||
#include <tc/Replica.h>
|
||||
|
||||
namespace tc {
|
||||
class Server;
|
||||
}
|
||||
|
||||
// TDB2 Class represents all the files in the task database.
|
||||
class TDB2
|
||||
{
|
||||
|
@ -73,6 +77,8 @@ public:
|
|||
|
||||
void dump ();
|
||||
|
||||
void sync (tc::Server server, bool avoid_snapshots);
|
||||
|
||||
private:
|
||||
tc::Replica replica;
|
||||
std::optional<tc::WorkingSet> _working_set;
|
||||
|
|
|
@ -1,576 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// https://www.opensource.org/licenses/mit-license.php
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cmake.h>
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
|
||||
#include <TLSClient.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
#include <gnutls/x509.h>
|
||||
#include <shared.h>
|
||||
#include <format.h>
|
||||
|
||||
#define HEADER_SIZE 4
|
||||
#define MAX_BUF 16384
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030406
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x020a00
|
||||
static int verify_certificate_callback (gnutls_session_t);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static void gnutls_log_function (int level, const char* message)
|
||||
{
|
||||
std::cout << "c: " << level << ' ' << message;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030406
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x020a00
|
||||
static int verify_certificate_callback (gnutls_session_t session)
|
||||
{
|
||||
const TLSClient* client = (TLSClient*) gnutls_session_get_ptr (session); // All
|
||||
return client->verify_certificate ();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
TLSClient::~TLSClient ()
|
||||
{
|
||||
gnutls_deinit (_session); // All
|
||||
gnutls_certificate_free_credentials (_credentials); // All
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
gnutls_global_deinit (); // All
|
||||
#endif
|
||||
|
||||
if (_socket)
|
||||
{
|
||||
shutdown (_socket, SHUT_RDWR);
|
||||
close (_socket);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TLSClient::limit (int max)
|
||||
{
|
||||
_limit = max;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Calling this method results in all subsequent socket traffic being sent to
|
||||
// std::cout, labelled with 'c: ...'.
|
||||
void TLSClient::debug (int level)
|
||||
{
|
||||
if (level)
|
||||
_debug = true;
|
||||
|
||||
gnutls_global_set_log_function (gnutls_log_function); // All
|
||||
gnutls_global_set_log_level (level); // All
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TLSClient::trust (const enum trust_level value)
|
||||
{
|
||||
_trust = value;
|
||||
if (_debug)
|
||||
{
|
||||
if (_trust == allow_all)
|
||||
std::cout << "c: INFO Server certificate will be trusted automatically.\n";
|
||||
else if (_trust == ignore_hostname)
|
||||
std::cout << "c: INFO Server certificate will be verified but hostname ignored.\n";
|
||||
else
|
||||
std::cout << "c: INFO Server certificate will be verified.\n";
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TLSClient::ciphers (const std::string& cipher_list)
|
||||
{
|
||||
_ciphers = cipher_list;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TLSClient::init (
|
||||
const std::string& ca,
|
||||
const std::string& cert,
|
||||
const std::string& key)
|
||||
{
|
||||
_ca = ca;
|
||||
_cert = cert;
|
||||
_key = key;
|
||||
|
||||
int ret;
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030300
|
||||
ret = gnutls_global_init (); // All
|
||||
if (ret < 0)
|
||||
throw format ("TLS init error. {1}", gnutls_strerror (ret)); // All
|
||||
#endif
|
||||
|
||||
ret = gnutls_certificate_allocate_credentials (&_credentials); // All
|
||||
if (ret < 0)
|
||||
throw format ("TLS allocation error. {1}", gnutls_strerror (ret)); // All
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030014
|
||||
// Automatic loading of system installed CA certificates.
|
||||
ret = gnutls_certificate_set_x509_system_trust (_credentials); // 3.0.20
|
||||
if (ret < 0)
|
||||
throw format ("Bad System Trust. {1}", gnutls_strerror (ret)); // All
|
||||
#endif
|
||||
|
||||
if (_ca != "")
|
||||
{
|
||||
// The gnutls_certificate_set_x509_key_file call returns number of
|
||||
// certificates parsed on success (including 0, when no certificate was
|
||||
// found) and negative values on error
|
||||
ret = gnutls_certificate_set_x509_trust_file (_credentials, _ca.c_str (), GNUTLS_X509_FMT_PEM); // All
|
||||
if (ret == 0)
|
||||
throw format ("CA file {1} contains no certificate.", _ca);
|
||||
else if (ret < 0)
|
||||
throw format ("Bad CA file: {1}", gnutls_strerror (ret)); // All
|
||||
|
||||
}
|
||||
|
||||
// TODO This may need 0x030111 protection.
|
||||
if (_cert != "" &&
|
||||
_key != "" &&
|
||||
(ret = gnutls_certificate_set_x509_key_file (_credentials, _cert.c_str (), _key.c_str (), GNUTLS_X509_FMT_PEM)) < 0) // 3.1.11
|
||||
throw format ("Bad client CERT/KEY file. {1}", gnutls_strerror (ret)); // All
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER < 0x030406
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x020a00
|
||||
// The automatic verification for the server certificate with
|
||||
// gnutls_certificate_set_verify_function only works with gnutls
|
||||
// >=2.9.10. So with older versions we should call the verify function
|
||||
// manually after the gnutls handshake.
|
||||
gnutls_certificate_set_verify_function (_credentials, verify_certificate_callback); // 2.10.0
|
||||
#endif
|
||||
#endif
|
||||
ret = gnutls_init (&_session, GNUTLS_CLIENT); // All
|
||||
if (ret < 0)
|
||||
throw format ("TLS client init error. {1}", gnutls_strerror (ret)); // All
|
||||
|
||||
// Use default priorities unless overridden.
|
||||
if (_ciphers == "")
|
||||
_ciphers = "NORMAL";
|
||||
|
||||
const char *err;
|
||||
ret = gnutls_priority_set_direct (_session, _ciphers.c_str (), &err); // All
|
||||
if (ret < 0)
|
||||
{
|
||||
if (_debug && ret == GNUTLS_E_INVALID_REQUEST)
|
||||
std::cout << "c: ERROR Priority error at: " << err << '\n';
|
||||
|
||||
throw format ("Error initializing TLS. {1}", gnutls_strerror (ret)); // All
|
||||
}
|
||||
|
||||
// Apply the x509 credentials to the current session.
|
||||
ret = gnutls_credentials_set (_session, GNUTLS_CRD_CERTIFICATE, _credentials); // All
|
||||
if (ret < 0)
|
||||
throw format ("TLS credentials error. {1}", gnutls_strerror (ret)); // All
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TLSClient::connect (const std::string& host, const std::string& port)
|
||||
{
|
||||
_host = host;
|
||||
_port = port;
|
||||
|
||||
int ret;
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030406
|
||||
// For _trust == TLSClient::allow_all we perform no action
|
||||
if (_trust == TLSClient::ignore_hostname)
|
||||
gnutls_session_set_verify_cert (_session, nullptr, 0); // 3.4.6
|
||||
else if (_trust == TLSClient::strict)
|
||||
gnutls_session_set_verify_cert (_session, _host.c_str (), 0); // 3.4.6
|
||||
#endif
|
||||
|
||||
// SNI. Only permitted when _host is a DNS name, not an IPv4/6 address.
|
||||
std::string dummyAddress;
|
||||
int dummyPort;
|
||||
if (! isIPv4Address (_host, dummyAddress, dummyPort) &&
|
||||
! isIPv6Address (_host, dummyAddress, dummyPort))
|
||||
{
|
||||
ret = gnutls_server_name_set (_session, GNUTLS_NAME_DNS, _host.c_str (), _host.length ()); // All
|
||||
if (ret < 0)
|
||||
throw format ("TLS SNI error. {1}", gnutls_strerror (ret)); // All
|
||||
}
|
||||
|
||||
// Store the TLSClient instance, so that the verification callback can access
|
||||
// it during the handshake below and call the verification method.
|
||||
gnutls_session_set_ptr (_session, (void*) this); // All
|
||||
|
||||
// use IPv4 or IPv6, does not matter.
|
||||
struct addrinfo hints {};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE; // use my IP
|
||||
|
||||
struct addrinfo* res;
|
||||
ret = ::getaddrinfo (host.c_str (), port.c_str (), &hints, &res);
|
||||
if (ret != 0)
|
||||
throw std::string (::gai_strerror (ret));
|
||||
|
||||
// Try them all, stop on success.
|
||||
struct addrinfo* p;
|
||||
for (p = res; p != nullptr; p = p->ai_next)
|
||||
{
|
||||
if ((_socket = ::socket (p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
|
||||
continue;
|
||||
|
||||
// When a socket is closed, it remains unavailable for a while (netstat -an).
|
||||
// Setting SO_REUSEADDR allows this program to assume control of a closed,
|
||||
// but unavailable socket.
|
||||
int on = 1;
|
||||
if (::setsockopt (_socket,
|
||||
SOL_SOCKET,
|
||||
SO_REUSEADDR,
|
||||
(const void*) &on,
|
||||
sizeof (on)) == -1)
|
||||
throw std::string (::strerror (errno));
|
||||
|
||||
if (::connect (_socket, p->ai_addr, p->ai_addrlen) == -1)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
free (res);
|
||||
|
||||
if (p == nullptr)
|
||||
throw format ("Could not connect to {1} {2}", host, port);
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030100
|
||||
gnutls_handshake_set_timeout (_session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); // 3.1.0
|
||||
#endif
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030109
|
||||
gnutls_transport_set_int (_session, _socket); // 3.1.9
|
||||
#else
|
||||
gnutls_transport_set_ptr (_session, (gnutls_transport_ptr_t) (intptr_t) _socket); // All
|
||||
#endif
|
||||
|
||||
// Perform the TLS handshake
|
||||
do
|
||||
{
|
||||
ret = gnutls_handshake (_session); // All
|
||||
}
|
||||
while (ret < 0 && gnutls_error_is_fatal (ret) == 0); // All
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030406
|
||||
if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR)
|
||||
{
|
||||
auto type = gnutls_certificate_type_get (_session); // All
|
||||
auto status = gnutls_session_get_verify_cert_status (_session); // 3.4.6
|
||||
gnutls_datum_t out;
|
||||
gnutls_certificate_verification_status_print (status, type, &out, 0); // 3.1.4
|
||||
|
||||
std::string error {(const char*) out.data};
|
||||
gnutls_free (out.data); // All
|
||||
|
||||
throw format ("Handshake failed. {1}", error); // All
|
||||
}
|
||||
#else
|
||||
throw format ("Handshake failed. {1}", gnutls_strerror (ret)); // All
|
||||
#endif
|
||||
}
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER < 0x020a00
|
||||
// The automatic verification for the server certificate with
|
||||
// gnutls_certificate_set_verify_function does only work with gnutls
|
||||
// >=2.10.0. So with older versions we should call the verify function
|
||||
// manually after the gnutls handshake.
|
||||
ret = verify_certificate ();
|
||||
if (ret < 0)
|
||||
{
|
||||
if (_debug)
|
||||
std::cout << "c: ERROR Certificate verification failed.\n";
|
||||
throw format ("Error initializing TLS. {1}", gnutls_strerror (ret)); // All
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_debug)
|
||||
{
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x03010a
|
||||
char* desc = gnutls_session_get_desc (_session); // 3.1.10
|
||||
std::cout << "c: INFO Handshake was completed: " << desc << '\n';
|
||||
gnutls_free (desc);
|
||||
#else
|
||||
std::cout << "c: INFO Handshake was completed.\n";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TLSClient::bye ()
|
||||
{
|
||||
gnutls_bye (_session, GNUTLS_SHUT_RDWR); // All
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int TLSClient::verify_certificate () const
|
||||
{
|
||||
if (_trust == TLSClient::allow_all)
|
||||
return 0;
|
||||
|
||||
if (_debug)
|
||||
std::cout << "c: INFO Verifying certificate.\n";
|
||||
|
||||
// This verification function uses the trusted CAs in the credentials
|
||||
// structure. So you must have installed one or more CA certificates.
|
||||
unsigned int status = 0;
|
||||
const char* hostname = _host.c_str();
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030104
|
||||
if (_trust == TLSClient::ignore_hostname)
|
||||
hostname = nullptr;
|
||||
|
||||
int ret = gnutls_certificate_verify_peers3 (_session, hostname, &status); // 3.1.4
|
||||
if (ret < 0)
|
||||
{
|
||||
if (_debug)
|
||||
std::cout << "c: ERROR Certificate verification peers3 failed. " << gnutls_strerror (ret) << '\n'; // All
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
// status 16450 == 0100000001000010
|
||||
// GNUTLS_CERT_INVALID 1<<1
|
||||
// GNUTLS_CERT_SIGNER_NOT_FOUND 1<<6
|
||||
// GNUTLS_CERT_UNEXPECTED_OWNER 1<<14 Hostname does not match
|
||||
|
||||
if (_debug && status)
|
||||
std::cout << "c: ERROR Certificate status=" << status << '\n';
|
||||
#else
|
||||
int ret = gnutls_certificate_verify_peers2 (_session, &status); // All
|
||||
if (ret < 0)
|
||||
{
|
||||
if (_debug)
|
||||
std::cout << "c: ERROR Certificate verification peers2 failed. " << gnutls_strerror (ret) << '\n'; // All
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
if (_debug && status)
|
||||
std::cout << "c: ERROR Certificate status=" << status << '\n';
|
||||
|
||||
if ((status == 0) && (_trust != TLSClient::ignore_hostname))
|
||||
{
|
||||
if (gnutls_certificate_type_get (_session) == GNUTLS_CRT_X509) // All
|
||||
{
|
||||
const gnutls_datum* cert_list;
|
||||
unsigned int cert_list_size;
|
||||
gnutls_x509_crt cert;
|
||||
|
||||
cert_list = gnutls_certificate_get_peers (_session, &cert_list_size); // All
|
||||
if (cert_list_size == 0)
|
||||
{
|
||||
if (_debug)
|
||||
std::cout << "c: ERROR Certificate get peers failed. " << gnutls_strerror (ret) << '\n'; // All
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
ret = gnutls_x509_crt_init (&cert); // All
|
||||
if (ret < 0)
|
||||
{
|
||||
if (_debug)
|
||||
std::cout << "c: ERROR x509 init failed. " << gnutls_strerror (ret) << '\n'; // All
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
ret = gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER); // All
|
||||
if (ret < 0)
|
||||
{
|
||||
if (_debug)
|
||||
std::cout << "c: ERROR x509 cert import. " << gnutls_strerror (ret) << '\n'; // All
|
||||
gnutls_x509_crt_deinit(cert); // All
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
if (gnutls_x509_crt_check_hostname (cert, hostname) == 0) // All
|
||||
{
|
||||
if (_debug)
|
||||
std::cout << "c: ERROR x509 cert check hostname. " << gnutls_strerror (ret) << '\n'; // All
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030104
|
||||
gnutls_certificate_type_t type = gnutls_certificate_type_get (_session); // All
|
||||
gnutls_datum_t out;
|
||||
ret = gnutls_certificate_verification_status_print (status, type, &out, 0); // 3.1.4
|
||||
if (ret < 0)
|
||||
{
|
||||
if (_debug)
|
||||
std::cout << "c: ERROR certificate verification status. " << gnutls_strerror (ret) << '\n'; // All
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
if (_debug)
|
||||
std::cout << "c: INFO " << out.data << '\n';
|
||||
gnutls_free (out.data);
|
||||
#endif
|
||||
|
||||
if (status != 0)
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
|
||||
// Continue handshake.
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TLSClient::send (const std::string& data)
|
||||
{
|
||||
std::string packet = "XXXX" + data;
|
||||
|
||||
// Encode the length.
|
||||
unsigned long l = packet.length ();
|
||||
packet[0] = l >>24;
|
||||
packet[1] = l >>16;
|
||||
packet[2] = l >>8;
|
||||
packet[3] = l;
|
||||
|
||||
unsigned int total = 0;
|
||||
|
||||
int status;
|
||||
do
|
||||
{
|
||||
status = gnutls_record_send (_session, packet.c_str () + total, packet.length () - total); // All
|
||||
}
|
||||
while ((status > 0 && (total += status) < packet.length ()) ||
|
||||
status == GNUTLS_E_INTERRUPTED ||
|
||||
status == GNUTLS_E_AGAIN);
|
||||
|
||||
if (_debug)
|
||||
std::cout << "c: INFO Sending 'XXXX"
|
||||
<< data.c_str ()
|
||||
<< "' (" << total << " bytes)"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TLSClient::recv (std::string& data)
|
||||
{
|
||||
data = ""; // No appending of data.
|
||||
int received = 0;
|
||||
int total = 0;
|
||||
|
||||
// Get the encoded length.
|
||||
unsigned char header[HEADER_SIZE] {};
|
||||
do
|
||||
{
|
||||
received = gnutls_record_recv (_session, header + total, HEADER_SIZE - total); // All
|
||||
}
|
||||
while ((received > 0 && (total += received) < HEADER_SIZE) ||
|
||||
received == GNUTLS_E_INTERRUPTED ||
|
||||
received == GNUTLS_E_AGAIN);
|
||||
|
||||
if (total < HEADER_SIZE) {
|
||||
throw std::string ("Failed to receive header: ") +
|
||||
(received < 0 ? gnutls_strerror(received) : "connection lost?");
|
||||
}
|
||||
|
||||
// Decode the length.
|
||||
unsigned long expected = (header[0]<<24) |
|
||||
(header[1]<<16) |
|
||||
(header[2]<<8) |
|
||||
header[3];
|
||||
if (_debug)
|
||||
std::cout << "c: INFO expecting " << expected << " bytes.\n";
|
||||
|
||||
if (_limit && expected >= (unsigned long) _limit) {
|
||||
std::ostringstream err_str;
|
||||
err_str << "Expected message size " << expected << " is larger than allowed limit " << _limit;
|
||||
throw err_str.str ();
|
||||
}
|
||||
|
||||
// Arbitrary buffer size.
|
||||
char buffer[MAX_BUF];
|
||||
|
||||
// Keep reading until no more data. Concatenate chunks of data if a) the
|
||||
// read was interrupted by a signal, and b) if there is more data than
|
||||
// fits in the buffer.
|
||||
do
|
||||
{
|
||||
int chunk_size = 0;
|
||||
do
|
||||
{
|
||||
received = gnutls_record_recv (_session, buffer + chunk_size, MAX_BUF - chunk_size); // All
|
||||
if (received > 0) {
|
||||
total += received;
|
||||
chunk_size += received;
|
||||
}
|
||||
}
|
||||
while ((received > 0 && (unsigned long) total < expected && chunk_size < MAX_BUF) ||
|
||||
received == GNUTLS_E_INTERRUPTED ||
|
||||
received == GNUTLS_E_AGAIN);
|
||||
|
||||
// Other end closed the connection.
|
||||
if (received == 0)
|
||||
{
|
||||
if (_debug)
|
||||
std::cout << "c: INFO Peer has closed the TLS connection\n";
|
||||
break;
|
||||
}
|
||||
|
||||
// Something happened.
|
||||
if (received < 0)
|
||||
throw std::string (gnutls_strerror (received)); // All
|
||||
|
||||
data.append (buffer, chunk_size);
|
||||
|
||||
// Stop at defined limit.
|
||||
if (_limit && total > _limit)
|
||||
break;
|
||||
}
|
||||
while (received > 0 && total < (int) expected);
|
||||
|
||||
if (_debug)
|
||||
std::cout << "c: INFO Receiving 'XXXX"
|
||||
<< data.c_str ()
|
||||
<< "' (" << total << " bytes)"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#endif
|
|
@ -1,72 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// https://www.opensource.org/licenses/mit-license.php
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef INCLUDED_TLSCLIENT
|
||||
#define INCLUDED_TLSCLIENT
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
|
||||
#include <string>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
class TLSClient
|
||||
{
|
||||
public:
|
||||
enum trust_level { strict, ignore_hostname, allow_all };
|
||||
|
||||
TLSClient () = default;
|
||||
~TLSClient ();
|
||||
void limit (int);
|
||||
void debug (int);
|
||||
void trust (const enum trust_level);
|
||||
void ciphers (const std::string&);
|
||||
void init (const std::string&, const std::string&, const std::string&);
|
||||
void connect (const std::string&, const std::string&);
|
||||
void bye ();
|
||||
int verify_certificate() const;
|
||||
|
||||
void send (const std::string&);
|
||||
void recv (std::string&);
|
||||
|
||||
private:
|
||||
std::string _ca {""};
|
||||
std::string _cert {""};
|
||||
std::string _key {""};
|
||||
std::string _ciphers {""};
|
||||
std::string _host {""};
|
||||
std::string _port {""};
|
||||
gnutls_certificate_credentials_t _credentials {};
|
||||
gnutls_session_t _session {nullptr};
|
||||
int _socket {0};
|
||||
int _limit {0};
|
||||
bool _debug {false};
|
||||
enum trust_level _trust {strict};
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -39,10 +39,6 @@
|
|||
#include <commit.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
#include <gnutls/gnutls.h>
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
CmdDiagnostics::CmdDiagnostics ()
|
||||
{
|
||||
|
@ -135,18 +131,6 @@ int CmdDiagnostics::execute (std::string& output)
|
|||
#endif
|
||||
<< '\n';
|
||||
|
||||
out << " libgnutls: "
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
#ifdef GNUTLS_VERSION
|
||||
<< GNUTLS_VERSION
|
||||
#elif defined LIBGNUTLS_VERSION
|
||||
<< LIBGNUTLS_VERSION
|
||||
#endif
|
||||
#else
|
||||
<< "n/a"
|
||||
#endif
|
||||
<< '\n';
|
||||
|
||||
out << " Build type: "
|
||||
#ifdef CMAKE_BUILD_TYPE
|
||||
<< CMAKE_BUILD_TYPE
|
||||
|
@ -214,82 +198,6 @@ int CmdDiagnostics::execute (std::string& output)
|
|||
else if ((peditor = getenv ("EDITOR")) != nullptr)
|
||||
out << " $EDITOR: " << peditor << '\n';
|
||||
|
||||
out << " Server: "
|
||||
<< Context::getContext ().config.get ("taskd.server")
|
||||
<< '\n';
|
||||
|
||||
auto ca_pem = Context::getContext ().config.get ("taskd.ca");
|
||||
out << " CA: ";
|
||||
if (ca_pem != "")
|
||||
{
|
||||
File file_ca (ca_pem);
|
||||
if (file_ca.exists ())
|
||||
out << ca_pem
|
||||
<< (file_ca.readable () ? ", readable, " : ", not readable, ")
|
||||
<< file_ca.size ()
|
||||
<< " bytes\n";
|
||||
else
|
||||
out << "not found\n";
|
||||
}
|
||||
else
|
||||
out << "-\n";
|
||||
|
||||
auto cert_pem = Context::getContext ().config.get ("taskd.certificate");
|
||||
out << "Certificate: ";
|
||||
if (cert_pem != "")
|
||||
{
|
||||
File file_cert (cert_pem);
|
||||
if (file_cert.exists ())
|
||||
out << cert_pem
|
||||
<< (file_cert.readable () ? ", readable, " : ", not readable, ")
|
||||
<< file_cert.size ()
|
||||
<< " bytes\n";
|
||||
else
|
||||
out << "not found\n";
|
||||
}
|
||||
else
|
||||
out << "-\n";
|
||||
|
||||
auto key_pem = Context::getContext ().config.get ("taskd.key");
|
||||
out << " Key: ";
|
||||
if (key_pem != "")
|
||||
{
|
||||
File file_key (key_pem);
|
||||
if (file_key.exists ())
|
||||
out << key_pem
|
||||
<< (file_key.readable () ? ", readable, " : ", not readable, ")
|
||||
<< file_key.size ()
|
||||
<< " bytes\n";
|
||||
else
|
||||
out << "not found\n";
|
||||
}
|
||||
else
|
||||
out << "-\n";
|
||||
|
||||
auto trust_value = Context::getContext ().config.get ("taskd.trust");
|
||||
if (trust_value == "strict" ||
|
||||
trust_value == "ignore hostname" ||
|
||||
trust_value == "allow all")
|
||||
out << " Trust: " << trust_value << '\n';
|
||||
else
|
||||
out << " Trust: Bad value - see 'man taskrc'\n";
|
||||
|
||||
out << " Ciphers: "
|
||||
<< Context::getContext ().config.get ("taskd.ciphers")
|
||||
<< '\n';
|
||||
|
||||
// Get credentials, but mask out the key.
|
||||
auto credentials = Context::getContext ().config.get ("taskd.credentials");
|
||||
auto last_slash = credentials.rfind ('/');
|
||||
if (last_slash != std::string::npos)
|
||||
credentials = credentials.substr (0, last_slash)
|
||||
+ '/'
|
||||
+ std::string (credentials.length () - last_slash - 1, '*');
|
||||
|
||||
out << " Creds: "
|
||||
<< credentials
|
||||
<< "\n\n";
|
||||
|
||||
// Display hook status.
|
||||
Path hookLocation;
|
||||
if (Context::getContext ().config.has ("hooks.location"))
|
||||
|
|
|
@ -145,7 +145,6 @@ int CmdShow::execute (std::string& output)
|
|||
" debug"
|
||||
" debug.hooks"
|
||||
" debug.parser"
|
||||
" debug.tls"
|
||||
" default.command"
|
||||
" default.due"
|
||||
" default.project"
|
||||
|
@ -193,14 +192,11 @@ int CmdShow::execute (std::string& output)
|
|||
" search.case.sensitive"
|
||||
" sugar"
|
||||
" summary.all.projects"
|
||||
" sync.local.server_dir"
|
||||
" sync.server.client_key"
|
||||
" sync.server.encryption_secret"
|
||||
" sync.server.origin"
|
||||
" tag.indicator"
|
||||
" taskd.server"
|
||||
" taskd.ca"
|
||||
" taskd.certificate"
|
||||
" taskd.ciphers"
|
||||
" taskd.credentials"
|
||||
" taskd.key"
|
||||
" taskd.trust"
|
||||
" undo.style"
|
||||
" urgency.active.coefficient"
|
||||
" urgency.scheduled.coefficient"
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <shared.h>
|
||||
#include <format.h>
|
||||
#include <util.h>
|
||||
#include "tc/Server.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
CmdSync::CmdSync ()
|
||||
|
@ -53,10 +54,37 @@ CmdSync::CmdSync ()
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int CmdSync::execute (std::string&)
|
||||
int CmdSync::execute (std::string& output)
|
||||
{
|
||||
throw std::string ("Sync support is disabled during transition to TaskChampion.");
|
||||
return 0;
|
||||
int status = 0;
|
||||
|
||||
tc::Server server;
|
||||
std::string server_ident;
|
||||
|
||||
// If no server is set up, quit.
|
||||
std::string origin = Context::getContext ().config.get ("sync.server.origin");
|
||||
std::string client_key = Context::getContext ().config.get ("sync.server.client_key");
|
||||
std::string encryption_secret = Context::getContext ().config.get ("sync.server.encryption_secret");
|
||||
std::string server_dir = Context::getContext ().config.get ("sync.local.server_dir");
|
||||
if (server_dir != "") {
|
||||
server = tc::Server (server_dir);
|
||||
server_ident = server_dir;
|
||||
} else if (origin != "" && client_key != "" && encryption_secret != "") {
|
||||
server = tc::Server (origin, client_key, encryption_secret);
|
||||
server_ident = origin;
|
||||
} else {
|
||||
throw std::string ("Neither sync.server nor sync.local are configured.");
|
||||
}
|
||||
|
||||
std::stringstream out;
|
||||
if (Context::getContext ().verbose ("sync"))
|
||||
out << format ("Syncing with {1}", server_ident)
|
||||
<< '\n';
|
||||
|
||||
Context::getContext ().tdb2.sync(std::move(server), false);
|
||||
|
||||
output = out.str ();
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -30,18 +30,12 @@
|
|||
#include <string>
|
||||
#include <Command.h>
|
||||
#include <Msg.h>
|
||||
#include <TLSClient.h>
|
||||
|
||||
class CmdSync : public Command
|
||||
{
|
||||
public:
|
||||
CmdSync ();
|
||||
int execute (std::string&);
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
private:
|
||||
bool send (const std::string&, const std::string&, const std::string&, const std::string&, const enum TLSClient::trust_level, const Msg&, Msg&);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -207,8 +207,7 @@ void feedback_unblocked (const Task& task)
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
void feedback_backlog ()
|
||||
{
|
||||
if (Context::getContext ().config.get ("taskd.server") != "" &&
|
||||
Context::getContext ().verbose ("sync"))
|
||||
if (Context::getContext ().verbose ("sync"))
|
||||
{
|
||||
int count = Context::getContext ().tdb2.num_local_changes ();
|
||||
if (count)
|
||||
|
|
|
@ -10,6 +10,7 @@ set (tc_SRCS
|
|||
ffi.h
|
||||
util.cpp util.h
|
||||
Replica.cpp Replica.h
|
||||
Server.cpp Server.h
|
||||
WorkingSet.cpp WorkingSet.h
|
||||
Task.cpp Task.h)
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <format.h>
|
||||
#include "tc/Replica.h"
|
||||
#include "tc/Task.h"
|
||||
#include "tc/Server.h"
|
||||
#include "tc/WorkingSet.h"
|
||||
#include "tc/util.h"
|
||||
|
||||
|
@ -142,6 +143,16 @@ tc::Task tc::Replica::import_task_with_uuid (const std::string &uuid)
|
|||
return Task (tctask);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void tc::Replica::sync (Server server, bool avoid_snapshots)
|
||||
{
|
||||
// The server remains owned by this function, per tc_replica_sync docs.
|
||||
auto res = tc_replica_sync (&*inner, server.inner.get(), avoid_snapshots);
|
||||
if (res != TC_RESULT_OK) {
|
||||
throw replica_error ();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void tc::Replica::undo (int32_t *undone_out)
|
||||
{
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
namespace tc {
|
||||
class Task;
|
||||
class WorkingSet;
|
||||
class Server;
|
||||
|
||||
// a unique_ptr to a TCReplica which will automatically free the value when
|
||||
// it goes out of scope.
|
||||
|
@ -91,7 +92,7 @@ namespace tc {
|
|||
tc::Task new_task (Status status, const std::string &description);
|
||||
tc::Task import_task_with_uuid (const std::string &uuid);
|
||||
// TODO: struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid);
|
||||
// TODO: TCResult tc_replica_sync(struct TCReplica *rep, struct TCServer *server, bool avoid_snapshots);
|
||||
void sync(Server server, bool avoid_snapshots);
|
||||
void undo (int32_t *undone_out);
|
||||
int64_t num_local_operations ();
|
||||
int64_t num_undo_points ();
|
||||
|
|
101
src/tc/Server.cpp
Normal file
101
src/tc/Server.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2022, Dustin J. Mitchell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// https://www.opensource.org/licenses/mit-license.php
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cmake.h>
|
||||
#include <format.h>
|
||||
#include "tc/Server.h"
|
||||
#include "tc/util.h"
|
||||
|
||||
using namespace tc::ffi;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
tc::Server::Server (const std::string &server_dir)
|
||||
{
|
||||
TCString tc_server_dir = tc_string_borrow (server_dir.c_str ());
|
||||
TCString error;
|
||||
auto tcserver = tc_server_new_local (tc_server_dir, &error);
|
||||
if (!tcserver) {
|
||||
auto errmsg = format ("Could not configure local server at {1}: {2}",
|
||||
server_dir, tc_string_content (&error));
|
||||
tc_string_free (&error);
|
||||
throw errmsg;
|
||||
}
|
||||
inner = unique_tcserver_ptr (
|
||||
tcserver,
|
||||
[](TCServer* rep) { tc_server_free (rep); });
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
tc::Server::Server (const std::string &origin, const std::string &client_key, const std::string &encryption_secret)
|
||||
{
|
||||
TCString tc_origin = tc_string_borrow (origin.c_str ());
|
||||
|
||||
TCString tc_client_key_str = tc_string_borrow (client_key.c_str ());
|
||||
|
||||
TCString tc_encryption_secret = tc_string_borrow (encryption_secret.c_str ());
|
||||
|
||||
TCUuid tc_client_key;
|
||||
if (tc_uuid_from_str(tc_client_key_str, &tc_client_key) != TC_RESULT_OK) {
|
||||
tc_string_free(&tc_origin);
|
||||
tc_string_free(&tc_encryption_secret);
|
||||
throw "client_key must be a valid UUID";
|
||||
}
|
||||
|
||||
TCString error;
|
||||
auto tcserver = tc_server_new_remote (tc_origin, tc_client_key, tc_encryption_secret, &error);
|
||||
if (!tcserver) {
|
||||
auto errmsg = format ("Could not configure connection to server at {1}: {2}",
|
||||
origin, tc_string_content (&error));
|
||||
tc_string_free (&error);
|
||||
throw errmsg;
|
||||
}
|
||||
inner = unique_tcserver_ptr (
|
||||
tcserver,
|
||||
[](TCServer* rep) { tc_server_free (rep); });
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
tc::Server::Server (tc::Server &&other) noexcept
|
||||
{
|
||||
// move inner from other
|
||||
inner = unique_tcserver_ptr (
|
||||
other.inner.release (),
|
||||
[](TCServer* rep) { tc_server_free (rep); });
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
tc::Server& tc::Server::operator= (tc::Server &&other) noexcept
|
||||
{
|
||||
if (this != &other) {
|
||||
// move inner from other
|
||||
inner = unique_tcserver_ptr (
|
||||
other.inner.release (),
|
||||
[](TCServer* rep) { tc_server_free (rep); });
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
80
src/tc/Server.h
Normal file
80
src/tc/Server.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2022, Dustin J. Mitchell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// https://www.opensource.org/licenses/mit-license.php
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDED_TC_SERVER
|
||||
#define INCLUDED_TC_SERVER
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include "tc/ffi.h"
|
||||
|
||||
namespace tc {
|
||||
// a unique_ptr to a TCServer which will automatically free the value when
|
||||
// it goes out of scope.
|
||||
using unique_tcserver_ptr = std::unique_ptr<
|
||||
tc::ffi::TCServer,
|
||||
std::function<void(tc::ffi::TCServer*)>>;
|
||||
|
||||
// Server wraps the TCServer type, managing its memory, errors, and so on.
|
||||
//
|
||||
// Except as noted, method names match the suffix to `tc_replica_..`.
|
||||
class Server
|
||||
{
|
||||
public:
|
||||
// Construct a null server
|
||||
Server () = default;
|
||||
|
||||
// Construct a local server (tc_server_new_local).
|
||||
Server (const std::string& server_dir);
|
||||
|
||||
// Construct a remote server (tc_server_new_remote).
|
||||
Server (const std::string &origin, const std::string &client_key, const std::string &encryption_secret);
|
||||
|
||||
// This object "owns" inner, so copy is not allowed.
|
||||
Server (const Server &) = delete;
|
||||
Server &operator=(const Server &) = delete;
|
||||
|
||||
// Explicit move constructor and assignment
|
||||
Server (Server &&) noexcept;
|
||||
Server &operator=(Server &&) noexcept;
|
||||
|
||||
protected:
|
||||
unique_tcserver_ptr inner;
|
||||
|
||||
// Replica accesses the inner pointer to call tc_replica_sync
|
||||
friend class Replica;
|
||||
|
||||
// construct an error message from the given string.
|
||||
std::string server_error (tc::ffi::TCString string);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
////////////////////////////////////////////////////////////////////////////////
|
4
src/tc/rust/Cargo.lock
generated
4
src/tc/rust/Cargo.lock
generated
|
@ -767,9 +767,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.3.4"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81"
|
||||
checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"serde",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue