M proto.c => proto.c +188 -22
@@ 8,6 8,9 @@
#include <stdio.h>
#include <errno.h>
+#include "proto.h"
+#include "window.h"
+
struct header {
uint32_t magic;
uint32_t version;
@@ 16,19 19,14 @@ struct header {
static int write_req(int fd, struct json_object* req) {
const char* reqstr = json_object_get_string(req);
- struct header header = {
- .magic = 0xAFBFCFDF,
- .version = 1,
- .payload_len = strlen(reqstr),
- };
-
- if (write(fd, &header, sizeof(struct header)) != sizeof(struct header)) {
+ uint32_t len = strlen(reqstr);
+ if (write(fd, &len, 4) != 4) {
return -1;
}
ssize_t off = 0;
- while (off < header.payload_len) {
- ssize_t n = write(fd, &reqstr[off], header.payload_len-off);
+ while (off < len) {
+ ssize_t n = write(fd, &reqstr[off], len-off);
if (n < 0) {
return -1;
}
@@ 39,30 37,24 @@ static int write_req(int fd, struct json_object* req) {
}
static struct json_object* read_resp(int fd) {
- struct header header = {};
struct json_object* resp = NULL;
char *respstr = NULL;
+ uint32_t len;
ssize_t off = 0;
- while (off < sizeof(struct header)) {
- char* headerp = (char*)&header;
- ssize_t n = read(fd, &headerp[off], sizeof(struct header)-off);
+ while (off < 4) {
+ char* headerp = (char*)&len;
+ ssize_t n = read(fd, &headerp[off], 4-off);
if (n < 0) {
goto end;
}
off += n;
}
-
- if (header.magic != 0xAFBFCFDF ||
- header.version != 1) {
- goto end;
- }
-
off = 0;
- respstr = (char*)calloc(1, header.payload_len+1);
- while (off < header.payload_len) {
- int n = read(fd, &respstr[off], header.payload_len-off);
+ respstr = (char*)calloc(1,len+1);
+ while (off <len) {
+ int n = read(fd, &respstr[off],len-off);
if (n < 0) {
goto end;
}
@@ 130,6 122,180 @@ end:
return resp;
}
+struct proto_status send_create_session(const char *username) {
+ struct json_object* req = json_object_new_object();
+ json_object_object_add(req, "type", json_object_new_string("create_session"));
+ json_object_object_add(req, "username", json_object_new_string(username));
+ struct json_object* resp = roundtrip(req);
+
+ struct proto_status ret;
+
+ struct json_object* type = json_object_object_get(resp, "type");
+ const char* typestr = json_object_get_string(type);
+ if (typestr == NULL || strcmp(typestr, "error") == 0) {
+ ret.success = 0;
+ goto done;
+ }
+
+ if (strcmp(typestr, "success") == 0) {
+ ret.success = 1;
+ ret.has_questions = 0;
+ goto done;
+ }
+
+ if (strcmp(typestr, "auth_question") == 0) {
+ ret.success = 1;
+ ret.has_questions = 1;
+ struct json_object *style = json_object_object_get(resp, "style");
+ const char* stylestr = json_object_get_string(style);
+ if (strcmp(stylestr, "visible") == 0) {
+ ret.question_type = QuestionTypeVisible;
+ } else if (strcmp(stylestr, "secret") == 0) {
+ ret.question_type = QuestionTypeSecret;
+ } else if (strcmp(stylestr, "info") == 0) {
+ ret.question_type = QuestionTypeInfo;
+ } else if (strcmp(stylestr, "error") == 0) {
+ ret.question_type = QuestionTypeError;
+ } else {
+ ret.success = 0;
+ goto done;
+ }
+
+ struct json_object *question = json_object_object_get(resp, "question");
+ const char* questionstr = json_object_get_string(question);
+ strncpy(ret.question, questionstr, 63);
+ goto done;
+ }
+
+ ret.success = 0;
+
+done:
+ json_object_put(resp);
+ return ret;
+}
+
+struct proto_status send_auth_answer(const char *answer) {
+ struct json_object* req = json_object_new_object();
+ json_object_object_add(req, "type", json_object_new_string("answer_auth_question"));
+ json_object_object_add(req, "answer", json_object_new_string(answer));
+ struct json_object* resp = roundtrip(req);
+
+ struct proto_status ret;
+
+ struct json_object* type = json_object_object_get(resp, "type");
+ const char* typestr = json_object_get_string(type);
+ if (typestr == NULL || strcmp(typestr, "error") == 0) {
+ ret.success = 0;
+ goto done;
+ }
+
+ if (strcmp(typestr, "success") == 0) {
+ ret.success = 1;
+ ret.has_questions = 0;
+ goto done;
+ }
+
+ if (strcmp(typestr, "auth_question") == 0) {
+ ret.success = 1;
+ ret.has_questions = 1;
+ struct json_object *style = json_object_object_get(resp, "style");
+ const char* stylestr = json_object_get_string(style);
+ if (strcmp(stylestr, "visible") == 0) {
+ ret.question_type = QuestionTypeVisible;
+ } else if (strcmp(stylestr, "secret") == 0) {
+ ret.question_type = QuestionTypeSecret;
+ } else if (strcmp(stylestr, "info") == 0) {
+ ret.question_type = QuestionTypeInfo;
+ } else if (strcmp(stylestr, "error") == 0) {
+ ret.question_type = QuestionTypeError;
+ } else {
+ ret.success = 0;
+ goto done;
+ }
+
+ struct json_object *question = json_object_object_get(resp, "question");
+ const char* questionstr = json_object_get_string(question);
+ strncpy(ret.question, questionstr, 63);
+ goto done;
+ }
+
+ ret.success = 0;
+
+done:
+ json_object_put(resp);
+ return ret;
+}
+
+
+struct proto_status send_start_session(const char *command) {
+ struct json_object* req = json_object_new_object();
+ json_object_object_add(req, "type", json_object_new_string("start_session"));
+
+ struct json_object* cmd = json_object_new_array();
+ json_object_array_add(cmd, json_object_new_string(command));
+ json_object_object_add(req, "command", cmd);
+
+ struct json_object* env = json_object_new_array();
+
+ char buf[128];
+ snprintf(buf, 128, "XDG_SESSION_DESKTOP=%s", command);
+ json_object_array_add(env, json_object_new_string(buf));
+ snprintf(buf, 128, "XDG_CURRENT_DESKTOP=%s", command);
+ json_object_array_add(env, json_object_new_string(buf));
+
+ json_object_object_add(req, "env", env);
+
+ struct json_object* resp = roundtrip(req);
+
+ struct proto_status ret;
+
+ struct json_object* type = json_object_object_get(resp, "type");
+ const char* typestr = json_object_get_string(type);
+ if (typestr == NULL || strcmp(typestr, "error") == 0) {
+ ret.success = 0;
+ goto done;
+ }
+
+ if (strcmp(typestr, "success") == 0) {
+ ret.success = 1;
+ ret.has_questions = 0;
+ goto done;
+ }
+
+ ret.success = 0;
+
+done:
+ json_object_put(resp);
+ return ret;
+}
+
+struct proto_status send_cancel_session() {
+ struct json_object* req = json_object_new_object();
+ json_object_object_add(req, "type", json_object_new_string("cancel_session"));
+ struct json_object* resp = roundtrip(req);
+
+ struct proto_status ret;
+
+ struct json_object* type = json_object_object_get(resp, "type");
+ const char* typestr = json_object_get_string(type);
+ if (typestr == NULL || strcmp(typestr, "error") == 0) {
+ ret.success = 0;
+ goto done;
+ }
+
+ if (strcmp(typestr, "success") == 0) {
+ ret.success = 1;
+ ret.has_questions = 0;
+ goto done;
+ }
+
+ ret.success = 0;
+
+done:
+ json_object_put(resp);
+ return ret;
+}
+
int send_login(const char *username, const char * password, const char *command) {
struct json_object* login_req = json_object_new_object();
json_object_object_add(login_req, "type", json_object_new_string("login"));
M proto.h => proto.h +14 -0
@@ 1,6 1,20 @@
#ifndef _PROTO_H
#define _PROTO_H
+#include "window.h"
+
+struct proto_status {
+ int success;
+ int has_questions;
+ enum QuestionType question_type;
+ char question[64];
+};
+
+struct proto_status send_create_session(const char *username);
+struct proto_status send_start_session(const char *username);
+struct proto_status send_auth_answer(const char *answer);
+struct proto_status send_cancel_session();
+
int send_login(const char *username, const char * password, const char *command);
int send_shutdown(const char *action);
M window.c => window.c +102 -101
@@ 57,84 57,120 @@ static gboolean draw_clock(gpointer data) {
return TRUE;
}
-static void login_action(GtkWidget *widget, gpointer data) {
- struct Window *ctx = (struct Window*)data;
- gtk_label_set_markup((GtkLabel*)ctx->info_label, "<span>Logging in</span>");
-
- const char *username = gtk_entry_get_text((GtkEntry*)ctx->username_entry);
- const char *password = gtk_entry_get_text((GtkEntry*)ctx->password_entry);
- gchar* selection = gtk_combo_box_text_get_active_text((GtkComboBoxText*)ctx->target_combo_box);
+static void setup_question(struct Window *ctx, enum QuestionType type, char* question);
- if (send_login(username, password, selection)) {
- exit(0);
+static void answer_question(GtkWidget *widget, gpointer data) {
+ struct Window *ctx = (struct Window*)data;
+ struct proto_status ret;
+ switch (ctx->question_type) {
+ case QuestionTypeInitial: {
+ ret = send_create_session(gtk_entry_get_text((GtkEntry*)widget));
+ break;
+ }
+ case QuestionTypeSecret:
+ case QuestionTypeVisible: {
+ ret = send_auth_answer(gtk_entry_get_text((GtkEntry*)widget));
+ break;
+ }
+ case QuestionTypeInfo:
+ case QuestionTypeError: {
+ ret = send_auth_answer(NULL);
+ break;
+ }
+ }
+ if (ret.success) {
+ if (ret.has_questions) {
+ setup_question(ctx, ret.question_type, ret.question);
+ } else {
+ ret = send_start_session("sway");
+ if (ret.success) {
+ exit(0);
+ } else {
+ setup_question(ctx, QuestionTypeInitial, "Username:");
+ }
+ }
} else {
- gtk_label_set_markup((GtkLabel*)ctx->info_label, "<span color=\"darkred\">Login failed</span>");
+ setup_question(ctx, QuestionTypeInitial, "Username:");
}
-
- g_free(selection);
}
-static void shutdown_action(struct Window *ctx, char *action) {
- gtk_label_set_markup((GtkLabel*)ctx->info_label, "<span>Executing shutdown action</span>");
- if (send_shutdown(action)) {
- exit(0);
- } else {
- gtk_label_set_markup((GtkLabel*)ctx->info_label, "<span color=\"darkred\">Exit action failed</span>");
+static void cancel_initial_action(GtkWidget *widget, gpointer data) {
+ exit(0);
+}
+static void cancel_question_action(GtkWidget *widget, gpointer data) {
+ struct Window *ctx = (struct Window*)data;
+ struct proto_status ret = send_cancel_session();
+ if (!ret.success) {
+ exit(1);
}
+
+ setup_question(ctx, QuestionTypeInitial, "Username:");
}
-static void window_create_shutdown_prompt(GtkWidget *widget, gpointer data) {
- struct Window *ctx = (struct Window*)data;
- GtkWidget *dialog = gtk_message_dialog_new(
- GTK_WINDOW(ctx->window),
- GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
- GTK_MESSAGE_QUESTION,
- GTK_BUTTONS_NONE,
- "What do you want to do?");
-
- gtk_dialog_add_button((GtkDialog*)dialog, "Cancel", GTK_RESPONSE_REJECT);
- gtk_dialog_add_button((GtkDialog*)dialog, "Quit", 1);
- GtkWidget *reboot_button = gtk_dialog_add_button((GtkDialog*)dialog, "Reboot", 2);
- gtk_style_context_add_class(gtk_widget_get_style_context(reboot_button), "destructive-action");
- GtkWidget *poweroff_button = gtk_dialog_add_button((GtkDialog*)dialog, "Power off", 3);
- gtk_style_context_add_class(gtk_widget_get_style_context(poweroff_button), "destructive-action");
-
-
- switch (gtk_dialog_run((GtkDialog*)dialog)) {
- case 1:
- exit(0);
- break;
- case 2:
- shutdown_action(ctx, "reboot");
+static void setup_question(struct Window *ctx, enum QuestionType type, char* question) {
+ if (ctx->input != NULL) {
+ gtk_widget_destroy(ctx->input);
+ ctx->input = NULL;
+ }
+ ctx->input = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
+
+ switch (type) {
+ case QuestionTypeInitial:
+ case QuestionTypeVisible: {
+ GtkWidget *label = gtk_label_new(question);
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_container_add(GTK_CONTAINER(ctx->input), label);
+
+ GtkWidget *widget = gtk_entry_new();
+ // gtk_entry_set_placeholder_text((GtkEntry*)widget, question);
+ g_signal_connect(widget, "activate", G_CALLBACK(answer_question), ctx);
+ gtk_container_add(GTK_CONTAINER(ctx->input), widget);
break;
- case 3:
- shutdown_action(ctx, "poweroff");
+ }
+ case QuestionTypeSecret: {
+ GtkWidget *label = gtk_label_new(question);
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_container_add(GTK_CONTAINER(ctx->input), label);
+
+ GtkWidget *widget = gtk_entry_new();
+ // gtk_entry_set_placeholder_text((GtkEntry*)widget, question);
+ gtk_entry_set_input_purpose((GtkEntry*)widget, GTK_INPUT_PURPOSE_PASSWORD);
+ gtk_entry_set_visibility((GtkEntry*)widget, FALSE);
+ g_signal_connect(widget, "activate", G_CALLBACK(answer_question), ctx);
+ gtk_container_add(GTK_CONTAINER(ctx->input), widget);
break;
- default:
+ }
+ case QuestionTypeInfo:
+ case QuestionTypeError:
break;
}
- gtk_widget_destroy(dialog);
-}
-static int update_env_combobox(GtkWidget *combobox) {
- char buffer[255];
- FILE *fp = fopen("/etc/greetd/environments", "r");
- if (fp == NULL) {
- return 0;
- }
+ gtk_container_add(GTK_CONTAINER(ctx->input_box), ctx->input);
- int entries = 0;
- while(fgets(buffer, 255, (FILE*) fp)) {
- size_t len = strnlen(buffer, 255);
- if (len > 0 && len < 255 && buffer[len-1] == '\n') {
- buffer[len-1] = '\0';
+
+ GtkWidget *bottom_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_widget_set_halign(bottom_box, GTK_ALIGN_END);
+ gtk_container_add(GTK_CONTAINER(ctx->input), bottom_box);
+
+ GtkWidget *button = gtk_button_new_with_label("Cancel");
+ switch (type) {
+ case QuestionTypeInitial: {
+ g_signal_connect(button, "clicked", G_CALLBACK(cancel_initial_action), ctx);
+ break;
+ }
+ case QuestionTypeVisible:
+ case QuestionTypeSecret:
+ case QuestionTypeInfo:
+ case QuestionTypeError: {
+ g_signal_connect(button, "clicked", G_CALLBACK(cancel_question_action), ctx);
+ break;
}
- gtk_combo_box_text_append((GtkComboBoxText*)combobox, NULL, buffer);
- entries++;
}
- fclose(fp);
- return entries;
+ gtk_widget_set_halign(button, GTK_ALIGN_END);
+ gtk_container_add(GTK_CONTAINER(bottom_box), button);
+
+ gtk_widget_show_all(ctx->window);
}
static void window_setup(struct Window *ctx) {
@@ 157,47 193,12 @@ static void window_setup(struct Window *ctx) {
g_timeout_add(5000, draw_clock, ctx);
draw_clock(ctx);
- GtkWidget *input_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
- gtk_widget_set_halign(input_box, GTK_ALIGN_CENTER);
- gtk_widget_set_size_request(input_box, 512, -1);
- gtk_container_add(GTK_CONTAINER(window_box), input_box);
-
- ctx->username_entry = gtk_entry_new();
- gtk_entry_set_placeholder_text((GtkEntry*)ctx->username_entry, "Username");
- gtk_container_add(GTK_CONTAINER(input_box), ctx->username_entry);
-
- ctx->password_entry = gtk_entry_new();
- g_signal_connect(ctx->password_entry, "activate", G_CALLBACK(login_action), ctx);
- gtk_entry_set_placeholder_text((GtkEntry*)ctx->password_entry, "Password");
- gtk_entry_set_input_purpose((GtkEntry*)ctx->password_entry, GTK_INPUT_PURPOSE_PASSWORD);
- gtk_entry_set_visibility((GtkEntry*)ctx->password_entry, FALSE);
- gtk_container_add(GTK_CONTAINER(input_box), ctx->password_entry);
-
- ctx->target_combo_box = gtk_combo_box_text_new_with_entry();
- update_env_combobox(ctx->target_combo_box);
- GtkWidget *combo_box_entry = gtk_bin_get_child((GtkBin*)ctx->target_combo_box);
- gtk_entry_set_placeholder_text((GtkEntry*)combo_box_entry, "Command to run");
- gtk_combo_box_set_active((GtkComboBox*)ctx->target_combo_box, 0);
- gtk_container_add(GTK_CONTAINER(input_box), ctx->target_combo_box);
-
- GtkWidget *bottom_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
- gtk_widget_set_halign(bottom_box, GTK_ALIGN_END);
- gtk_container_add(GTK_CONTAINER(input_box), bottom_box);
-
- ctx->info_label = gtk_label_new("");
- gtk_widget_set_halign(ctx->info_label, GTK_ALIGN_START);
- g_object_set(ctx->info_label, "margin-right", 10, NULL);
- gtk_container_add(GTK_CONTAINER(bottom_box), ctx->info_label);
-
- GtkWidget *shutdown_button = gtk_button_new_with_label("Shutdown");
- g_signal_connect(shutdown_button, "clicked", G_CALLBACK(window_create_shutdown_prompt), ctx);
- gtk_widget_set_halign(shutdown_button, GTK_ALIGN_END);
- gtk_container_add(GTK_CONTAINER(bottom_box), shutdown_button);
+ ctx->input_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
+ gtk_widget_set_halign(ctx->input_box, GTK_ALIGN_CENTER);
+ gtk_widget_set_size_request(ctx->input_box, 512, -1);
+ gtk_container_add(GTK_CONTAINER(window_box), ctx->input_box);
- GtkWidget *button = gtk_button_new_with_label("Login");
- g_signal_connect(button, "clicked", G_CALLBACK(login_action), ctx);
- gtk_widget_set_halign(button, GTK_ALIGN_END);
- gtk_container_add(GTK_CONTAINER(bottom_box), button);
+ setup_question(ctx, QuestionTypeInitial, "Username:");
gtk_widget_show_all(ctx->window);
}
M window.h => window.h +12 -3
@@ 3,15 3,24 @@
#include <gtk/gtk.h>
+enum QuestionType {
+ QuestionTypeInitial = 0,
+ QuestionTypeVisible = 1,
+ QuestionTypeSecret = 2,
+ QuestionTypeInfo = 3,
+ QuestionTypeError = 4,
+};
+
struct Window {
GdkMonitor *monitor;
GtkWidget *window;
- GtkWidget *username_entry;
- GtkWidget *password_entry;
+ GtkWidget *input_box;
+ GtkWidget *input;
GtkWidget *info_label;
GtkWidget *clock_label;
- GtkWidget *target_combo_box;
+
+ enum QuestionType question_type;
};
void create_window(GdkMonitor *monitor);