#include "html.h" #include "mytime.h" #include #include #include #include static void emit_stylesheet(FILE *f) { fprintf(f, "\n" ); } static void mask_secret(const char *secret, char *out, size_t out_len) { size_t len = strlen(secret); if (len >= 8 && out_len >= 13) { snprintf(out, out_len, "%.4s....%.4s", secret, secret + len - 4); } else if (len > 0) { snprintf(out, out_len, "****"); } else { out[0] = '\0'; } } static const char *status_to_badge_class(enum BackupStatus status) { switch (status) { case STATUS_RUNNING: return "run"; case STATUS_INTERRUPTED: return "fail"; case STATUS_FINISHED: return "ok"; } abort(); } static const char *status_to_display(enum BackupStatus status) { switch (status) { case STATUS_RUNNING: return "Running"; case STATUS_INTERRUPTED: return "Failed"; case STATUS_FINISHED: return "Done"; } abort(); } char *html_render_clients_page(const struct Request *request, const struct ListClientResult *result, size_t *out_len) { // Count stale clients int stale_count = 0; for (int i = 0; i < result->clients_len; i++) { if (result->clients[i].is_stale) stale_count++; } char *buf = NULL; size_t buf_len = 0; FILE *f = open_memstream(&buf, &buf_len); if (!f) { return NULL; } fprintf(f, "\n" "\n" "\n" " \n" " \n" " BorgFlag - Clients\n"); emit_stylesheet(f); fprintf(f, "\n" "\n" "
\n" " BorgFlag\n" "
\n" "
\n" "
\n" "

\n" " Clients\n" " %d registered", result->clients_len); if (stale_count > 0) { fprintf(f, "%d stale", stale_count); } fprintf(f, "\n" "

\n" "
\n" " New Client\n" "
\n" "
\n" " \n" " \n" "
\n" " \n" "
\n" "
\n" "
\n" "
\n" " Name\n" " Secret\n" " Last Backup\n" " Status\n" " \n" "
\n" ); for (int i = 0; i < result->clients_len; i++) { char masked_secret[16]; mask_secret(result->clients[i].secret, masked_secret, sizeof(masked_secret)); fprintf(f, " \n" " %s\n" " %s\n" " %s\n" " %s\n" " View →\n" " \n", result->clients[i].id, result->clients[i].is_stale ? "stale" : "", result->clients[i].name, masked_secret, result->clients[i].last_backup.tv_sec > 0.0 ? format_time(result->clients[i].last_backup).buf : "", status_to_badge_class(result->clients[i].status), status_to_display(result->clients[i].status) ); } fprintf(f, "
\n" "
\n" "\n" "\n"); fclose(f); *out_len = buf_len; return buf; } char *html_render_client_detail_page(const struct Request *request, const struct GetClientOnlyResult *client_result, const struct GetBackupsResult *backups_result, size_t *out_len) { // Determine status and staleness int is_stale = 0; enum BackupStatus current_status = 0; if (backups_result->backups_len > 0) { current_status = backups_result->backups[0].status; } char *buf = NULL; size_t buf_len = 0; FILE *f = open_memstream(&buf, &buf_len); if (!f) { return NULL; } fprintf(f, "\n" "\n" "\n" " \n" " \n" " BorgFlag - %s\n", client_result->name); emit_stylesheet(f); fprintf(f, "\n" "\n" "
\n" " BorgFlag\n" "
\n" "
\n" "
\n" " ← Back to clients\n" " \n" "

%s

\n" "

%d backups %s

\n", is_stale ? " class=\"stale\"" : "", client_result->name, backups_result->backups_len, status_to_badge_class(current_status), status_to_display(current_status) ); if (is_stale) { fprintf(f, "

Warning: Last backup was more than 24 hours ago. Expected every 24 hours.

\n" ); } fprintf(f, " Secret: %s\n" " \n" "
\n" " How To\n" "
\n" "
curl -s -X POST %s://%s/api/report \\\n"
		"    -H \"Content-Type: application/json\" \\\n"
		"    -d \'{\"client\": \"%s\", \"secret\": \"%s\", \"event\": \"begin_backup\"}\'"
		        "
\n" "
\n" "
\n", client_result->secret, request->proto != NULL ? request->proto : "http", request->host != NULL ? request->host : "localhost", client_result->name, client_result->secret ); fprintf(f, "

Recent Backups

\n"); if(backups_result->backups_len == 0) { // @CLEANUP: Remove the inline style here fprintf(f, "

No backups recorded.

\n"); } else { fprintf(f, "
\n"); for (int i = 0; i < backups_result->backups_len; i++) { bool duration_valid = false; if (backups_result->backups[i].status == STATUS_INTERRUPTED) { duration_valid = true; } else { duration_valid = true; } struct timespec duration = time_sub(&backups_result->backups[i].completed, &backups_result->backups[i].started); fprintf(f, "
%s
\n", format_time(backups_result->backups[i].started).buf, duration_valid ? format_duration(duration).buf : "", status_to_badge_class(backups_result->backups[i].status), status_to_display(backups_result->backups[i].status) ); } fprintf(f, "
\n"); } fprintf(f, "
\n" "\n" "\n"); fclose(f); *out_len = buf_len; return buf; } char *html_render_login_page(const char *error_msg, size_t *out_len) { char *buf = NULL; size_t buf_len = 0; FILE *f = open_memstream(&buf, &buf_len); if (!f) return NULL; fprintf(f, "\n" "\n" "\n" " \n" " \n" " BorgFlag - Login\n"); emit_stylesheet(f); fprintf(f, "\n" "\n" "
\n" "
\n" "

BorgFlag

\n"); if (error_msg) { fprintf(f, "
%s
\n", error_msg); } fprintf(f, "
\n" " \n" " \n" "
\n" "
\n" " \n" " \n" "
\n" " \n" "
\n" "
\n" "\n" "\n"); fclose(f); *out_len = buf_len; return buf; }