382 lines
13 KiB
Text
382 lines
13 KiB
Text
//
|
|
// FinderSyncClient.m
|
|
// seafile-client-fsplugin
|
|
//
|
|
// Created by Chilledheart on 1/10/15.
|
|
// Copyright (c) 2015 Haiwen. All rights reserved.
|
|
//
|
|
|
|
#import "FinderSyncClient.h"
|
|
#include <cstdint>
|
|
#include <sstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <servers/bootstrap.h>
|
|
#include "../src/utils/stl.h"
|
|
|
|
#if !__has_feature(objc_arc)
|
|
#error this file must be built with ARC support
|
|
#endif
|
|
|
|
static NSString *const kFinderSyncMachPort =
|
|
@"com.seafile.seafile-client.findersync.machport";
|
|
|
|
static constexpr int kPathMaxSize = 1024;
|
|
static constexpr uint32_t kFinderSyncProtocolVersion = 0x00000004;
|
|
static volatile int32_t message_id_ =
|
|
100; // we start from 100, the number below than 100 is reserved
|
|
|
|
typedef std::vector<std::string> stringlist;
|
|
|
|
stringlist split_string(const std::string& v, char delim) {
|
|
using namespace std;
|
|
stringlist strings;
|
|
istringstream ss(v);
|
|
string s;
|
|
while (getline(ss, s, delim)) {
|
|
strings.push_back(s);
|
|
}
|
|
return strings;
|
|
}
|
|
|
|
//
|
|
// MachPort Message
|
|
// - mach_msg_header_t command
|
|
// - uint32_t version
|
|
// - uint32_t command
|
|
// - body
|
|
// - mach_msg_trailer_t trailer (for rcv only)
|
|
//
|
|
//
|
|
|
|
// buffer
|
|
// <-char[36]-><----- char? ------------>1 1 1
|
|
// <-repo_id--><---- [worktree name] --->0<[status]>0
|
|
static std::vector<LocalRepo> *deserializeWatchSet(const char *buffer,
|
|
uint32_t size) {
|
|
std::vector<LocalRepo> *repos = new std::vector<LocalRepo>();
|
|
const char *const end = buffer + size - 1;
|
|
const char *pos;
|
|
while (buffer != end) {
|
|
unsigned worktree_size;
|
|
uint8_t status;
|
|
const char *repo_id = buffer;
|
|
buffer += 36;
|
|
pos = buffer;
|
|
|
|
while (*pos != '\0' && pos != end)
|
|
++pos;
|
|
worktree_size = pos - buffer;
|
|
pos += 2;
|
|
if (pos > end || *pos != '\0')
|
|
break;
|
|
|
|
status = *(pos - 1);
|
|
if (status >= LocalRepo::MAX_SYNC_STATE) {
|
|
status = LocalRepo::SYNC_STATE_UNSET;
|
|
}
|
|
bool internal_link_supported = false;
|
|
std::string worktree = std::string(buffer, worktree_size);
|
|
// NSLog(@"full line = %s", worktree.c_str());
|
|
stringlist parts = split_string(worktree, '\t');
|
|
if (parts.size() > 1) {
|
|
worktree = parts[0];
|
|
// NSLog(@"worktree = %s", worktree.c_str());
|
|
internal_link_supported = parts[1] == "internal-link-supported";
|
|
}
|
|
|
|
repos->emplace_back(std::string(repo_id, 36),
|
|
worktree,
|
|
static_cast<LocalRepo::SyncState>(status),
|
|
internal_link_supported);
|
|
buffer = ++pos;
|
|
}
|
|
return repos;
|
|
}
|
|
|
|
struct mach_msg_command_send_t {
|
|
mach_msg_header_t header;
|
|
uint32_t version;
|
|
uint32_t command;
|
|
// used only in DoShareLink
|
|
char repo[36];
|
|
char body[kPathMaxSize];
|
|
};
|
|
|
|
struct mach_msg_file_status_rcv_t {
|
|
mach_msg_header_t header;
|
|
uint32_t version;
|
|
uint32_t command;
|
|
uint32_t status;
|
|
mach_msg_trailer_t trailer;
|
|
};
|
|
|
|
FinderSyncClient::FinderSyncClient(FinderSync *parent)
|
|
: parent_(parent), local_port_(MACH_PORT_NULL),
|
|
remote_port_(MACH_PORT_NULL) {}
|
|
|
|
FinderSyncClient::~FinderSyncClient() {
|
|
if (local_port_) {
|
|
mach_port_mod_refs(mach_task_self(), local_port_,
|
|
MACH_PORT_RIGHT_RECEIVE, -1);
|
|
}
|
|
if (remote_port_) {
|
|
NSLog(@"disconnecting from Seafile Client");
|
|
mach_port_deallocate(mach_task_self(), remote_port_);
|
|
}
|
|
}
|
|
|
|
void FinderSyncClient::connectionBecomeInvalid() {
|
|
/* clean up old connection stage! */
|
|
if (local_port_) {
|
|
mach_port_mod_refs(mach_task_self(), local_port_,
|
|
MACH_PORT_RIGHT_RECEIVE, -1);
|
|
local_port_ = MACH_PORT_NULL;
|
|
}
|
|
if (remote_port_) {
|
|
NSLog(@"lost connection with Seafile Client");
|
|
mach_port_deallocate(mach_task_self(), remote_port_);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[parent_ updateWatchSet:nil];
|
|
});
|
|
remote_port_ = MACH_PORT_NULL;
|
|
}
|
|
}
|
|
|
|
bool FinderSyncClient::connect() {
|
|
if (!local_port_) {
|
|
// Create a local port.
|
|
kern_return_t kr = mach_port_allocate(
|
|
mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &local_port_);
|
|
if (kr != KERN_SUCCESS) {
|
|
NSLog(@"unable to create connection");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!remote_port_) {
|
|
// connect to the mach_port
|
|
kern_return_t kr = bootstrap_look_up(
|
|
bootstrap_port,
|
|
[kFinderSyncMachPort cStringUsingEncoding:NSASCIIStringEncoding],
|
|
&remote_port_);
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
return false;
|
|
}
|
|
NSLog(@"connected to Seafile Client");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FinderSyncClient::getWatchSet() {
|
|
if ([NSThread isMainThread]) {
|
|
NSLog(@"%s isn't supported to be called from main thread",
|
|
__PRETTY_FUNCTION__);
|
|
return;
|
|
}
|
|
if (!connect())
|
|
return;
|
|
mach_msg_command_send_t msg;
|
|
const int32_t recv_msgh_id = OSAtomicAdd32(2, &message_id_) - 1;
|
|
bzero(&msg, sizeof(mach_msg_header_t));
|
|
msg.header.msgh_id = recv_msgh_id - 1;
|
|
msg.header.msgh_local_port = local_port_;
|
|
msg.header.msgh_remote_port = remote_port_;
|
|
msg.header.msgh_size = sizeof(msg);
|
|
msg.header.msgh_bits =
|
|
MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
|
msg.version = kFinderSyncProtocolVersion;
|
|
msg.command = GetWatchSet;
|
|
// send a message and wait for the reply
|
|
kern_return_t kr = mach_msg(&msg.header, /* header*/
|
|
MACH_SEND_MSG | MACH_SEND_TIMEOUT, /*option*/
|
|
sizeof(msg), /*send size*/
|
|
0, /*receive size*/
|
|
local_port_, /*receive port*/
|
|
100, /*timeout, in milliseconds*/
|
|
MACH_PORT_NULL); /*no notification*/
|
|
if (kr != MACH_MSG_SUCCESS) {
|
|
if (kr == MACH_SEND_INVALID_DEST) {
|
|
connectionBecomeInvalid();
|
|
return;
|
|
}
|
|
NSLog(@"failed to send request to Seafile Client");
|
|
NSLog(@"mach error %s", mach_error_string(kr));
|
|
connectionBecomeInvalid();
|
|
return;
|
|
}
|
|
mach_msg_destroy(&msg.header);
|
|
|
|
utils::BufferArray recv_msg;
|
|
recv_msg.resize(4096);
|
|
mach_msg_header_t *recv_msg_header =
|
|
reinterpret_cast<mach_msg_header_t *>(recv_msg.data());
|
|
bzero(recv_msg.data(), sizeof(mach_msg_header_t));
|
|
recv_msg_header->msgh_local_port = local_port_;
|
|
recv_msg_header->msgh_remote_port = remote_port_;
|
|
// recv_msg.header.msgh_size = sizeof(recv_msg);
|
|
// receive the reply
|
|
kr = mach_msg(recv_msg_header, /* header*/
|
|
MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_LARGE, /*option*/
|
|
0, /*send size*/
|
|
recv_msg.size(), /*receive size*/
|
|
local_port_, /*receive port*/
|
|
100, /*timeout, in milliseconds*/
|
|
MACH_PORT_NULL); /*no notification*/
|
|
// retry
|
|
if (kr == MACH_RCV_TOO_LARGE) {
|
|
recv_msg.resize(recv_msg_header->msgh_size +
|
|
sizeof(mach_msg_trailer_t));
|
|
recv_msg_header =
|
|
reinterpret_cast<mach_msg_header_t *>(recv_msg.data());
|
|
|
|
kr = mach_msg(recv_msg_header, /* header*/
|
|
MACH_RCV_MSG | MACH_RCV_TIMEOUT, /*option*/
|
|
0, /*send size*/
|
|
recv_msg.size(), /*receive size*/
|
|
local_port_, /*receive port*/
|
|
100, /*timeout, in milliseconds*/
|
|
MACH_PORT_NULL); /*no notification*/
|
|
}
|
|
if (kr != MACH_MSG_SUCCESS) {
|
|
NSLog(@"failed to receive Seafile Client's reply");
|
|
NSLog(@"mach error %s", mach_error_string(kr));
|
|
connectionBecomeInvalid();
|
|
return;
|
|
}
|
|
if (recv_msg_header->msgh_id != recv_msgh_id) {
|
|
NSLog(@"mach error unmatched message id %d, expected %d",
|
|
recv_msg_header->msgh_id, recv_msgh_id);
|
|
connectionBecomeInvalid();
|
|
return;
|
|
}
|
|
const char *body = recv_msg.data() + sizeof(mach_msg_header_t);
|
|
uint32_t body_size =
|
|
(recv_msg_header->msgh_size - sizeof(mach_msg_header_t));
|
|
std::vector<LocalRepo> *repos = deserializeWatchSet(body, body_size);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[parent_ updateWatchSet:repos];
|
|
delete repos;
|
|
});
|
|
mach_msg_destroy(recv_msg_header);
|
|
}
|
|
|
|
void FinderSyncClient::doSendCommandWithPath(CommandType command,
|
|
const char *fileName) {
|
|
if ([NSThread isMainThread]) {
|
|
NSLog(@"%s isn't supported to be called from main thread",
|
|
__PRETTY_FUNCTION__);
|
|
return;
|
|
}
|
|
if (!connect())
|
|
return;
|
|
mach_msg_command_send_t msg;
|
|
bzero(&msg, sizeof(msg));
|
|
msg.header.msgh_id = OSAtomicIncrement32(&message_id_) - 1;
|
|
msg.header.msgh_local_port = MACH_PORT_NULL;
|
|
msg.header.msgh_remote_port = remote_port_;
|
|
msg.header.msgh_size = sizeof(msg);
|
|
msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND);
|
|
strncpy(msg.body, fileName, kPathMaxSize);
|
|
msg.version = kFinderSyncProtocolVersion;
|
|
msg.command = command;
|
|
// send a message only
|
|
kern_return_t kr = mach_msg_send(&msg.header);
|
|
if (kr != MACH_MSG_SUCCESS) {
|
|
if (kr == MACH_SEND_INVALID_DEST) {
|
|
connectionBecomeInvalid();
|
|
return;
|
|
}
|
|
NSLog(@"failed to send sharing link request for %s", fileName);
|
|
NSLog(@"mach error %s from msg id %d", mach_error_string(kr),
|
|
msg.header.msgh_id);
|
|
connectionBecomeInvalid();
|
|
return;
|
|
}
|
|
mach_msg_destroy(&msg.header);
|
|
}
|
|
|
|
void FinderSyncClient::doGetFileStatus(const char *repo, const char *fileName) {
|
|
if ([NSThread isMainThread]) {
|
|
NSLog(@"%s isn't supported to be called from main thread",
|
|
__PRETTY_FUNCTION__);
|
|
return;
|
|
}
|
|
if (!connect())
|
|
return;
|
|
mach_msg_command_send_t msg;
|
|
const int32_t recv_msgh_id = OSAtomicAdd32(2, &message_id_) - 1;
|
|
bzero(&msg, sizeof(mach_msg_header_t));
|
|
msg.header.msgh_id = recv_msgh_id - 1;
|
|
msg.header.msgh_local_port = local_port_;
|
|
msg.header.msgh_remote_port = remote_port_;
|
|
msg.header.msgh_size = sizeof(msg);
|
|
msg.header.msgh_bits =
|
|
MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
|
strncpy(msg.repo, repo, 36);
|
|
strncpy(msg.body, fileName, kPathMaxSize);
|
|
msg.version = kFinderSyncProtocolVersion;
|
|
msg.command = DoGetFileStatus;
|
|
// send a message and wait for the reply
|
|
kern_return_t kr = mach_msg(&msg.header, /* header*/
|
|
MACH_SEND_MSG | MACH_SEND_TIMEOUT, /*option*/
|
|
sizeof(msg), /*send size*/
|
|
0, /*receive size*/
|
|
local_port_, /*receive port*/
|
|
100, /*timeout, in milliseconds*/
|
|
MACH_PORT_NULL); /*no notification*/
|
|
if (kr != MACH_MSG_SUCCESS) {
|
|
if (kr == MACH_SEND_INVALID_DEST) {
|
|
connectionBecomeInvalid();
|
|
return;
|
|
}
|
|
NSLog(@"failed to send request to Seafile Client");
|
|
NSLog(@"mach error %s", mach_error_string(kr));
|
|
connectionBecomeInvalid();
|
|
return;
|
|
}
|
|
mach_msg_destroy(&msg.header);
|
|
|
|
mach_msg_file_status_rcv_t recv_msg;
|
|
mach_msg_header_t *recv_msg_header =
|
|
reinterpret_cast<mach_msg_header_t *>(&recv_msg);
|
|
bzero(&recv_msg, sizeof(mach_msg_header_t));
|
|
recv_msg_header->msgh_local_port = local_port_;
|
|
recv_msg_header->msgh_remote_port = remote_port_;
|
|
// recv_msg.header.msgh_size = sizeof(recv_msg);
|
|
// receive the reply
|
|
kr = mach_msg(recv_msg_header, /* header*/
|
|
MACH_RCV_MSG | MACH_RCV_TIMEOUT, /*option*/
|
|
0, /*send size*/
|
|
sizeof(mach_msg_file_status_rcv_t), /*receive size*/
|
|
local_port_, /*receive port*/
|
|
100, /*timeout, in milliseconds*/
|
|
MACH_PORT_NULL); /*no notification*/
|
|
if (kr != MACH_MSG_SUCCESS) {
|
|
NSLog(@"failed to receive Seafile Client's reply");
|
|
NSLog(@"mach error %s", mach_error_string(kr));
|
|
connectionBecomeInvalid();
|
|
return;
|
|
}
|
|
if (recv_msg_header->msgh_id != recv_msgh_id) {
|
|
NSLog(@"mach error unmatched message id %d, expected %d",
|
|
recv_msg_header->msgh_id, recv_msgh_id);
|
|
connectionBecomeInvalid();
|
|
return;
|
|
}
|
|
uint32_t status = recv_msg.status;
|
|
if (status >= PathStatus::MAX_SYNC_STATUS)
|
|
status = PathStatus::SYNC_STATUS_NONE;
|
|
|
|
// copy to heap before starting block
|
|
std::string repo_id = repo;
|
|
std::string path = fileName;
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[parent_ updateFileStatus:repo_id.c_str()
|
|
path:path.c_str()
|
|
status:status];
|
|
});
|
|
mach_msg_destroy(recv_msg_header);
|
|
}
|