--- /dev/null
+# Mesange
+
+/!\ This is still in early development. /!\
+
+Mesange is a federated messaging project focusing on privacy.
+
+I'm working on a server and CLI client implementation and I plan on making a GUI
+client too, but you're obviously free to make your own servers and clients or
+even branch the ones I made.
selected_profile: Maybe(string)
if parsed_selected_profile, ok := parsed_config.selected_profile.?; ok {
- selected_profile, _ = strings.clone(parsed_selected_profile)
+ selected_profile = parsed_selected_profile
}
return {
config_load :: proc(app: ^App) -> (Config, Maybe(Config_Load_Error)) {
config_path := fpath.join({ app.storage_path, "config.json" }, context.temp_allocator)
-
raw_config, config_read := os.read_entire_file_from_filename(config_path)
+
parsed_config: Config_Parsed
if !config_read {
return {}, .No_File_Present
}
- err := json.unmarshal(raw_config, &parsed_config, allocator = context.temp_allocator)
+ err := json.unmarshal(raw_config, &parsed_config)
if err != nil {
return {}, .Invalid_File
}
config_deinit :: proc(config: Config) {
- if selected_profiles, ok := config.selected_profile.?; ok {
- delete(selected_profiles)
+ if selected_profile, ok := config.selected_profile.?; ok {
+ delete(selected_profile)
}
}
nc.delwin(box_message)
}
- nc.endwin()
+ nc.endwin()
delete(app.profiles_path)
}
nc.wrefresh(box_message)
- sync.mutex_lock(&app.mutex)
- app.box_message = box_message
- sync.mutex_unlock(&app.mutex)
+ {
+ sync.mutex_lock(&app.mutex)
+ defer sync.mutex_unlock(&app.mutex)
+
+ if box_message, ok := app.box_message.?; ok {
+ nc.delwin(box_message)
+ }
+
+ app.box_message = box_message
+ }
}
app_update_info_bar :: proc(app: ^App) {
buffer[buffer_len] = 0
output := string(buffer[:buffer_len + 1])
- if !hidden {
- nc.wclear(app.input_window)
+ nc.wclear(app.input_window)
+
+ if !hidden || (buffer_len != 0 && buffer[0] == ':') {
nc.mvwprintw(app.input_window, 0, 0, strings.unsafe_string_to_cstring(output))
- nc.wrefresh(app.input_window)
nc.wmove(app.input_window, 0, i32(cursor_pos))
}
+
+ nc.wrefresh(app.input_window)
}
nc.curs_set(0)
main :: proc() {
home_path := os.get_env("HOME")
+ defer delete(home_path)
storage_path := fpath.join({ home_path, "mesange" })
defer delete(storage_path)
+ // Setup logger.
log_path := fpath.join({ storage_path, "log.txt" }, context.temp_allocator)
if !os.exists(storage_path) {
context.logger = log.create_file_logger(log_file)
defer log.destroy_file_logger(context.logger)
- now := transmute(u64)time.now()
- rng_state := rand.create(now)
-
- context.random_generator = rand.default_random_generator(&rng_state)
-
when ODIN_DEBUG {
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
defer {
if len(track.allocation_map) > 0 {
- log.errorf("%v allocations not freed:", len(track.allocation_map))
+ log.errorf("%v allocation not freed.", len(track.allocation_map))
for _, entry in track.allocation_map {
- log.errorf("- %v bytes at %v\n", entry.size, entry.location)
+ log.errorf("- %v bytes at %v", entry.size, entry.location)
}
}
if len(track.bad_free_array) > 0 {
- log.errof("%v incorrect frees:\n", len(track.bad_free_array))
+ log.errorf("%v incorrect frees.", len(track.bad_free_array))
for entry in track.bad_free_array {
- log.errorf("- %p at %v\n", entry.memory, entry.location)
+ log.errorf("- %p at %v", entry.memory, entry.location)
}
}
mem.tracking_allocator_destroy(&track)
}
}
+
+ { // Setup random generator.
+ now := transmute(u64)time.now()
+ rng_state := rand.create(now)
+
+ context.random_generator = rand.default_random_generator(&rng_state)
+ }
app := app_init(storage_path)
defer app_deinit(&app)
+ // Setup state thread.
handle_state_thread_context := runtime.default_context()
handle_state_thread_context.logger = context.logger
handle_state_thread_context.random_generator = context.random_generator
+ defer free_all(handle_state_thread_context.temp_allocator)
+
handle_state_thread := thread.create_and_start_with_poly_data(&app, handle_state, handle_state_thread_context)
- defer thread.terminate(handle_state_thread, 0)
+
+ defer {
+ thread.terminate(handle_state_thread, 0)
+ thread.destroy(handle_state_thread)
+ }
for app.running {
defer {
private_key: ed25519.Private_Key
_ = ed25519.private_key_set_bytes(&private_key, parsed_profile.private_key[:])
- host, err := strings.clone(parsed_profile.host, allocator)
-
return {
private_key = private_key,
- host = host,
+ host = parsed_profile.host,
}
}
chacha_context: chacha.Context
chacha.init_xchacha(&chacha_context, password_hash[:])
- iv := buffer[:chacha.XIV_SIZE]
- tag := buffer[len(iv):][:chacha.TAG_SIZE]
- ciphertext := buffer[len(iv) + len(tag):]
+ iv := profile_content[:chacha.XIV_SIZE]
+ tag := profile_content[len(iv):][:chacha.TAG_SIZE]
+ ciphertext := profile_content[len(iv) + len(tag):]
if !chacha.open(&chacha_context, buffer[:len(ciphertext)], iv, {}, ciphertext, tag) {
return {}, .Invalid_Password
json_string := string(buffer[:len(ciphertext)])
parsed_profile: Profile_Parsed
- err := json.unmarshal(transmute([]u8)json_string, &parsed_profile, allocator = context.temp_allocator)
+ err := json.unmarshal(transmute([]u8)json_string, &parsed_profile)
if err != nil {
return {}, .Invalid_File
}
}
- app.config = config
+ {
+ sync.mutex_lock(&app.mutex)
+ defer sync.mutex_unlock(&app.mutex)
+
+ app.config = config
+ }
app_set_state(app, .Load_Profile)
}
return
}
- app.profile = profile
+ {
+ sync.mutex_lock(&app.mutex)
+ defer sync.mutex_unlock(&app.mutex)
- app_set_state(app, .Main)
+ app.profile = profile
+ }
+
+ app_set_state(app, .Connect_To_Host)
}
state_invalid_profile :: proc(app: ^App) {
delete(app.profile_password)
}
- app.profile_password = app_get_input(app, true, true)
+ input := app_get_input(app, true, true)
+
+ if err, ok := handle_command(app, input).?; ok {
+ if err != .Not_A_Command {
+ return
+ }
+ } else {
+ return
+ }
+
+ app.profile_password = input
app_set_state(app, .Load_Profile)
}
},
)
- app.profile_password = app_get_input(app, true, true)
+ input := app_get_input(app, true, true)
+
+ if err, ok := handle_command(app, input).?; ok {
+ if err != .Not_A_Command {
+ return
+ }
+ } else {
+ return
+ }
+
+ app.profile_password = input
app_set_state(app, .Ask_Profile_Confirm_Password)
}
input := app_get_input(app, true, true, context.temp_allocator)
+ if err, ok := handle_command(app, input).?; ok {
+ if err != .Not_A_Command {
+ return
+ }
+ } else {
+ return
+ }
+
if input != app.profile_password {
app_set_info_bar(app, "Passwords don't match.")
return
}
state_main :: proc(app: ^App) {
-
+ input := app_get_input(app)
+
+ if err, ok := handle_command(app, input).?; ok {
+ if err != .Not_A_Command {
+ return
+ }
+ } else {
+ delete(input)
+ return
+ }
}