diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client.h | 5 | ||||
-rw-r--r-- | src/entry.c | 9 | ||||
-rw-r--r-- | src/entry.h | 4 | ||||
-rw-r--r-- | src/greetd.c | 110 | ||||
-rw-r--r-- | src/greetd.h | 43 | ||||
-rw-r--r-- | src/image.c | 2 | ||||
-rw-r--r-- | src/ipc.c | 120 | ||||
-rw-r--r-- | src/ipc.h | 13 | ||||
-rw-r--r-- | src/log.c | 2 | ||||
-rw-r--r-- | src/main.c | 163 |
10 files changed, 453 insertions, 18 deletions
diff --git a/src/client.h b/src/client.h index d356094..7ebea86 100644 --- a/src/client.h +++ b/src/client.h @@ -48,6 +48,11 @@ struct client_state { struct xkb_state *xkb_state; struct xkb_context *xkb_context; struct xkb_keymap *xkb_keymap; + + /* greetd state */ + const char *username; + const char *command; + bool submit; }; #endif /* CLIENT_H */ diff --git a/src/entry.c b/src/entry.c index 7c34acf..63b6f87 100644 --- a/src/entry.c +++ b/src/entry.c @@ -133,15 +133,12 @@ void entry_update(struct entry *entry) /* Draw our text with Pango. */ color = entry->foreground_color; cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); - //const wchar_t *src = entry->password; - //wcsrtombs(entry->password_mb, &src, N_ELEM(entry->password_mb), NULL); size_t len = 0; for (unsigned int i = 0; i < entry->password_length; i++) { - len += wcrtomb(entry->password_mb + len, entry->password_character, NULL); + len += wcrtomb(entry->password_mb_print + len, entry->password_character, NULL); } - entry->password_mb[len] = '\0'; - fprintf(stderr, "%s\n", entry->password_mb); - pango_layout_set_text(layout, entry->password_mb, -1); + entry->password_mb_print[len] = '\0'; + pango_layout_set_text(layout, entry->password_mb_print, -1); pango_cairo_update_layout(cr, layout); pango_cairo_show_layout(cr, layout); diff --git a/src/entry.h b/src/entry.h index b1b9670..f487410 100644 --- a/src/entry.h +++ b/src/entry.h @@ -6,7 +6,7 @@ #include "image.h" #include "surface.h" -#define MAX_PASSWORD_LENGTH 256 +#define MAX_PASSWORD_LENGTH 64 struct entry { struct surface surface; @@ -22,7 +22,9 @@ struct entry { wchar_t password[MAX_PASSWORD_LENGTH]; /* Assume maximum of 4 bytes per wchar_t (for UTF-8) */ char password_mb[4*MAX_PASSWORD_LENGTH]; + char password_mb_print[4*MAX_PASSWORD_LENGTH]; uint32_t password_length; + bool password_visible; /* Options */ uint32_t font_size; diff --git a/src/greetd.c b/src/greetd.c new file mode 100644 index 0000000..eb356af --- /dev/null +++ b/src/greetd.c @@ -0,0 +1,110 @@ +#include "greetd.h" +#include "ipc.h" +#include <json-c/json_object.h> +#include <string.h> + +struct json_object *greetd_create_session(const char *username) +{ + struct json_object *request = json_object_new_object(); + + struct json_object *type = json_object_new_string("create_session"); + json_object_object_add(request, "type", type); + + struct json_object *name = json_object_new_string(username); + json_object_object_add(request, "username", name); + + struct json_object *resp = ipc_submit(request); + json_object_put(request); + return resp; +} + +struct json_object *greetd_post_auth_message_response(const char *response) +{ + struct json_object *request = json_object_new_object(); + + struct json_object *type = json_object_new_string("post_auth_message_response"); + json_object_object_add(request, "type", type); + + if (response != NULL) { + struct json_object *resp = json_object_new_string(response); + json_object_object_add(request, "response", resp); + } + + struct json_object *resp = ipc_submit(request); + json_object_put(request); + return resp; +} + +struct json_object *greetd_start_session(const char *command) +{ + struct json_object *request = json_object_new_object(); + + struct json_object *type = json_object_new_string("start_session"); + json_object_object_add(request, "type", type); + + struct json_object *arr = json_object_new_array_ext(1); + struct json_object *cmd = json_object_new_string(command); + json_object_array_add(arr, cmd); + json_object_object_add(request, "cmd", arr); + + struct json_object *resp = ipc_submit(request); + json_object_put(request); + return resp; +} + +struct json_object *greetd_cancel_session(void) +{ + struct json_object *request = json_object_new_object(); + + struct json_object *type = json_object_new_string("cancel_session"); + json_object_object_add(request, "type", type); + + struct json_object *resp = ipc_submit(request); + json_object_put(request); + return resp; +} + +enum greetd_response_type greetd_parse_response_type(struct json_object *response) +{ + const char *str = json_object_get_string(json_object_object_get(response, "type")); + if (!strcmp(str, "success")) { + return GREETD_RESPONSE_SUCCESS; + } + if (!strcmp(str, "error")) { + return GREETD_RESPONSE_ERROR; + } + if (!strcmp(str, "auth_message")) { + return GREETD_RESPONSE_AUTH_MESSAGE; + } + return GREETD_RESPONSE_INVALID; +} + +enum greetd_auth_message_type greetd_parse_auth_message_type(struct json_object *response) +{ + const char *str = json_object_get_string(json_object_object_get(response, "auth_message_type")); + if (!strcmp(str, "visible")) { + return GREETD_AUTH_MESSAGE_VISIBLE; + } + if (!strcmp(str, "secret")) { + return GREETD_AUTH_MESSAGE_SECRET; + } + if (!strcmp(str, "info")) { + return GREETD_AUTH_MESSAGE_INFO; + } + if (!strcmp(str, "error")) { + return GREETD_AUTH_MESSAGE_ERROR; + } + return GREETD_AUTH_MESSAGE_INVALID; +} + +enum greetd_error_type greetd_parse_error_type(struct json_object *response) +{ + const char *str = json_object_get_string(json_object_object_get(response, "error_type")); + if (!strcmp(str, "auth_error")) { + return GREETD_ERROR_AUTH_ERROR; + } + if (!strcmp(str, "error")) { + return GREETD_ERROR_ERROR; + } + return GREETD_ERROR_INVALID; +} diff --git a/src/greetd.h b/src/greetd.h new file mode 100644 index 0000000..3901d85 --- /dev/null +++ b/src/greetd.h @@ -0,0 +1,43 @@ +#ifndef GREETD_H +#define GREETD_H + +#include <json-c/json_object.h> + +enum greetd_request_type { + GREETD_REQUEST_CREATE_SESSION, + GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE, + GREETD_REQUEST_START_SESSION, + GREETD_REQUEST_CANCEL_SESSION +}; + +enum greetd_response_type { + GREETD_RESPONSE_INVALID, + GREETD_RESPONSE_SUCCESS, + GREETD_RESPONSE_ERROR, + GREETD_RESPONSE_AUTH_MESSAGE +}; + +enum greetd_auth_message_type { + GREETD_AUTH_MESSAGE_INVALID, + GREETD_AUTH_MESSAGE_VISIBLE, + GREETD_AUTH_MESSAGE_SECRET, + GREETD_AUTH_MESSAGE_INFO, + GREETD_AUTH_MESSAGE_ERROR +}; + +enum greetd_error_type { + GREETD_ERROR_INVALID, + GREETD_ERROR_AUTH_ERROR, + GREETD_ERROR_ERROR +}; + +[[nodiscard]] struct json_object *greetd_create_session(const char *username); +[[nodiscard]] struct json_object *greetd_post_auth_message_response(const char *response); +[[nodiscard]] struct json_object *greetd_start_session(const char *command); +[[nodiscard]] struct json_object *greetd_cancel_session(void); + +enum greetd_response_type greetd_parse_response_type(struct json_object *response); +enum greetd_auth_message_type greetd_parse_auth_message_type(struct json_object *response); +enum greetd_error_type greetd_parse_error_type(struct json_object *response); + +#endif /* GREETD_H */ diff --git a/src/image.c b/src/image.c index 5e8c511..07195c1 100644 --- a/src/image.c +++ b/src/image.c @@ -11,7 +11,7 @@ void image_load(struct image *image, const char *filename) { - log_debug("Loading image %s\n", filename); + log_debug("Loading image '%s'\n", filename); FILE *fp = fopen(filename, "rb"); uint8_t header[HEADER_BYTES]; if (!fp) { diff --git a/src/ipc.c b/src/ipc.c new file mode 100644 index 0000000..2e92097 --- /dev/null +++ b/src/ipc.c @@ -0,0 +1,120 @@ +#include <json-c/json_object.h> +#include <json-c/json_tokener.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include "ipc.h" +#include "log.h" + +static int ipc_open(void); +static int ipc_send(int socket, struct json_object *request); +static struct json_object *ipc_receive(int socket); + +struct json_object *ipc_submit(struct json_object *request) +{ + int sock = ipc_open(); + if (sock == -1) { + return NULL; + } + + if (ipc_send(sock, request) == -1) { + close(sock); + return NULL; + } + + struct json_object *resp = ipc_receive(sock); + close(sock); + return resp; +} + +/* + * Open a connection to the UNIX socket specified by + * the environment variable GREETD_SOCK. + * + * Returns the socket file descriptor on success, or -1 on failure. + */ +int ipc_open(void) +{ + char *greetd_sock = getenv("GREETD_SOCK"); + + if (greetd_sock == NULL) { + log_error("GREETD_SOCK not set.\n"); + return -1; + } + + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock == -1) { + log_error("Unable to create socket: %s\n", strerror(errno)); + return -1; + } + + struct sockaddr_un remote = { .sun_family = AF_UNIX }; + strncpy(remote.sun_path, greetd_sock, sizeof(remote.sun_path)); + + if (connect(sock, (struct sockaddr *)&remote, sizeof(remote)) == -1) { + log_error("Unable to connect to greetd: %s\n", strerror(errno)); + close(sock); + return -1; + } + return sock; +} + +/* + * Send an IPC request to the specified socket. + * + * Returns 0 on success, or -1 on failure. + */ +int ipc_send(int sock, struct json_object *request) +{ + const char *str = json_object_to_json_string(request); + uint32_t len = strlen(str); + + if (send(sock, &len, sizeof(len), 0) == -1) { + log_error("Error sending request size: %s\n", strerror(errno)); + return -1; + } + + if (send(sock, str, len, 0) == -1) { + log_error("Error sending request: %s\n", strerror(errno)); + return -1; + } + return 0; +} + +/* + * Receive an IPC response on the specified socket. + * + * Returns the response on success, or NULL on failure. + */ +struct json_object *ipc_receive(int sock) +{ + uint32_t len = 0; + + if (recv(sock, &len, sizeof(len), 0) != sizeof(len)) { + log_error("Error receiving response size: %s\n", strerror(errno)); + return NULL; + } + + char *buf = malloc(len + 1); + if (recv(sock, buf, len, 0) != len) { + log_error("Error receiving response: %s\n", strerror(errno)); + free(buf); + return NULL; + } + + buf[len] = '\0'; + + enum json_tokener_error error; + struct json_object *resp = json_tokener_parse_verbose(buf, &error); + free(buf); + + if (resp == NULL) { + log_error("Error parsing response: %s\n", json_tokener_error_desc(error)); + } + return resp; + +} diff --git a/src/ipc.h b/src/ipc.h new file mode 100644 index 0000000..30a953a --- /dev/null +++ b/src/ipc.h @@ -0,0 +1,13 @@ +#ifndef IPC_H +#define IPC_H + +#include <json-c/json_object.h> + +/* + * Submit an IPC request to greetd. + * + * Returns the response on success, or NULL on failure. + */ +struct json_object *ipc_submit(struct json_object *request); + +#endif /* IPC_H */ @@ -29,7 +29,7 @@ void log_debug(const char *const fmt, ...) va_list args; va_start(args, fmt); fprintf(stderr, "[%ld.%03ld][DEBUG]: ", t.tv_sec, t.tv_nsec / 1000000); - vprintf(fmt, args); + vfprintf(stderr, fmt, args); va_end(args); } @@ -19,6 +19,7 @@ #include "entry.h" #include "image.h" #include "gl.h" +#include "greetd.h" #include "log.h" #include "nelem.h" #include "xdg-shell-client-protocol.h" @@ -26,6 +27,17 @@ #undef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) + +static void handle_response( + struct client_state *state, + struct json_object *response, + enum greetd_request_type request); +static void create_session(struct client_state *state); +static void start_session(struct client_state *state); +static void post_auth_message_response(struct client_state *state); +static void cancel_session(struct client_state *state); +static void restart_session(struct client_state *state); + static void resize(struct client_state *state) { struct surface *surface = &state->window.surface; @@ -192,7 +204,6 @@ static void wl_keyboard_key( entry->password_length++; entry->password[entry->password_length] = L'\0'; } - fprintf(stderr, "%ls\n", entry->password); } else if (entry->password_length > 0 && sym == XKB_KEY_BackSpace) { entry->password[entry->password_length - 1] = '\0'; entry->password_length--; @@ -210,8 +221,15 @@ static void wl_keyboard_key( } else if (entry->password_length > 0 && (sym == XKB_KEY_Return || sym == XKB_KEY_KP_Enter)) { + const wchar_t *src = entry->password; + wcsrtombs( + entry->password_mb, + &src, + N_ELEM(entry->password_mb), + NULL); entry->password[0] = '\0'; entry->password_length = 0; + client_state->submit = true; } entry_update(&client_state->window.entry); client_state->window.entry.surface.redraw = true; @@ -359,21 +377,21 @@ static void registry_global( uint32_t version) { struct client_state *state = data; - if (strcmp(interface, wl_compositor_interface.name) == 0) { + if (!strcmp(interface, wl_compositor_interface.name)) { state->wl_compositor = wl_registry_bind( wl_registry, name, &wl_compositor_interface, 4); log_debug("Bound to compositor %u.\n", name); - } else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { + } else if (!strcmp(interface, wl_subcompositor_interface.name)) { state->wl_subcompositor = wl_registry_bind( wl_registry, name, &wl_subcompositor_interface, 1); log_debug("Bound to subcompositor %u.\n", name); - } else if (strcmp(interface, wl_seat_interface.name) == 0) { + } else if (!strcmp(interface, wl_seat_interface.name)) { state->wl_seat = wl_registry_bind( wl_registry, name, @@ -384,7 +402,7 @@ static void registry_global( &wl_seat_listener, state); log_debug("Bound to seat %u.\n", name); - } else if (strcmp(interface, wl_output_interface.name) == 0) { + } else if (!strcmp(interface, wl_output_interface.name)) { state->wl_output = wl_registry_bind( wl_registry, name, @@ -395,7 +413,7 @@ static void registry_global( &wl_output_listener, state); log_debug("Bound to output %u.\n", name); - } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + } else if (!strcmp(interface, xdg_wm_base_interface.name)) { state->xdg_wm_base = wl_registry_bind( wl_registry, name, @@ -449,6 +467,8 @@ int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); struct client_state state = { + .username = "nobody", + .command = "false", .window = { .background_color = {0.89, 0.8, 0.824, 1.0}, .scale = 1, @@ -485,12 +505,14 @@ int main(int argc, char *argv[]) {"text_color", required_argument, NULL, 'T'}, {"font_name", required_argument, NULL, 'f'}, {"font_size", required_argument, NULL, 'F'}, - {"password_character", required_argument, NULL, 'c'}, + {"password_character", required_argument, NULL, 'C'}, + {"command", required_argument, NULL, 'c'}, + {"username", required_argument, NULL, 'u'}, {"width_characters", required_argument, NULL, 'n'}, {"wide_layout", no_argument, NULL, 'w'}, {NULL, 0, NULL, 0} }; - const char *short_options = "b:B:e:E:f:F:r:R:n:o:O:c:T:w"; + const char *short_options = "b:B:c:C:e:E:f:F:r:R:n:o:O:T:u:w"; int opt = getopt_long(argc, argv, short_options, long_options, NULL); while (opt != -1) { @@ -539,16 +561,23 @@ int main(int argc, char *argv[]) state.window.entry.font_size = strtol(optarg, NULL, 0); break; - case 'c': + case 'C': mbrtowc( &state.window.entry.password_character, optarg, 4, NULL); break; + case 'c': + state.command = optarg; + break; + case 'u': + state.username = optarg; + break; case 'n': state.window.entry.num_characters = strtol(optarg, NULL, 0); + break; case 'w': state.window.entry.tight_layout = false; break; @@ -724,6 +753,8 @@ int main(int argc, char *argv[]) state.window.surface.redraw = false; state.window.entry.surface.redraw = false; + create_session(&state); + while (wl_display_dispatch(state.wl_display) != -1) { if (state.closed) { break; @@ -746,6 +777,10 @@ int main(int argc, char *argv[]) &state.window.entry.image); state.window.entry.surface.redraw = false; } + if (state.submit) { + post_auth_message_response(&state); + state.submit = false; + } } log_info("Window closed, performing cleanup.\n"); @@ -754,3 +789,113 @@ int main(int argc, char *argv[]) log_info("Finished, exiting.\n"); return 0; } + +void handle_response( + struct client_state *state, + struct json_object *response, + enum greetd_request_type request) +{ + if (response == NULL) { + return; + } + enum greetd_response_type type = greetd_parse_response_type(response); + + switch (type) { + case GREETD_RESPONSE_SUCCESS: + switch (request) { + case GREETD_REQUEST_CREATE_SESSION: + case GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE: + start_session(state); + break; + case GREETD_REQUEST_START_SESSION: + exit(EXIT_SUCCESS); + break; + case GREETD_REQUEST_CANCEL_SESSION: + break; + } + break; + case GREETD_RESPONSE_ERROR: + switch (request) { + case GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE: + case GREETD_REQUEST_START_SESSION: + log_error( + "Failed to create greetd session: %s\n", + json_object_get_string( + json_object_object_get( + response, + "description") + ) + ); + restart_session(state); + break; + case GREETD_REQUEST_CREATE_SESSION: + log_error("Failed to connect to greetd session.\n"); + break; + case GREETD_REQUEST_CANCEL_SESSION: + break; + } + break; + case GREETD_RESPONSE_AUTH_MESSAGE: + switch (greetd_parse_auth_message_type(response)) { + case GREETD_AUTH_MESSAGE_VISIBLE: + state->window.entry.password_visible = true; + break; + case GREETD_AUTH_MESSAGE_SECRET: + state->window.entry.password_visible = false; + break; + case GREETD_AUTH_MESSAGE_INFO: + case GREETD_AUTH_MESSAGE_ERROR: + /* TODO */ + restart_session(state); + break; + case GREETD_AUTH_MESSAGE_INVALID: + break; + } + break; + case GREETD_RESPONSE_INVALID: + break; + } + json_object_put(response); +} + +void create_session(struct client_state *state) +{ + log_debug("Creating greetd session for user '%s'.\n", state->username); + handle_response( + state, + greetd_create_session(state->username), + GREETD_REQUEST_CREATE_SESSION); +} + +void start_session(struct client_state *state) +{ + log_debug("Starting session with command '%s'.\n", state->command); + handle_response( + state, + greetd_start_session(state->command), + GREETD_REQUEST_START_SESSION); +} + +void post_auth_message_response(struct client_state *state) +{ + log_debug("Posting auth message response.\n"); + handle_response( + state, + greetd_post_auth_message_response(state->window.entry.password_mb), + GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE); +} + +void cancel_session(struct client_state *state) +{ + log_debug("Cancelling session.\n"); + handle_response( + state, + greetd_cancel_session(), + GREETD_REQUEST_CANCEL_SESSION); +} + +void restart_session(struct client_state *state) +{ + cancel_session(state); + create_session(state); +} |