mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 14:43:01 +02:00
chore: fixed server backend using linters
This commit is contained in:
parent
3379064a38
commit
da16e9d33d
@ -9,7 +9,10 @@ server_c: server_c.c
|
||||
|
||||
lint:
|
||||
@echo "Running clang-tidy..."
|
||||
@clang-tidy -checks=-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling server_c.c -- -std=gnu11 $(CFLAGS) $(LDFLAGS) || true
|
||||
@clang-tidy \
|
||||
-checks=clang-analyzer-*,clang-diagnostic-*,readability-function-size,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling \
|
||||
-config='{ "CheckOptions": [ { "key": "readability-function-size.LineThreshold", "value": "20" } ] }' \
|
||||
server_c.c -- -std=gnu11 $(CFLAGS) $(LDFLAGS) || true
|
||||
@echo "Running cppcheck..."
|
||||
@cppcheck --enable=all --inconclusive --std=c11 --language=c --quiet --inline-suppr --check-level=exhaustive --suppress=missingIncludeSystem server_c.c || true
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
[{"id":"199259f41b8933b8","title":"Whats heveier","body":"A kilogrem of stel or kilogrem of fethers","thumb":"/uploads/27d6402e78a09d65.jpg","createdAt":1757272818104},{"id":"19925965dead21e7","title":"UwU its my first article","body":":)))<div><img src=\"/uploads/27d640335a16f85f.jpg\"><br></div>","thumb":"/uploads/27d640310e163446.jpg","createdAt":1757272235498}]
|
||||
[{"id":"29176c917f1a66c3","title":"full featuresd article","author":"author","body":"<img loading=\"lazy\" decoding=\"async\" src=\"/uploads/29176c9e5c008daf.jpg\" alt=\"image\"><div><br></div><div>This is an important image of course :)</div><div><br></div><div>and nother:<br><img loading=\"lazy\" decoding=\"async\" src=\"/uploads/29176c9f62367245.jpg\" alt=\"image\"><br></div>","thumb":"/uploads/29176c9e7818ee30.jpg","createdAt":1757331025041},{"id":"199259f41b8933b8","title":"Whats heveier","body":"A kilogrem of stel or kilogrem of fethers","thumb":"/uploads/27d6402e78a09d65.jpg","createdAt":1757272818104},{"id":"19925965dead21e7","title":"UwU its my first article","body":":)))<div><img src=\"/uploads/27d640335a16f85f.jpg\"><br></div>","thumb":"/uploads/27d640310e163446.jpg","createdAt":1757272235498}]
|
||||
@ -44,36 +44,40 @@ static int write_file_all(const char* path, const char* data, size_t len){ FILE*
|
||||
// removed unused append_file_line (lint)
|
||||
|
||||
// ---- JSON helpers (minimal) ----
|
||||
static char* json_escape(const char* s){ size_t n=0; for(const char* p=s; *p; ++p){ if(*p=='"'||*p=='\\'||*p=='\n'||*p=='\r'||*p=='\t') n+=2; else n++; }
|
||||
char* out=malloc(n+1); if(!out) return NULL; char* w=out; for(const char* p=s; *p; ++p){ if(*p=='"'){ *w++='\\'; *w++='"'; } else if(*p=='\\'){ *w++='\\'; *w++='\\'; } else if(*p=='\n'){ *w++='\\'; *w++='n'; } else if(*p=='\r'){ *w++='\\'; *w++='r'; } else if(*p=='\t'){ *w++='\\'; *w++='t'; } else { *w++=*p; } } *w='\0'; return out; }
|
||||
static char* json_escape(const char* s){ size_t n=0; for(const char* p=s; *p; ++p){ if(*p=='"'||*p=='\\'||*p=='\n'||*p=='\r'||*p=='\t') n+=2; else n++; } char* out=malloc(n+1); if(!out) return NULL; char* w=out; for(const char* p=s; *p; ++p){ if(*p=='"'){ *w++='\\'; *w++='"'; } else if(*p=='\\'){ *w++='\\'; *w++='\\'; } else if(*p=='\n'){ *w++='\\'; *w++='n'; } else if(*p=='\r'){ *w++='\\'; *w++='r'; } else if(*p=='\t'){ *w++='\\'; *w++='t'; } else { *w++=*p; } } *w='\0'; return out; }
|
||||
|
||||
static const char* skip_ws_commas(const char* p){ while(*p==' '||*p=='\n'||*p=='\r'||*p=='\t'||*p==',') p++; return p; }
|
||||
|
||||
// Parse a JSON string starting at v (after the opening quote). Returns malloc'd string and sets *after_end to the char after closing quote.
|
||||
static char* parse_json_string_value(const char* v, const char** after_end){
|
||||
char* out = malloc(strlen(v)+1); if(!out){ *after_end = v; return NULL; }
|
||||
size_t w=0; bool esc=false; const char* p=v; for(; *p; ++p){ char c=*p; if(esc){ if(c=='"'||c=='\\'||c=='/'){ out[w++]=c; } else if(c=='n'){ out[w++]='\n'; } else if(c=='r'){ out[w++]='\r'; } else if(c=='t'){ out[w++]='\t'; } else { out[w++]=c; } esc=false; continue; } if(c=='\\'){ esc=true; continue; } if(c=='"'){ out[w]='\0'; *after_end = p+1; return out; } out[w++]=c; }
|
||||
out[w]='\0'; *after_end = p; return out; // unterminated; best effort
|
||||
}
|
||||
|
||||
static const char* ensure_quoted_key(const char* key, char** to_free, size_t* out_len){
|
||||
*to_free=NULL; size_t klen=strlen(key); if(klen==0){ *out_len=0; return key; }
|
||||
if(key[0]=='"'){ *out_len=klen; return key; }
|
||||
char* tmp=malloc(klen+3); if(!tmp){ *out_len=klen; return key; }
|
||||
tmp[0]='"'; memcpy(tmp+1,key,klen); tmp[klen+1]='"'; tmp[klen+2]='\0'; *to_free=tmp; *out_len=klen+2; return tmp;
|
||||
}
|
||||
|
||||
// Find string field value for key in compact JSON. Returns malloc'd string (unescaped). Empty string if not found.
|
||||
static char* json_get_string(const char* json, const char* key){
|
||||
// Build quoted key pattern if needed
|
||||
const char* qkey = key;
|
||||
char* tmp = NULL;
|
||||
size_t klen = strlen(key);
|
||||
if(klen==0) return strdup("");
|
||||
if(key[0] != '"'){
|
||||
tmp = malloc(klen + 3);
|
||||
if(!tmp) return strdup("");
|
||||
tmp[0] = '"'; memcpy(tmp+1, key, klen); tmp[klen+1] = '"'; tmp[klen+2] = '\0';
|
||||
qkey = tmp; klen += 2;
|
||||
}
|
||||
const char* p = json;
|
||||
while((p = strstr(p, qkey))){
|
||||
const char* colon = strchr(p + klen, ':'); if(!colon){ p += klen; continue; }
|
||||
const char* v = colon + 1; while(*v==' '||*v=='\t') v++;
|
||||
char* tmp=NULL; size_t klen=0; const char* qkey=ensure_quoted_key(key,&tmp,&klen); if(klen==0){ return strdup(""); }
|
||||
const char* p=json;
|
||||
while((p=strstr(p,qkey))){
|
||||
const char* colon=strchr(p+klen,':'); if(!colon){ p+=klen; continue; }
|
||||
const char* v=colon+1; while(*v==' '||*v=='\t') v++;
|
||||
if(*v!='"'){
|
||||
if(tmp) free(tmp);
|
||||
return strdup("");
|
||||
}
|
||||
v++; // inside string
|
||||
char* out = malloc(strlen(v)+1); if(!out){ if(tmp) free(tmp); return strdup(""); }
|
||||
size_t w=0; bool esc=false; for(const char* q=v; *q; ++q){ char c=*q; if(esc){ if(c=='"'||c=='\\'||c=='/'){ out[w++]=c; } else if(c=='n'){ out[w++]='\n'; } else if(c=='r'){ out[w++]='\r'; } else if(c=='t'){ out[w++]='\t'; } else { out[w++]=c; } esc=false; continue; } if(c=='\\'){ esc=true; continue; } if(c=='"'){ out[w]='\0'; if(tmp) free(tmp); return out; } out[w++]=c; }
|
||||
free(out);
|
||||
if(tmp) free(tmp);
|
||||
return strdup("");
|
||||
v++;
|
||||
const char* after=NULL; char* out=parse_json_string_value(v,&after);
|
||||
if(out){ if(tmp) free(tmp); return out; }
|
||||
if(tmp) free(tmp);
|
||||
return strdup("");
|
||||
}
|
||||
if(tmp) free(tmp);
|
||||
return strdup("");
|
||||
@ -83,51 +87,15 @@ static long long json_get_number(const char* json, const char* key){ const char*
|
||||
|
||||
// Parse a top-level JSON object and return the string value for a given key (unquoted key name). Caller frees.
|
||||
static char* json_get_top_string(const char* json, const char* key){
|
||||
size_t keylen = strlen(key);
|
||||
const char* p = json;
|
||||
// seek to first '{'
|
||||
while(*p && *p!='{') p++;
|
||||
if(*p!='{') return strdup("");
|
||||
p++;
|
||||
while(*p){
|
||||
while(*p==' '||*p=='\n'||*p=='\r'||*p=='\t'||*p==',') p++;
|
||||
if(*p=='}' || !*p) break;
|
||||
if(*p!='"'){ // invalid
|
||||
// skip token to next comma or end
|
||||
while(*p && *p!=',' && *p!='}') {
|
||||
p++;
|
||||
}
|
||||
if(*p==',') {
|
||||
p++;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
size_t keylen=strlen(key); const char* p=json; while(*p && *p!='{') p++; if(*p!='{') return strdup(""); p++;
|
||||
while(*p){ p=skip_ws_commas(p); if(*p=='}' || !*p) break; if(*p!='"'){ while(*p && *p!=',' && *p!='}') p++; continue; }
|
||||
// parse key string
|
||||
p++; const char* ks=p; size_t klen=0; bool esc=false; for(; *p; ++p){ char c=*p; if(esc){ esc=false; continue; } if(c=='\\'){ esc=true; continue; } if(c=='"'){ break; } klen++; }
|
||||
const char* key_start=ks; if(*p=='"') p++; while(*p==' '||*p=='\t') p++; if(*p!=':') { // malformed
|
||||
while(*p && *p!=',' && *p!='}') {
|
||||
p++;
|
||||
}
|
||||
if(*p==',') {
|
||||
p++;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
p++; while(*p==' '||*p=='\t') p++;
|
||||
int key_match = (klen==keylen) && (strncmp(key_start, key, keylen)==0);
|
||||
if(*p=='"'){
|
||||
p++; // read string value
|
||||
char* out = malloc(strlen(p)+1); if(!out) return strdup(""); size_t w=0; bool e=false; for(; *p; ++p){ char c=*p; if(e){ if(c=='"'||c=='\\'||c=='/'){ out[w++]=c; } else if(c=='n'){ out[w++]='\n'; } else if(c=='r'){ out[w++]='\r'; } else if(c=='t'){ out[w++]='\t'; } else { out[w++]=c; } e=false; continue; } if(c=='\\'){ e=true; continue; } if(c=='"'){ out[w]='\0'; p++; break; } out[w++]=c; }
|
||||
if(key_match){ return out; } else { free(out); }
|
||||
} else {
|
||||
// non-string value: skip until comma or end (simple)
|
||||
while(*p && *p!=',' && *p!='}') p++;
|
||||
}
|
||||
if(*p==','){ p++; }
|
||||
const char* key_start=ks; if(*p=='"') p++; while(*p==' '||*p=='\t') p++; if(*p!=':'){ while(*p && *p!=',' && *p!='}') p++; continue; }
|
||||
p++; while(*p==' '||*p=='\t') p++; int key_match=(klen==keylen) && (strncmp(key_start,key,keylen)==0);
|
||||
if(*p=='"'){ p++; const char* after=NULL; char* out=parse_json_string_value(p,&after); p=after; if(key_match){ return out?out:strdup(""); } else { free(out); } }
|
||||
else { while(*p && *p!=',' && *p!='}') p++; }
|
||||
if(*p==',') p++;
|
||||
}
|
||||
return strdup("");
|
||||
}
|
||||
@ -180,106 +148,29 @@ static const char* ext_from_mime(const char* mime){ if(!mime) return NULL; if(st
|
||||
|
||||
static char* save_bytes_with_ext(const unsigned char* bytes, size_t blen, const char* ext){ if(!bytes||!blen) return NULL; const char* updir="uploads"; ensure_dir(updir); char* name=gen_id(); if(!name) return NULL; const char* e = (ext&&*ext)? ext: "bin"; size_t need=strlen(updir)+1+strlen(name)+1+strlen(e)+1; char* path=malloc(need); if(!path){ free(name); return NULL; } snprintf(path,need,"%s/%s.%s", updir, name, e); free(name); if(write_file_all(path,(const char*)bytes,blen)!=0){ free(path); return NULL; } return path; }
|
||||
|
||||
static int ensure_capacity(char** out, size_t* cap, size_t need){ if(need<=*cap) return 0; size_t newcap = need + 64; void* tmp = realloc(*out, newcap); if(!tmp) return -1; *out=tmp; *cap=newcap; return 0; }
|
||||
|
||||
static void buf_copy(char* out, size_t* w, const char* src, size_t len){ memcpy(out+*w, src, len); *w += len; }
|
||||
|
||||
static int write_saved_url(char** out, size_t* cap, size_t* w, const char* saved){ const char* rel=saved; size_t need=*w + 1 + strlen(rel) + 1; if(ensure_capacity(out, cap, need)) return -1; (*out)[(*w)++]='/'; memcpy((*out)+*w, rel, strlen(rel)); *w += strlen(rel); return 0; }
|
||||
|
||||
static int process_data_url_segment(const char* url_start, size_t url_len, char** out, size_t* cap, size_t* w, int* did){ char* mime=NULL; unsigned char* bytes=NULL; size_t bl=0; char* urlbuf=strndup(url_start,url_len); if(!urlbuf) return -1; int rc=0; if(parse_data_url(urlbuf,&mime,&bytes,&bl)==0){ const char* ext=ext_from_mime(mime); char* saved=save_bytes_with_ext(bytes,bl,ext); if(saved){ if(write_saved_url(out,cap,w,saved)==0){ *did=1; } else { rc=-1; } free(saved); } } free(bytes); free(mime); free(urlbuf); return rc; }
|
||||
|
||||
static char* migrate_inline_images_in_body(const char* body, bool* changed){
|
||||
if(!body) return NULL;
|
||||
const char* p = body;
|
||||
size_t outcap = strlen(body) + 1;
|
||||
char* out = malloc(outcap);
|
||||
const char* p=body; size_t outcap=strlen(body)+1; char* out=malloc(outcap);
|
||||
if(!out) return NULL;
|
||||
size_t w = 0; int did = 0;
|
||||
while(*p){
|
||||
const char* m = strstr(p, "src=\"");
|
||||
const char* m2 = strstr(p, "src='");
|
||||
const char* hit = NULL; char quote='\0';
|
||||
if(m && (!m2 || m < m2)){ hit = m; quote='\"'; }
|
||||
else if(m2){ hit = m2; quote='\''; }
|
||||
if(!hit){
|
||||
size_t L = strlen(p);
|
||||
if(w + L + 1 > outcap){
|
||||
size_t newcap = w + L + 1;
|
||||
void* tmp = realloc(out, newcap);
|
||||
if(!tmp){ free(out); return NULL; }
|
||||
out = tmp;
|
||||
}
|
||||
memcpy(out + w, p, L);
|
||||
w += L;
|
||||
break;
|
||||
}
|
||||
// copy up to src=
|
||||
size_t prefix_len = (size_t)(hit - p);
|
||||
if(w + prefix_len + 6 > outcap){
|
||||
size_t newcap = w + prefix_len + 64;
|
||||
void* tmp = realloc(out, newcap);
|
||||
if(!tmp){ free(out); return NULL; }
|
||||
out = tmp; outcap = newcap;
|
||||
}
|
||||
memcpy(out + w, p, prefix_len);
|
||||
w += prefix_len;
|
||||
memcpy(out + w, "src=\"", 5);
|
||||
w += 5; // normalize to double quote
|
||||
const char* url_start = hit + 5;
|
||||
if(*url_start=='\'' || *url_start=='"') url_start++;
|
||||
const char endq = (quote=='\'') ? '\'' : '"';
|
||||
const char* url_end = strchr(url_start, endq);
|
||||
if(!url_end){ // malformed; copy rest
|
||||
size_t L = strlen(url_start);
|
||||
if(w + L + 1 > outcap){
|
||||
size_t newcap = w + L + 1;
|
||||
void* tmp = realloc(out, newcap);
|
||||
if(!tmp){ free(out); return NULL; }
|
||||
out = tmp;
|
||||
}
|
||||
memcpy(out + w, url_start, L);
|
||||
w += L;
|
||||
break;
|
||||
}
|
||||
size_t url_len = (size_t)(url_end - url_start);
|
||||
if(url_len>5 && strncmp(url_start, "data:", 5)==0){
|
||||
char* mime = NULL; unsigned char* bytes = NULL; size_t bl = 0; char* urlbuf = strndup(url_start, url_len);
|
||||
if(parse_data_url(urlbuf,&mime,&bytes,&bl)==0){
|
||||
const char* ext = ext_from_mime(mime);
|
||||
char* saved = save_bytes_with_ext(bytes, bl, ext);
|
||||
if(saved){
|
||||
const char* rel = saved;
|
||||
size_t need = 1 + strlen(rel);
|
||||
if(w + need + 1 > outcap){
|
||||
size_t newcap = w + need + 64;
|
||||
void* tmp = realloc(out, newcap);
|
||||
if(!tmp){ free(saved); free(bytes); free(mime); free(urlbuf); free(out); return NULL; }
|
||||
out = tmp; outcap = newcap;
|
||||
}
|
||||
out[w++] = '/';
|
||||
memcpy(out + w, rel, strlen(rel));
|
||||
w += strlen(rel);
|
||||
did = 1;
|
||||
free(saved);
|
||||
}
|
||||
}
|
||||
free(bytes); free(mime); free(urlbuf);
|
||||
} else {
|
||||
if(w + url_len + 1 > outcap){
|
||||
size_t newcap = w + url_len + 64;
|
||||
void* tmp = realloc(out, newcap);
|
||||
if(!tmp){ free(out); return NULL; }
|
||||
out = tmp; outcap = newcap;
|
||||
}
|
||||
memcpy(out + w, url_start, url_len);
|
||||
w += url_len;
|
||||
}
|
||||
// closing quote
|
||||
if(w + 1 > outcap){
|
||||
size_t newcap = w + 64;
|
||||
void* tmp = realloc(out, newcap);
|
||||
if(!tmp){ free(out); return NULL; }
|
||||
out = tmp; outcap = newcap;
|
||||
}
|
||||
out[w++] = '"';
|
||||
p = url_end + 1;
|
||||
}
|
||||
out[w] = '\0';
|
||||
if(changed) *changed = did;
|
||||
return out;
|
||||
}
|
||||
size_t w=0; int did=0;
|
||||
while(*p){ const char* m=strstr(p,"src=\""); const char* m2=strstr(p,"src='"); const char* hit=NULL; char quote='\0'; if(m && (!m2 || m<m2)){ hit=m; quote='\"'; } else if(m2){ hit=m2; quote='\''; }
|
||||
if(!hit){ size_t L=strlen(p); if(ensure_capacity(&out,&outcap,w+L+1)){ free(out); return NULL; } buf_copy(out,&w,p,L); break; }
|
||||
size_t prefix_len=(size_t)(hit-p); if(ensure_capacity(&out,&outcap,w+prefix_len+6)){ free(out); return NULL; } buf_copy(out,&w,p,prefix_len); memcpy(out+w,"src=\"",5); w+=5;
|
||||
const char* url_start=hit+5; if(*url_start=='\''||*url_start=='"') url_start++; const char endq=(quote=='\'')?'\'':'"'; const char* url_end=strchr(url_start,endq);
|
||||
if(!url_end){ size_t L=strlen(url_start); if(ensure_capacity(&out,&outcap,w+L+1)){ free(out); return NULL; } buf_copy(out,&w,url_start,L); break; }
|
||||
size_t url_len=(size_t)(url_end-url_start);
|
||||
if(url_len>5 && strncmp(url_start,"data:",5)==0){ if(process_data_url_segment(url_start,url_len,&out,&outcap,&w,&did)){ free(out); return NULL; } }
|
||||
else { if(ensure_capacity(&out,&outcap,w+url_len+1)){ free(out); return NULL; } buf_copy(out,&w,url_start,url_len); }
|
||||
if(ensure_capacity(&out,&outcap,w+1+1)){ free(out); return NULL; } out[w++]='"'; p=url_end+1; }
|
||||
out[w]='\0'; if(changed) *changed=did; return out; }
|
||||
|
||||
// ---- HTTP helpers ----
|
||||
// removed unused send_str (lint)
|
||||
@ -315,35 +206,28 @@ static char* find_article_by_id(const char* id){
|
||||
static int rewrite_articles_map(char** out_json_updated, const char* match_id, const char* patch_json, bool is_delete){
|
||||
char* file=data_file(); if(!file) return -1; size_t n=0; char* content=read_file_all(file,&n); if(!content){ free(file); return -1; }
|
||||
char* t=ltrim_dup(content); free(content); if(!t){ free(file); return -1; }
|
||||
if(t[0] != '['){ // nothing to do
|
||||
FILE* f=fopen(file,"wb"); if(!f){ free(file); free(t); return -1; }
|
||||
fputs("[]", f); fclose(f); free(file); free(t); return -1;
|
||||
}
|
||||
// collect objects
|
||||
if(t[0] != '['){ FILE* f=fopen(file,"wb"); if(!f){ free(file); free(t); return -1; } fputs("[]", f); fclose(f); free(file); free(t); return -1; }
|
||||
size_t len=strlen(t); size_t i=1; int depth=0; size_t start=0; size_t count=0, cap=8; char** objs=malloc(cap*sizeof(char*)); if(!objs){ free(t); free(file); return -1; } bool found=false; char* updated_copy=NULL;
|
||||
for(; i<len; ++i){ char c=t[i]; if(c=='{'){ if(depth==0) start=i; depth++; } else if(c=='}'){ depth--; if(depth==0){ size_t end=i; size_t obj_len=end-start+1; char* obj=malloc(obj_len+1); if(!obj){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(file); free(t); return -1; } memcpy(obj, t+start, obj_len); obj[obj_len]='\0';
|
||||
char* id=json_get_string(obj, "id"); bool isMatch=id && strcmp(id,match_id)==0; if(isMatch){ found=true; if(!is_delete){ char* title=json_get_string(obj, "title"); char* author=json_get_string(obj, "author"); char* body=json_get_string(obj, "body"); char* thumb=json_get_string(obj, "thumb"); char* ptitle=json_get_top_string(patch_json, "title"); if(ptitle&&*ptitle){ free(title); title=ptitle; } else free(ptitle); char* pauthor=json_get_top_string(patch_json, "author"); if(pauthor&&*pauthor){ free(author); author=pauthor; } else free(pauthor); char* pbody=json_get_top_string(patch_json, "body"); if(pbody&&*pbody){ free(body); body=pbody; } else free(pbody); char* pthumb=json_get_top_string(patch_json, "thumb"); if(pthumb&&*pthumb){ free(thumb); thumb=pthumb; } else free(pthumb); long long createdAt=json_get_number(obj, "\"createdAt\""); long long updatedAt=now_ms(); char* obj2=build_article_json(id,title,author,body,thumb,createdAt,updatedAt); free(title); free(author); free(body); free(thumb);
|
||||
free(updated_copy); updated_copy=strdup(obj2); free(obj); obj=obj2; }
|
||||
}
|
||||
free(id);
|
||||
if(!(isMatch && is_delete)){
|
||||
if(count==cap){ cap*=2; void* tmp=realloc(objs, cap*sizeof(char*)); if(!tmp){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(obj); free(file); free(t); return -1; } objs=(char**)tmp; }
|
||||
objs[count++]=obj;
|
||||
} else {
|
||||
free(obj);
|
||||
}
|
||||
char* id=json_get_string(obj, "id"); bool isMatch=id && strcmp(id,match_id)==0; if(isMatch){ found=true; if(!is_delete){
|
||||
char* title=json_get_string(obj, "title"); char* author=json_get_string(obj, "author"); char* body=json_get_string(obj, "body"); char* thumb=json_get_string(obj, "thumb");
|
||||
char* ptitle=json_get_top_string(patch_json, "title"); if(ptitle&&*ptitle){ free(title); title=ptitle; } else free(ptitle);
|
||||
char* pauthor=json_get_top_string(patch_json, "author"); if(pauthor&&*pauthor){ free(author); author=pauthor; } else free(pauthor);
|
||||
char* pbody=json_get_top_string(patch_json, "body"); if(pbody&&*pbody){ free(body); body=pbody; } else free(pbody);
|
||||
char* pthumb=json_get_top_string(patch_json, "thumb"); if(pthumb&&*pthumb){ free(thumb); thumb=pthumb; } else free(pthumb);
|
||||
long long createdAt=json_get_number(obj, "\"createdAt\""); long long updatedAt=now_ms(); char* obj2=build_article_json(id,title,author,body,thumb,createdAt,updatedAt);
|
||||
free(title); free(author); free(body); free(thumb); free(updated_copy); updated_copy=strdup(obj2); free(obj); obj=obj2; }
|
||||
}
|
||||
free(id);
|
||||
if(!(isMatch && is_delete)){ if(count==cap){ cap*=2; void* tmp=realloc(objs, cap*sizeof(char*)); if(!tmp){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(obj); free(file); free(t); return -1; } objs=(char**)tmp; } objs[count++]=obj; } else { free(obj); }
|
||||
}
|
||||
}
|
||||
}
|
||||
// write back array
|
||||
size_t total_len=2; for(size_t k=0;k<count;k++){ total_len+=strlen(objs[k]); if(k+1<count) total_len++; }
|
||||
char* out=malloc(total_len+1); if(!out){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(file); free(t); return -1; } size_t w=0; out[w++]='['; for(size_t k=0;k<count;k++){ size_t L=strlen(objs[k]); memcpy(out+w, objs[k], L); w+=L; if(k+1<count) out[w++]=','; } out[w++]=']'; out[w]='\0';
|
||||
int rc = write_file_all(file, out, w);
|
||||
for(size_t k=0;k<count;k++) free(objs[k]);
|
||||
free(objs);
|
||||
free(out);
|
||||
free(file);
|
||||
free(t);
|
||||
char* out=malloc(total_len+1); if(!out){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(file); free(t); return -1; }
|
||||
size_t w=0; out[w++]='['; for(size_t k=0;k<count;k++){ size_t L=strlen(objs[k]); memcpy(out+w, objs[k], L); w+=L; if(k+1<count) out[w++]=','; } out[w++]=']'; out[w]='\0'; int rc=write_file_all(file,out,w);
|
||||
for(size_t k=0;k<count;k++){ free(objs[k]); }
|
||||
free(objs); free(out); free(file); free(t);
|
||||
if(found && !is_delete && out_json_updated){ *out_json_updated = updated_copy ? updated_copy : strdup(""); } else { free(updated_copy); }
|
||||
return found && rc==0 ? 0 : -1;
|
||||
}
|
||||
@ -388,153 +272,92 @@ static char* strndup_local(const char* s, size_t n){ char* r=malloc(n+1); if(!r)
|
||||
|
||||
static char* save_upload(const char* body, size_t blen, const char* ext_hint){ if(!body||blen==0) return NULL; const char* updir = "uploads"; ensure_dir(updir); char* name=gen_id(); if(!name) return NULL; const char* ext = (ext_hint&&*ext_hint)? ext_hint: "bin"; size_t need=strlen(updir)+1+strlen(name)+1+strlen(ext)+1; char* path=malloc(need); if(!path){ free(name); return NULL; } snprintf(path,need,"%s/%s.%s", updir, name, ext); free(name); if(write_file_all(path, body, blen)!=0){ free(path); return NULL; } return path; }
|
||||
|
||||
static void handle_api(int c, const char* method, const char* path, const char* body, size_t blen, const char* content_type){
|
||||
if(!strncmp(method,"OPTIONS",7)){
|
||||
send_response(c, 204, "No Content", "application/json", "", 0, true); return;
|
||||
}
|
||||
// /api/articles or /api/articles/<id>
|
||||
const char* base = "/api/articles";
|
||||
size_t bl = strlen(base);
|
||||
if(!strncmp(path, base, bl)){
|
||||
if(!strcmp(method,"GET")){
|
||||
if(!strcmp(path, base)){
|
||||
// Load, migrate thumbs/body inline images to files if needed, persist, then return
|
||||
char* file=data_file(); if(!file){ send_response(c,200,"OK","application/json","[]",2,true); return; }
|
||||
size_t n=0; char* content=read_file_all(file,&n); if(!content){ free(file); send_response(c,200,"OK","application/json","[]",2,true); return; }
|
||||
char* t=ltrim_dup(content); free(content); if(!t){ free(file); send_response(c,200,"OK","application/json","[]",2,true); return; }
|
||||
static void api_get_articles_array(int c){
|
||||
char* file=data_file(); if(!file){ send_response(c,200,"OK","application/json","[]",2,true); return; }
|
||||
size_t n=0; char* content=read_file_all(file,&n); if(!content){ free(file); send_response(c,200,"OK","application/json","[]",2,true); return; }
|
||||
char* t=ltrim_dup(content); free(content); if(!t){ free(file); send_response(c,200,"OK","application/json","[]",2,true); return; }
|
||||
if(t[0] != '['){ free(file); send_response(c,200,"OK","application/json","[]",2,true); free(t); return; }
|
||||
// iterate and rebuild array
|
||||
size_t len=strlen(t); size_t i=1; int depth=0; size_t start=0; size_t count=0, cap=8; char** objs=malloc(cap*sizeof(char*)); if(!objs){ free(t); free(file); send_response(c,500,"Internal Server Error","application/json","",0,true); return; } int changed=0;
|
||||
for(; i<len; ++i){ char ch=t[i]; if(ch=='{'){ if(depth==0) start=i; depth++; } else if(ch=='}'){ depth--; if(depth==0){ size_t end=i; size_t obj_len=end-start+1; char* obj=malloc(obj_len+1); if(!obj){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(t); free(file); send_response(c,500,"Internal Server Error","application/json","",0,true); return; } memcpy(obj, t+start, obj_len); obj[obj_len]='\0';
|
||||
// check thumb
|
||||
char* id=json_get_string(obj, "id"); char* title=json_get_string(obj, "title"); char* author=json_get_string(obj, "author"); char* body_s=json_get_string(obj, "body"); char* thumb=json_get_string(obj, "thumb"); long long createdAt=json_get_number(obj, "\"createdAt\"");
|
||||
int obj_changed=0;
|
||||
// migrate thumb if data URL
|
||||
if(thumb && strncmp(thumb, "data:",5)==0){ char* mime=NULL; unsigned char* bytes=NULL; size_t bl2=0; if(parse_data_url(thumb,&mime,&bytes,&bl2)==0){ const char* ext=ext_from_mime(mime); char* saved=save_bytes_with_ext(bytes,bl2,ext); if(saved){ size_t urlL=strlen(saved)+2; char* newthumb=malloc(urlL); if(newthumb){ snprintf(newthumb,urlL,"/%s",saved); free(thumb); thumb=newthumb; obj_changed=1; } free(saved); } free(mime); free(bytes); }
|
||||
}
|
||||
// migrate inline images in body
|
||||
bool bchanged=false; char* new_body=migrate_inline_images_in_body(body_s,&bchanged); if(new_body && bchanged){ free(body_s); body_s=new_body; obj_changed=1; } else { free(new_body); }
|
||||
if(obj_changed){ changed=1; free(obj); char* obj2=build_article_json(id?id:"",title?title:"",author?author:"",body_s?body_s:"",thumb?thumb:"",createdAt,0); obj=obj2; }
|
||||
free(id); free(title); free(author); free(body_s); free(thumb);
|
||||
if(count==cap){ cap*=2; void* tmp=realloc(objs, cap*sizeof(char*)); if(!tmp){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(t); free(file); send_response(c,500,"Internal Server Error","application/json","",0,true); return; } objs=(char**)tmp; }
|
||||
objs[count++]=obj;
|
||||
}
|
||||
}
|
||||
for(; i<len; ++i){ char ch=t[i]; if(ch=='{'){ if(depth==0) start=i; depth++; } else if(ch=='}'){ depth--; if(depth==0){ size_t end=i; size_t obj_len=end-start+1; char* obj=malloc(obj_len+1); if(!obj){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(t); free(file); send_response(c,500,"Internal Server Error","application/json","",0,true); return; } memcpy(obj, t+start, obj_len); obj[obj_len]='\0';
|
||||
char* id=json_get_string(obj, "id"); char* title=json_get_string(obj, "title"); char* author=json_get_string(obj, "author"); char* body_s=json_get_string(obj, "body"); char* thumb=json_get_string(obj, "thumb"); long long createdAt=json_get_number(obj, "\"createdAt\"");
|
||||
int obj_changed=0; if(thumb && strncmp(thumb, "data:",5)==0){ char* mime=NULL; unsigned char* bytes=NULL; size_t bl2=0; if(parse_data_url(thumb,&mime,&bytes,&bl2)==0){ const char* ext=ext_from_mime(mime); char* saved=save_bytes_with_ext(bytes,bl2,ext); if(saved){ size_t urlL=strlen(saved)+2; char* newthumb=malloc(urlL); if(newthumb){ snprintf(newthumb,urlL,"/%s",saved); free(thumb); thumb=newthumb; obj_changed=1; } free(saved); } free(mime); free(bytes); } }
|
||||
bool bchanged=false; char* new_body=migrate_inline_images_in_body(body_s,&bchanged); if(new_body && bchanged){ free(body_s); body_s=new_body; obj_changed=1; } else { free(new_body); }
|
||||
if(obj_changed){ changed=1; free(obj); char* obj2=build_article_json(id?id:"",title?title:"",author?author:"",body_s?body_s:"",thumb?thumb:"",createdAt,0); obj=obj2; }
|
||||
free(id); free(title); free(author); free(body_s); free(thumb);
|
||||
if(count==cap){ cap*=2; void* tmp=realloc(objs, cap*sizeof(char*)); if(!tmp){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(t); free(file); send_response(c,500,"Internal Server Error","application/json","",0,true); return; } objs=(char**)tmp; }
|
||||
objs[count++]=obj;
|
||||
}
|
||||
// build array string
|
||||
size_t total_len=2; for(size_t k=0;k<count;k++){ total_len+=strlen(objs[k]); if(k+1<count) total_len++; }
|
||||
char* out=malloc(total_len+1); if(!out){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(t); free(file); send_response(c,500,"Internal Server Error","application/json","",0,true); return; } size_t w=0; out[w++]='['; for(size_t k=0;k<count;k++){ size_t Lx=strlen(objs[k]); memcpy(out+w, objs[k], Lx); w+=Lx; if(k+1<count) out[w++]=','; } out[w++]=']'; out[w]='\0';
|
||||
if(changed){ write_file_all(file, out, w); }
|
||||
}
|
||||
}
|
||||
size_t total_len=2; for(size_t k=0;k<count;k++){ total_len+=strlen(objs[k]); if(k+1<count) total_len++; }
|
||||
char* out=malloc(total_len+1); if(!out){ for(size_t z=0; z<count; ++z) free(objs[z]); free(objs); free(t); free(file); send_response(c,500,"Internal Server Error","application/json","",0,true); return; }
|
||||
size_t w=0; out[w++]='['; for(size_t k=0;k<count;k++){ size_t Lx=strlen(objs[k]); memcpy(out+w, objs[k], Lx); w+=Lx; if(k+1<count) out[w++]=','; } out[w++]=']'; out[w]='\0'; if(changed){ write_file_all(file, out, w); }
|
||||
send_response(c,200,"OK","application/json",out,w,true);
|
||||
for(size_t k=0;k<count;k++) free(objs[k]);
|
||||
free(objs);
|
||||
free(out);
|
||||
free(file);
|
||||
free(t);
|
||||
return;
|
||||
} else if(path[bl]=='/' && strlen(path)>bl+1){
|
||||
const char* id=path+bl+1; char* obj=find_article_by_id(id); if(!obj){ send_response(c,404,"Not Found","application/json","",0,true); return;}
|
||||
// migrate this object if needed
|
||||
for(size_t k=0;k<count;k++){ free(objs[k]); }
|
||||
free(objs); free(out); free(file); free(t);
|
||||
}
|
||||
|
||||
static void api_get_article_by_id(int c, const char* id){
|
||||
char* obj=find_article_by_id(id); if(!obj){ send_response(c,404,"Not Found","application/json","",0,true); return; }
|
||||
char* title=json_get_string(obj, "title"); char* author=json_get_string(obj, "author"); char* body_s=json_get_string(obj, "body"); char* thumb=json_get_string(obj, "thumb"); long long createdAt=json_get_number(obj, "\"createdAt\""); int obj_changed=0;
|
||||
if(thumb && strncmp(thumb, "data:",5)==0){ char* mime=NULL; unsigned char* bytes=NULL; size_t bl2=0; if(parse_data_url(thumb,&mime,&bytes,&bl2)==0){ const char* ext=ext_from_mime(mime); char* saved=save_bytes_with_ext(bytes,bl2,ext); if(saved){ size_t urlL=strlen(saved)+2; char* newthumb=malloc(urlL); if(newthumb){ snprintf(newthumb,urlL,"/%s",saved); free(thumb); thumb=newthumb; obj_changed=1; } free(saved); } free(mime); free(bytes); } }
|
||||
bool bchanged=false; char* new_body=migrate_inline_images_in_body(body_s,&bchanged); if(new_body && bchanged){ free(body_s); body_s=new_body; obj_changed=1; } else { free(new_body); }
|
||||
if(obj_changed){ char* id_copy=json_get_string(obj, "id"); char* updated=build_article_json(id_copy?id_copy:"", title?title:"", author?author:"", body_s?body_s:"", thumb?thumb:"", createdAt, 0); // persist
|
||||
if(updated){ rewrite_articles_map(NULL, id_copy, updated, false); free(updated); }
|
||||
free(id_copy);
|
||||
free(obj); obj=build_article_json(json_get_string("",""), title?title:"", author?author:"", body_s?body_s:"", thumb?thumb:"", createdAt, 0); // rebuild for response
|
||||
// Above line uses placeholder; simpler: rebuild from previously updated string parsing is heavy; instead send the updated we built
|
||||
// But rewrite_articles_map returned updated; safer to just re-find
|
||||
free(obj); obj=find_article_by_id(id);
|
||||
}
|
||||
free(title); free(author); free(body_s); free(thumb);
|
||||
size_t L=strlen(obj); send_response(c,200,"OK","application/json",obj,L,true); free(obj); return;
|
||||
}
|
||||
} else if(!strcmp(method,"POST") && !strcmp(path, base)){
|
||||
char* obj=create_article_from_body(body?body:""); if(!obj){ send_response(c,400,"Bad Request","application/json","",0,true); return;} size_t L=strlen(obj); send_response(c,201,"Created","application/json",obj,L,true); free(obj); return;
|
||||
} else if(!strcmp(method,"PUT") && path[bl]=='/' && strlen(path)>bl+1){
|
||||
const char* id=path+bl+1; char* updated=NULL; if(rewrite_articles_map(&updated,id,body?body:"",false)==0){ size_t L=strlen(updated); send_response(c,200,"OK","application/json",updated,L,true); free(updated); } else { send_response(c,404,"Not Found","application/json","",0,true);} return;
|
||||
} else if(!strcmp(method,"DELETE") && path[bl]=='/' && strlen(path)>bl+1){
|
||||
const char* id=path+bl+1; if(rewrite_articles_map(NULL,id,NULL,true)==0){ send_response(c,204,"No Content","application/json","",0,true);} else { send_response(c,404,"Not Found","application/json","",0,true);} return;
|
||||
bool bchanged=false; char* new_body=migrate_inline_images_in_body(body_s,&bchanged); if(new_body && bchanged){ free(body_s); body_s=new_body; obj_changed=1; } else { free(new_body); }
|
||||
if(obj_changed){ char* id_copy=json_get_string(obj, "id"); char* updated=build_article_json(id_copy?id_copy:"", title?title:"", author?author:"", body_s?body_s:"", thumb?thumb:"", createdAt, 0); if(updated){ rewrite_articles_map(NULL, id_copy, updated, false); free(updated); } free(id_copy); free(obj); obj=find_article_by_id(id); }
|
||||
free(title); free(author); free(body_s); free(thumb); size_t L=strlen(obj); send_response(c,200,"OK","application/json",obj,L,true); free(obj);
|
||||
}
|
||||
|
||||
static void api_post_article(int c, const char* body){ char* obj=create_article_from_body(body?body:""); if(!obj){ send_response(c,400,"Bad Request","application/json","",0,true); return;} size_t L=strlen(obj); send_response(c,201,"Created","application/json",obj,L,true); free(obj); }
|
||||
static void api_put_article(int c, const char* id, const char* body){ char* updated=NULL; if(rewrite_articles_map(&updated,id,body?body:"",false)==0){ size_t L=strlen(updated); send_response(c,200,"OK","application/json",updated,L,true); free(updated); } else { send_response(c,404,"Not Found","application/json","",0,true);} }
|
||||
static void api_delete_article(int c, const char* id){ if(rewrite_articles_map(NULL,id,NULL,true)==0){ send_response(c,204,"No Content","application/json","",0,true);} else { send_response(c,404,"Not Found","application/json","",0,true);} }
|
||||
|
||||
static void api_post_upload(int c, const char* path, const char* body, size_t blen, const char* content_type){
|
||||
const char* ext_q=get_qparam(path,"ext"); const char* ext_from_ct=ext_from_content_type(content_type); const char* ext=ext_from_ct?ext_from_ct:(ext_q?ext_q:"bin"); size_t elen=0; while(elen<4 && ext[elen] && isalnum((unsigned char)ext[elen])) elen++; char* ext_safe=strndup_local(ext,elen?elen:3); if(!ext_safe){ send_response(c,500,"Internal Server Error","application/json","",0,true); return; }
|
||||
char* saved=save_upload(body,blen,ext_safe); free(ext_safe); if(!saved){ send_response(c,500,"Internal Server Error","application/json","",0,true); return; }
|
||||
size_t L=strlen(saved)+20; char* res=malloc(L); if(!res){ free(saved); send_response(c,500,"Internal Server Error","application/json","",0,true); return; } snprintf(res,L,"{\"url\":\"/%s\"}",saved); send_response(c,201,"Created","application/json",res,strlen(res),true); free(res); free(saved);
|
||||
}
|
||||
|
||||
static void handle_api(int c, const char* method, const char* path, const char* body, size_t blen, const char* content_type){
|
||||
if(!strncmp(method,"OPTIONS",7)){ send_response(c, 204, "No Content", "application/json", "", 0, true); return; }
|
||||
const char* base="/api/articles"; size_t bl=strlen(base);
|
||||
if(!strncmp(path, base, bl)){
|
||||
if(!strcmp(method,"GET")){
|
||||
if(!strcmp(path, base)) api_get_articles_array(c);
|
||||
else if(path[bl]=='/' && strlen(path)>bl+1) api_get_article_by_id(c, path+bl+1);
|
||||
else send_response(c,404,"Not Found","application/json","",0,true);
|
||||
return;
|
||||
}
|
||||
if(!strcmp(method,"POST") && !strcmp(path, base)){ api_post_article(c, body); return; }
|
||||
if(!strcmp(method,"PUT") && path[bl]=='/' && strlen(path)>bl+1){ api_put_article(c, path+bl+1, body); return; }
|
||||
if(!strcmp(method,"DELETE") && path[bl]=='/' && strlen(path)>bl+1){ api_delete_article(c, path+bl+1); return; }
|
||||
}
|
||||
// /api/upload for binary image upload
|
||||
if(!strncmp(path, "/api/upload", 12)){
|
||||
if(!strcmp(method,"POST")){
|
||||
const char* ext_q = get_qparam(path, "ext"); const char* ext_from_ct = ext_from_content_type(content_type);
|
||||
const char* ext = ext_from_ct ? ext_from_ct : (ext_q ? ext_q : "bin");
|
||||
// trim ext to safe token
|
||||
size_t elen=0; while(elen<4 && ext[elen] && isalnum((unsigned char)ext[elen])) elen++; char* ext_safe=strndup_local(ext,elen?elen:3); if(!ext_safe){ send_response(c,500,"Internal Server Error","application/json","",0,true); return; }
|
||||
char* saved = save_upload(body, blen, ext_safe); free(ext_safe);
|
||||
if(!saved){ send_response(c,500,"Internal Server Error","application/json","",0,true); return; }
|
||||
// build URL
|
||||
const char* rel = saved; // saved like "uploads/.."
|
||||
size_t L = strlen(rel)+20; char* res=malloc(L); if(!res){ free(saved); send_response(c,500,"Internal Server Error","application/json","",0,true); return; }
|
||||
snprintf(res, L, "{\"url\":\"/%s\"}", rel);
|
||||
send_response(c,201,"Created","application/json",res,strlen(res),true);
|
||||
free(res); free(saved); return;
|
||||
}
|
||||
if(!strcmp(method,"POST")){ api_post_upload(c, path, body, blen, content_type); return; }
|
||||
}
|
||||
send_response(c,404,"Not Found","text/plain","Not Found",9,true);
|
||||
}
|
||||
|
||||
static bool safe_path(const char* p){ if(strstr(p, "..")) return false; return true; }
|
||||
|
||||
static void handle_static(int c, const char* path){
|
||||
char rel[SMALL_BUF];
|
||||
if(!strcmp(path,"/")){
|
||||
snprintf(rel, sizeof(rel), "%s", "/index.html");
|
||||
} else {
|
||||
snprintf(rel, sizeof(rel), "%s", path);
|
||||
}
|
||||
if(!safe_path(rel)){ send_response(c,403,"Forbidden","text/plain","Forbidden",9,false); return; }
|
||||
// build abs path
|
||||
char full[SMALL_BUF*2]; snprintf(full,sizeof(full),"%s%s", DOC_ROOT?DOC_ROOT:".", rel);
|
||||
FILE* f=fopen(full,"rb"); if(!f){ send_response(c,404,"Not Found","text/plain","Not Found",9,false); return; }
|
||||
fseek(f,0,SEEK_END); long sz=ftell(f); fseek(f,0,SEEK_SET); char* buf=malloc((size_t)sz); if(!buf){ fclose(f); send_response(c,500,"Internal Server Error","text/plain","",0,false); return; }
|
||||
size_t n=fread(buf,1,(size_t)sz,f); fclose(f);
|
||||
const char* mime=guess_mime(full);
|
||||
// Add strong cache for uploaded assets
|
||||
int is_upload = (strncmp(rel, "/uploads/", 9)==0);
|
||||
if(is_upload){
|
||||
char head[SMALL_BUF];
|
||||
int hlen=snprintf(head,sizeof(head),
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %zu\r\nCache-Control: public, max-age=31536000, immutable\r\n\r\n",
|
||||
mime, (size_t)n);
|
||||
send(c, head, (size_t)hlen, 0);
|
||||
if(n) send(c, buf, n, 0);
|
||||
} else {
|
||||
send_response(c,200,"OK",mime,buf,n,false);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
static void normalize_rel_path(const char* path, char* rel, size_t relsz){ if(!strcmp(path,"/")) snprintf(rel, relsz, "%s", "/index.html"); else snprintf(rel, relsz, "%s", path); }
|
||||
static int read_file_to_buf(const char* full, char** out, size_t* n){ FILE* f=fopen(full,"rb"); if(!f) return -1; fseek(f,0,SEEK_END); long sz=ftell(f); fseek(f,0,SEEK_SET); char* buf=malloc((size_t)sz); if(!buf){ fclose(f); return -1; } *n=fread(buf,1,(size_t)sz,f); fclose(f); *out=buf; return 0; }
|
||||
static void send_cached_or_plain(int c, const char* rel, const char* mime, const char* buf, size_t n){ int is_upload=(strncmp(rel,"/uploads/",9)==0); if(is_upload){ char head[SMALL_BUF]; int hlen=snprintf(head,sizeof(head),"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %zu\r\nCache-Control: public, max-age=31536000, immutable\r\n\r\n",mime,(size_t)n); send(c, head, (size_t)hlen, 0); if(n) send(c, buf, n, 0);} else { send_response(c,200,"OK",mime,buf,n,false);} }
|
||||
static void handle_static(int c, const char* path){ char rel[SMALL_BUF]; normalize_rel_path(path,rel,sizeof(rel)); if(!safe_path(rel)){ send_response(c,403,"Forbidden","text/plain","Forbidden",9,false); return; } char full[SMALL_BUF*2]; snprintf(full,sizeof(full),"%s%s", DOC_ROOT?DOC_ROOT:".", rel); char* buf=NULL; size_t n=0; if(read_file_to_buf(full,&buf,&n)!=0){ send_response(c,404,"Not Found","text/plain","Not Found",9,false); return; } const char* mime=guess_mime(full); send_cached_or_plain(c,rel,mime,buf,n); free(buf); }
|
||||
|
||||
static void handle_client(int c){
|
||||
char buf[RECV_BUF]; ssize_t rcv=0, total=0;
|
||||
// read headers
|
||||
while((rcv=recv(c, buf+total, sizeof(buf)-1-total, 0))>0){ total+=rcv; buf[total]='\0'; if(strstr(buf, "\r\n\r\n")) break; if(total >= (ssize_t)sizeof(buf)-1) break; }
|
||||
// removed unused read_headers_into
|
||||
static void parse_request_line(const char* buf, char* method, char* path){ if(sscanf(buf, "%15s %4095s", method, path) < 2){ method[0]='\0'; path[0]='\0'; } }
|
||||
static void parse_headers(const char* buf, size_t* content_length, char* ctype, size_t ctype_sz){ *content_length=0; ctype[0]='\0'; const char* cl=strcasestr(buf,"Content-Length:"); if(cl) *content_length=strtoul(cl+15,NULL,10); const char* ct=strcasestr(buf,"Content-Type:"); if(ct){ ct+=13; while(*ct==' '||*ct=='\t') ct++; size_t i=0; while(*ct && *ct!='\r' && *ct!='\n' && i<ctype_sz-1){ ctype[i++]=*ct++; } ctype[i]='\0'; } }
|
||||
static char* read_body_if_needed(int c, const char* buf, size_t total, size_t content_length){ const char* hdr_end=strstr(buf,"\r\n\r\n"); size_t header_bytes=hdr_end? (size_t)(hdr_end-buf)+4 : total; size_t have_body = total > header_bytes ? total - header_bytes : 0; char* body=NULL; if(content_length){ body=malloc(content_length+1); if(!body) return NULL; size_t off=0; if(have_body){ size_t cpy = have_body>content_length?content_length:have_body; memcpy(body, buf+header_bytes, cpy); off=cpy; } size_t remain=content_length-off; while(remain>0){ ssize_t rr=recv(c, body+off, remain, 0); if(rr<=0) break; off+=rr; remain-=rr; } body[content_length]='\0'; } return body; }
|
||||
static void handle_client(int c){ char buf[RECV_BUF]; ssize_t total=0; // read headers
|
||||
while(true){ ssize_t rcv=recv(c, buf+total, sizeof(buf)-1-total, 0); if(rcv<=0) break; total+=rcv; buf[total]='\0'; if(strstr(buf, "\r\n\r\n")) break; if(total >= (ssize_t)sizeof(buf)-1) break; }
|
||||
if(total<=0){ close(c); return; }
|
||||
// parse request line
|
||||
char method[16]={0}, path[SMALL_BUF]={0};
|
||||
if(sscanf(buf, "%15s %4095s", method, path) < 2){ close(c); return; }
|
||||
// headers
|
||||
size_t content_length=0; const char* cl = strcasestr(buf, "Content-Length:"); if(cl){ content_length = strtoul(cl+15, NULL, 10); }
|
||||
const char* ct_hdr = strcasestr(buf, "Content-Type:"); char ctype[128]={0}; if(ct_hdr){ ct_hdr+=13; while(*ct_hdr==' '||*ct_hdr=='\t') ct_hdr++; size_t i=0; while(*ct_hdr && *ct_hdr!='\r' && *ct_hdr!='\n' && i<sizeof(ctype)-1){ ctype[i++]=*ct_hdr++; } ctype[i]='\0'; }
|
||||
// find body start
|
||||
const char* hdr_end = strstr(buf, "\r\n\r\n"); size_t header_bytes = hdr_end? (size_t)(hdr_end - buf) + 4 : (size_t)total; size_t have_body = total > (ssize_t)header_bytes ? (size_t)total - header_bytes : 0;
|
||||
char* body = NULL; if(content_length){ body = malloc(content_length+1); if(!body){ close(c); return; } size_t off=0; if(have_body){ size_t cpy = have_body>content_length?content_length:have_body; memcpy(body, buf+header_bytes, cpy); off = cpy; }
|
||||
size_t remain = content_length - off; while(remain>0){ ssize_t rr = recv(c, body+off, remain, 0); if(rr<=0) break; off+=rr; remain-=rr; } body[content_length]='\0'; }
|
||||
|
||||
if(!strncmp(path, "/api/", 5)){
|
||||
handle_api(c, method, path, body, content_length, ctype[0]?ctype:NULL);
|
||||
} else if(!strcmp(method,"GET")){
|
||||
handle_static(c, path);
|
||||
} else if(!strcmp(method,"OPTIONS")){
|
||||
send_response(c,204,"No Content","text/plain","",0,false);
|
||||
} else {
|
||||
send_response(c,405,"Method Not Allowed","text/plain","",0,false);
|
||||
}
|
||||
free(body);
|
||||
close(c);
|
||||
char method[16]={0}, path[SMALL_BUF]={0}; parse_request_line(buf,method,path); if(!method[0]){ close(c); return; }
|
||||
size_t content_length=0; char ctype[128]={0}; parse_headers(buf,&content_length,ctype,sizeof(ctype));
|
||||
char* body=read_body_if_needed(c,buf,(size_t)total,content_length);
|
||||
if(!strncmp(path, "/api/", 5)) handle_api(c, method, path, body, content_length, ctype[0]?ctype:NULL);
|
||||
else if(!strcmp(method,"GET")) handle_static(c, path);
|
||||
else if(!strcmp(method,"OPTIONS")) send_response(c,204,"No Content","text/plain","",0,false);
|
||||
else send_response(c,405,"Method Not Allowed","text/plain","",0,false);
|
||||
free(body); close(c);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv){
|
||||
|
||||
Loading…
Reference in New Issue
Block a user