diff --git a/Makefile b/Makefile index 82793f4..a7c541a 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ LIBOUT := libpcloudcc_lib.so # Build type specific flags ifeq ($(BUILD), debug) - CFLAGS += -g -O0 -DDEBUG -Wall + CFLAGS += -g -O0 -DDEBUG -Wall CXXFLAGS += -g -O0 -DDEBUG -Wall else ifeq ($(BUILD), release) CFLAGS += -O2 -DNDEBUG diff --git a/control_tools.cpp b/control_tools.cpp index aa344a5..f2cba34 100644 --- a/control_tools.cpp +++ b/control_tools.cpp @@ -25,6 +25,8 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include #include #include @@ -37,9 +39,9 @@ #include "control_tools.h" #include "overlay_client.h" -#include "pclsync_lib_c.h" #include "pclsync_lib.h" +#include "psynclib.h" namespace cc = console_client; @@ -56,23 +58,129 @@ enum command_ids_ { STOPSYNC }; +std::pair split_paths(const std::string &input) { + std::string path1, path2; + bool in_quotes = false; + bool escaped = false; + std::string current_path; + + for (char c : input) { + if (escaped) { + current_path += c; + escaped = false; + } else if (c == '\\') { + escaped = true; + } else if (c == '"') { + in_quotes = !in_quotes; + current_path += c; + } else if (c == ' ' && !in_quotes) { + if (!current_path.empty()) { + if (path1.empty()) { + path1 = current_path; + } else { + path2 = current_path; + break; + } + current_path.clear(); + } + } else { + current_path += c; + } + } + + if (!current_path.empty()) { + if (path1.empty()) { + path1 = current_path; + } else if (path2.empty()) { + path2 = current_path; + } + } + + auto remove_quotes = [](std::string &s) { + if (s.size() >= 2 && s.front() == '"' && s.back() == '"') { + s = s.substr(1, s.size() - 2); + } + }; + + remove_quotes(path1); + remove_quotes(path2); + + return {path1, path2}; +} + +int list_sync_folders() { + int ret; + char *errm; + size_t errmsz; + void *rep; + size_t repsz; + int result; + psync_folder_list_t *flist; + psync_folder_t *folder; + int rval; + + errm = NULL; + errmsz = 0; + rep = NULL; + repsz = 0; + rval = 0; + + result = SendCall(LISTSYNC, "", &ret, &errm, &errmsz, &rep, &repsz); + + if (result != 0) { + std::cout << "List Sync Folders failed. return is " << ret + << " and message is " << (errm ? errm : "no message") + << std::endl; + rval = result; + } else if (rep && repsz > 0) { + flist = static_cast(rep); + + if (repsz < sizeof(psync_folder_list_t)) { + std::cout << "Error: Insufficient data for folder list structure" + << std::endl; + rval = -1; + } else { + + const int id_width = 12; + const int path_width = 30; + + std::cout << std::left << std::setw(id_width) << "Folder ID" + << std::setw(path_width) << "Local Path" + << std::setw(path_width) << "Remote Path" << std::endl; + std::cout << std::string(id_width, '-') + << std::string(path_width - 1, '-') + << std::string(path_width - 1, '-') << std::endl; + + for (uint32_t i = 0; i < flist->foldercnt; i++) { + folder = &flist->folders[i]; + std::cout << std::left << std::setw(id_width) << folder->folderid + << std::setw(path_width) << folder->localpath + << std::setw(path_width) << folder->remotepath << std::endl; + } + rval = ret; + } + } else { + std::cout << "No synchronized folders found." << std::endl; + rval = ret; + } + + free(errm); + free(rep); + return rval; +} + int start_crypto(const char *pass) { int ret; char *errm; size_t errm_size; - int result = SendCall(STARTCRYPTO, pass, &ret, &errm, &errm_size); - - // in this case, it is not enough to check for result; the server - // should return (ret==0) indicating that the crypto folder was unlocked - // successfully. + int result = SendCall(STARTCRYPTO, pass, &ret, &errm, &errm_size, NULL, NULL); if (result != 0 || ret != 0) { std::cout << "Start Crypto failed. return is " << ret << " and message is " << (errm ? errm : "no message") << std::endl; } else { std::cout << "Crypto started. " << std::endl; } - if (errm) free(errm); return ret; @@ -83,7 +191,7 @@ int stop_crypto() { char *errm; size_t errm_size; - int result = SendCall(STOPCRYPTO, "", &ret, &errm, &errm_size); + int result = SendCall(STOPCRYPTO, "", &ret, &errm, &errm_size, NULL, NULL); if (result != 0) { std::cout << "Stop Crypto failed. return is " << ret << " and message is " << (errm ? errm : "no message") << std::endl; @@ -96,19 +204,88 @@ int stop_crypto() { return ret; } +int remove_sync_folder(const char *folderid) { + int ret; + char *errm; + size_t errmsz; + int result; + int rval; + + errm = NULL; + errmsz = 0; + rval = 0; + + result = SendCall(STOPSYNC, folderid, &ret, &errm, &errmsz, NULL, NULL); + if (result != 0) { + std::cout << "Remove Sync Folder failed with unknown error. return is " + << ret << " and message is " << (errm ? errm : "no message") + << std::endl; + rval = result; + } else { + std::cout << "Successfully removed sync folder with folderid " << folderid + << std::endl; + } + free(errm); + return rval; +} + +// TODO: should add support for specifying sync type. Need a better +// CLI processing solution first. +int add_sync_folder(std::string localpath, std::string remotepath) { + int ret; + char *errm; + size_t errmsz; + void *rep; + size_t repsz; + int result; + int rval; + + errm = NULL; + errmsz = 0; + rep = NULL; + repsz = 0; + rval = 0; + + std::string combinedPaths = localpath + '|' + remotepath; + result = SendCall(ADDSYNC, combinedPaths.c_str(), &ret, &errm, &errmsz, &rep, + &repsz); + + if (result != 0) { + if (result == -1) { + std::cout << "Add Sync Folders failed: remote folder " << remotepath + << " not found." << std::endl; + } else { + std::cout << "Add Sync Folders failed with unknown error. return is " + << ret << " and message is " << (errm ? errm : "no message") + << std::endl; + } + rval = result; + } else if (rep && repsz > 0) { + if (repsz < sizeof(psync_syncid_t)) { + std::cout << "Error: Insufficient data for folder list structure" + << std::endl; + rval = -1; + } else { + rval = ret; + } + } else { + std::cout << "Error: Did not get a syncid from add_sync_folder." + << std::endl; + rval = ret; + } + + free(errm); + free(rep); + return rval; +} + int finalize() { int ret; char *errm; size_t errm_size; - int result = SendCall(FINALIZE, "", &ret, &errm, &errm_size); - if (result != 0) { - std::cout << "Finalize failed. return code is " << result << ", ret is " - << ret << ", and message is " << (errm ? errm : "no message") - << std::endl; - } else { - std::cout << "Exiting ..." << std::endl; - } + SendCall(FINALIZE, "", &ret, &errm, &errm_size, NULL, NULL); + std::cout << "Exiting ..." << std::endl; if (errm) free(errm); @@ -116,18 +293,29 @@ int finalize() { return ret; } -void process_commands() { +void help() { std::cout << "Supported commands are:" << std::endl - << "startcrypto , " - << "stopcrypto, " - << "finalize, " - << "q, quit" << std::endl; - std::cout << "> "; + << "help(?): Show this help message" << std::endl + << "startcrypto : Unlock crypto folder" << std::endl + << "stopcrypto: Lock crypto folder" << std::endl + << "finalize: Kill daemon and quit" << std::endl + << "syncls: List sync folders" << std::endl + << "syncadd : Add sync folder (full sync)" + << std::endl + << "syncrm : Remove sync folder" << std::endl + << "quit(q): Exit this program" << std::endl; +} +void process_commands() { + std::cout << "Type 'help' or '?' for a list of supported commands." + << std::endl; + std::cout << "> "; for (std::string line; std::getline(std::cin, line);) { if (!line.compare("finalize")) { finalize(); break; + } else if (!line.compare("help") || !line.compare("?")) { + help(); } else if (!line.compare("stopcrypto")) { stop_crypto(); } else if (!line.compare(0, 11, "startcrypto", 0, 11) && @@ -135,6 +323,13 @@ void process_commands() { start_crypto(line.c_str() + 12); } else if (!line.compare("q") || !line.compare("quit")) { break; + } else if (!line.compare("syncls")) { + list_sync_folders(); + } else if (!line.compare(0, 7, "syncadd", 0, 7) && (line.length() > 7)) { + auto [lpath, rpath] = split_paths(line.c_str() + 8); + add_sync_folder(lpath, rpath); + } else if (!line.compare(0, 6, "syncrm", 0, 6) && (line.length() > 6)) { + remove_sync_folder(line.c_str() + 7); } std::cout << "> "; } diff --git a/main.cpp b/main.cpp index 0470903..a5ffae4 100644 --- a/main.cpp +++ b/main.cpp @@ -31,7 +31,6 @@ #include #include "control_tools.h" -#include "pclsync_lib_c.h" #include "pclsync_lib.h" diff --git a/pclsync/overlay_client.c b/pclsync/overlay_client.c index de42eae..9ad75ce 100644 --- a/pclsync/overlay_client.c +++ b/pclsync/overlay_client.c @@ -26,6 +26,28 @@ DAMAGE. */ +/* + overlay_client is responsible for invoking the pCloud API and + directing the result back to the calling function. It defines the + core logic of the request-response flow: + + - SendCall (this function) writes the request to the socket. + + - instance_thread reads the request from the socket and calls + get_response (see poverlay.c). + + - get_response invokes the appropriate callback function and + generates a response_message. The callback functions are + registered in pclsync_lib.cpp using psync_add_overlay_callback + (see poverlay.c). + + - instance_thread writes the resulting response_message to the + socket (see poverlay.c). + + - SendCall reads the response from the socket. It writes the + response output data back to its caller (see control_tools.cpp). +*/ + #include #include #include @@ -38,22 +60,129 @@ #include "debug.h" #include "overlay_client.h" -#define POVERLAY_BUFSIZE 512 +#include "poverlay_protocol.h" -typedef struct _message { - uint32_t type; - uint64_t length; - char value[]; -} message; +#define POVERLAY_BUFSIZE 16 * 1024 -const char *clsoc = "/tmp/pcloud_unix_soc.sock"; +// for easier error tracing... +#define POVERLAY_SOCKET_CREATE_FAILED -100 +#define POVERLAY_SOCKET_CONNECT_FAILED -101 +#define POVERLAY_WRITE_SOCK_ERR -102 +#define POVERLAY_WRITE_COMM_ERR -103 +#define POVERLAY_READ_SOCK_ERR -104 +#define POVERLAY_READ_INCOMPLETE -105 +#define POVERLAY_READ_INVALID_RESPONSE -106 + +response_message *deserialize_response_message(const char *buffer, + size_t buffer_size) { + if (buffer_size < 2 * sizeof(size_t) + sizeof(uint32_t) + sizeof(uint64_t)) { + debug(D_ERROR, "Buffer size too small: %zu", buffer_size); + return NULL; + } + + const char *ptr = buffer; + + // Allocate response_message + response_message *resp = (response_message *)malloc(sizeof(response_message)); + if (resp == NULL) { + debug(D_ERROR, "Failed to allocate response_message"); + return NULL; + } + + // Get message size + size_t msg_size = be64toh(*(size_t *)ptr); + ptr += sizeof(size_t); + + debug(D_ERROR, "Message size: %zu", msg_size); + + if (msg_size < sizeof(uint32_t) + sizeof(uint64_t)) { + debug(D_ERROR, "Invalid message size: %zu", msg_size); + free(resp); + return NULL; + } + + // Allocate message with extra space for the flexible array member + size_t alloc_size = + sizeof(message) + msg_size - sizeof(uint32_t) - sizeof(uint64_t); + resp->msg = (message *)malloc(alloc_size); + if (resp->msg == NULL) { + debug(D_ERROR, "Failed to allocate message"); + free(resp); + return NULL; + } + + resp->msg->type = ntohl(*(uint32_t *)ptr); + ptr += sizeof(uint32_t); + + resp->msg->length = be64toh(*(uint64_t *)ptr); + ptr += sizeof(uint64_t); + + debug(D_ERROR, "Message type: %u, length: %lu", resp->msg->type, + resp->msg->length); + + // Calculate the size of the value array + size_t value_size = msg_size - sizeof(uint32_t) - sizeof(uint64_t); + + if (value_size > 0) { + if (ptr + value_size > buffer + buffer_size) { + debug(D_ERROR, "Buffer overflow detected"); + free(resp->msg); + free(resp); + return NULL; + } + memcpy(resp->msg->value, ptr, value_size); + ptr += value_size; + } + + // Deserialize payload size + if (ptr + sizeof(size_t) > buffer + buffer_size) { + debug(D_ERROR, "Buffer overflow detected when reading payload size"); + free(resp->msg); + free(resp); + return NULL; + } + resp->payloadsz = be64toh(*(size_t *)ptr); + ptr += sizeof(size_t); + + debug(D_ERROR, "Payload size: %zu", resp->payloadsz); + + // Deserialize payload + if (resp->payloadsz > 0) { + if (ptr + resp->payloadsz > buffer + buffer_size) { + debug(D_ERROR, "Buffer overflow detected when reading payload"); + free(resp->msg); + free(resp); + return NULL; + } + resp->payload = malloc(resp->payloadsz); + if (resp->payload == NULL) { + debug(D_ERROR, "Failed to allocate payload"); + free(resp->msg); + free(resp); + return NULL; + } + memcpy(resp->payload, ptr, resp->payloadsz); + } else { + resp->payload = NULL; + } + + return resp; +} + +void free_response_message(response_message *resp) { + if (resp) { + free(resp->msg); + free(resp->payload); + free(resp); + } +} int QueryState(pCloud_FileState *state, char *path) { int rep = 0; char *errm; size_t errm_size; - if (!SendCall(4, path /*IN*/, &rep, &errm, &errm_size)) { + if (!SendCall(4, path /*IN*/, &rep, &errm, &errm_size, NULL, NULL)) { debug(D_NOTICE, "QueryState responese rep[%d] path[%s]", rep, path); if (errm) debug(D_NOTICE, "The error is %s", errm); @@ -71,196 +200,214 @@ int QueryState(pCloud_FileState *state, char *path) { return 0; } -int SendCall(int id /*IN*/, const char *path /*IN*/, int *ret /*OUT*/, - char **out /*OUT*/, size_t *out_size) { +// socket_connect creates and connects to a unix socket at the +// specified sockpath. it may write an error message and error message +// size to out and out_size, and a "ret" value to ret (i think this is +// redundant maybe...) +int socket_connect(const char *sockpath, char **out, size_t *out_size, + int *ret) { + int fd; struct sockaddr_un addr; - - int result, rc; - uint64_t sendbytes = 0; - int fd = -1; - int sendlen = strlen(path); - int sendsize = sizeof(message) + sendlen + 1; - - char sendbuf[sendsize]; - - char *recvbuf = NULL; - size_t recvsize = 0; - size_t recvbytes = 0; - size_t recvchunk = 32; // read response in 32-byte chunks - - message *rep = NULL; const char *error_msg; - // init output params - *out = NULL; - *out_size = 0; - *ret = 0; - result = 0; - - debug(D_NOTICE, "SendCall id[%d] path[%s]\n", id, path); - - // prepare socket fd if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { error_msg = "Unable to create unix socket"; *out = strdup(error_msg); - if (*out == NULL) { - debug(D_ERROR, - "on socket(): failed to allocate memory for output message"); - *ret = -255; - return -255; - } *out_size = strlen(error_msg) + 1; - *ret = -3; - return -3; + *ret = POVERLAY_SOCKET_CREATE_FAILED; + return POVERLAY_SOCKET_CREATE_FAILED; } - // connect to socket memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, clsoc, sizeof(addr.sun_path) - 1); + strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path) - 1); if (connect(fd, (struct sockaddr *)&addr, SUN_LEN(&addr)) == -1) { error_msg = "Unable to connect to UNIX socket"; *out = strdup(error_msg); - if (*out == NULL) { - debug(D_ERROR, - "on connect(): failed to allocate memory for output message"); - *ret = -254; - return -254; - } *out_size = strlen(error_msg) + 1; - *ret = -4; - return -4; + *ret = POVERLAY_SOCKET_CONNECT_FAILED; + return POVERLAY_SOCKET_CONNECT_FAILED; } + return fd; +} - debug(D_NOTICE, "SendCall: Sending message type %d, path '%s'", id, path); +// write_request writes a request_message to given socket file +// descriptor of a given type and value. it may write out an error +// message and error message size to out and out_size, and a "ret" +// value to ret (i think this is redundant, maybe...) +int write_request(int fd, int msgtype, const char *value, char **out, + size_t *out_size, int *ret) { + uint64_t bytes_written; + int rc; + int len; + int size; + char *buf; + const char *err; + request_message *request; + char *curbuf; - // prepare and send the message to the socket - message *mes = (message *)sendbuf; - memset(mes, 0, sendsize); - mes->type = id; - strncpy(mes->value, path, sendlen + 1); - mes->length = sendsize; + *ret = 0; // Initialize ret to 0 - char *curbuf = (char *)mes; - while (sendbytes < mes->length) { - rc = write(fd, curbuf, (mes->length - sendbytes)); + len = strlen(value); + size = sizeof(request_message) + len + 1; + buf = (char *)malloc(size); + request = (request_message *)buf; + memset(request, 0, size); + request->type = msgtype; + strncpy(request->value, value, len + 1); + request->length = size; + bytes_written = 0; + curbuf = (char *)request; + + while (bytes_written < request->length && *ret == 0) { + rc = write(fd, curbuf, (request->length - bytes_written)); if (rc <= 0) { - if (errno == EINTR) - continue; // try again on interrupt - - error_msg = "failed to write to socket."; - *out = strdup(error_msg); - if (*out == NULL) { - debug(D_ERROR, "failed to allocate memory for output message"); - *ret = -253; - return -253; + if (errno != EINTR) { + err = "failed to write to socket."; + *out = strdup(err); + *ret = POVERLAY_WRITE_SOCK_ERR; } - *ret = -248; - return -248; + } else { + bytes_written += rc; + curbuf += rc; } - sendbytes += rc; } - debug(D_NOTICE, "SendCall: Sent %lu bytes", sendbytes); - if (sendbytes != mes->length) { - error_msg = "Communication error"; + if (*ret == 0 && bytes_written != request->length) { + err = "Communication error"; + *out = strdup(err); + *out_size = strlen(err) + 1; + *ret = POVERLAY_WRITE_COMM_ERR; + } + + free(buf); + return *ret; +} + +// read_response reads a response message from a given socket. it may +// write an error message OR an API response value to out (and its +// size to out_size), a "ret" value to ret (redundant?), and the +// callback's return data to payload and payloadsz. +int read_response(int fd, char **out, size_t *out_size, int *ret, + void **payload, size_t *payloadsz) { + char buffer[POVERLAY_BUFSIZE]; + ssize_t bytes_read = read(fd, buffer, POVERLAY_BUFSIZE); + + if (bytes_read <= 0) { + const char *error_msg = + (bytes_read == 0) ? "Connection closed" : strerror(errno); *out = strdup(error_msg); - if (*out == NULL) { - debug(D_ERROR, "while checking bytes_written: failed to allocate memory " - "for output message"); - *ret = -253; - return -253; - } *out_size = strlen(error_msg) + 1; - *ret = -5; - return -5; + *ret = -1; + return -1; } - // read the response from the socket - bool received_data = false; - for (;;) { - char *new_buf = realloc(recvbuf, recvsize + recvchunk); - if (!new_buf) { - debug(D_ERROR, "failed to allocate memory to response buffer"); - *ret = -252; - result = -252; - goto cleanup; - } - recvbuf = new_buf; - - rc = read(fd, recvbuf + recvsize, recvchunk); - if (rc < 0) { - debug(D_ERROR, "failed to read from socket into response buffer"); - *ret = -251; - result = -251; - goto cleanup; - } - if (rc == 0) { - break; // end of response - } - received_data = true; - recvsize += rc; - recvbytes += rc; - if (recvbytes >= sizeof(message)) { - message *rep = (message *)recvbuf; - if (recvbytes >= rep->length) { - break; // entire message read - } - } + if (bytes_read < sizeof(response_message)) { + const char *error_msg = "Incomplete response"; + *out = strdup(error_msg); + *out_size = strlen(error_msg) + 1; + *ret = -1; + return -1; } - debug(D_NOTICE, "SendCall: Received %zu bytes", recvbytes); - - if (!received_data) { - *ret = 0; - *out = strdup(""); - if (*out == NULL) { - debug(D_ERROR, "failed to allocate memory for empty output message"); - *ret = -248; - result = -248; - goto cleanup; - } - *out_size = 1; - result = 0; - goto cleanup; + // response_message *resp = (response_message *)buffer; + response_message *resp = deserialize_response_message(buffer, bytes_read); + if (resp == NULL) { + const char *error_msg = "Failed to deserialize response"; + *out = strdup(error_msg); + *out_size = strlen(error_msg) + 1; + *ret = -1; + return -1; } - if (recvbytes >= sizeof(message)) { - message *rep = (message *)recvbuf; - debug(D_NOTICE, "SendCall: Received message type: %d, length: %lu", - rep->type, rep->length); - if (recvbytes >= rep->length) { - debug(D_NOTICE, "SendCall: Entire message content: '%s'", rep->value); - } + // Validate the response_message structure + if (resp->msg == NULL || + (size_t)bytes_read < sizeof(response_message) + sizeof(message) || + (size_t)bytes_read < sizeof(response_message) + resp->msg->length) { + const char *error_msg = "Invalid response structure"; + *out = strdup(error_msg); + *out_size = strlen(error_msg) + 1; + *ret = -1; + return -1; } - if (recvbytes < sizeof(message)) { - debug(D_ERROR, "got incomplete message from socket"); - *ret = -250; - result = -250; - goto cleanup; + message *msg = resp->msg; + size_t value_size = msg->length - sizeof(message); + + // Validate message length + if (msg->length < sizeof(message) || + (size_t)bytes_read < sizeof(response_message) + msg->length) { + const char *error_msg = "Invalid message length"; + *out = strdup(error_msg); + *out_size = strlen(error_msg) + 1; + *ret = -1; + return -1; } - rep = (message *)recvbuf; - *ret = rep->type; - - size_t value_size = rep->length - sizeof(message); *out = malloc(value_size + 1); - if (!*out) { - debug(D_ERROR, "failed to allocate memory to output buffer"); - *ret = -249; - result = -249; - goto cleanup; + if (*out == NULL) { + const char *error_msg = "Memory allocation failed"; + *out = strdup(error_msg); + *out_size = strlen(error_msg) + 1; + *ret = -1; + free_response_message(resp); + return -1; } - memcpy(*out, rep->value, value_size); + + memcpy(*out, msg->value, value_size); (*out)[value_size] = '\0'; *out_size = value_size + 1; -cleanup: - if (fd != -1) - close(fd); - if (recvbuf) - free(recvbuf); + *ret = msg->type; - return result; + if (payload != NULL && payloadsz != NULL) { + if (resp->payload != NULL && resp->payloadsz > 0) { + *payload = malloc(resp->payloadsz); + if (*payload == NULL) { + const char *error_msg = "Memory allocation failed for payload"; + free(*out); + *out = strdup(error_msg); + *out_size = strlen(error_msg) + 1; + *ret = -1; + free_response_message(resp); + return -1; + } + memcpy(*payload, resp->payload, resp->payloadsz); + *payloadsz = resp->payloadsz; + } else { + *payload = NULL; + *payloadsz = 0; + } + } + + free_response_message(resp); + return 0; +} + +int SendCall(int id /*IN*/, const char *path /*IN*/, int *ret /*OUT*/, + char **out /*OUT*/, size_t *out_size, void **reply_data, + size_t *reply_size) { + int result; + int sockfd; + + sockfd = -1; + result = 0; + *out = NULL; + *out_size = 0; + *ret = 0; + + // side effects: modify out, out_size, ret + sockfd = socket_connect(POVERLAY_SOCK_PATH, out, out_size, ret); + if (sockfd >= 0) { + // side effects: modify out, out_size, ret + if ((result = write_request(sockfd, id, path, out, out_size, ret)) == 0) { + // side effects: modify out, out_size, ret, reply_data, reply_size + result = + read_response(sockfd, out, out_size, ret, reply_data, reply_size); + } + close(sockfd); + } else { + result = -1; + } + return *ret; // always 0 on success } diff --git a/pclsync/overlay_client.h b/pclsync/overlay_client.h index af1b2ee..0b86d53 100644 --- a/pclsync/overlay_client.h +++ b/pclsync/overlay_client.h @@ -49,7 +49,8 @@ typedef enum _pCloud_FileState { int QueryState(pCloud_FileState *state /*OUT*/, char *path /*IN*/); int SendCall(int id /*IN*/, const char *path /*IN*/, int *ret /*OUT*/, - char **out /*OUT*/, size_t *out_size); + char **out /*OUT*/, size_t *out_size, void **reply_data, + size_t *reply_size); #ifdef __cplusplus } #endif diff --git a/pclsync/pdiff.c b/pclsync/pdiff.c index 0b6b77d..228e970 100644 --- a/pclsync/pdiff.c +++ b/pclsync/pdiff.c @@ -2978,7 +2978,7 @@ void psync_diff_wake() { static void psync_diff_thread() { psync_socket *sock; - binresult *res; + binresult *res = NULL; const binresult *entries; uint64_t newdiffid, result; psync_socket_t exceptionsock, socks[2]; @@ -2986,8 +2986,11 @@ static void psync_diff_thread() { int sel, ret = 0; char ex; char *err = NULL; + int should_free_res = 0; + psync_set_status(PSTATUS_TYPE_ONLINE, PSTATUS_ONLINE_CONNECTING); psync_send_status_update(); + restart: psync_set_status(PSTATUS_TYPE_ONLINE, PSTATUS_ONLINE_CONNECTING); sock = get_connected_socket(); @@ -3005,8 +3008,9 @@ restart: P_NUM("limit", PSYNC_DIFF_LIMIT), P_NUM("diffid", ids.diffid)}; if (!psync_do_run) - break; + goto cleanup; res = send_command(sock, "diff", diffparams); + should_free_res = 1; if (!res) { psync_socket_close(sock); goto restart; @@ -3015,7 +3019,6 @@ restart: if (unlikely(result)) { debug(D_ERROR, "diff returned error %u: %s", (unsigned int)result, psync_find_result(res, "error", PARAM_STR)->str); - psync_free(res); psync_socket_close(sock); psync_milisleep(PSYNC_SLEEP_BEFORE_RECONNECT); goto restart; @@ -3026,13 +3029,17 @@ restart: debug(D_NOTICE, "processing diff with %u entries", (unsigned)entries->length); ids.diffid = process_entries(entries, newdiffid); - // psync_diff_refresh_fs(entries); -- don't do this for initial loading debug(D_NOTICE, "got diff with %u entries, new diffid %lu", (unsigned)entries->length, (unsigned long)ids.diffid); } result = entries->length; - psync_free(res); + if (should_free_res) { + psync_free(res); + should_free_res = 0; + res = NULL; + } } while (result); + psync_fs_refresh_folder(0); debug(D_NOTICE, "initial sync finished"); if (psync_diff_check_quota(sock)) { @@ -3049,7 +3056,7 @@ restart: if (unlikely(exceptionsock == INVALID_SOCKET)) { debug(D_ERROR, "could not create pipe"); psync_socket_close(sock); - return; + goto cleanup; } socks[0] = exceptionsock; socks[1] = sock->sock; @@ -3059,6 +3066,7 @@ restart: send_diff_command(sock, ids); psync_milisleep(50); last_event = 0; + while (psync_do_run) { if (unlinked) { unlinked = 0; @@ -3085,6 +3093,7 @@ restart: } else if (sel == 1) { sock->pending = 1; res = get_result(sock); + should_free_res = 1; if (unlikely_log(!res)) { psync_timer_notify_exception(); handle_exception(&sock, &ids, 'r'); @@ -3098,13 +3107,11 @@ restart: if (result == 6003 || result == 6002) { // timeout or cancel debug(D_NOTICE, "got \"%s\" from the socket", psync_find_result(res, "error", PARAM_STR)->str); - psync_free(res); send_diff_command(sock, ids); continue; } debug(D_ERROR, "diff returned error %u: %s", (unsigned int)result, psync_find_result(res, "error", PARAM_STR)->str); - psync_free(res); handle_exception(&sock, &ids, 'r'); socks[1] = sock->sock; continue; @@ -3123,18 +3130,17 @@ restart: initialdownload = 0; } else debug(D_NOTICE, "diff with 0 entries, did we send a nop recently?"); - psync_free(res); } else if (entries->length == 13 && !strcmp(entries->str, "notifications")) { ids.notificationid = psync_find_result(res, "notificationid", PARAM_NUM)->num; - // do not free res psync_notifications_notify(res); + should_free_res = 0; // Don't free res in this case } else if (entries->length == 8 && !strcmp(entries->str, "publinks")) { ids.publinkid = psync_find_result(res, "publinkid", PARAM_NUM)->num; ret = cache_links(err, 256); if (ret < 0) - debug(D_ERROR, "Cacheing links faild with err %s", err); + debug(D_ERROR, "Cacheing links failed with err %s", err); else psync_notify_cache_change(PACCOUNT_CHANGE_LINKS); } else if (entries->length == 11 && @@ -3146,7 +3152,6 @@ restart: debug(D_ERROR, "Cacheing upload links failed with err %s", err); else psync_notify_cache_change(PACCOUNT_CHANGE_LINKS); - } else if (entries->length == 5 && !strcmp(entries->str, "teams")) { cache_account_teams(); cache_ba_my_teams(); @@ -3159,14 +3164,22 @@ restart: psync_notify_cache_change(PACCOUNT_CHANGE_CONTACTS); } else { debug(D_NOTICE, "got no from, did we send a nop recently?"); - psync_free(res); } send_diff_command(sock, ids); } else { - psync_free(res); debug(D_NOTICE, "got no from, did we send a nop recently?"); } } + + if (should_free_res) { + psync_free(res); + should_free_res = 0; + res = NULL; + } + } + +cleanup: + if (should_free_res && res) { psync_free(res); } psync_socket_close(sock); diff --git a/pclsync/pfolder.c b/pclsync/pfolder.c index a1d45fd..95fa38b 100644 --- a/pclsync/pfolder.c +++ b/pclsync/pfolder.c @@ -103,6 +103,7 @@ psync_folderid_t psync_get_folderid_by_path(const char *path) { } } else psync_sql_reset(res); + psync_sql_bind_uint(res, 1, cfolderid); psync_sql_bind_lstring(res, 2, path, len); row = psync_sql_fetch_rowint(res); @@ -878,7 +879,10 @@ psync_folder_list_t *psync_list_get_list(char *syncTypes) { l = folders[i].locallen; memcpy(str, folders[i].localpath, l); psync_free(folders[i].localpath); - ret->folders[i].localpath = str; + strncpy(ret->folders[i].localpath, str, + sizeof(ret->folders[i].localpath) - 1); + ret->folders[i].localpath[sizeof(ret->folders[i].localpath) - 1] = '\0'; + l--; while (l && str[l] != PSYNC_DIRECTORY_SEPARATORC && str[l] != '/') @@ -887,13 +891,17 @@ psync_folder_list_t *psync_list_get_list(char *syncTypes) { if ((str[l] == PSYNC_DIRECTORY_SEPARATORC || str[l] == '/') && str[l + 1]) l++; - ret->folders[i].localname = str + l; + strncpy(ret->folders[i].localname, str, + sizeof(ret->folders[i].localname) - 1); + ret->folders[i].localname[sizeof(ret->folders[i].localname) - 1] = '\0'; str += folders[i].locallen; l = folders[i].remotelen; memcpy(str, folders[i].remotepath, l); psync_free(folders[i].remotepath); - ret->folders[i].remotepath = str; + strncpy(ret->folders[i].remotepath, str, + sizeof(ret->folders[i].remotepath) - 1); + ret->folders[i].remotepath[sizeof(ret->folders[i].remotepath) - 1] = '\0'; if (l) l--; @@ -904,7 +912,9 @@ psync_folder_list_t *psync_list_get_list(char *syncTypes) { if (str[l] == '/') l++; - ret->folders[i].remotename = str + l; + strncpy(ret->folders[i].remotename, str + l, + sizeof(ret->folders[i].remotename) - 1); + ret->folders[i].remotename[sizeof(ret->folders[i].remotename) - 1] = '\0'; str += folders[i].remotelen; ret->folders[i].folderid = folders[i].folderid; ret->folders[i].syncid = folders[i].syncid; diff --git a/pclsync/poverlay.c b/pclsync/poverlay.c index 89d4828..c396b85 100644 --- a/pclsync/poverlay.c +++ b/pclsync/poverlay.c @@ -26,41 +26,93 @@ DAMAGE. */ -#include "poverlay.h" -#include "pcache.h" -#include "pcompat.h" -#include "plibs.h" -#include "ppathstatus.h" +/* + poverlay is the "overlay server" (contrast with overlay_client). It + listens for request messages generated by overlay_client on a unix + socket, invokes the appropriate callback based on the message type, + and generates a response message. It then writes the response + message back to the socket where the overlay_client will read and + act upon it. +*/ +#include #include #include #include #include +#include + +#include "pcompat.h" +#include "poverlay_protocol.h" + +#include "poverlay.h" +#include "ppathstatus.h" + +#include "plibs.h" #define POVERLAY_BUFSIZE 512 int overlays_running = 1; int callbacks_running = 1; -char *mysoc = "/tmp/pcloud_unix_soc.sock"; +// Serialization function +size_t serialize_response_message(const response_message *resp, + char **out_buffer) { + // Calculate total size needed + size_t msg_size = sizeof(uint32_t) + sizeof(uint64_t) + resp->msg->length; + size_t total_size = + sizeof(size_t) + msg_size + sizeof(size_t) + resp->payloadsz; -void overlay_main_loop() { + // Allocate buffer + *out_buffer = (char *)malloc(total_size); + if (*out_buffer == NULL) + return 0; + + char *ptr = *out_buffer; + + // Serialize msg size + *(size_t *)ptr = htobe64(msg_size); + ptr += sizeof(size_t); + + // Serialize msg + *(uint32_t *)ptr = htonl(resp->msg->type); + ptr += sizeof(uint32_t); + + *(uint64_t *)ptr = htobe64(resp->msg->length); + ptr += sizeof(uint64_t); + + memcpy(ptr, resp->msg->value, resp->msg->length); + ptr += resp->msg->length; + + // Serialize payload size + *(size_t *)ptr = htobe64(resp->payloadsz); + ptr += sizeof(size_t); + + // Serialize payload + if (resp->payloadsz > 0 && resp->payload != NULL) { + memcpy(ptr, resp->payload, resp->payloadsz); + } + + return total_size; +} + +void psync_overlay_main_loop() { struct sockaddr_un addr; int fd, cl; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - // debug(D_NOTICE, "Unix socket error failed to open %s", mysoc); + debug(D_ERROR, "Unix socket error failed to open %s", POVERLAY_SOCK_PATH); return; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, mysoc, sizeof(addr.sun_path) - 1); + strncpy(addr.sun_path, POVERLAY_SOCK_PATH, sizeof(addr.sun_path) - 1); - unlink(mysoc); + unlink(POVERLAY_SOCK_PATH); if (bind(fd, (struct sockaddr *)&addr, - strlen(mysoc) + sizeof(addr.sun_family)) == -1) { + strlen(POVERLAY_SOCK_PATH) + sizeof(addr.sun_family)) == -1) { debug(D_ERROR, "Unix socket bind error"); return; } @@ -75,159 +127,289 @@ void overlay_main_loop() { debug(D_ERROR, "Unix socket accept error"); continue; } + + // handle the request in a new thread psync_run_thread1("Pipe request handle routine", - instance_thread, // thread proc - (LPVOID)&cl // thread parameter + psync_overlay_handle_request, // thread proc + (LPVOID)&cl // thread parameter ); } return; } -void instance_thread(void *lpvParam) { - int *cl, rc; - char chbuf[POVERLAY_BUFSIZE]; - message *request = NULL; - char *curbuf = &chbuf[0]; - int bytes_read = 0; - message *reply = (message *)psync_malloc(POVERLAY_BUFSIZE); +void psync_overlay_handle_request(void *lpvParam) { + int *sockfd; // pcloud socket file descriptor + int rc; // bytes read / written per iteration + int readbytes; // total bytes read from request + char rqbuf[POVERLAY_BUFSIZE]; // request buffer, contains the request message + char *rqbufp; // request buffer ptr, for convenient iteration + request_message *request; // request message + response_message *response; // response message and payload - memset(reply, 0, POVERLAY_BUFSIZE); - memset(chbuf, 0, POVERLAY_BUFSIZE); + request = NULL; + response = NULL; + readbytes = 0; + rqbufp = &rqbuf[0]; + sockfd = (int *)lpvParam; - cl = (int *)lpvParam; - - while ((rc = read(*cl, curbuf, (POVERLAY_BUFSIZE - bytes_read))) > 0) { - bytes_read += rc; - // debug(D_ERROR, "Read %u bytes: %u %s", bytes_read, rc, curbuf ); - curbuf = curbuf + rc; - if (bytes_read > 12) { - request = (message *)chbuf; - if (request->length == bytes_read) + // read the request from the socket into the request buffer + memset(rqbuf, 0, POVERLAY_BUFSIZE); + while ((rc = read(*sockfd, rqbufp, (POVERLAY_BUFSIZE - readbytes))) > 0) { + readbytes += rc; + rqbufp = rqbufp + rc; + if (readbytes > 12) { + request = (request_message *)rqbuf; + if (request->length == (uint64_t)readbytes) break; } } if (rc == -1) { - // debug(D_ERROR,"Unix socket read"); - close(*cl); - return; - } else if (rc == 0) { - // debug(D_NOTICE,"Message received"); - close(*cl); + debug(D_ERROR, "Unix socket read error"); + goto cleanup; + } else if (rc == 0 && readbytes == 0) { + debug(D_NOTICE, "Connection closed by client before sending data"); + goto cleanup; } - request = (message *)chbuf; - if (request) { - get_answer_to_request(request, reply); - if (reply) { - rc = write(*cl, reply, reply->length); - if (rc != reply->length) - debug(D_ERROR, "Unix socket reply not sent."); - } - } - if (cl) { - close(*cl); - } - // debug(D_NOTICE, "InstanceThread exitting.\n"); - psync_free(reply); - return; -}; + // allocate and initialize response message + response = (response_message *)psync_malloc(sizeof(response_message)); + memset(response, 0, sizeof(response_message)); + response->msg = NULL; + response->payload = NULL; + response->payloadsz = 0; + + // get the response for the request, and write the response to the + // sockfd. + request = (request_message *)rqbuf; + if (request) { + psync_overlay_get_response(request, response); + + char *rsbufp; + size_t bytes_written = 0; + size_t responsesz = serialize_response_message(response, &rsbufp); + if (responsesz <= 0) { + debug(D_ERROR, "failed to serialize response message"); + return; + } + + while (bytes_written < responsesz) { + ssize_t written = + write(*sockfd, rsbufp + bytes_written, responsesz - bytes_written); + if (written == -1) { + if (errno == EINTR) { + // Interrupted by signal, try again + continue; + } + debug(D_ERROR, "failed to write to socket: %s", strerror(errno)); + break; + } + bytes_written += written; + } + free(rsbufp); + if (bytes_written < responsesz) { + debug(D_ERROR, "failed to write entire message to socket"); + return; + } + + debug(D_NOTICE, + "Successfully sent full reply: %zu bytes structure, %zu bytes " + "additional data", + response->msg->length, response->payloadsz); + } else { + debug(D_ERROR, "No valid request received"); + } + +cleanup: + if (sockfd) { + close(*sockfd); + } + if (response) { + if (response->msg) { + psync_free(response->msg); + } + if (response->payload) { + psync_free(response->payload); + } + psync_free(response); + } + + debug(D_NOTICE, "InstanceThread exiting."); +} poverlay_callback *callbacks; static int callbacks_size = 15; static const int calbacks_lower_band = 20; -int psync_add_overlay_callback(int id, poverlay_callback callback) { +// registers an overlay callback for a given message type. +int psync_overlay_register_callback(int msgtype, poverlay_callback callback) { poverlay_callback *callbacks_old = callbacks; int callbacks_size_old = callbacks_size; - if (id < calbacks_lower_band) + if (msgtype < calbacks_lower_band) return -1; - if (id > (calbacks_lower_band + callbacks_size)) { - callbacks_size = id - calbacks_lower_band + 1; - init_overlay_callbacks(); + if (msgtype > (calbacks_lower_band + callbacks_size)) { + callbacks_size = msgtype - calbacks_lower_band + 1; + psync_overlay_init_callbacks(); memcpy(callbacks, callbacks_old, callbacks_size_old * sizeof(poverlay_callback)); psync_free(callbacks_old); } - callbacks[id - calbacks_lower_band] = callback; + callbacks[msgtype - calbacks_lower_band] = callback; return 0; } -void init_overlay_callbacks() { +void psync_overlay_init_callbacks() { callbacks = (poverlay_callback *)psync_malloc(sizeof(poverlay_callback) * callbacks_size); memset(callbacks, 0, sizeof(poverlay_callback) * callbacks_size); } -void psync_stop_overlays() { overlays_running = 0; } -void psync_start_overlays() { overlays_running = 1; } +static void psync_overlay_get_status_response(request_message *request, + response_message *response, + size_t available_space) { + psync_path_status_t stat; -void psync_stop_overlay_callbacks() { callbacks_running = 0; } -void psync_start_overlay_callbacks() { callbacks_running = 1; } + stat = PSYNC_PATH_STATUS_NOT_OURS; -void get_answer_to_request(message *request, message *reply) { - psync_path_status_t stat = PSYNC_PATH_STATUS_NOT_OURS; - memcpy(reply->value, "Ok.", 4); - reply->length = sizeof(message) + 4; - - if (request->type == 20 /* STARTCRYPTO, see control_tools.cpp */) { - // don't publish the crypto password to the logs in plain text... - debug(D_NOTICE, "Client Request type [%u] len [%lu] string: [%s]", - request->type, request->length, "REDACTED"); - } else { - debug(D_NOTICE, "Client Request type [%u] len [%lu] string: [%s]", - request->type, request->length, request->value); + if (overlays_running) { + stat = psync_path_status_get(request->value); } - - if (request->type < 20) { - if (overlays_running) - stat = psync_path_status_get(request->value); - switch (psync_path_status_get_status(stat)) { - case PSYNC_PATH_STATUS_IN_SYNC: - reply->type = 10; - break; - case PSYNC_PATH_STATUS_IN_PROG: - reply->type = 12; - break; - case PSYNC_PATH_STATUS_PAUSED: - case PSYNC_PATH_STATUS_REMOTE_FULL: - case PSYNC_PATH_STATUS_LOCAL_FULL: - reply->type = 11; - break; - default: - reply->type = 13; - memcpy(reply->value, "No.", 4); - } - } else if ((callbacks_running) && - (request->type < (calbacks_lower_band + callbacks_size))) { - int ind = request->type - 20; - int ret = 0; - message *rep = NULL; - - if (callbacks[ind]) { - ret = callbacks[ind](request->value, rep); - if (ret == 0) { - if (rep) { - psync_free(reply); - reply = rep; - } else { - reply->type = 0; - } - } else { - reply->type = ret; - memcpy(reply->value, "No.", 4); - } - } else { - reply->type = 13; - memcpy(reply->value, "No callback with this id registered.", 37); - reply->length = sizeof(message) + 37; - } - } else { - reply->type = 13; - memcpy(reply->value, "Invalid type.", 14); - reply->length = sizeof(message) + 14; + switch (psync_path_status_get_status(stat)) { + case PSYNC_PATH_STATUS_IN_SYNC: + response->msg->type = 10; + break; + case PSYNC_PATH_STATUS_IN_PROG: + response->msg->type = 12; + break; + case PSYNC_PATH_STATUS_PAUSED: + case PSYNC_PATH_STATUS_REMOTE_FULL: + case PSYNC_PATH_STATUS_LOCAL_FULL: + response->msg->type = 11; + break; + default: + response->msg->type = 13; + snprintf(response->msg->value, available_space, "No."); } } -int psync_overlays_running() { return overlays_running; } -int psync_ovr_callbacks_running() { return callbacks_running; } +static void +psync_overlay_get_overlay_response_payload(request_message *request, + response_message *response) { + if (!response->payload) { + response->payloadsz = 0; + debug(D_NOTICE, "Callback succeeded with no reply data"); + return; + } + + if (request->type == 23) { + // LISTSYNC + psync_folder_list_t *folders = (psync_folder_list_t *)response->payload; + response->payloadsz = sizeof(psync_folder_list_t) + + folders->foldercnt * sizeof(psync_folder_t); + } else if (request->type == 24) { + // ADDSYNC + response->payloadsz = sizeof(psync_syncid_t); + } else { + // default: response has no payload + response->payloadsz = 0; + } + + if (response->payload == NULL) { + response->payloadsz = 0; + debug(D_NOTICE, "No reply data received"); + return; + } + + debug(D_NOTICE, "Callback succeeded with reply data, length: %zu", + response->payloadsz); +} + +static void psync_overlay_get_overlay_response(request_message *request, + response_message *response, + size_t available_space) { + int cbidx; // callback index (based on message type) + int cbret; // callback return value + + cbidx = request->type - 20; + cbret = 0; + + if (!callbacks_running || (request->type >= ((uint32_t)calbacks_lower_band + + (uint32_t)callbacks_size))) { + response->msg->type = 13; + snprintf(response->msg->value, available_space, "Invalid type."); + debug(D_NOTICE, "Invalid request type: %u", request->type); + return; + } + + if (!callbacks[cbidx]) { + response->msg->type = 13; + snprintf(response->msg->value, available_space, + "No callback with this id registered."); + debug(D_NOTICE, "No callback registered for type: %u", request->type); + return; + } + + cbret = callbacks[cbidx](request->value, &response->payload); + if (cbret == 0) { + response->msg->type = 0; + psync_overlay_get_overlay_response_payload(request, response); + } else { + response->msg->type = cbret; + snprintf(response->msg->value, available_space, + "Callback returned error code."); + debug(D_NOTICE, "Callback failed with return code: %d", cbret); + } +} + +void psync_overlay_get_response(request_message *request, + response_message *response) { + + const char *dbgmsg; // debug messages + size_t value_avail; // space available to store value (flexible array) + + dbgmsg = NULL; + response->msg = (message *)psync_malloc(POVERLAY_BUFSIZE); + memset(response->msg, 0, POVERLAY_BUFSIZE); + response->msg->length = 0; + response->payload = NULL; + response->payloadsz = 0; + value_avail = POVERLAY_BUFSIZE - sizeof(message); + + // never print the crypto password to the logs in plain text + dbgmsg = (request->type == 20) ? "REDACTED" : request->value; + debug(D_NOTICE, "Client Request type [%u] len [%lu] string: [%s]", + request->type, request->length, dbgmsg); + + if (request->type < 20) { + psync_overlay_get_status_response(request, response, value_avail); + } else { + psync_overlay_get_overlay_response(request, response, value_avail); + } + + // a message with a type != 13 and a null string value after + // processing is considered successful. set value to "Ok." + if (response->msg->type != 13 && response->msg->value[0] == '\0') { + snprintf(response->msg->value, value_avail, "Ok."); + } else { + debug(D_WARNING, + "not updating value to Ok: response->msg->type=%d, " + "response->msg->value=%s", + response->msg->type, response->msg->value); + } + + // truncate messages that exceed the buffer boundaries + size_t value_length = strnlen(response->msg->value, value_avail); + response->msg->length = sizeof(message) + value_length + 1; + if (response->msg->length > POVERLAY_BUFSIZE) { + response->msg->length = POVERLAY_BUFSIZE; + response->msg->value[value_avail - 1] = '\0'; + debug(D_WARNING, "Response message truncated to fit buffer"); + } +} + +void psync_overlay_stop_overlays() { overlays_running = 0; } +void psync_overlay_start_overlays() { overlays_running = 1; } +void psync_overlay_stop_overlay_callbacks() { callbacks_running = 0; } +void psync_overlay_start_overlay_callbacks() { callbacks_running = 1; } +int psync_overlay_overlays_running() { return overlays_running; } +int psync_overlay_callbacks_running() { return callbacks_running; } diff --git a/pclsync/poverlay.h b/pclsync/poverlay.h index 8f37284..02ffedd 100644 --- a/pclsync/poverlay.h +++ b/pclsync/poverlay.h @@ -26,6 +26,10 @@ DAMAGE. */ +// dependencies: +// - poverlay_protocol.h +// - psynclib.h + #ifndef POVERLAY_H #define POVERLAY_H @@ -39,26 +43,21 @@ #include "psynclib.h" -typedef struct _message { - uint32_t type; - uint64_t length; - char value[]; -} message; - extern int overlays_running; extern int callbacks_running; -void overlay_main_loop(VOID); -void instance_thread(LPVOID); -void get_answer_to_request(message *requesr /*IN*/, message *replay /*OUT*/); -void psync_stop_overlays(); -void psync_start_overlays(); -void psync_stop_overlay_callbacks(); -void psync_start_overlay_callbacks(); -int psync_overlays_running(); -int psync_ovr_callbacks_running(); +void psync_overlay_main_loop(VOID); +void psync_overlay_handle_request(LPVOID); +void psync_overlay_get_response(request_message *rq /*IN*/, + response_message *rs /*OUT*/); +void psync_overlay_stop_overlays(); +void psync_overlay_start_overlays(); +void psync_overlay_stop_overlay_callbacks(); +void psync_overlay_start_overlay_callbacks(); +int psync_overlay_overlays_running(); +int psync_overlay_callbacks_running(); -void init_overlay_callbacks(); -int psync_add_overlay_callback(int id, poverlay_callback callback); +void psync_overlay_init_callbacks(); +int psync_overlay_register_callback(int id, poverlay_callback callback); #endif // POVERLAY_H diff --git a/pclsync/poverlay_protocol.h b/pclsync/poverlay_protocol.h new file mode 100644 index 0000000..6f2448f --- /dev/null +++ b/pclsync/poverlay_protocol.h @@ -0,0 +1,22 @@ +// defines the messaging protocol for pclsync + +#ifndef POVERLAY_SOCK_PATH +#define POVERLAY_SOCK_PATH "/tmp/pcloud_unix_soc.sock" +#endif + +typedef struct _message { + uint32_t type; + uint64_t length; + char value[]; +} message; + +// represents a request message +typedef message request_message; + +// represents a response message; this includes the API response +// message and any payload data returned by the callback function. +typedef struct { + void *payload; // data returned by the callback function + size_t payloadsz; // size of payload + message *msg; // API response message +} response_message; diff --git a/pclsync/psynclib.c b/pclsync/psynclib.c index 4cf5bf7..9ac7965 100644 --- a/pclsync/psynclib.c +++ b/pclsync/psynclib.c @@ -61,6 +61,9 @@ #include "pmemlock.h" #include "pnetlibs.h" #include "pnotifications.h" + +#include "poverlay_protocol.h" + #include "poverlay.h" #include "pp2p.h" #include "ppagecache.h" @@ -297,8 +300,8 @@ int psync_init() { pthread_mutex_unlock(&psync_libstate_mutex); } - psync_run_thread("Overlay main thread", overlay_main_loop); - init_overlay_callbacks(); + psync_run_thread("Overlay main thread", psync_overlay_main_loop); + psync_overlay_init_callbacks(); if (PSYNC_SSL_DEBUG_LEVEL) psync_set_ssl_debug_callback(ssl_debug_cb); @@ -3045,7 +3048,7 @@ char *get_backup_root_name() { char *get_pc_name() { return get_machine_name(); } void psync_async_delete_sync(void *ptr) { - psync_syncid_t syncId = *(psync_syncid_t *)ptr; + psync_syncid_t syncId = (psync_syncid_t)(uintptr_t)ptr; int res; res = psync_delete_sync(syncId); @@ -3074,32 +3077,23 @@ void psync_async_ui_callback(void *ptr) { int psync_delete_sync_by_folderid(psync_folderid_t fId) { psync_sql_res *sqlRes; psync_uint_row row; - - psync_syncid_t *syncId; - psync_syncid_t *syncIdT; + psync_syncid_t syncId; sqlRes = - psync_sql_query_nolock("SELECT id FROM syncfolder WHERE folderid = ?"); + psync_sql_query_rdlock("SELECT id FROM syncfolder WHERE folderid = ?"); psync_sql_bind_uint(sqlRes, 1, fId); row = psync_sql_fetch_rowint(sqlRes); - if (unlikely(!row)) { debug(D_ERROR, "Sync to delete not found!"); psync_sql_free_result(sqlRes); - return -1; } - // XXX: results in truncation (uint64 -> uint) - syncId = (psync_syncid_t *)row[0]; - + syncId = (psync_syncid_t)row[0]; psync_sql_free_result(sqlRes); - syncIdT = psync_new(psync_syncid_t); - syncIdT = syncId; - psync_run_thread1("psync_async_sync_delete", psync_async_delete_sync, - syncIdT); + (void *)(uintptr_t)syncId); return 0; } diff --git a/pclsync/psynclib.h b/pclsync/psynclib.h index 216ff63..37a3e23 100644 --- a/pclsync/psynclib.h +++ b/pclsync/psynclib.h @@ -381,6 +381,9 @@ typedef struct pstatus_struct_ { #define PSYNC_CRYPTO_STATUS_ACTIVE 4 #define PSYNC_CRYPTO_STATUS_SETUP 5 +// limitation: path length of 255, plus null terminator. +#define PSYNC_MAX_PATH_LENGTH 256 + #define PSYNC_CRYPTO_INVALID_FOLDERID ((psync_folderid_t) - 1) #define PSYNC_CRYPTO_FLAG_TEMP_PASS 1 @@ -402,10 +405,10 @@ typedef struct pstatus_struct_ { // Lib error codes end typedef struct { - const char *localname; - const char *localpath; - const char *remotename; - const char *remotepath; + char localname[PSYNC_MAX_PATH_LENGTH]; + char localpath[PSYNC_MAX_PATH_LENGTH]; + char remotename[PSYNC_MAX_PATH_LENGTH]; + char remotepath[PSYNC_MAX_PATH_LENGTH]; psync_folderid_t folderid; psync_syncid_t syncid; psync_synctype_t synctype; @@ -1791,10 +1794,21 @@ void psync_get_current_userid(psync_userid_t * /*OUT*/ ret); void psync_get_folder_ownerid(psync_folderid_t folderid, psync_userid_t * /*OUT*/ ret); -/* Callback to be registered to be called from file manager extension. - */ - -typedef int (*poverlay_callback)(const char *path, void *rep); +// Defines the function signature of an overlay server-side +// callback. poverlay_callback implementations must satisfy the +// following: +// +// - Accepts request data as a string. +// +// - Returns 0 on success, and non-zero on failure +// +// - If the function invoked by the callback function returns data +// that can be used by the client (e.g., list_sync_folders), then +// allocate the void** pointer and write the data there. If the +// void** pointer is null, then do not write any data back out for +// the client. +// +typedef int (*poverlay_callback)(const char *, void **); /* Registers file manager extension callback that will be called when packet * with id equals to the give one had arrived from extension. The id must be @@ -1806,11 +1820,11 @@ typedef int (*poverlay_callback)(const char *path, void *rep); * synchronize. */ -int psync_add_overlay_callback(int id, poverlay_callback callback); -void psync_stop_overlays(); -void psync_start_overlays(); -void psync_stop_overlay_callbacks(); -void psync_start_overlay_callbacks(); +int psync_overlay_register_callback(int id, poverlay_callback callback); +void psync_overlay_stop_overlays(); +void psync_overlay_start_overlays(); +void psync_overlay_stop_overlay_callbacks(); +void psync_overlay_start_overlay_callbacks(); int psync_setlanguage(const char *language, char **err); diff --git a/pclsync/ptools.c b/pclsync/ptools.c index 5190ca5..711e43d 100644 --- a/pclsync/ptools.c +++ b/pclsync/ptools.c @@ -286,7 +286,7 @@ int backend_call(const char *binapi, const char *wsPath, j++; } - for (i = 0; i <= totalParCnt; i++) { + for (i = 0; i < totalParCnt; i++) { if (localParams[i].paramtype == 0) { continue; } diff --git a/pclsync_lib.cpp b/pclsync_lib.cpp index 9610d6e..a96b133 100644 --- a/pclsync_lib.cpp +++ b/pclsync_lib.cpp @@ -30,11 +30,16 @@ #include #include +#include #include +#include #include +#include #include -#include "pclsync_lib_c.h" +#include "poverlay_protocol.h" + +#include "poverlay.h" #include "psynclib.h" #include "pclsync_lib.h" @@ -111,36 +116,61 @@ void clib::pclsync_lib::do_get_pass_from_console(std::string &password) { void event_handler(psync_eventtype_t event, psync_eventdata_t eventdata) { if (event < PEVENT_FIRST_USER_EVENT) { if (event & PEVENT_TYPE_FOLDER) { - std::cout << "folder event=" << event - << ", syncid=" << eventdata.folder->syncid - << ", folderid=" << eventdata.folder->folderid - << ", name=" << eventdata.folder->name - << ", local=" << eventdata.folder->localpath - << ", remote=" << eventdata.folder->remotepath << std::endl; + if (eventdata.folder) { + std::cout << "folder event=" << event + << ", syncid=" << eventdata.folder->syncid + << ", folderid=" << eventdata.folder->folderid << ", name=" + << (eventdata.folder->name ? eventdata.folder->name : "") + << ", local=" + << (eventdata.folder->localpath ? eventdata.folder->localpath + : "") + << ", remote=" + << (eventdata.folder->remotepath + ? eventdata.folder->remotepath + : "") + << std::endl; + } else { + std::cout << "folder event=" << event << " (no folder data)" + << std::endl; + } } else { - std::cout << "file event=" << event - << ", syncid=" << eventdata.folder->syncid - << ", file=" << eventdata.file->fileid - << ", name=" << eventdata.file->name - << ", local=" << eventdata.file->localpath - << ", remote=" << eventdata.file->remotepath << std::endl; + if (eventdata.file) { + std::cout + << "file event=" << event << ", syncid=" << eventdata.file->syncid + << ", file=" << eventdata.file->fileid + << ", name=" << (eventdata.file->name ? eventdata.file->name : "") + << ", local=" + << (eventdata.file->localpath ? eventdata.file->localpath : "") + << ", remote=" + << (eventdata.file->remotepath ? eventdata.file->remotepath : "") + << std::endl; + } else { + std::cout << "file event=" << event << " (no file data)" << std::endl; + } } } else if (event >= PEVENT_FIRST_SHARE_EVENT) { - std::cout << "share event=" << event - << ", folderid=" << eventdata.share->folderid - << ", sharename=" << eventdata.share->sharename - << ", email=" << eventdata.share->toemail - << ", message=" << eventdata.share->message - << ", userid=" << eventdata.share->userid - << ", shareid=" << eventdata.share->shareid - << ", sharerequestid=" << eventdata.share->sharerequestid - << ", created=" << eventdata.share->created - << ", canread=" << eventdata.share->canread - << ", cancreate=" << eventdata.share->cancreate - << ", canmodify=" << eventdata.share->canmodify - << ", candelete=" << eventdata.share->candelete << std::endl; + if (eventdata.share) { + std::cout << "share event=" << event + << ", folderid=" << eventdata.share->folderid << ", sharename=" + << (eventdata.share->sharename ? eventdata.share->sharename + : "") + << ", email=" + << (eventdata.share->toemail ? eventdata.share->toemail : "") + << ", message=" + << (eventdata.share->message ? eventdata.share->message : "") + << ", userid=" << eventdata.share->userid + << ", shareid=" << eventdata.share->shareid + << ", sharerequestid=" << eventdata.share->sharerequestid + << ", created=" << eventdata.share->created + << ", canread=" << eventdata.share->canread + << ", cancreate=" << eventdata.share->cancreate + << ", canmodify=" << eventdata.share->canmodify + << ", candelete=" << eventdata.share->candelete << std::endl; + } else { + std::cout << "share event=" << event << " (no share data)" << std::endl; + } } else { - std::cout << "event" << event << std::endl; + std::cout << "event " << event << std::endl; } } @@ -222,9 +252,6 @@ static void status_change(pstatus_t *status) { clib::pclsync_lib::get_lib().get_pass_from_console(); } - // std::cout << "Username: " << - // clib::pclsync_lib::get_lib().get_username().c_str() << "| Password: " << - // clib::pclsync_lib::get_lib().get_password().c_str() << std::endl; psync_set_user_pass(clib::pclsync_lib::get_lib().get_username().c_str(), clib::pclsync_lib::get_lib().get_password().c_str(), (int)clib::pclsync_lib::get_lib().save_pass_); @@ -270,28 +297,99 @@ static void status_change(pstatus_t *status) { psync_free(err); } -int clib::pclsync_lib::start_crypto(const char *pass, void *rep) { +int clib::pclsync_lib::start_crypto(const char *pass, void **payload) { + (void)payload; + std::cout << "calling startcrypto pass: " << pass << std::endl; get_lib().crypto_pass_ = pass; return lib_setup_cripto(); } -int clib::pclsync_lib::stop_crypto(const char *path, void *rep) { - int res = -1; +int clib::pclsync_lib::stop_crypto(const char *path, void **payload) { + (void)payload; + (void)path; + psync_crypto_stop(); get_lib().crypto_on_ = false; - return res; + return 0; } -int clib::pclsync_lib::finalize(const char *path, void *rep) { +int clib::pclsync_lib::finalize(const char *path, void **payload) { + (void)payload; + (void)path; + psync_destroy(); exit(0); } -int clib::pclsync_lib::list_sync_folders(const char *path, void *rep) { +int clib::pclsync_lib::add_sync_folder(const char *path, void **payload) { + if (payload == nullptr) { + std::cerr << "Error: payload pointer is null" << std::endl; + return -255; + } + + if (path == nullptr) { + std::cerr << "Error: path is nullptr" << std::endl; + return -255; + } + const char delimiter = '|'; + std::string combined(path); + size_t delimiter_pos = combined.find(delimiter); + if (delimiter_pos == std::string::npos) { + std::cerr << "Error: Invalid path format. Expected 'localpath|remotepath'" + << std::endl; + return -255; + } + + std::string localpath = combined.substr(0, delimiter_pos); + std::string remotepath = combined.substr(delimiter_pos + 1); + + psync_syncid_t syncid = + psync_add_sync_by_path(localpath.c_str(), remotepath.c_str(), PSYNC_FULL); + + uint64_t *payload_ptr = + static_cast(psync_malloc(sizeof(uint64_t))); + if (payload_ptr == nullptr) { + std::cerr << "Error: Failed to allocate memory for payload" << std::endl; + return -255; + } + + if (syncid == PSYNC_INVALID_SYNCID) { + std::cerr << "psync_add_sync_by_path returned PSYNC_INVALID_SYNCID" + << std::endl; + *payload_ptr = (static_cast(1) << 32) | + static_cast(PSYNC_INVALID_SYNCID); + return -1; + } else { + *payload_ptr = static_cast(syncid); + } + *payload = payload_ptr; + return 0; +} + +int clib::pclsync_lib::remove_sync_folder(const char *path, void **payload) { + (void)payload; + psync_folderid_t folderid; + folderid = static_cast(std::stoull(path, nullptr, 10)); + return psync_delete_sync_by_folderid(folderid); +} + +int clib::pclsync_lib::list_sync_folders(const char *path, void **payload) { + (void)path; + psync_folder_list_t *folders = psync_get_sync_list(); - rep = psync_malloc(sizeof(*folders)); - memcpy(rep, folders, sizeof(*folders)); + if (!folders) { + return -1; + } + size_t alloc_size = + sizeof(*folders) + folders->foldercnt * sizeof(psync_folder_t); + *payload = psync_malloc(alloc_size); + if (*payload == NULL) { + psync_free(folders); + return -1; + } + memcpy(*payload, folders, alloc_size); + psync_free(folders); return 0; } @@ -330,10 +428,12 @@ int clib::pclsync_lib::init() { psync_free(username_old); } - psync_add_overlay_callback(20, &clib::pclsync_lib::start_crypto); - psync_add_overlay_callback(21, &clib::pclsync_lib::stop_crypto); - psync_add_overlay_callback(22, &clib::pclsync_lib::finalize); - psync_add_overlay_callback(23, &clib::pclsync_lib::list_sync_folders); + psync_overlay_register_callback(20, &clib::pclsync_lib::start_crypto); + psync_overlay_register_callback(21, &clib::pclsync_lib::stop_crypto); + psync_overlay_register_callback(22, &clib::pclsync_lib::finalize); + psync_overlay_register_callback(23, &clib::pclsync_lib::list_sync_folders); + psync_overlay_register_callback(24, &clib::pclsync_lib::add_sync_folder); + psync_overlay_register_callback(25, &clib::pclsync_lib::remove_sync_folder); return 0; } diff --git a/pclsync_lib.h b/pclsync_lib.h index 4319477..a60bb9b 100644 --- a/pclsync_lib.h +++ b/pclsync_lib.h @@ -36,6 +36,7 @@ #define PCLSYNC_LIB_H struct pstatus_struct_; +typedef void (*status_callback_t)(int status, const char *stat_string); namespace console_client { namespace clibrary { @@ -81,10 +82,12 @@ public: int init(); // std::string& username, std::string& password, std::string* // crypto_pass, int setup_crypto = 1, int usesrypto_userpass = 0); - static int start_crypto(const char *pass, void *rep); - static int stop_crypto(const char *path, void *rep); - static int finalize(const char *path, void *rep); - static int list_sync_folders(const char *path, void *rep); + static int start_crypto(const char *pass, void **rep); + static int stop_crypto(const char *path, void **rep); + static int finalize(const char *path, void **rep); + static int list_sync_folders(const char *path, void **rep); + static int add_sync_folder(const char *path, void **rep); + static int remove_sync_folder(const char *path, void **rep); char *get_token(); int logout(); diff --git a/pclsync_lib_c.cpp b/pclsync_lib_c.cpp deleted file mode 100644 index ccb9e65..0000000 --- a/pclsync_lib_c.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - Copyright (c) 2013-2015 pCloud Ltd. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided with - the distribution. Neither the name of pCloud Ltd nor the names of - its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud - Ltd BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. -*/ - -#include - -#include "pclsync_lib_c.h" - -#include "pclsync_lib.h" - -#ifdef __cplusplus -extern "C" { -#endif - -namespace cc = console_client::clibrary; - -int init() { - if (!cc::pclsync_lib::get_lib().was_init_) - return cc::pclsync_lib::get_lib().init(); - else - return 0; -} - -int start_crypto(const char *pass) { - return cc::pclsync_lib::start_crypto(pass, NULL); -} -int stop_crypto() { return cc::pclsync_lib::stop_crypto(NULL, NULL); } -int finalize() { return cc::pclsync_lib::finalize(NULL, NULL); } -void set_status_callback(status_callback_t c) { - cc::pclsync_lib::get_lib().set_status_callback(c); -} - -char *get_token() { return cc::pclsync_lib::get_lib().get_token(); } - -int login(const char *user, const char *pass, int save) { - return cc::pclsync_lib::get_lib().login(user, pass, save); -} - -int logout() { return cc::pclsync_lib::get_lib().logout(); } - -int unlinklib() { return cc::pclsync_lib::get_lib().unlink(); } - -#ifdef __cplusplus -} -#endif diff --git a/pclsync_lib_c.h b/pclsync_lib_c.h deleted file mode 100644 index f7603c3..0000000 --- a/pclsync_lib_c.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - Copyright (c) 2013-2015 pCloud Ltd. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided with - the distribution. Neither the name of pCloud Ltd nor the names of - its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud - Ltd BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -void psync_fs_pause_until_login(); - -typedef void (*status_callback_t)(int status, const char *stat_string); - -int init(); -int start_crypto(const char *pass); -int stop_crypto(); -int finalize(); -char *get_token(); -void set_status_callback(status_callback_t); -int logout(); -int unlinklib(); -int login(const char *user, const char *pass, int save); - -#ifdef __cplusplus -}; -#endif