Files
profanity/src/tools/editor.c
jabber.developer2 1dd22af294 fix: follow-ups to cherry-picked d3d7c328b
- src/xmpp/jid.c: add missing #include "log.h" — d3d7c328b
  introduced a log_debug() call in jid_create without pulling in
  the header, so the source tree did not actually build.

- src/omemo/omemo.c omemo_on_message_send: add the missing
  NULL-check after the gcry_malloc_secure calls for 'tag' /
  'key_tag' so a real OOM does not sail into the memcpy/encrypt
  path. (The 'key' check d3d7c328b added is defensive only —
  gcry_random_bytes_secure aborts internally on failure.)

- src/omemo/omemo.c _cache_device_identity: drop the second
  'if (!fingerprint) return' on the autocomplete path. fingerprint
  is already NULL-checked at the top of the function and is not
  reassigned in between, so the second guard is dead code (upstream
  artefact).

- src/tools/editor.c launch_editor: replace the
  g_main_context_acquire/release pair with
  g_child_watch_source_new(getpid()) + g_source_unref.
  acquire/release does not install GLib's SIGCHLD handler — that
  handler is registered as a side effect of constructing a
  GChildWatchSource. Now we actually pre-arm SIGCHLD before
  forking, which is what d3d7c328b's comment claimed.

- src/ui/statusbar.c status_bar_inactive / _create_tab: switch the
  remaining two literal '10' tab-id values to the CONSOLE_TAB_ID
  define introduced by d3d7c328b, then inline the expression
  directly into the GINT_TO_POINTER() argument so the one-shot
  local 'true_win' goes away.

Verified with ci-build.sh in Debian docker: all 4 configs pass
(644/0, 605/0, 605/0, 644/0 unit + 130/0 functional).
2026-05-23 16:57:45 +03:00

159 lines
4.9 KiB
C

/*
* editor.c
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2022 - 2026 Michael Vetter <jubalh@iodoru.org>
* Copyright (C) 2022 MarcoPolo PasTonMolo <marcopolopastonmolo@protonmail.com>
* Copyright (C) 2022 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include <glib.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include "config/files.h"
#include "config/preferences.h"
#include "editor.h"
#include "log.h"
#include "common.h"
#include "ncurses.h"
#include "ui/ui.h"
#include "xmpp/xmpp.h"
#include "ui/ui.h"
typedef struct EditorContext
{
gchar* filename;
void (*callback)(gchar* content, void* user_data);
void* user_data;
} EditorContext;
static void
_editor_exit_cb(GPid pid, gint status, gpointer data)
{
EditorContext* ctx = data;
gchar* contents = NULL;
GError* error = NULL;
ui_resume();
ui_resize();
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
if (g_file_get_contents(ctx->filename, &contents, NULL, &error)) {
g_strchomp(contents);
ctx->callback(contents, ctx->user_data);
g_free(contents);
} else {
log_error("[Editor] could not read from %s: %s", ctx->filename, error->message);
cons_show_error("Could not read edited content: %s", error->message);
g_error_free(error);
}
} else {
cons_show_error("Editor exited with error status %d", WEXITSTATUS(status));
}
if (remove(ctx->filename) != 0) {
log_error("[Editor] error during file deletion of %s", ctx->filename);
}
g_free(ctx->filename);
g_free(ctx);
g_spawn_close_pid(pid);
}
// Returns a message via callback once the editor is closed.
// Returns true if an error occurred.
gboolean
launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* data), void* user_data)
{
auto_gchar gchar* filename = NULL;
auto_gerror GError* glib_error = NULL;
const char* jid = connection_get_barejid();
if (jid) {
filename = files_file_in_account_data_path(DIR_EDITOR, jid, "compose.md");
} else {
log_debug("[Editor] could not get JID");
auto_gchar gchar* data_dir = files_get_data_path(DIR_EDITOR);
if (!create_dir(data_dir)) {
cons_show_error("Could not create editor directory.");
return TRUE;
}
filename = g_strdup_printf("%s/compose.md", data_dir);
}
if (!filename) {
log_error("[Editor] something went wrong while creating compose file");
cons_show_error("Could not create compose file.");
return TRUE;
}
gsize messagelen = 0;
if (initial_content != NULL) {
messagelen = strlen(initial_content);
}
if (!g_file_set_contents(filename, initial_content, messagelen, &glib_error)) {
log_error("[Editor] could not write to %s: %s", filename, PROF_GERROR_MESSAGE(glib_error));
cons_show_error("Could not write to compose file: %s", PROF_GERROR_MESSAGE(glib_error));
return TRUE;
}
auto_gchar gchar* editor = prefs_get_string(PREF_COMPOSE_EDITOR);
gchar** editor_argv = NULL;
GError* error = NULL;
auto_gchar gchar* full_cmd = g_strdup_printf("%s %s", editor, filename);
if (!g_shell_parse_argv(full_cmd, NULL, &editor_argv, &error)) {
log_error("[Editor] Failed to parse editor command: %s", error->message);
cons_show_error("Failed to parse editor command: %s", error->message);
g_error_free(error);
return TRUE;
}
EditorContext* ctx = g_new0(EditorContext, 1);
ctx->filename = g_steal_pointer(&filename);
ctx->callback = callback;
ctx->user_data = user_data;
ui_suspend();
// Force GLib to install its SIGCHLD handler before forking. Creating a
// GChildWatchSource for our own pid is the documented trigger; we never
// attach or dispatch it, just rely on the side effect.
GSource* sigchld_warmup = g_child_watch_source_new(getpid());
g_source_unref(sigchld_warmup);
pid_t pid = fork();
if (pid == -1) {
log_error("[Editor] Failed to fork: %s", strerror(errno));
ui_resume();
ui_resize();
cons_show_error("Failed to start editor: %s", strerror(errno));
g_strfreev(editor_argv);
g_free(ctx->filename);
g_free(ctx);
return TRUE;
} else if (pid == 0) {
// Child process: Inherits TTY from parent
// Reset signal handlers that profanity sets so the editor doesn't inherit them
signal(SIGINT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
if (editor_argv && editor_argv[0]) {
execvp(editor_argv[0], editor_argv);
}
_exit(EXIT_FAILURE);
}
// Parent process: Watch the child asynchronously
g_child_watch_add((GPid)pid, _editor_exit_cb, ctx);
g_strfreev(editor_argv);
return FALSE;
}