Unquoting ' (').
[shell-fm.git] / source / http.c
bloba1715990835114ba9d5cf1e749562c3ddca19b0f
1 /*
2 Copyright (C) 2006 by Jonas Kramer
3 Published under the terms of the GNU General Public License (GPL).
4 */
6 #define _GNU_SOURCE
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <assert.h>
13 #include <ctype.h>
14 #include <stdint.h>
15 #include <time.h>
16 #include <stdarg.h>
18 #include <sys/socket.h>
19 #include <sys/stat.h>
21 #include "hash.h"
22 #include "history.h"
23 #include "settings.h"
24 #include "getln.h"
25 #include "http.h"
26 #include "ropen.h"
27 #include "util.h"
28 #include "globals.h"
31 #ifndef USERAGENT
32 #define USERAGENT "Shell.FM/" PACKAGE_VERSION
33 #endif
36 char ** fetch(const char * url, FILE ** pHandle, const char * post, const char * type) {
37 char ** resp = NULL, * host, * file, * port, * status = NULL, * line = NULL;
38 char * connhost;
39 char urlcpy[512 + 1];
40 unsigned short nport = 80, chunked = 0;
41 unsigned nline = 0, nstatus = 0, size = 0;
42 signed validHead = 0;
43 FILE * fd;
44 int useproxy;
46 const char * headFormat =
47 "%s /%s HTTP/1.1\r\n"
48 "Host: %s\r\n"
49 "User-Agent: " USERAGENT "\r\n"
50 "Cookie: Session=%s\r\n"
51 "Connection: close\r\n";
53 const char * proxiedheadFormat =
54 "%s http://%s/%s HTTP/1.1\r\n"
55 "Host: %s\r\n"
56 "User-Agent: " USERAGENT "\r\n"
57 "Cookie: Session=%s\r\n";
59 if(!batch && !enabled(QUIET))
60 fputs("...\r", stderr);
62 useproxy = haskey(& rc, "proxy");
63 if(type == NULL)
64 type = "application/x-www-form-urlencoded";
66 if(pHandle)
67 * pHandle = NULL;
69 strncpy(urlcpy, url, sizeof(urlcpy) - 1);
71 host = & urlcpy[strncmp(urlcpy, "http://", 7) ? 0 : 7];
72 connhost = host;
74 if(useproxy) {
75 char proxcpy[512 + 1];
76 memset(proxcpy, (char) 0, sizeof(proxcpy));
77 strncpy(proxcpy, value(& rc, "proxy"), sizeof(proxcpy) - 1);
78 connhost = & proxcpy[strncmp(proxcpy, "http://", 7) ? 0 : 7];
81 port = strchr(connhost, 0x3A);
82 file = strchr(host, 0x2F);
83 fflush(stderr);
84 * file = (char) 0;
85 ++file;
87 if(port && port < file) {
88 char * ptr = NULL;
89 * port = (char) 0;
90 ++port;
91 nport = (unsigned short) strtol(port, & ptr, 10);
92 if(ptr == port)
93 nport = 80;
96 if(!(fd = ropen(connhost, nport)))
97 return NULL;
99 if(useproxy)
100 fprintf(fd, proxiedheadFormat, post ? "POST" : "GET", host,
101 file ? file : "", host, value(& data, "session"));
102 else
103 fprintf(fd, headFormat, post ? "POST" : "GET", file ? file : "", host, value(& data, "session"));
105 if(post != NULL) {
106 fprintf(fd, "Content-Type: %s\r\n", type);
107 fprintf(fd, "Content-Length: %ld\r\n\r\n%s\r\n", (long) strlen(post), post);
110 fputs("\r\n", fd);
111 fflush(fd);
113 if(getln(& status, & size, fd) >= 12)
114 validHead = sscanf(status, "HTTP/%*f %u", & nstatus);
116 if(nstatus != 200 && nstatus != 301 && nstatus != 302) {
117 fshutdown(& fd);
118 if(size) {
119 if(validHead != 2)
120 fprintf(stderr, "Invalid HTTP: %s from: %s\n", status, url);
121 else
122 fprintf(stderr, "HTTP Response: %s", status);
126 freeln(& status, & size);
128 return NULL;
131 freeln(& status, & size);
133 while(!0) {
134 if(getln(& line, & size, fd) < 3)
135 break;
137 if(!strncasecmp(line, "Transfer-Encoding: chunked", 26))
138 chunked = !0;
140 if((nstatus == 301 || nstatus == 302) && !strncasecmp(line, "Location: ", 10)) {
141 char newurl[512 + 1];
142 memset(newurl, 0, sizeof(newurl));
143 sscanf(line, "Location: %512[^\r\n]", newurl);
144 fshutdown(& fd);
146 return fetch(newurl, pHandle, post, type);
150 freeln(& line, & size);
152 if(pHandle) {
153 * pHandle = fd;
154 if(!batch && !enabled(QUIET))
155 fputs("\r \r", stderr);
157 return NULL;
160 if(chunked)
161 puts("DEBUG: Chunked!");
163 while(!feof(fd)) {
164 line = NULL;
165 size = 0;
167 if(getln(& line, & size, fd)) {
168 char * ptr = strchr(line, 10);
170 if(ptr != NULL)
171 * ptr = (char) 0;
173 resp = realloc(resp, (nline + 2) * sizeof(char *));
174 assert(resp != NULL);
176 resp[nline] = line;
177 resp[++nline] = NULL;
178 } else if(size)
179 free(line);
182 fshutdown(& fd);
184 if(!batch && !enabled(QUIET))
185 fputs("\r \r", stderr);
186 return resp;
189 unsigned encode(const char * orig, char ** encoded) {
190 register unsigned i = 0, x = 0;
192 * encoded = calloc((strlen(orig) * 3) + 1, sizeof(char));
194 assert(* encoded != NULL);
196 while(i < strlen(orig)) {
197 if(isalnum(orig[i]) || orig[i] == ' ')
198 (* encoded)[x++] = orig[i];
199 else {
200 snprintf(
201 (* encoded) + x,
202 strlen(orig) * 3 - strlen(* encoded) + 1,
203 "%%%02X",
204 (uint8_t) orig[i]
206 x += 3;
208 ++i;
211 for(i = 0; i < x; ++i)
212 if((* encoded)[i] == ' ')
213 (* encoded)[i] = '+';
215 return x;
218 unsigned decode(const char * orig, char ** decoded) {
219 register unsigned i = 0, x = 0;
220 const unsigned len = strlen(orig);
222 * decoded = calloc(len + 1, sizeof(char));
224 assert(decoded != NULL);
226 while(i < len) {
227 if(orig[i] != '%')
228 (* decoded)[x] = orig[i];
229 else {
230 unsigned hex;
231 if(sscanf(orig + i, "%%%02x", & hex) != 1)
232 (* decoded)[x] = orig[i];
233 else {
234 (* decoded)[x] = (char) hex;
235 i += 2;
239 ++i;
240 ++x;
243 * decoded = realloc(* decoded, (x + 1) * sizeof(char));
245 assert(decoded != NULL);
247 for(i = 0; i < x; ++i)
248 if((* decoded)[i] == '+')
249 (* decoded)[i] = ' ';
251 return x;
254 void freeln(char ** line, unsigned * size) {
255 if(size)
256 * size = 0;
258 if(line && * line) {
259 free(* line);
260 * line = NULL;
265 void unhtml(char * html) {
266 unsigned i;
267 const char * codes [] = {
268 "&amp;", "&",
269 "&lt;", "<",
270 "&gt;", ">",
271 "&nbsp;", " ",
272 "&quot;", "\"",
273 "&apos;", "\'",
276 for(i = 0; i < (sizeof(codes) / sizeof(char *)); i += 2) {
277 unsigned length = strlen(codes[i]);
278 char * ptr;
279 while((ptr = strcasestr(html, codes[i])) != NULL) {
280 * ptr = codes[i + 1][0];
281 memmove(ptr + 1, ptr + length, strlen(ptr + length));
282 * (ptr + strlen(ptr) - length + 1) = 0;
288 const char * makeurl(const char * fmt, ...) {
289 static char url[512];
290 const char * src = fmt;
291 char * ptr = NULL;
292 unsigned pos = 0;
293 va_list argv;
295 va_start(argv, fmt);
297 memset(url, 0, sizeof(url));
299 while(* src && pos < sizeof(url) - 1) {
300 if(* src != '%')
301 url[pos++] = * (src++);
302 else if(* (src + 1)) {
303 switch(* (++src)) {
304 case '%':
305 url[pos] = '%';
306 break;
308 case 's':
309 encode(va_arg(argv, char *), & ptr);
310 pos += snprintf(& url[pos], sizeof(url) - pos, "%s", ptr);
311 free(ptr);
312 ptr = NULL;
313 break;
315 case 'i':
316 pos += sprintf(& url[pos], "%d", va_arg(argv, int));
317 break;
319 case 'u':
320 pos += sprintf(& url[pos], "%u", va_arg(argv, unsigned));
321 break;
323 ++src;
327 return url;
330 char ** cache(const char * url, const char * name, int refresh) {
331 time_t expiry = 60 * 60 * 24;
332 char path[4096];
334 if(haskey(& rc, "expiry"))
335 expiry = atoi(value(& rc, "expiry"));
337 memset(path, (char) 0, sizeof(path));
338 strncpy(path, rcpath("cache"), sizeof(path));
339 if(access(path, W_OK | X_OK))
340 mkdir(path, 0700);
342 snprintf(path, sizeof(path), "%s/%s", rcpath("cache"), name);
344 if(!refresh) {
345 if(access(path, R_OK | W_OK))
346 refresh = !0;
347 else {
348 time_t now = time(NULL);
349 struct stat status;
351 stat(path, & status);
352 if(status.st_mtime < now - expiry)
353 refresh = !0;
357 if(!refresh)
358 return slurp(path);
359 else {
360 char ** data = fetch(url, NULL, NULL, NULL);
361 if(data) {
362 FILE * fd = fopen(path, "w");
363 if(fd != NULL) {
364 unsigned line = 0;
365 while(data[line])
366 fprintf(fd, "%s\n", data[line++]);
367 fclose(fd);
368 } else {
369 fputs("Couldn't write cache.\n", stderr);
373 return data;