/* * Read all the available data from a filedescriptor. * This is intended for short answers, i.e. when we know the server * will write it all before being preempted. For answers that may come * as an stream with delays, non-blocking is better. * Return value: read data, or NULL on error and no data. */ static char *Dpi_blocking_read(int fd) { int st; const int buf_sz = 8*1024; char buf[buf_sz], *msg = NULL; Dstr *dstr = dStr_sized_new(buf_sz); do { st = read(fd, buf, buf_sz); if (st < 0) { if (errno == EINTR) { continue; } else { MSG_ERR("[Dpi_blocking_read] %s\n", dStrerror(errno)); break; } } else if (st > 0) { dStr_append_l(dstr, buf, st); } } while (st == buf_sz); msg = (dstr->len > 0) ? dstr->str : NULL; dStr_free(dstr, (dstr->len > 0) ? FALSE : TRUE); return msg; }
/* * Decode chunked data */ static Dstr *Decode_chunked(Decode *dc, const char *instr, int inlen) { char *inputPtr, *eol; int inputRemaining; int chunkRemaining = *((int *)dc->state); Dstr *output = dStr_sized_new(inlen); dStr_append_l(dc->leftover, instr, inlen); inputPtr = dc->leftover->str; inputRemaining = dc->leftover->len; while (inputRemaining > 0) { if (chunkRemaining > 2) { /* chunk body to copy */ int copylen = MIN(chunkRemaining - 2, inputRemaining); dStr_append_l(output, inputPtr, copylen); chunkRemaining -= copylen; inputRemaining -= copylen; inputPtr += copylen; } if ((chunkRemaining == 2) && (inputRemaining > 0)) { /* CR to discard */ chunkRemaining--; inputRemaining--; inputPtr++; } if ((chunkRemaining == 1) && (inputRemaining > 0)) { /* LF to discard */ chunkRemaining--; inputRemaining--; inputPtr++; } /* * A chunk has a one-line header that begins with the chunk length * in hexadecimal. */ if (!(eol = (char *)memchr(inputPtr, '\n', inputRemaining))) { break; /* We don't have the whole line yet. */ } if (!(chunkRemaining = strtol(inputPtr, NULL, 0x10))) { break; /* A chunk length of 0 means we're done! */ } inputRemaining -= (eol - inputPtr) + 1; inputPtr = eol + 1; chunkRemaining += 2; /* CRLF at the end of every chunk */ } /* If we have a partial chunk header, save it for next time. */ dStr_erase(dc->leftover, 0, inputPtr - dc->leftover->str); *(int *)dc->state = chunkRemaining; return output; }
/* * Create and initialize a dpip socket handler */ Dsh *a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz) { Dsh *dsh = dNew(Dsh, 1); /* init descriptors and streams */ dsh->fd_in = fd_in; dsh->fd_out = fd_out; /* init buffer */ dsh->wrbuf = dStr_sized_new(8 *1024); dsh->rdbuf = dStr_sized_new(8 *1024); dsh->flush_sz = flush_sz; dsh->mode = DPIP_TAG; if (fcntl(dsh->fd_in, F_GETFL) & O_NONBLOCK) dsh->mode |= DPIP_NONBLOCK; dsh->status = 0; return dsh; }
/* * Escape characters as %XX sequences. * Return value: New string. */ char *a_Misc_escape_chars(const char *str, const char *esc_set) { static const char *const hex = "0123456789ABCDEF"; char *p = NULL; Dstr *dstr; int i; dstr = dStr_sized_new(64); for (i = 0; str[i]; ++i) { if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) { dStr_append_c(dstr, '%'); dStr_append_c(dstr, hex[(str[i] >> 4) & 15]); dStr_append_c(dstr, hex[str[i] & 15]); } else {
/* * Escape URI characters in 'esc_set' as %XX sequences. * Return value: New escaped string. */ char *Escape_uri_str(const char *str, const char *p_esc_set) { static const char *esc_set, *hex = "0123456789ABCDEF"; char *p; Dstr *dstr; int i; esc_set = (p_esc_set) ? p_esc_set : "%#:' "; dstr = dStr_sized_new(64); for (i = 0; str[i]; ++i) { if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) { dStr_append_c(dstr, '%'); dStr_append_c(dstr, hex[(str[i] >> 4) & 15]); dStr_append_c(dstr, hex[str[i] & 15]); } else {
/* * Printf like function for building dpip commands. * It takes care of dpip escaping of its arguments. * NOTE : It ONLY accepts string parameters, and * only one %s per parameter. */ char *a_Dpip_build_cmd(const char *format, ...) { va_list argp; char *p, *q, *s; Dstr *cmd; /* Don't allow Quote characters in attribute names */ if (strchr(format, Quote)) return NULL; cmd = dStr_sized_new(64); dStr_append_c(cmd, '<'); va_start(argp, format); for (p = q = (char*)format; *q; ) { p = strstr(q, "%s"); if (!p) { dStr_append(cmd, q); break; } else { /* Copy format's part */ while (q != p) dStr_append_c(cmd, *q++); q += 2; dStr_append_c(cmd, Quote); /* Stuff-copy of argument */ s = va_arg (argp, char *); for ( ; *s; ++s) { dStr_append_c(cmd, *s); if (*s == Quote) dStr_append_c(cmd, *s); } dStr_append_c(cmd, Quote); } } va_end(argp); dStr_append_c(cmd, ' '); dStr_append_c(cmd, Quote); dStr_append_c(cmd, '>'); p = cmd->str; dStr_free(cmd, FALSE); return p; }
/* * Set safe values for a new cache entry */ static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url) { NewEntry->Url = a_Url_dup(Url); NewEntry->TypeDet = NULL; NewEntry->TypeHdr = NULL; NewEntry->TypeMeta = NULL; NewEntry->TypeNorm = NULL; NewEntry->Header = dStr_new(""); NewEntry->Location = NULL; NewEntry->Auth = NULL; NewEntry->Data = dStr_sized_new(8*1024); NewEntry->UTF8Data = NULL; NewEntry->DataRefcount = 0; NewEntry->TransferDecoder = NULL; NewEntry->ContentDecoder = NULL; NewEntry->CharsetDecoder = NULL; NewEntry->ExpectedSize = 0; NewEntry->TransferSize = 0; NewEntry->Flags = CA_IsEmpty; }
/* * Scan, allocate, and set things according to header info. * (This function needs the whole header to work) */ static void Cache_parse_header(CacheEntry_t *entry) { char *header = entry->Header->str; char *Length, *Type, *location_str, *encoding; #ifndef DISABLE_COOKIES Dlist *Cookies; #endif Dlist *warnings; void *data; int i; _MSG("Cache_parse_header\n"); if (entry->Header->len > 12) { if (header[9] == '1' && header[10] == '0' && header[11] == '0') { /* 100: Continue. The "real" header has not come yet. */ MSG("An actual 100 Continue header!\n"); entry->Flags &= ~CA_GotHeader; dStr_free(entry->Header, 1); entry->Header = dStr_new(""); return; } if (header[9] == '3' && header[10] == '0' && (location_str = Cache_parse_field(header, "Location"))) { /* 30x: URL redirection */ DilloUrl *location_url = a_Url_new(location_str,URL_STR_(entry->Url)); if (prefs.filter_auto_requests == PREFS_FILTER_SAME_DOMAIN && !a_Url_same_organization(entry->Url, location_url)) { /* don't redirect; just show body like usual (if any) */ MSG("Redirection not followed from %s to %s\n", URL_HOST(entry->Url), URL_STR(location_url)); a_Url_free(location_url); } else { entry->Flags |= CA_Redirect; if (header[11] == '1') entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */ else if (header[11] == '2') entry->Flags |= CA_TempRedirect; /* 302 Temporary Redirect */ if (URL_FLAGS(location_url) & (URL_Post + URL_Get) && dStrAsciiCasecmp(URL_SCHEME(location_url), "dpi") == 0 && dStrAsciiCasecmp(URL_SCHEME(entry->Url), "dpi") != 0) { /* Forbid dpi GET and POST from non dpi-generated urls */ MSG("Redirection Denied! '%s' -> '%s'\n", URL_STR(entry->Url), URL_STR(location_url)); a_Url_free(location_url); } else { entry->Location = location_url; } } dFree(location_str); } else if (strncmp(header + 9, "401", 3) == 0) { entry->Auth = Cache_parse_multiple_fields(header, "WWW-Authenticate"); } else if (strncmp(header + 9, "404", 3) == 0) { entry->Flags |= CA_NotFound; } } if ((warnings = Cache_parse_multiple_fields(header, "Warning"))) { for (i = 0; (data = dList_nth_data(warnings, i)); ++i) { MSG_HTTP("%s\n", (char *)data); dFree(data); } dList_free(warnings); } /* * Get Transfer-Encoding and initialize decoder */ encoding = Cache_parse_field(header, "Transfer-Encoding"); entry->TransferDecoder = a_Decode_transfer_init(encoding); if ((Length = Cache_parse_field(header, "Content-Length")) != NULL) { if (encoding) { /* * If Transfer-Encoding is present, Content-Length must be ignored. * If the Transfer-Encoding is non-identity, it is an error. */ if (dStrAsciiCasecmp(encoding, "identity")) MSG_HTTP("Content-Length and non-identity Transfer-Encoding " "headers both present.\n"); } else { entry->Flags |= CA_GotLength; entry->ExpectedSize = MAX(strtol(Length, NULL, 10), 0); } dFree(Length); } dFree(encoding); /* free Transfer-Encoding */ #ifndef DISABLE_COOKIES if ((Cookies = Cache_parse_multiple_fields(header, "Set-Cookie"))) { CacheClient_t *client; for (i = 0; (client = dList_nth_data(ClientQueue, i)); ++i) { if (client->Url == entry->Url) { DilloWeb *web = client->Web; if (!web->requester || a_Url_same_organization(entry->Url, web->requester)) { char *server_date = Cache_parse_field(header, "Date"); a_Cookies_set(Cookies, entry->Url, server_date); dFree(server_date); break; } } } if (i >= dList_length(ClientQueue)) { MSG("Cache: cookies not accepted from '%s'\n", URL_STR(entry->Url)); } for (i = 0; (data = dList_nth_data(Cookies, i)); ++i) dFree(data); dList_free(Cookies); } #endif /* !DISABLE_COOKIES */ /* * Get Content-Encoding and initialize decoder */ encoding = Cache_parse_field(header, "Content-Encoding"); entry->ContentDecoder = a_Decode_content_init(encoding); dFree(encoding); if (entry->ExpectedSize > 0) { if (entry->ExpectedSize > HUGE_FILESIZE) { entry->Flags |= CA_HugeFile; } /* Avoid some reallocs. With MAX_INIT_BUF we avoid a SEGFAULT * with huge files (e.g. iso files). * Note: the buffer grows automatically. */ dStr_free(entry->Data, 1); entry->Data = dStr_sized_new(MIN(entry->ExpectedSize, MAX_INIT_BUF)); } /* Get Content-Type */ if ((Type = Cache_parse_field(header, "Content-Type"))) { /* This HTTP Content-Type is not trusted. It's checked against real data * in Cache_process_queue(); only then CA_GotContentType becomes true. */ a_Cache_set_content_type(entry->Url, Type, "http"); _MSG("TypeHdr {%s} {%s}\n", Type, URL_STR(entry->Url)); _MSG("TypeMeta {%s}\n", entry->TypeMeta); dFree(Type); } Cache_ref_data(entry); }