mirror of
https://github.com/seregonwar/zftpd.git
synced 2026-10-22 03:32:24 -05:00
Adds several FTP protocol improvements and a new HTTP stats API. FTP: - Added EPSV, OPTS, SITE and CLNT command handlers and registered them in the command table. - Implemented MLSD (machine-readable directory list) and redirected MLST to STAT. - Changed USER/PASS to accept immediate login for better compatibility with Android clients. - Relaxed PORT IP validation and added debug logging for PORT targets. - Open data connections before sending 150 and add short pacing delay to avoid merged 150+226 replies (LIST/NLST/MLSD). - Implemented EPSV passive socket setup and OPTS handling (UTF8/MLST) and SITE CHMOD no-op. - Implemented atomic STOR uploads using a temp file and rename on success; cleanup on failure. - Improved active-mode connect behavior with a short send timeout and logging; simplified data close path to match upstream behavior. - Accept bare LF line endings from some clients and make command lookup case-insensitive. - Enabled TCP_NODELAY on control socket and turned on FTP command logging; bumped RELEASE_VERSION to 1.2.3. HTTP / Server infrastructure: - Extended http_response to support in-memory streaming, segment splicing, and added APIs (set_body_ref, set_body_splice). - Modified static serving to use splicing for injected HTML and fall back to body ref for binary assets (PNG); added image/png content-type for .png. - Added a new /api/stats endpoint providing JSON with disk usage, items in directory, boot/uptime, and CPU temperature where available, plus numerous helper functions for gathering and formatting stats. Build / repo: - Updated .gitignore, added web/zftpd-logo.png and bumped embedded web resources (large generated resource changes not detailed here). Overall these changes improve client compatibility (especially mobile/Android and GoldHEN clients), make uploads atomic, add diagnostics via the HTTP API, and enhance HTTP response streaming.
118 lines
4.6 KiB
C
118 lines
4.6 KiB
C
/*
|
|
MIT License
|
|
|
|
Copyright (c) 2026 Seregon
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
/**
|
|
* @file http_response.h
|
|
* @brief HTTP response builder
|
|
*
|
|
* STATUS CODES:
|
|
* 2xx Success (200, 201, 204)
|
|
* 3xx Redirection (301, 304)
|
|
* 4xx Client error (400, 403, 404, 405)
|
|
* 5xx Server error (500)
|
|
*/
|
|
|
|
#ifndef HTTP_RESPONSE_H
|
|
#define HTTP_RESPONSE_H
|
|
|
|
#include "http_config.h"
|
|
#include <stddef.h>
|
|
#include <sys/types.h>
|
|
|
|
/*===========================================================================*
|
|
* HTTP STATUS CODES
|
|
*===========================================================================*/
|
|
|
|
typedef enum {
|
|
/* 2xx ── Success */
|
|
HTTP_STATUS_200_OK = 200,
|
|
HTTP_STATUS_201_CREATED = 201,
|
|
HTTP_STATUS_204_NO_CONTENT = 204,
|
|
|
|
/* 3xx ── Redirection */
|
|
HTTP_STATUS_301_MOVED = 301,
|
|
HTTP_STATUS_304_NOT_MODIFIED = 304,
|
|
|
|
/* 4xx ── Client Error */
|
|
HTTP_STATUS_400_BAD_REQUEST = 400,
|
|
HTTP_STATUS_403_FORBIDDEN = 403,
|
|
HTTP_STATUS_404_NOT_FOUND = 404,
|
|
HTTP_STATUS_405_METHOD_NOT_ALLOWED = 405,
|
|
|
|
/* 5xx ── Server Error */
|
|
HTTP_STATUS_500_INTERNAL_ERROR = 500,
|
|
} http_status_t;
|
|
|
|
/*===========================================================================*
|
|
* RESPONSE BUFFER
|
|
*===========================================================================*/
|
|
|
|
typedef struct {
|
|
char data[HTTP_RESPONSE_BUFFER_SIZE];
|
|
size_t used;
|
|
|
|
/* In-memory body streaming (for large embedded static assets) */
|
|
const void *mem_body; /**< Pointer to body bytes (NULL = not used) */
|
|
size_t mem_length; /**< Total bytes to send */
|
|
size_t mem_sent; /**< Bytes already sent */
|
|
|
|
/* In-memory segmented body streaming (for injected HTML) */
|
|
const void *mem_segs[3]; /**< Segment pointers */
|
|
size_t mem_lens[3]; /**< Segment lengths */
|
|
size_t mem_seg_count; /**< 0 = disabled, otherwise 1..3 */
|
|
size_t mem_seg_index; /**< Current segment index */
|
|
size_t mem_seg_sent; /**< Bytes sent in current segment */
|
|
char mem_inline[160]; /**< Inline storage for small injected data */
|
|
|
|
/* sendfile() support for zero-copy file downloads */
|
|
int sendfile_fd; /**< File fd (-1 = not used) */
|
|
off_t sendfile_offset; /**< Current offset in file */
|
|
size_t sendfile_count; /**< Remaining bytes to send */
|
|
|
|
/* Chunked directory streaming (for /api/list) */
|
|
void *stream_dir; /**< DIR* — NULL = not streaming */
|
|
char stream_path[1024]; /**< Base path for stat() calls */
|
|
} http_response_t;
|
|
|
|
/*===========================================================================*
|
|
* PUBLIC API
|
|
*===========================================================================*/
|
|
|
|
http_response_t *http_response_create(http_status_t status);
|
|
int http_response_add_header(http_response_t *resp, const char *name,
|
|
const char *value);
|
|
int http_response_set_body(http_response_t *resp, const void *body,
|
|
size_t length);
|
|
int http_response_set_body_ref(http_response_t *resp, const void *body,
|
|
size_t length);
|
|
int http_response_set_body_splice(http_response_t *resp, const void *prefix,
|
|
size_t prefix_len, const void *insert,
|
|
size_t insert_len, const void *suffix,
|
|
size_t suffix_len);
|
|
int http_response_append_raw(http_response_t *resp, const void *data,
|
|
size_t length);
|
|
int http_response_finalize(http_response_t *resp);
|
|
void http_response_destroy(http_response_t *resp);
|
|
|
|
#endif /* HTTP_RESPONSE_H */
|