2026-06-09 00:31:08 +02:00
|
|
|
#include "http.h"
|
|
|
|
|
#include "config.h" /* g_cfg */
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
char *g_header = NULL; int g_header_len = 0;
|
|
|
|
|
char *g_footer = NULL; int g_footer_len = 0;
|
|
|
|
|
char *g_css = NULL; int g_css_len = 0;
|
|
|
|
|
|
|
|
|
|
StaticJS g_js[] = {
|
2026-06-09 01:04:50 +02:00
|
|
|
{ "/iptv.js", "js/iptv.js", NULL, 0 },
|
|
|
|
|
{ "/downloads.js", "js/downloads.js", NULL, 0 },
|
|
|
|
|
{ "/series_show.js", "js/series_show.js", NULL, 0 },
|
2026-06-09 00:31:08 +02:00
|
|
|
{ NULL, NULL, NULL, 0 }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int load_file(const char *path, char **out, int *out_len) {
|
|
|
|
|
FILE *f = fopen(path, "rb");
|
|
|
|
|
if (!f) { fprintf(stderr, "iptv-dl: cannot open %s: %s\n", path, strerror(errno)); return 0; }
|
|
|
|
|
fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET);
|
|
|
|
|
*out = malloc(sz + 1);
|
|
|
|
|
if (!*out) { fclose(f); return 0; }
|
|
|
|
|
*out_len = (int)fread(*out, 1, sz, f);
|
|
|
|
|
(*out)[*out_len] = '\0';
|
|
|
|
|
fclose(f);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int http_load_templates(void) {
|
|
|
|
|
char path[512];
|
2026-06-09 01:04:50 +02:00
|
|
|
snprintf(path, sizeof(path), "%s/html/header.html", g_cfg.template_dir);
|
2026-06-09 00:31:08 +02:00
|
|
|
if (!load_file(path, &g_header, &g_header_len)) return 0;
|
2026-06-09 01:04:50 +02:00
|
|
|
snprintf(path, sizeof(path), "%s/html/footer.html", g_cfg.template_dir);
|
2026-06-09 00:31:08 +02:00
|
|
|
if (!load_file(path, &g_footer, &g_footer_len)) return 0;
|
2026-06-09 01:04:50 +02:00
|
|
|
snprintf(path, sizeof(path), "%s/css/iptv.css", g_cfg.template_dir);
|
2026-06-09 00:31:08 +02:00
|
|
|
if (!load_file(path, &g_css, &g_css_len)) return 0;
|
|
|
|
|
for (int i = 0; g_js[i].url_path; i++) {
|
|
|
|
|
snprintf(path, sizeof(path), "%s/%s", g_cfg.template_dir, g_js[i].fs_name);
|
|
|
|
|
/* non-fatal if JS file missing */
|
|
|
|
|
load_file(path, &g_js[i].data, &g_js[i].len);
|
|
|
|
|
}
|
|
|
|
|
fprintf(stderr, "iptv-dl: templates loaded (header=%d footer=%d css=%d bytes)\n",
|
|
|
|
|
g_header_len, g_footer_len, g_css_len);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int parse_request(int fd, Req *req) {
|
|
|
|
|
char buf[131072]; int total = 0, n;
|
|
|
|
|
memset(req, 0, sizeof(*req));
|
|
|
|
|
while ((n = read(fd, buf+total, sizeof(buf)-1-total)) > 0) {
|
|
|
|
|
total += n; buf[total] = 0;
|
|
|
|
|
if (strstr(buf, "\r\n\r\n")) break;
|
|
|
|
|
}
|
|
|
|
|
if (total <= 0) return -1;
|
|
|
|
|
buf[total] = 0;
|
|
|
|
|
char *sp1 = strchr(buf, ' '); if (!sp1) return -1;
|
|
|
|
|
size_t mlen = sp1 - buf; if (mlen >= 8) mlen = 7;
|
|
|
|
|
memcpy(req->method, buf, mlen); req->method[mlen] = 0;
|
|
|
|
|
char *sp2 = strchr(sp1+1, ' '); if (!sp2) return -1;
|
|
|
|
|
size_t plen = sp2 - sp1 - 1; if (plen >= 256) plen = 255;
|
|
|
|
|
char full[256]; memcpy(full, sp1+1, plen); full[plen] = 0;
|
|
|
|
|
char *qm = strchr(full, '?');
|
|
|
|
|
if (qm) { *qm = 0; strncpy(req->query, qm+1, sizeof(req->query)-1); }
|
|
|
|
|
strncpy(req->path, full, sizeof(req->path)-1);
|
|
|
|
|
char *body_start = strstr(buf, "\r\n\r\n");
|
|
|
|
|
if (body_start) {
|
|
|
|
|
body_start += 4;
|
|
|
|
|
int blen = total - (int)(body_start - buf);
|
|
|
|
|
if (blen > 0 && blen < (int)sizeof(req->body)) { memcpy(req->body, body_start, blen); req->body_len = blen; }
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void send_page(int fd, const char *title, Buf *body, const char *script_src) {
|
|
|
|
|
Buf page; buf_init(&page);
|
|
|
|
|
buf_str(&page, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\n\r\n");
|
2026-06-09 01:46:53 +02:00
|
|
|
/* inject <base href> so relative assets resolve correctly at any sub-path */
|
2026-06-09 00:31:08 +02:00
|
|
|
const char *ph = strstr(g_header, "{{TITLE}}");
|
2026-06-09 01:46:53 +02:00
|
|
|
const char *head_end = strstr(g_header, "</head>");
|
|
|
|
|
if (ph && head_end && head_end > ph) {
|
|
|
|
|
/* write up to </head>, inject base tag, then rest of header */
|
|
|
|
|
buf_append(&page, g_header, ph - g_header);
|
|
|
|
|
buf_str(&page, title);
|
|
|
|
|
const char *after_title = ph + 9;
|
|
|
|
|
buf_append(&page, after_title, head_end - after_title);
|
|
|
|
|
if (g_cfg.base_path[0])
|
|
|
|
|
buf_fmt(&page, "<base href=\"%s/\">", g_cfg.base_path);
|
|
|
|
|
buf_str(&page, head_end);
|
|
|
|
|
} else if (ph) {
|
2026-06-09 00:31:08 +02:00
|
|
|
buf_append(&page, g_header, ph - g_header);
|
|
|
|
|
buf_str(&page, title);
|
|
|
|
|
buf_str(&page, ph + 9);
|
|
|
|
|
} else {
|
|
|
|
|
buf_str(&page, g_header);
|
|
|
|
|
}
|
|
|
|
|
buf_append(&page, body->data, body->len);
|
|
|
|
|
if (script_src && script_src[0])
|
|
|
|
|
buf_fmt(&page, "<script src=\"%s\"></script>", script_src);
|
|
|
|
|
buf_str(&page, g_footer);
|
|
|
|
|
write(fd, page.data, page.len);
|
|
|
|
|
buf_free(&page);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void send_css(int fd) {
|
|
|
|
|
char hdr[256];
|
|
|
|
|
int hl = snprintf(hdr, sizeof(hdr),
|
|
|
|
|
"HTTP/1.1 200 OK\r\nContent-Type: text/css\r\n"
|
|
|
|
|
"Content-Length: %d\r\nConnection: close\r\n\r\n", g_css_len);
|
|
|
|
|
write(fd, hdr, hl);
|
|
|
|
|
write(fd, g_css, g_css_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void send_static_js(int fd, const char *path) {
|
|
|
|
|
for (int i = 0; g_js[i].url_path; i++) {
|
|
|
|
|
if (strcmp(g_js[i].url_path, path) == 0) {
|
|
|
|
|
if (!g_js[i].data) { send_404(fd); return; }
|
|
|
|
|
char hdr[256];
|
|
|
|
|
int hl = snprintf(hdr, sizeof(hdr),
|
|
|
|
|
"HTTP/1.1 200 OK\r\nContent-Type: application/javascript\r\n"
|
|
|
|
|
"Content-Length: %d\r\nConnection: close\r\n\r\n", g_js[i].len);
|
|
|
|
|
write(fd, hdr, hl);
|
|
|
|
|
write(fd, g_js[i].data, g_js[i].len);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
send_404(fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void send_json(int fd, const char *json) {
|
|
|
|
|
char hdr[256];
|
|
|
|
|
snprintf(hdr, sizeof(hdr),
|
|
|
|
|
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n"
|
|
|
|
|
"Access-Control-Allow-Origin: *\r\n"
|
|
|
|
|
"Content-Length: %zu\r\nConnection: close\r\n\r\n", strlen(json));
|
|
|
|
|
write(fd, hdr, strlen(hdr));
|
|
|
|
|
write(fd, json, strlen(json));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void send_json_buf(int fd, Buf *b) {
|
|
|
|
|
char hdr[256];
|
|
|
|
|
snprintf(hdr, sizeof(hdr),
|
|
|
|
|
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n"
|
|
|
|
|
"Access-Control-Allow-Origin: *\r\n"
|
|
|
|
|
"Content-Length: %zu\r\nConnection: close\r\n\r\n", b->len);
|
|
|
|
|
write(fd, hdr, strlen(hdr));
|
|
|
|
|
write(fd, b->data, b->len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void send_404(int fd) {
|
|
|
|
|
const char *r = "HTTP/1.1 404 Not Found\r\nContent-Length: 9\r\nConnection: close\r\n\r\nNot Found";
|
|
|
|
|
write(fd, r, strlen(r));
|
|
|
|
|
}
|