/* vim:syntax=c tabstop=2 shiftwidth=2 noexpandtab Shell.FM - http.c Copyright (C) 2006 by Jonas Kramer Published under the terms of the GNU General Public License (GPL). */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "hash.h" #include "history.h" #include "settings.h" #include "getln.h" #include "http.h" #include "ropen.h" #include "globals.h" #ifndef USERAGENT #define USERAGENT "Shell.FM/0.3" #endif char ** fetch(const char * url, FILE ** pHandle, const char * data, const char * type) { char ** resp = NULL, * host, * file, * port, * status = NULL, * line = NULL; char * connhost; char urlcpy[512 + 1]; unsigned short nport = 80, chunked = 0; unsigned nline = 0, nstatus = 0, size = 0; signed validHead = 0; FILE * fd; int useproxy; const char * headFormat = "%s /%s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: " USERAGENT "\r\n" "Connection: close\r\n"; const char * proxiedheadFormat = "%s http://%s/%s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: " USERAGENT "\r\n"; fputs("...\r", stderr); useproxy = haskey(& rc, "proxy"); if(type == NULL) type = "application/x-www-form-urlencoded"; if(pHandle) * pHandle = NULL; strncpy(urlcpy, url, sizeof(urlcpy) - 1); host = & urlcpy[strncmp(urlcpy, "http://", 7) ? 0 : 7]; connhost = host; if(useproxy) { char proxcpy[512 + 1]; memset(proxcpy, (char) 0, sizeof(proxcpy)); strncpy(proxcpy, value(& rc, "proxy"), sizeof(proxcpy) - 1); connhost = & proxcpy[strncmp(proxcpy, "http://", 7) ? 0 : 7]; } port = strchr(connhost, 0x3A); file = strchr(host, 0x2F); fflush(stderr); * file = (char) 0; ++file; if(port) { char * ptr = NULL; * port = (char) 0; ++port; nport = (unsigned short) strtol(port, & ptr, 10); if(ptr == port) nport = 80; } if(!(fd = ropen(connhost, nport))) return NULL; if(useproxy) fprintf(fd, proxiedheadFormat, data ? "POST" : "GET", host, file ? file : "", host); else fprintf(fd, headFormat, data ? "POST" : "GET", file ? file : "", host); if(data != NULL) { fprintf(fd, "Content-Type: %s\r\n", type); fprintf(fd, "Content-Length: %ld\r\n\r\n%s\r\n", (long) strlen(data), data); } fputs("\r\n", fd); fflush(fd); if(getln(& status, & size, fd) >= 12) validHead = sscanf(status, "HTTP/%*f %u", & nstatus); if(nstatus != 200 && nstatus != 301 && nstatus != 302) { fshutdown(& fd); if(size) { if(validHead != 2) fprintf(stderr, "Invalid HTTP: %s\n", status); else fprintf(stderr, "HTTP Response: %s", status); free(status); } return NULL; } freeln(& status, & size); while(!0) { if(getln(& line, & size, fd) < 3) break; if(!strncasecmp(line, "Transfer-Encoding: chunked", 26)) chunked = !0; if((nstatus == 301 || nstatus == 302) && !strncasecmp(line, "Location: ", 10)) { char newurl[512 + 1]; memset(newurl, 0, sizeof(newurl)); sscanf(line, "Location: %512[^\r\n]", newurl); fshutdown(& fd); return fetch(newurl, pHandle, data, type); } } freeln(& line, & size); if(pHandle) { * pHandle = fd; if(!enabled(QUIET)) fputs("\r \r", stderr); return NULL; } if(chunked) puts("DEBUG: Chunked!"); while(!feof(fd)) { line = NULL; size = 0; if(getln(& line, & size, fd)) { char * ptr = strchr(line, 10); if(ptr != NULL) * ptr = (char) 0; resp = realloc(resp, (nline + 2) * sizeof(char *)); assert(resp != NULL); resp[nline] = line; resp[++nline] = NULL; } else if(size) free(line); } fshutdown(& fd); if(!enabled(QUIET)) fputs("\r \r", stderr); return resp; } unsigned encode(const char * orig, char ** encoded) { register unsigned i = 0, x = 0; * encoded = calloc((strlen(orig) * 3) + 1, sizeof(char)); while(i < strlen(orig)) { if(isalnum(orig[i]) || orig[i] == ' ') (* encoded)[x++] = orig[i]; else { snprintf( (* encoded) + x, strlen(orig) * 3 - strlen(* encoded) + 1, "%%%02X", (uint8_t) orig[i] ); x += 3; } ++i; } for(i = 0; i < x; ++i) if((* encoded)[i] == ' ') (* encoded)[i] = '+'; return x; } unsigned decode(const char * orig, char ** decoded) { register unsigned i = 0, x = 0; const unsigned len = strlen(orig); * decoded = calloc(len + 1, sizeof(char)); while(i < len) { if(orig[i] != '%') (* decoded)[x] = orig[i]; else { unsigned hex; if(sscanf(orig + i, "%%%02x", & hex) != 1) (* decoded)[x] = orig[i]; else { (* decoded)[x] = (char) hex; i += 2; } } ++i; ++x; } * decoded = realloc(* decoded, (x + 1) * sizeof(char)); for(i = 0; i < x; ++i) if((* decoded)[i] == '+') (* decoded)[i] = ' '; return x; } void freeln(char ** line, unsigned * size) { if(size) * size = 0; if(line && * line) { free(* line); * line = NULL; } } void unhtml(char * html) { unsigned i; const char * codes [] = { "&", "&", "<", "<", ">", ">", " ", " ", }; for(i = 0; i < (sizeof(codes) / sizeof(char *)); i += 2) { unsigned length = strlen(codes[i]); char * ptr; while((ptr = strcasestr(html, codes[i])) != NULL) { * ptr = codes[i + 1][0]; memmove(ptr + 1, ptr + length, strlen(ptr + length)); * (ptr + strlen(ptr) - length + 1) = 0; } } } const char * makeurl(const char * fmt, ...) { static char url[512]; const char * src = fmt; char * ptr = NULL; unsigned pos = 0; va_list argv; va_start(argv, fmt); memset(url, 0, sizeof(url)); while(* src && pos < sizeof(url) - 1) { if(* src != '%') url[pos++] = * (src++); else if(* (src + 1)) { switch(* (++src)) { case '%': url[pos] = '%'; break; case 's': encode(va_arg(argv, char *), & ptr); pos += snprintf(& url[pos], sizeof(url) - pos, "%s", ptr); free(ptr); ptr = NULL; break; case 'i': pos += sprintf(& url[pos], "%d", va_arg(argv, int)); break; case 'u': pos += sprintf(& url[pos], "%u", va_arg(argv, unsigned)); break; } ++src; } } return url; } char ** cache(const char * url, const char * name, int refresh) { time_t expiry = 60 * 60 * 24; char path[4096]; if(haskey(& rc, "expiry")) expiry = atoi(value(& rc, "expiry")); memset(path, (char) 0, sizeof(path)); snprintf(path, sizeof(path), "%s/.shell-fm/cache", getenv("HOME")); if(access(path, W_OK | X_OK)) mkdir(path, 0700); snprintf(path, sizeof(path), "%s/.shell-fm/cache/%s", getenv("HOME"), name); if(!refresh) { if(access(path, R_OK | W_OK)) refresh = !0; else { time_t now = time(NULL); struct stat status; stat(path, & status); if(status.st_mtime < now - expiry) refresh = !0; } } if(!refresh) return slurp(path); else { char ** data = fetch(url, NULL, NULL, NULL); if(data) { FILE * fd = fopen(path, "w"); if(fd != NULL) { unsigned line = 0; while(data[line]) fprintf(fd, "%s\n", data[line++]); fclose(fd); } else { fputs("Couldn't write cache.\n", stderr); } } return data; } }