From db2114a4c6f6378b6fc7b8de340ad8e7da840225 Mon Sep 17 00:00:00 2001
From: teknomunk
Date: Tue, 28 Nov 2023 06:11:36 -0600
Subject: [PATCH 01/11] Make build.sh work from fresh checkout, fix crash on
first start when Tor hasn't initialized yet
---
build.sh | 2 ++
src/model/server.c | 3 +++
2 files changed, 5 insertions(+)
diff --git a/build.sh b/build.sh
index 967571a..4495fd2 100755
--- a/build.sh
+++ b/build.sh
@@ -2,6 +2,8 @@
set -e
+git submodule update --init --recursive
+
rm debug release 2>/dev/null || true
find src | grep -E "\.template$" | while read FILE; do
ruby tools/builder/tools/embed.rb "$FILE"
diff --git a/src/model/server.c b/src/model/server.c
index 12d168a..408aa1a 100644
--- a/src/model/server.c
+++ b/src/model/server.c
@@ -42,6 +42,9 @@ void app_args_refresh_tor_hidden_service()
g_server->tor_hidden_service = NULL;
FILE* f = fopen("data/tor/hidden_service/hostname","r");
+ if( !f ) {
+ return;
+ }
size_t n;
getline( &g_server->tor_hidden_service, &n, f );
n = strlen( g_server->tor_hidden_service );
From 48db4e78ab72fdc902df2d7034fe46f5435fc53f Mon Sep 17 00:00:00 2001
From: teknomunk
Date: Tue, 28 Nov 2023 06:47:17 -0600
Subject: [PATCH 02/11] Get apogee to start running without crashing from
non-existent data/ directory
---
src/controller/inbox.c | 3 +++
src/controller/outbox.c | 3 +++
src/ffdb | 2 +-
src/main.c | 2 ++
src/model.c | 17 +++++++++++++++++
src/model/server.c | 4 ++--
src/process.c | 2 ++
7 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/src/controller/inbox.c b/src/controller/inbox.c
index da321e1..0ef7f69 100644
--- a/src/controller/inbox.c
+++ b/src/controller/inbox.c
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
extern bool terminate;
@@ -697,6 +698,8 @@ bool cleanup_box( const char* box )
void process_inbox()
{
+ mkdir( "data/inbox", 0750 );
+
while( !terminate ) {
bool activity = false;
activity |= process_one();
diff --git a/src/controller/outbox.c b/src/controller/outbox.c
index b73c456..0a04795 100644
--- a/src/controller/outbox.c
+++ b/src/controller/outbox.c
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
static bool blacklisted( struct outbox_envelope* env )
{
@@ -366,6 +367,8 @@ bool cleanup_box( const char* box );
extern bool terminate;
void process_outbox()
{
+ mkdir( "data/outbox", 0750 );
+
while( !terminate ) {
bool activity = false;
activity |= process_pending();
diff --git a/src/ffdb b/src/ffdb
index c57a8bb..987273a 160000
--- a/src/ffdb
+++ b/src/ffdb
@@ -1 +1 @@
-Subproject commit c57a8bb19fedfb717ac0ae498b2e121f96d3507f
+Subproject commit 987273a603b922cdd3c216bb028d54eb146fa884
diff --git a/src/main.c b/src/main.c
index c4d97c3..ecde9cc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -13,6 +13,7 @@
#include
#include
+#include
bool terminate = false;
@@ -23,6 +24,7 @@ void handle_ctrl_c(int)
int main( int argc, char* argv[], char* envp[] )
{
+ srand( time(NULL) );
curl_global_init(CURL_GLOBAL_DEFAULT);
model_init();
diff --git a/src/model.c b/src/model.c
index dd07d3a..6293697 100644
--- a/src/model.c
+++ b/src/model.c
@@ -2,8 +2,25 @@
#include "model/status.h"
#include "model/peer.h"
+#include
+
void model_init()
{
+ mkdir( "data", 0750 );
+ mkdir( "data/accounts", 0750 );
+ mkdir( "data/activities", 0750 );
+ mkdir( "data/bookmarks", 0750 );
+ mkdir( "data/cache", 0750 );
+ mkdir( "data/client_apps", 0750 );
+ mkdir( "data/config", 0750 );
+ mkdir( "data/crypto", 0750 );
+ mkdir( "data/emoji", 0750 );
+ mkdir( "data/indempotency", 0750 );
+ mkdir( "data/media", 0750 );
+ mkdir( "data/notices", 0750 );
+ mkdir( "data/owner", 0750 );
+ mkdir( "data/webfinger", 0750 );
+
status_model_init();
peer_model_init();
}
diff --git a/src/model/server.c b/src/model/server.c
index 408aa1a..0e966ff 100644
--- a/src/model/server.c
+++ b/src/model/server.c
@@ -61,9 +61,9 @@ struct app_args* app_args_new( int argc, char** argv )
g_server = args;
- args->http_settings.bind_port = 9053;
+ args->http_settings.bind_port = 9000 + ( rand() % 41000 );
args->http_settings.bind_address = strdup("0.0.0.0");
- args->tor_socks_port = 9123;
+ args->tor_socks_port = ( rand() % 41000 ) + 9000;
args->section = -1;
args->outbox_discard_limit = 5;
args->user_agent = strdup( "Apogee/0.1" );
diff --git a/src/process.c b/src/process.c
index abb8367..e14f862 100644
--- a/src/process.c
+++ b/src/process.c
@@ -130,6 +130,8 @@ static void redirect_io( int section )
const char* section_name_str = process_get_section_name(section);
if( !section_name_str ) { return; }
+ mkdir( "data/logs", 0640 );
+
char filename[512];
snprintf( filename,512, "data/logs/%s.log", section_name_str );
From ce178cd5a9be414b89426049c6868ed7b511584d Mon Sep 17 00:00:00 2001
From: teknomunk
Date: Wed, 29 Nov 2023 06:43:47 -0600
Subject: [PATCH 03/11] Start implementing administrative interface, add
initial installation instructions to README, make soapbox.json into a
server-created file
---
README.md | 32 +++++++++-
src/controller/admin.c | 45 ++++++++++++++
src/controller/admin.h | 9 +++
src/controller/main.c | 12 ++++
src/main.c | 1 +
src/model/server.c | 13 ++++
src/model/server.h | 3 +
src/process.c | 2 +-
src/view/admin/server-setup.html.template | 76 +++++++++++++++++++++++
src/view/soapbox.json.template | 33 ++++++++++
10 files changed, 224 insertions(+), 2 deletions(-)
create mode 100644 src/controller/admin.c
create mode 100644 src/controller/admin.h
create mode 100644 src/view/admin/server-setup.html.template
create mode 100644 src/view/soapbox.json.template
diff --git a/README.md b/README.md
index 9a47167..e5bfc8e 100644
--- a/README.md
+++ b/README.md
@@ -18,5 +18,35 @@ USE AT YOUR OWN RISK.
# Dependencies
* libCURL
* OpenSSL
-
+* C Compiler (test with GCC)
+* Ruby (hopefully temporary)
+* Globaly resolvable domain name
+
+# Installation Instructions
+
+Ensure you have system packages for the dependencies installed, then run the following code in
+a terminal window:
+
+````
+git clone https://gitea.polaris-1.work/teknomunk/apogee.git
+cd apogee
+./build.sh
+./apogee
+````
+
+If everything worked correctly, you should see something similar to the following:
+
+````
+Starting Apogee ActivityPub server...
+Using port 21630 for web server
+Starting section 6 (tor)
+Starting section 0 (webserver)
+Starting section 1 (inbox)
+Starting section 2 (outbox)
+Starting section 5 (fetch)
+````
+
+The listening port is chosen at random each time the program is run until setup, then the configured
+port is used. Open a browser and navigate to http://${hostname-or-ip}:${port}/, then fill in the
+required information.
diff --git a/src/controller/admin.c b/src/controller/admin.c
new file mode 100644
index 0000000..e8a7f01
--- /dev/null
+++ b/src/controller/admin.c
@@ -0,0 +1,45 @@
+#include "controller/admin.h"
+
+// Model
+#include "src/model/server.h"
+
+// View
+
+// Controller
+#include "src/controller/api/client_apps.h"
+
+#include "http/server/request.h"
+
+const char* view_checkbox( bool value )
+{
+ return value ? "checked" : "";
+}
+
+bool route_admin_request( struct http_request* req )
+{
+ // TODO: authenticate
+ if( !check_authentication_header(req) ) {
+ printf( "User-Agent: %s\n", http_request_get_header(req,"user-agent") );
+
+ http_request_send_headers( req, 401, "text/plain", true );
+ FILE* f = http_request_get_response_body( req );
+ fprintf( f, "Not authorized to use this endpoint.\n" );
+ return true;
+ }
+
+ if( http_request_route_term( req, "/server-setup" ) ) {
+ return handle_admin_server_setup(req);
+ }
+ return false;
+}
+
+// Route: /admin/server-setup
+// Special: / (when server hasn't been configured)
+bool handle_admin_server_setup( struct http_request* req )
+{
+ http_request_send_headers( req, 200, "text/html", true );
+ FILE* f = http_request_get_response_body( req );
+ #include "view/admin/server-setup.html.inc"
+ return true;
+}
+
diff --git a/src/controller/admin.h b/src/controller/admin.h
new file mode 100644
index 0000000..3e46bc5
--- /dev/null
+++ b/src/controller/admin.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include
+
+struct http_request;
+
+bool route_admin_request( struct http_request* req );
+bool handle_admin_server_setup( struct http_request* req );
+
diff --git a/src/controller/main.c b/src/controller/main.c
index 316960c..a39a8cb 100644
--- a/src/controller/main.c
+++ b/src/controller/main.c
@@ -17,6 +17,7 @@
#include "controller/inbox.h"
#include "controller/activity_pub.h"
#include "controller/api/emoji.h"
+#include "controller/admin.h"
// Standard Library
#include
@@ -196,6 +197,13 @@ bool route_request( struct http_request* req )
}
} else if( http_request_route( req, "/nodeinfo" ) ) {
return route_nodeinfo(req);
+ } else if( http_request_route_term( req, "/instance/soapbox.json" ) ) {
+ http_request_send_headers( req, 200, "application/json", true );
+ FILE* f = http_request_get_response_body( req );
+
+ #include "view/soapbox.json.inc"
+
+ return true;
} else {
return route_asset(req);
}
@@ -207,8 +215,12 @@ bool route_request( struct http_request* req )
return route_mastodon_api( req );
} else if( http_request_route( req, "/api/pleroma" ) ) {
return route_pleroma_api2( req );
+ } else if( http_request_route( req, "/admin" ) ) {
+ return route_admin_request( req );
} else if( inner( req ) ) {
return true;
+ } else if( http_request_route_term( req, "/" ) && !g_server->configured ) {
+ return handle_admin_server_setup( req );
} else {
return send_asset( req, "assets/soapbox/index.html" );
}
diff --git a/src/main.c b/src/main.c
index ecde9cc..0d00692 100644
--- a/src/main.c
+++ b/src/main.c
@@ -38,6 +38,7 @@ int main( int argc, char* argv[], char* envp[] )
int code = 0;
if( g_server->section == -1 ) {
printf( "Starting Apogee ActivityPub server...\n" );
+ printf( "Using port %d for web server\n", g_server->http_settings.bind_port );
process_start_section(6);
// Make sure we have a hidden service hostname
diff --git a/src/model/server.c b/src/model/server.c
index 0e966ff..7c8b274 100644
--- a/src/model/server.c
+++ b/src/model/server.c
@@ -29,6 +29,8 @@ static struct json_object_field app_args_layout[] = {
JSON_FIELD_INTEGER( outbox_discard_limit, false ),
JSON_FIELD_BOOL( develop, false ),
+ JSON_FIELD_BOOL( enabled, false ),
+ JSON_FIELD_BOOL( configured, false ),
JSON_FIELD_END,
};
#undef OBJ_TYPE
@@ -87,6 +89,12 @@ struct app_args* app_args_new( int argc, char** argv )
// Sections by number
if( sscanf(arg,"--section=%d",&args->section) ) { goto next_arg; }
+ // Override server port
+ if( sscanf(arg,"--listen=%d",&args->http_settings.bind_port) ) {
+ printf( "Listening on port %d\n", args->http_settings.bind_port );
+ goto next_arg;
+ }
+
// Sections by name
if( ( argv[i][0] == '-' ) && ( argv[i][1] == '-' ) ) {
for( int i = 0; i <= process_get_max_section(); ++i ) {
@@ -122,4 +130,9 @@ void app_args_release( struct app_args* args )
free(args->user_agent);
free(args);
}
+void app_args_save()
+{
+ // TODO: save server settings to disk
+ json_write_object_layout_to_file( "data/server.json", "\t", app_args_layout, g_server );
+}
diff --git a/src/model/server.h b/src/model/server.h
index e227d79..a62a56f 100644
--- a/src/model/server.h
+++ b/src/model/server.h
@@ -21,9 +21,12 @@ struct app_args
int outbox_discard_limit;
bool develop;
+ bool enabled;
+ bool configured;
};
struct app_args* app_args_new( int argc, char** argv );
void app_args_release( struct app_args* args );
+void app_args_save();
extern struct app_args* g_server;
diff --git a/src/process.c b/src/process.c
index e14f862..7b55540 100644
--- a/src/process.c
+++ b/src/process.c
@@ -130,7 +130,7 @@ static void redirect_io( int section )
const char* section_name_str = process_get_section_name(section);
if( !section_name_str ) { return; }
- mkdir( "data/logs", 0640 );
+ mkdir( "data/logs", 0750 );
char filename[512];
snprintf( filename,512, "data/logs/%s.log", section_name_str );
diff --git a/src/view/admin/server-setup.html.template b/src/view/admin/server-setup.html.template
new file mode 100644
index 0000000..6c138e7
--- /dev/null
+++ b/src/view/admin/server-setup.html.template
@@ -0,0 +1,76 @@
+%(/*
+ vim: filetype=html
+*/)
+
+Welcome to Apogee
+
+Apogee is a single-user Activity Pub federated server intended for self-hosting.
+
+
+Server Settings
+
+
+
diff --git a/src/view/soapbox.json.template b/src/view/soapbox.json.template
new file mode 100644
index 0000000..1c2fc90
--- /dev/null
+++ b/src/view/soapbox.json.template
@@ -0,0 +1,33 @@
+{
+ "logo":"/instance/images/logo.png",
+ "brandColor": "#0482d8",
+ "promoPanel":{
+ "items":[
+ {
+ "icon": "area-chart",
+ "text": "Site stats",
+ "url": "https://fediverse.network/%s{ g_server->domain }"
+ },
+ {
+ "icon": "admin",
+ "text": "Server Settings",
+ "url": "/admin/server-settings"
+ }
+ ]
+ },
+ "defaultSettings": {
+ "autoPlayGif": false,
+ "themeMode": "light"
+ },
+ "copyright": "♡2020. Copying is an act of love. Please copy and share.",
+ "navlinks": {
+ "homeFooter":[
+ { "title": "About", "url": "/about" },
+ { "title": "Terms of Service", "url": "/about/tos" },
+ { "title": "Privacy Policy", "url": "/about/privacy" },
+ { "title": "DMCA", "url": "/about/dmca" },
+ { "title": "Source Code", "url": "/about#opensource" }
+ ]
+ },
+ "allowedEmoji": ["🤔", "😂", "😭", "😡", "🥰", "👍🏼", "👎🏼", "🔥", "😩", "🍆"]
+}
From 9d99318265fdecf817074254ada07b926ab3109f Mon Sep 17 00:00:00 2001
From: teknomunk
Date: Thu, 30 Nov 2023 19:53:05 -0600
Subject: [PATCH 04/11] Implement server setup wizard
---
.gitignore | 1 +
README.md | 10 +-
src/controller/admin.c | 135 +++++++++++++++++++++-
src/controller/admin.h | 1 +
src/controller/main.c | 35 +++---
src/model/owner.c | 20 +++-
src/model/owner.h | 1 +
src/model/server.c | 113 +++++++++++++-----
src/model/server.h | 8 +-
src/view/admin/owner-setup.html.template | 34 ++++++
src/view/admin/server-setup.html.template | 11 +-
11 files changed, 312 insertions(+), 57 deletions(-)
create mode 100644 src/view/admin/owner-setup.html.template
diff --git a/.gitignore b/.gitignore
index 3887c73..6a71d16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ apogee
apogee.debug
/*.log
/*.json
+obj/
diff --git a/README.md b/README.md
index e5bfc8e..a79bac5 100644
--- a/README.md
+++ b/README.md
@@ -16,13 +16,17 @@ USE AT YOUR OWN RISK.
* ActivityPub federation - follow interesting people on the many instances of the fediverse.
# Dependencies
+## Runtime
* libCURL
* OpenSSL
+* Globaly resolvable domain name
+* Tor
+
+## Source Install/Development
* C Compiler (test with GCC)
* Ruby (hopefully temporary)
-* Globaly resolvable domain name
-# Installation Instructions
+# Source Installation Instructions
Ensure you have system packages for the dependencies installed, then run the following code in
a terminal window:
@@ -47,6 +51,6 @@ Starting section 5 (fetch)
````
The listening port is chosen at random each time the program is run until setup, then the configured
-port is used. Open a browser and navigate to http://${hostname-or-ip}:${port}/, then fill in the
+port is used. Open a browser and navigate to http://${hostnameOrIPAddress}:${port}/, then fill in the
required information.
diff --git a/src/controller/admin.c b/src/controller/admin.c
index e8a7f01..e562d63 100644
--- a/src/controller/admin.c
+++ b/src/controller/admin.c
@@ -2,14 +2,22 @@
// Model
#include "src/model/server.h"
+#include "src/model/owner.h"
+#include "src/model/account.h"
// View
// Controller
#include "src/controller/api/client_apps.h"
+// Submodules
+#include "form.h"
#include "http/server/request.h"
+// Platform Headers
+#include
+#include
+
const char* view_checkbox( bool value )
{
return value ? "checked" : "";
@@ -33,13 +41,132 @@ bool route_admin_request( struct http_request* req )
return false;
}
-// Route: /admin/server-setup
-// Special: / (when server hasn't been configured)
-bool handle_admin_server_setup( struct http_request* req )
+// Special: /, step=1
+bool handle_admin_initial_owner_setup( struct http_request* req )
{
+ if( http_request_route_method( req, "POST" ) ) {
+ // TODO: handle post
+ FILE* body = http_request_get_request_data(req);
+ struct form_parser* fp = form_pull_parser_new(body);
+ if( !fp ) { goto show_owner_setup; }
+
+ struct owner* o = owner_new();
+
+ // Create owner account
+ struct account* owner = account_new();
+ owner->id = owner_account_id;
+ account_save(owner);
+
+ // Create home timeline account
+ {
+ struct account* home = account_new();
+ home->id = home_timeline_id;
+ home->handle = strdup("%home-timeline");
+ home->server = strdup("localhost");
+ account_save(home);
+ account_free(home);
+ }
+
+ // Create public timeline account
+ {
+ struct account* public = account_new();
+ public->id = public_timeline_id;
+ public->handle = strdup("%public-timeline");
+ public->server = strdup("localhost");
+ account_save(public);
+ account_free(public);
+ }
+
+ bool success = false;
+ char* password = NULL;
+ char* confirm = NULL;
+
+ char* key = NULL;
+ while( (key=form_pull_parser_read_key(fp)) ) {
+ if( 0 == strcmp(key,"password") ) {
+ password = strdup(form_pull_parser_read_value(fp));
+ } else if( 0 == strcmp(key,"confirm") ) {
+ confirm = strdup(form_pull_parser_read_value(fp));
+ } else if( 0 == strcmp(key,"handle") ) {
+ owner->handle = strdup(form_pull_parser_read_value(fp));
+ account_save(owner);
+ }
+ }
+
+ if( owner->handle && *owner->handle && password && confirm && ( 0 == strcmp(password,confirm) ) ) {
+ owner_set_password( o, password );
+ success = true;
+ }
+ form_pull_parser_release(fp);
+
+ if( success ) {
+ owner_save(o);
+ }
+ owner_free(o);
+ account_free(owner);
+
+ if( success ) {
+ // TODO: generate crypto keys
+
+ // Advance wizard to next step
+ g_server->configured = true;
+ app_args_save();
+
+ http_request_begin_send_headers( req, 302, false );
+ http_request_send_header( req, "Location", "/?complete" );
+ http_request_end_send_headers( req, false );
+ return true;
+ }
+ }
+
+show_owner_setup:
http_request_send_headers( req, 200, "text/html", true );
FILE* f = http_request_get_response_body( req );
- #include "view/admin/server-setup.html.inc"
+ #include "view/admin/owner-setup.html.inc"
return true;
}
+// Route: /admin/server-setup
+// Special: /, step=0 (when server hasn't been configured)
+bool handle_admin_server_setup( struct http_request* req )
+{
+ if( http_request_route_method( req, "POST" ) ) {
+ // TODO: handle post
+ FILE* body = http_request_get_request_data(req);
+ struct form_parser* fp = form_pull_parser_new(body);
+ if( !fp ) { return false; }
+
+ app_args_load_from_form( g_server, fp );
+ form_pull_parser_release(fp);
+
+ // Advance wizard to next step
+ if( g_server->setup_wizard_step == 0 ) {
+ g_server->setup_wizard_step = 1;
+ }
+
+ app_args_save();
+ app_args_load();
+
+ // Redirect
+ http_request_begin_send_headers( req, 302, false );
+ http_request_send_header( req, "Location", "/?account" );
+ http_request_end_send_headers( req, false );
+ return true;
+ } else {
+ http_request_send_headers( req, 200, "text/html", true );
+ FILE* f = http_request_get_response_body( req );
+ #include "view/admin/server-setup.html.inc"
+ return true;
+ }
+}
+
+bool handle_admin_server_setup_wizard( struct http_request* req )
+{
+ switch(g_server->setup_wizard_step) {
+ case 0: return handle_admin_server_setup(req);
+ case 1: return handle_admin_initial_owner_setup(req);
+ }
+
+ return false;
+}
+
diff --git a/src/controller/admin.h b/src/controller/admin.h
index 3e46bc5..9f8ce11 100644
--- a/src/controller/admin.h
+++ b/src/controller/admin.h
@@ -6,4 +6,5 @@ struct http_request;
bool route_admin_request( struct http_request* req );
bool handle_admin_server_setup( struct http_request* req );
+bool handle_admin_server_setup_wizard( struct http_request* req );
diff --git a/src/controller/main.c b/src/controller/main.c
index a39a8cb..7394cf6 100644
--- a/src/controller/main.c
+++ b/src/controller/main.c
@@ -170,6 +170,9 @@ bool route_request( struct http_request* req )
char onion_location[512];
snprintf( onion_location,512, "http://%s%s", g_server->tor_hidden_service, http_request_get_full_path(req) );
http_request_add_header( req, "Onion-Location", onion_location );
+ #define SOURCE_URL "https://gitea.polaris-1.work/teknomunk/apogee"
+ #define LICENSE_URL "https://www.gnu.org/licenses/agpl-3.0.html"
+ http_request_add_header( req, "Link", "<" LICENSE_URL ">; rel=\"license\", <" SOURCE_URL ">; rel=\"source\"" );
bool inner( struct http_request* req ) {
if( http_request_route( req, "/oauth" ) ) {
@@ -211,20 +214,26 @@ bool route_request( struct http_request* req )
return false;
}
- if( http_request_route( req, "/api/v1/" ) ) {
- return route_mastodon_api( req );
- } else if( http_request_route( req, "/api/pleroma" ) ) {
- return route_pleroma_api2( req );
- } else if( http_request_route( req, "/admin" ) ) {
- return route_admin_request( req );
- } else if( inner( req ) ) {
- return true;
- } else if( http_request_route_term( req, "/" ) && !g_server->configured ) {
- return handle_admin_server_setup( req );
+ if( g_server->configured ) {
+ if( http_request_route( req, "/api/v1/" ) ) {
+ return route_mastodon_api( req );
+ } else if( http_request_route( req, "/api/pleroma" ) ) {
+ return route_pleroma_api2( req );
+ } else if( http_request_route( req, "/admin" ) ) {
+ return route_admin_request( req );
+ } else if( inner( req ) ) {
+ return true;
+ } else {
+ return send_asset( req, "assets/soapbox/index.html" );
+ }
+
+ return false;
} else {
- return send_asset( req, "assets/soapbox/index.html" );
- }
+ if( http_request_route_term( req, "/" ) || http_request_route( req, "/?") ) {
+ return handle_admin_server_setup_wizard( req );
+ }
- return false;
+ return false;
+ }
}
diff --git a/src/model/owner.c b/src/model/owner.c
index 2622cdd..0d576e2 100644
--- a/src/model/owner.c
+++ b/src/model/owner.c
@@ -3,6 +3,7 @@
#include "json/json.h"
#include "json/layout.h"
#include "sha256/sha256.h"
+#include "form.h"
#include
#include
@@ -32,10 +33,7 @@ struct owner* owner_new()
{
struct owner* o = allocate(sizeof(struct owner));
- if( !json_read_object_layout_from_file( "data/owner.json", owner_layout, o ) ) {
- owner_free(o);
- return NULL;
- }
+ json_read_object_layout_from_file( "data/owner.json", owner_layout, o );
return o;
}
@@ -46,15 +44,24 @@ void owner_free( struct owner* o )
free(o->password_hash);
free(o);
}
+void owner_save( struct owner* o )
+{
+ json_write_object_layout_to_file( "data/owner.json", "\t", owner_layout, o );
+}
bool owner_check_password( struct owner* o, const char* passwd )
{
char buffer[512];
snprintf( buffer, 512, "%s:%s", o->password_salt, passwd );
+ printf( "passwd='%s'\n", passwd );
+
char hash[65] = "";
sha256_easy_hash_hex( buffer, strlen(buffer), hash );
+ printf( "hash=%s\n", hash );
+ printf( "expt=%s\n", o->password_hash );
+
return 0 == strcmp(hash,o->password_hash);
}
@@ -64,6 +71,7 @@ void owner_set_password( struct owner* o, const char* passwd )
free(o->password_hash);
char* new_salt = o->password_salt = malloc(65);
+ memset(new_salt,0,65);
for( int i = 0; i < 64; ++i ) {
new_salt[i] = 'a' + (rand()%26);
}
@@ -73,6 +81,10 @@ void owner_set_password( struct owner* o, const char* passwd )
snprintf( buffer, 512, "%s:%s", new_salt, passwd );
char* new_hash = o->password_hash = malloc(65);
+ memset(new_hash,0,65);
sha256_easy_hash_hex( buffer, strlen(buffer), new_hash );
+
+ o->password_hash = new_hash;
+ o->password_salt = new_salt;
}
diff --git a/src/model/owner.h b/src/model/owner.h
index 1dfe20d..320a52b 100644
--- a/src/model/owner.h
+++ b/src/model/owner.h
@@ -15,4 +15,5 @@ void owner_free( struct owner* );
bool owner_check_password( struct owner* o, const char* passwd );
void owner_set_password( struct owner* o, const char* passwd );
+struct form_pull_parser;
diff --git a/src/model/server.c b/src/model/server.c
index 7c8b274..40fb316 100644
--- a/src/model/server.c
+++ b/src/model/server.c
@@ -1,17 +1,20 @@
#include "server.h"
-#include "json/layout.h"
-
#include "controller/cli.h"
#include "process.h"
+// Submodules
+#include "json/layout.h"
+#include "form.h"
+
+// Platform libraries
#include
#include
#include
#define OBJ_TYPE struct app_args
static struct json_object_field app_args_layout[] = {
- JSON_FIELD_STRING( domain, true ),
+ JSON_FIELD_STRING( icann_domain, false ),
JSON_FIELD_STRING( user_agent, false ),
{
.key = "addr",
@@ -29,8 +32,10 @@ static struct json_object_field app_args_layout[] = {
JSON_FIELD_INTEGER( outbox_discard_limit, false ),
JSON_FIELD_BOOL( develop, false ),
- JSON_FIELD_BOOL( enabled, false ),
+ JSON_FIELD_BOOL( disabled, false ),
JSON_FIELD_BOOL( configured, false ),
+
+ JSON_FIELD_INTEGER( setup_wizard_step, false ),
JSON_FIELD_END,
};
#undef OBJ_TYPE
@@ -55,28 +60,40 @@ void app_args_refresh_tor_hidden_service()
}
fclose(f);
}
-struct app_args* app_args_new( int argc, char** argv )
+void app_args_load()
{
- struct app_args* args = (struct app_args*)malloc( sizeof(struct app_args) );
- memset(args,0,sizeof(*args));
- if( !args ) { return NULL; }
+ g_server->http_settings.bind_port = 9000 + ( rand() % 41000 );
+ g_server->http_settings.bind_address = strdup("0.0.0.0");
+ g_server->tor_socks_port = ( rand() % 41000 ) + 9000;
+ g_server->section = -1;
+ g_server->outbox_discard_limit = 5;
+ g_server->user_agent = strdup( "Apogee/0.1" );
+ g_server->disabled = true;
+
+ json_read_object_layout_from_file( "data/server.json", app_args_layout, g_server );
- g_server = args;
+ app_args_refresh_tor_hidden_service();
- args->http_settings.bind_port = 9000 + ( rand() % 41000 );
- args->http_settings.bind_address = strdup("0.0.0.0");
- args->tor_socks_port = ( rand() % 41000 ) + 9000;
- args->section = -1;
- args->outbox_discard_limit = 5;
- args->user_agent = strdup( "Apogee/0.1" );
+ if( g_server->icann_domain ) {
+ g_server->domain = strdup(g_server->icann_domain);
+ } else if( g_server->tor_hidden_service ) {
+ g_server->domain = strdup(g_server->tor_hidden_service);
+ }
+}
+struct app_args* app_args_new( int argc, char** argv )
+{
+ if( !g_server ) {
+ struct app_args* args = (struct app_args*)malloc( sizeof(struct app_args) );
+ memset(args,0,sizeof(*args));
+ if( !args ) { return NULL; }
- json_read_object_layout_from_file( "data/server.json", app_args_layout, args );
+ g_server = args;
+ }
- app_args_refresh_tor_hidden_service();
+ app_args_load();
if( ( argc > 1 ) && ( 0 != strncmp(argv[1],"--",2) ) ) {
handle_command( argv, argc );
- free(args);
return NULL;
}
@@ -84,14 +101,14 @@ struct app_args* app_args_new( int argc, char** argv )
const char* arg = argv[i];
// Debug flag
- if( 0 == strcmp(argv[i],"--debug") ) { args->debug = true; goto next_arg; }
+ if( 0 == strcmp(argv[i],"--debug") ) { g_server->debug = true; goto next_arg; }
// Sections by number
- if( sscanf(arg,"--section=%d",&args->section) ) { goto next_arg; }
+ if( sscanf(arg,"--section=%d",&g_server->section) ) { goto next_arg; }
// Override server port
- if( sscanf(arg,"--listen=%d",&args->http_settings.bind_port) ) {
- printf( "Listening on port %d\n", args->http_settings.bind_port );
+ if( sscanf(arg,"--listen=%d",&g_server->http_settings.bind_port) ) {
+ printf( "Listening on port %d\n", g_server->http_settings.bind_port );
goto next_arg;
}
@@ -99,7 +116,7 @@ struct app_args* app_args_new( int argc, char** argv )
if( ( argv[i][0] == '-' ) && ( argv[i][1] == '-' ) ) {
for( int i = 0; i <= process_get_max_section(); ++i ) {
if( strcmp( &argv[i][2], process_get_section_name(i) ) ) {
- args->section = i;
+ g_server->section = i;
goto next_arg;
}
}
@@ -107,32 +124,74 @@ struct app_args* app_args_new( int argc, char** argv )
// Development
if( 0 == strcmp(argv[i],"--devel") ) {
- args->section = 100;
+ g_server->section = 100;
goto next_arg;
}
// Unknown argument
printf( "Unknown argument: %s\n", argv[i] );
- free(args);
return NULL;
next_arg:;
}
- return args;
+ return g_server;
}
void app_args_release( struct app_args* args )
{
free(args->http_settings.bind_address);
free(args->domain);
+ free(args->icann_domain);
free(args->tor_hidden_service);
free(args->user_agent);
free(args);
}
void app_args_save()
{
- // TODO: save server settings to disk
+ // Save to disk
json_write_object_layout_to_file( "data/server.json", "\t", app_args_layout, g_server );
}
+void app_args_load_from_form( struct app_args* args, struct form_parser* fp )
+{
+ char* key = NULL;
+ while( (key=form_pull_parser_read_key(fp)) != NULL ) {
+ for( struct json_object_field* jof = app_args_layout; jof->key; jof += 1 ) {
+ intptr_t field_offset = (intptr_t)args + jof->offset;
+
+ if( strcmp(jof->key, key) == 0 ) {
+ if( jof->type == &json_field_string ) {
+ const char* value = form_pull_parser_read_value(fp);
+ char** field = (char**)field_offset;
+
+ printf( "%s: %s\n", jof->key, value );
+ free( *field );
+ *field = NULL;
+
+ if( value ) {
+ *field = strdup(value);
+ }
+ } else if( jof->type == &json_field_integer ) {
+ const char* value = form_pull_parser_read_value(fp);
+ printf( "%s: %s\n", jof->key, value );
+
+ int* field = (int*)field_offset;
+ sscanf( value, "%d", field );
+ } else if( jof->type == &json_field_bool ) {
+ const char* value = form_pull_parser_read_value(fp);
+ printf( "%s: %s\n", jof->key, value );
+
+ bool* field = (bool*)field_offset;
+ if( strcmp(value,"on") == 0 ) {
+ *field = true;
+ } else {
+ *field = false;
+ }
+ } else {
+ printf( "Unknown type for field %s\n", jof->key );
+ }
+ }
+ }
+ }
+}
diff --git a/src/model/server.h b/src/model/server.h
index a62a56f..f3d26f6 100644
--- a/src/model/server.h
+++ b/src/model/server.h
@@ -11,6 +11,7 @@ struct app_args
struct http_server_args http_settings;
//char* addr;
char* domain;
+ char* icann_domain;
char* tor_hidden_service;
char* user_agent;
bool debug;
@@ -21,12 +22,17 @@ struct app_args
int outbox_discard_limit;
bool develop;
- bool enabled;
+ bool disabled;
bool configured;
+ int setup_wizard_step;
};
struct app_args* app_args_new( int argc, char** argv );
+void app_args_load();
void app_args_release( struct app_args* args );
void app_args_save();
+
+struct form_parser;
+void app_args_load_from_form( struct app_args* args, struct form_parser* fp );
extern struct app_args* g_server;
diff --git a/src/view/admin/owner-setup.html.template b/src/view/admin/owner-setup.html.template
new file mode 100644
index 0000000..9192198
--- /dev/null
+++ b/src/view/admin/owner-setup.html.template
@@ -0,0 +1,34 @@
+%(/*
+ vim: filetype=html
+*/)
+
+Welcome to Apogee
+
+Apogee is a single-user Activity Pub federated server intended for self-hosting.
+
+
+Account Settings
+
+
+
diff --git a/src/view/admin/server-setup.html.template b/src/view/admin/server-setup.html.template
index 6c138e7..44d5825 100644
--- a/src/view/admin/server-setup.html.template
+++ b/src/view/admin/server-setup.html.template
@@ -8,13 +8,14 @@ Apogee is a single-user Activity Pub federated server intended for self-hosting.
Server Settings
-