root/cgi/cgi.h
/*INCLUDED FROM
DEFINITIONS
This source file includes following definitions.- my_strdup
- dup2nul
- norm_path
- gzip_init
- gzip_start
- http_header_end
- http_header_html
- http_header_attachment
- cache_clean
- cache_full
- cache_init
- cache_end
1 #ifndef __cgi_cgi__ 2 #define __cgi_cgi__ 1 3 4 #include <unistd.h> 5 #include <string.h> 6 #include <stdlib.h> 7 #include <stdio.h> 8 #include <time.h> 9 10 #ifdef USE_BOEHM 11 # include <gc.h> 12 # define my_malloc GC_MALLOC 13 # define my_realloc GC_REALLOC 14 # define my_free(x) 15 static char * 16 my_strdup(const char *s) 17 { 18 size_t l = strlen(s); 19 char *r = GC_MALLOC_ATOMIC(l + 1); 20 21 if (!r) return NULL; 22 *r = '\0'; 23 strncat(r, s, l); 24 25 return r; 26 } 27 #else 28 # define my_malloc malloc 29 # define my_realloc realloc 30 # define my_free free 31 # define my_strdup strdup 32 #endif 33 34 #define MINL 511 35 #define MINB 512 36 37 static char * 38 dup2nul(const char *t, size_t tsz) 39 { 40 char *r = my_malloc(tsz + 1); 41 42 if (!r) return NULL; 43 44 r[tsz] = '\0'; 45 memcpy(r, t, tsz); 46 47 return r; 48 } 49 50 51 static char * 52 norm_path(const char *p, int root) 53 { 54 size_t s = strlen(p); 55 char *cp, *cp2, *t = my_malloc(s + 2); 56 int i; 57 58 if (!t) return NULL; 59 60 /* guarantee root */ 61 memcpy(t + 1, p, s + 1); 62 *t = '/'; 63 64 /* remove duplicate slashes */ 65 cp = t; while (*cp != '\0') { 66 while (*cp == '/' && *(cp + 1) == '/') { 67 memmove(cp, cp + 1, strlen(cp + 1) + 1); 68 } 69 cp++; 70 } 71 72 /* remove cwd */ 73 cp = t; while (*cp != '\0') { 74 while (*cp == '/' 75 && *(cp + 1) == '.' 76 && *(cp + 2) == '/') { 77 memmove(cp, cp + 2, strlen(cp + 2) + 1); 78 } 79 cp++; 80 } 81 82 /* remove parents */ 83 cp = t; while (*cp != '\0') { 84 while (*cp == '/' 85 && *(cp + 1) == '.' 86 && *(cp + 2) == '.' 87 && *(cp + 3) == '/') { 88 *cp = '\0'; 89 cp2 = strrchr(t, '/'); 90 cp2 = cp2 ? cp2 : cp; 91 memmove(cp2, cp + 3, strlen(cp + 3) + 1); 92 } 93 cp++; 94 } 95 96 /* remove leading */ 97 cp = t; while((cp = strrchr(cp, '/')) && *(cp + 1) == '\0') 98 *cp = '\0'; 99 100 /* remove root */ 101 if (!root && *t == '/') 102 memmove(t, t + 1, strlen(t)); 103 104 return t; 105 } 106 107 108 #define thing_empty(x,y) (x)?(x):(y) 109 110 /* close end a of s, adjust t to mean to b of s */ 111 #define dup_pat(s, a, b, t) do { close(s[a]); \ 112 dup2(s[b], t); \ 113 close(s[b]); } while (0) 114 #define dup_sgl(a, t) do { dup2(a, t); \ 115 close(a); } while (0) 116 117 118 struct cache { 119 char filen[MINB]; 120 char fileu[MINB]; 121 int new_entry; 122 }; 123 124 extern struct cache cstate; 125 126 127 static int 128 gzip_init(void) 129 { 130 char *env = getenv("HTTP_ACCEPT_ENCODING"); 131 132 if (env && strstr(env, "gzip")) { 133 printf("Content-Encoding: gzip\n\ 134 Vary: Accept-Encoding\n"); 135 136 return 0; 137 } 138 139 return -1; 140 } 141 142 143 static void 144 gzip_start(void) 145 { 146 int pfd[2]; 147 pid_t p; 148 149 fflush(stdout); 150 151 if (pipe(pfd) < 0) return; /* err */ 152 if ((p = fork()) < 0) return; /* err */ 153 if (p == 0) { 154 dup_pat(pfd, 1, 0, STDIN_FILENO); 155 156 execlp("gzip", "gzip", "-9cf", 157 (char *) NULL); 158 } else dup_pat(pfd, 0, 1, STDOUT_FILENO); 159 } 160 161 162 static void 163 http_header_end(int gzip) 164 { 165 printf("\n"); 166 167 if (gzip) gzip_start(); 168 } 169 170 171 static void 172 http_header_html(void) 173 { 174 printf("Content-type: text/html\n"); 175 } 176 177 178 static void 179 http_header_attachment(int binary, const char *name) 180 { 181 printf("Content-Type: %s\n\ 182 Content-Disposition: inline; filename=\"%s\"\n", 183 binary ? "application/octet-stream" : "text/plain", name); 184 } 185 186 187 # ifdef CACHE_CODE 188 189 #include <sys/stat.h> 190 #include <dirent.h> 191 #include <xxhash.h> 192 193 static size_t 194 cache_clean(void) 195 { 196 char cfilen[MINB] = {0}; 197 DIR *d = opendir(TMP_DIR); 198 FILE *fp; 199 struct dirent *e; 200 struct stat sb; 201 size_t a = 0; 202 time_t now = time(NULL); 203 time_t then = 0; 204 205 if (!d) return MAX_CACHE; 206 207 #define CACHE_LAST TMP_DIR "/last_" CACHE_CODE 208 if (!(fp = fopen(CACHE_LAST, "r+"))) 209 if (!(fp = fopen(CACHE_LAST, "w+"))) 210 goto cc_prohibit_skip; 211 212 fread(&then, sizeof(time_t), 1, fp); /* XXX: if this fails we get 0 */ 213 if (now - then < 240) return 0; 214 fseek(fp, 0, SEEK_SET); 215 216 cc_prohibit_skip: 217 while ((e = readdir(d))) { 218 if (!(strstr(e->d_name, CACHE_CODE) == e->d_name)) continue; 219 220 snprintf(cfilen, MINB, TMP_DIR "/%s", e->d_name); 221 if (stat(cfilen, &sb) < 0) 222 continue; 223 224 if ((time(NULL) - sb.st_mtim.tv_sec) > 480) 225 unlink(cfilen); 226 else 227 a += sb.st_size; 228 } 229 230 closedir(d); 231 fwrite(&now, sizeof(time_t), 1, fp); 232 fclose(fp); 233 234 return a; 235 } 236 237 238 static int 239 cache_full(void) 240 { 241 if (cache_clean() >= MAX_CACHE) 242 return 1; 243 244 return 0; 245 } 246 247 248 static int 249 cache_init(void *ptr, size_t sz) 250 { 251 XXH64_hash_t state; 252 XXH64_canonical_t cano; 253 FILE *cf; 254 char cbuf[MINB]; 255 int pfd[2]; 256 pid_t p; 257 size_t i; 258 259 state = XXH3_64bits(ptr, sz); 260 XXH64_canonicalFromHash(&cano, state); 261 262 /* construct our cache filenames */ 263 strncat(cstate.filen, TMP_DIR "/" CACHE_CODE "_", MINL); 264 for(i = 0; i < sizeof(cano.digest); ++i) { 265 char hdp[3] = {0}; 266 snprintf(hdp, 3, "%02X", cano.digest[i]); 267 strncat(cstate.filen, hdp, MINL); 268 } 269 snprintf(cstate.fileu, MINB, "%s-%d", cstate.filen, rand()); 270 271 if (getenv("NO_CGI_CACHE")) 272 return -1; 273 274 if ((cf = fopen(cstate.filen, "r"))) { 275 do { 276 i = fread(cbuf, 1, MINB, cf); 277 fwrite(cbuf, 1, i, stdout); 278 } while (i > 0); 279 280 return 1; /* cached */ 281 } else { 282 /* XXX: need better error handling here */ 283 284 if (cache_full()) 285 return -1; 286 287 cstate.new_entry = 1; 288 289 fflush(stdout); 290 291 if (pipe(pfd) < 0) return -1; /* err */ 292 if ((p = fork()) < 0) return -1; /* err */ 293 if (p == 0) { 294 dup_pat(pfd , 1, 0, STDIN_FILENO); 295 296 execlp("tee", "tee", cstate.fileu, 297 (char *) NULL); 298 } else dup_pat(pfd, 0, 1, STDOUT_FILENO); 299 } 300 301 return 0; /* process page */ 302 } 303 304 305 static void 306 cache_end(int e) 307 { 308 struct stat sb; 309 310 if (cstate.new_entry) { 311 if (e == 0 312 && stat(cstate.fileu, &sb) >= 0 313 && sb.st_size < MAX_SINGLE_CACHE) { 314 rename(cstate.fileu, cstate.filen); 315 316 return; 317 } 318 319 unlink(cstate.fileu); 320 } 321 } 322 323 324 # endif 325 326 #endif
/*