Mercurial
view seobeo/snapshot_creator.c @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | 7eb79fd91c7e |
| children |
line wrap: on
line source
#include "seobeo/snapshot_creator.h" #include "seobeo/seobeo_internal.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> static void path_to_filename(const char *path, char *filename, size_t max_len) { if (strcmp(path, "/") == 0) { snprintf(filename, max_len, "root.snapshot"); return; } const char *p = path; if (*p == '/') p++; char *out = filename; size_t remaining = max_len - 1; while (*p && remaining > 0) { if (*p == '/') { *out++ = '_'; remaining--; } else { *out++ = *p; remaining--; } p++; } snprintf(out, remaining, ".snapshot"); } // Helper: Create directory recursively static int create_directory(const char *path) { char tmp[1024]; char *p = NULL; size_t len; snprintf(tmp, sizeof(tmp), "%s", path); len = strlen(tmp); if (tmp[len - 1] == '/') { tmp[len - 1] = 0; } for (p = tmp + 1; *p; p++) { if (*p == '/') { *p = 0; mkdir(tmp, 0755); *p = '/'; } } return mkdir(tmp, 0755); } // Helper: Build full HTTP response from Seobeo_Client_Response static char* build_full_response(Seobeo_Client_Response *p_resp, size_t *len_out) { if (!p_resp) return NULL; // Calculate total response size size_t status_line_len = 100; // "HTTP/1.1 200 OK\r\n" size_t headers_len = 0; size_t body_len = p_resp->body_length; // Estimate header size if (p_resp->headers) { size_t header_count = Dowa_Array_Length(p_resp->headers); for (size_t i = 0; i < header_count; i++) { headers_len += strlen(p_resp->headers[i].key) + strlen(p_resp->headers[i].value) + 4; // ": " + "\r\n" } } size_t total_size = status_line_len + headers_len + 2 + body_len + 1; // +2 for "\r\n", +1 for null terminator char *response = malloc(total_size); if (!response) return NULL; // Build status line int offset = snprintf(response, total_size, "HTTP/1.1 %d %s\r\n", p_resp->status_code, p_resp->status_text ? p_resp->status_text : "OK"); // Add headers if (p_resp->headers) { size_t header_count = Dowa_Array_Length(p_resp->headers); for (size_t i = 0; i < header_count; i++) { offset += snprintf(response + offset, total_size - offset, "%s: %s\r\n", p_resp->headers[i].key, p_resp->headers[i].value); } } // End of headers offset += snprintf(response + offset, total_size - offset, "\r\n"); // Add body if (p_resp->body && body_len > 0) { memcpy(response + offset, p_resp->body, body_len); offset += body_len; } response[offset] = '\0'; *len_out = offset; return response; } // Helper: Write snapshot to file static int write_snapshot(const char *filepath, const char *data, size_t size) { // Create directory if needed char dir_copy[1024]; snprintf(dir_copy, sizeof(dir_copy), "%s", filepath); char *last_slash = strrchr(dir_copy, '/'); if (last_slash) { *last_slash = '\0'; create_directory(dir_copy); } FILE *f = fopen(filepath, "wb"); if (!f) { perror("fopen"); return -1; } size_t written = fwrite(data, 1, size, f); fclose(f); return (written == size) ? 0 : -1; } int Seobeo_Snapshot_Create(const SnapshotConfig *config) { if (!config || !config->path || !config->snapshot_dir || !config->host || !config->port) { fprintf(stderr, "Invalid snapshot config\n"); return -1; } printf("Creating snapshot: %s (expecting %d)\n", config->path, config->expected_status); // Build URL char url[2048]; snprintf(url, sizeof(url), "http://%s:%s%s", config->host, config->port, config->path); // Create HTTP request Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(url); if (!p_req) { fprintf(stderr, " ✗ Failed to create request for %s\n", url); return -1; } // Execute request Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req); if (!p_resp) { fprintf(stderr, " ✗ Failed to get response\n"); Seobeo_Client_Request_Destroy(p_req); return -1; } // Check status code if (p_resp->status_code != config->expected_status) { fprintf(stderr, " ✗ Status mismatch: expected %d, got %d\n", config->expected_status, p_resp->status_code); // fprintf(stderr, "%s", p_resp->body); Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); return -1; } printf(" ✓ Status: %d\n", p_resp->status_code); // Generate snapshot filename char filename[256]; path_to_filename(config->path, filename, sizeof(filename)); char filepath[1024]; snprintf(filepath, sizeof(filepath), "%s/%s", config->snapshot_dir, filename); // Write snapshot if (write_snapshot(filepath, p_resp->body, p_resp->body_length) == 0) { printf(" ✓ Snapshot saved: %s (%zu bytes)\n", filepath, p_resp->body_length); return 0; } else { fprintf(stderr, " ✗ Failed to write snapshot: %s\n", filepath); return -1; } Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); } int Seobeo_Snapshots_Create_Batch(const SnapshotConfig configs[], int count) { int failed = 0; int passed = 0; for (int i = 0; i < count; i++) { if (Seobeo_Snapshot_Create(&configs[i]) == 0) passed++; else failed++; printf("\n"); } printf("=== Summary ===\n"); printf("Created: %d\n", passed); printf("Failed: %d\n", failed); return (failed == 0) ? 0 : -1; }