Source-Tag: v1.0.3 Manifest-SHA256: b7b09bf48f9e092ed8ad82f99c0319d6c3837d0a1549bd0cc4a374276c0f3896
328 lines
9.5 KiB
C
328 lines
9.5 KiB
C
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h>
|
|
#include <weechat/weechat-plugin.h>
|
|
|
|
WEECHAT_PLUGIN_NAME("neodrop_manager")
|
|
WEECHAT_PLUGIN_DESCRIPTION("NeoDrop manager control buffer")
|
|
WEECHAT_PLUGIN_AUTHOR("{{PRODUCT_NAME}}")
|
|
WEECHAT_PLUGIN_VERSION("1.1")
|
|
WEECHAT_PLUGIN_LICENSE("GPL3")
|
|
|
|
#define CONTROL_SOCKET "{{MANAGER_CONTROL_SOCKET_PATH}}"
|
|
#define SEND_LIMIT 8192U
|
|
#define RECEIVE_LIMIT 16384U
|
|
|
|
#ifndef MSG_NOSIGNAL
|
|
#define MSG_NOSIGNAL 0
|
|
#endif
|
|
|
|
struct t_weechat_plugin *weechat_plugin;
|
|
static struct t_gui_buffer *manager_buffer;
|
|
static struct t_hook *manager_fd_hook;
|
|
static int manager_fd = -1;
|
|
static char receive_buffer[RECEIVE_LIMIT];
|
|
static size_t receive_length;
|
|
|
|
static void print_error(const char *message)
|
|
{
|
|
if (manager_buffer != NULL)
|
|
weechat_printf(manager_buffer, "%s%s", weechat_color("lightred"), message);
|
|
}
|
|
|
|
static void disconnect_manager(int announce)
|
|
{
|
|
int was_connected = manager_fd >= 0;
|
|
|
|
if (manager_fd_hook != NULL) {
|
|
weechat_unhook(manager_fd_hook);
|
|
manager_fd_hook = NULL;
|
|
}
|
|
if (manager_fd >= 0)
|
|
close(manager_fd);
|
|
manager_fd = -1;
|
|
receive_length = 0;
|
|
|
|
if (announce && was_connected)
|
|
print_error("NeoDrop manager disconnected; reconnecting in the background.");
|
|
}
|
|
|
|
static void print_protocol_line(const char *line, size_t length)
|
|
{
|
|
const char *payload;
|
|
const char *color = NULL;
|
|
size_t payload_length;
|
|
size_t index;
|
|
char output[RECEIVE_LIMIT];
|
|
|
|
if (manager_buffer == NULL)
|
|
return;
|
|
|
|
if (length > 0 && line[length - 1] == '\r')
|
|
length--;
|
|
|
|
if (length >= 5 && memcmp(line, "LINE\t", 5) == 0) {
|
|
payload = line + 5;
|
|
payload_length = length - 5;
|
|
} else if (length >= 6 && memcmp(line, "ERROR\t", 6) == 0) {
|
|
payload = line + 6;
|
|
payload_length = length - 6;
|
|
color = "lightred";
|
|
} else {
|
|
print_error("NeoDrop manager sent an invalid response.");
|
|
return;
|
|
}
|
|
|
|
for (index = 0; index < payload_length; index++) {
|
|
unsigned char byte = (unsigned char)payload[index];
|
|
output[index] = (byte < 0x20U || byte == 0x7fU) ? ' ' : (char)byte;
|
|
}
|
|
output[payload_length] = '\0';
|
|
|
|
if (color != NULL)
|
|
weechat_printf(manager_buffer, "%s%s", weechat_color(color), output);
|
|
else
|
|
weechat_printf(manager_buffer, "%s", output);
|
|
}
|
|
|
|
static int manager_read_cb(const void *pointer, void *data, int fd)
|
|
{
|
|
ssize_t count;
|
|
size_t consumed = 0;
|
|
char *newline;
|
|
|
|
(void)pointer;
|
|
(void)data;
|
|
(void)fd;
|
|
|
|
if (receive_length >= RECEIVE_LIMIT - 1U) {
|
|
print_error("NeoDrop manager response exceeded the line limit.");
|
|
disconnect_manager(0);
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
|
|
count = recv(manager_fd, receive_buffer + receive_length,
|
|
RECEIVE_LIMIT - receive_length - 1U, 0);
|
|
if (count < 0 && errno == EINTR)
|
|
return WEECHAT_RC_OK;
|
|
if (count <= 0) {
|
|
disconnect_manager(1);
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
receive_length += (size_t)count;
|
|
|
|
while (consumed < receive_length) {
|
|
size_t available = receive_length - consumed;
|
|
|
|
newline = memchr(receive_buffer + consumed, '\n', available);
|
|
if (newline == NULL)
|
|
break;
|
|
print_protocol_line(receive_buffer + consumed,
|
|
(size_t)(newline - (receive_buffer + consumed)));
|
|
consumed = (size_t)(newline - receive_buffer) + 1U;
|
|
}
|
|
|
|
if (consumed > 0) {
|
|
receive_length -= consumed;
|
|
memmove(receive_buffer, receive_buffer + consumed, receive_length);
|
|
}
|
|
if (receive_length >= RECEIVE_LIMIT - 1U) {
|
|
print_error("NeoDrop manager response exceeded the line limit.");
|
|
disconnect_manager(0);
|
|
}
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
|
|
static int connect_manager(void)
|
|
{
|
|
struct sockaddr_un address;
|
|
int fd;
|
|
|
|
if (manager_fd >= 0)
|
|
return 1;
|
|
|
|
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (fd < 0)
|
|
return 0;
|
|
|
|
#ifdef SO_NOSIGPIPE
|
|
{
|
|
int enabled = 1;
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &enabled, sizeof(enabled)) != 0) {
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
memset(&address, 0, sizeof(address));
|
|
address.sun_family = AF_UNIX;
|
|
memcpy(address.sun_path, CONTROL_SOCKET, sizeof(CONTROL_SOCKET));
|
|
if (connect(fd, (struct sockaddr *)&address, sizeof(address)) != 0) {
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
manager_fd = fd;
|
|
manager_fd_hook = weechat_hook_fd(manager_fd, 1, 0, 0,
|
|
&manager_read_cb, NULL, NULL);
|
|
if (manager_fd_hook == NULL) {
|
|
disconnect_manager(0);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void send_command(const char *command)
|
|
{
|
|
static const char prefix[] = "COMMAND\t";
|
|
char message[SEND_LIMIT];
|
|
size_t command_length;
|
|
size_t message_length;
|
|
size_t sent = 0;
|
|
|
|
if (command == NULL || command[0] == '\0')
|
|
return;
|
|
|
|
command_length = strlen(command);
|
|
if (command_length > SEND_LIMIT - sizeof("COMMAND\t\n")) {
|
|
print_error("NeoDrop manager command exceeded the line limit.");
|
|
return;
|
|
}
|
|
if (strpbrk(command, "\t\r\n") != NULL) {
|
|
print_error("NeoDrop manager commands cannot contain tabs or newlines.");
|
|
return;
|
|
}
|
|
if (!connect_manager()) {
|
|
print_error("Unable to connect to the NeoDrop manager.");
|
|
return;
|
|
}
|
|
|
|
memcpy(message, prefix, sizeof(prefix) - 1U);
|
|
memcpy(message + sizeof(prefix) - 1U, command, command_length);
|
|
message_length = sizeof(prefix) - 1U + command_length;
|
|
message[message_length++] = '\n';
|
|
|
|
while (sent < message_length) {
|
|
ssize_t count = send(manager_fd, message + sent, message_length - sent,
|
|
MSG_NOSIGNAL);
|
|
|
|
if (count < 0 && errno == EINTR)
|
|
continue;
|
|
if (count <= 0) {
|
|
disconnect_manager(1);
|
|
return;
|
|
}
|
|
sent += (size_t)count;
|
|
}
|
|
}
|
|
|
|
static int reconnect_cb(const void *pointer, void *data, int remaining_calls)
|
|
{
|
|
(void)pointer;
|
|
(void)data;
|
|
(void)remaining_calls;
|
|
(void)connect_manager();
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
|
|
static int input_cb(const void *pointer, void *data, struct t_gui_buffer *buffer,
|
|
const char *input)
|
|
{
|
|
(void)pointer;
|
|
(void)data;
|
|
(void)buffer;
|
|
send_command(input);
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
|
|
static int close_cb(const void *pointer, void *data, struct t_gui_buffer *buffer)
|
|
{
|
|
(void)pointer;
|
|
(void)data;
|
|
(void)buffer;
|
|
manager_buffer = NULL;
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
|
|
static int ensure_buffer(void)
|
|
{
|
|
if (manager_buffer == NULL)
|
|
manager_buffer = weechat_buffer_search("neodrop_manager", "control");
|
|
if (manager_buffer == NULL) {
|
|
manager_buffer = weechat_buffer_new("control", &input_cb, NULL, NULL,
|
|
&close_cb, NULL, NULL);
|
|
if (manager_buffer == NULL)
|
|
return 0;
|
|
}
|
|
weechat_buffer_set_pointer(manager_buffer, "input_callback", &input_cb);
|
|
weechat_buffer_set_pointer(manager_buffer, "close_callback", &close_cb);
|
|
weechat_buffer_set(manager_buffer, "short_name", "NeoDrop");
|
|
weechat_buffer_set(manager_buffer, "title",
|
|
"NeoDrop | list | put-text NAME TEXT | show/read/download/delete ID | edit ID | help");
|
|
weechat_buffer_set(manager_buffer, "hidden", "0");
|
|
weechat_buffer_set(manager_buffer, "notify", "3");
|
|
weechat_buffer_set(manager_buffer, "localvar_set_no_log", "1");
|
|
weechat_buffer_set(manager_buffer, "localvar_set_pinned", "true");
|
|
weechat_buffer_set(manager_buffer, "localvar_set_type", "other");
|
|
return 1;
|
|
}
|
|
|
|
static int command_cb(const void *pointer, void *data, struct t_gui_buffer *buffer,
|
|
int argc, char **argv, char **argv_eol)
|
|
{
|
|
const char *command = NULL;
|
|
int background_registration;
|
|
|
|
(void)pointer;
|
|
(void)data;
|
|
(void)buffer;
|
|
(void)argv;
|
|
|
|
if (argc > 1 && argv_eol != NULL && argv_eol[1] != NULL && argv_eol[1][0] != '\0')
|
|
command = argv_eol[1];
|
|
background_registration = command != NULL && strncmp(command, "register ", 9) == 0;
|
|
|
|
if (!ensure_buffer())
|
|
return WEECHAT_RC_ERROR;
|
|
|
|
if (!background_registration)
|
|
weechat_buffer_set(manager_buffer, "display", "1");
|
|
if (command != NULL)
|
|
send_command(command);
|
|
else
|
|
send_command("list");
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
|
|
int weechat_plugin_init(struct t_weechat_plugin *plugin, int argc, char *argv[])
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
weechat_plugin = plugin;
|
|
|
|
if (!ensure_buffer())
|
|
return WEECHAT_RC_ERROR;
|
|
|
|
if (weechat_hook_command("drops", "open/control NeoDrop manager",
|
|
"list || status || help || put-text <name> <text> || show <selector> || read <selector> || download <selector> || delete <selector> || edit <selector> || line <number> <text> || insert <number> <text> || delete-line <number> || save || cancel",
|
|
"Manage encrypted NeoDrop files and text revisions",
|
|
"list|status|help|put-text|show|read|download|delete|edit|line|insert|delete-line|save|cancel",
|
|
&command_cb, NULL, NULL) == NULL)
|
|
return WEECHAT_RC_ERROR;
|
|
if (weechat_hook_timer(5000, 0, 0, &reconnect_cb, NULL, NULL) == NULL)
|
|
return WEECHAT_RC_ERROR;
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
|
|
int weechat_plugin_end(struct t_weechat_plugin *plugin)
|
|
{
|
|
(void)plugin;
|
|
disconnect_manager(0);
|
|
return WEECHAT_RC_OK;
|
|
}
|