/* * Return a newlly allocated string with the next dpip token in the socket. * Return value: token string and length on success, NULL otherwise. * (useful for handling null characters in the data stream) */ char *a_Dpip_dsh_read_token2(Dsh *dsh, int blocking, int *DataSize) { char *p, *ret = NULL; *DataSize = 0; /* Read all available data without blocking */ Dpip_dsh_read(dsh, 0); /* switch mode upon request */ if (dsh->mode & DPIP_LAST_TAG) dsh->mode = DPIP_RAW; if (blocking) { if (dsh->mode & DPIP_TAG) { /* Only wait for data when the tag is incomplete */ if (!strstr(dsh->rdbuf->str, DPIP_TAG_END)) { do { Dpip_dsh_read(dsh, 1); p = strstr(dsh->rdbuf->str, DPIP_TAG_END); } while (!p && dsh->status == EAGAIN); } } else if (dsh->mode & DPIP_RAW) { /* Wait for data when the buffer is empty and there's no ERR/EOF */ while (dsh->rdbuf->len == 0 && dsh->status != DPIP_ERROR && dsh->status != DPIP_EOF) Dpip_dsh_read(dsh, 1); } } if (dsh->mode & DPIP_TAG) { /* return a full tag */ if ((p = strstr(dsh->rdbuf->str, DPIP_TAG_END))) { ret = dStrndup(dsh->rdbuf->str, p - dsh->rdbuf->str + 3); *DataSize = p - dsh->rdbuf->str + 3; dStr_erase(dsh->rdbuf, 0, p - dsh->rdbuf->str + 3); if (strstr(ret, DPIP_MODE_SWITCH_TAG)) dsh->mode |= DPIP_LAST_TAG; } } else { /* raw mode, return what we have "as is" */ if (dsh->rdbuf->len > 0) { ret = dStrndup(dsh->rdbuf->str, dsh->rdbuf->len); *DataSize = dsh->rdbuf->len; dStr_truncate(dsh->rdbuf, 0); } } return ret; }
/* * Extract a single field from the header, allocating and storing the value * in 'field'. ('fieldname' must not include the trailing ':') * Return a new string with the field-content if found (NULL on error) * (This function expects a '\r'-stripped header, with one-line header fields) */ static char *Cache_parse_field(const char *header, const char *fieldname) { char *field; uint_t i, j; for (i = 0; header[i]; i++) { /* Search fieldname */ for (j = 0; fieldname[j]; j++) if (D_ASCII_TOLOWER(fieldname[j]) != D_ASCII_TOLOWER(header[i + j])) break; if (fieldname[j]) { /* skip to next line */ for ( i += j; header[i] != '\n'; i++); continue; } i += j; if (header[i] == ':') { /* Field found! */ while (header[++i] == ' ' || header[i] == '\t'); for (j = 0; header[i + j] != '\n'; j++); while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t')) j--; field = dStrndup(header + i, j); return field; } while (header[i] != '\n') i++; } return NULL; }
/* * If the url belongs to a dpi server, return its name. */ static int Capi_url_uses_dpi(DilloUrl *url, char **server_ptr) { char *p, *server = NULL, *url_str = URL_STR(url); Dstr *tmp; if ((dStrncasecmp(url_str, "http:", 5) == 0) || (dStrncasecmp(url_str, "about:", 6) == 0)) { /* URL doesn't use dpi (server = NULL) */ } else if (dStrncasecmp(url_str, "dpi:/", 5) == 0) { /* dpi prefix, get this server's name */ if ((p = strchr(url_str + 5, '/')) != NULL) { server = dStrndup(url_str + 5, (uint_t)(p - url_str - 5)); } else { server = dStrdup("?"); } if (strcmp(server, "bm") == 0) { dFree(server); server = dStrdup("bookmarks"); } } else if ((p = strchr(url_str, ':')) != NULL) { tmp = dStr_new("proto."); dStr_append_l(tmp, url_str, p - url_str); server = tmp->str; dStr_free(tmp, 0); } return ((*server_ptr = server) ? 1 : 0); }
/* * Task: given a tag, its size and an attribute name, return the * attribute value (stuffing of ' is removed here). * * Return value: the attribute value, or NULL if not present or malformed. */ char *a_Dpip_get_attr_l(const char *tag, size_t tagsize, const char *attrname) { uint_t i, n = 0, found = 0; const char *p, *q, *start; char *r, *s, *val = NULL; DpipTagParsingState state = SEEK_NAME; if (!tag || !tagsize || !attrname || !*attrname) return NULL; for (i = 1; i < tagsize && !found; ++i) { switch (state) { case SEEK_NAME: if (tag[i] == attrname[0] && (tag[i-1] == ' ' || tag[i-1] == '<')) { n = 1; state = MATCH_NAME; } else if (tag[i] == Quote && tag[i-1] == '=') state = SKIP_VALUE; break; case MATCH_NAME: if (tag[i] == attrname[n]) ++n; else if (tag[i] == '=' && !attrname[n]) state = FOUND; else state = SEEK_NAME; break; case SKIP_VALUE: if (tag[i] == Quote) state = (tag[i+1] == Quote) ? SKIP_QUOTE : SEEK_NAME; break; case SKIP_QUOTE: state = SKIP_VALUE; break; case FOUND: found = 1; break; } } if (found) { p = start = tag + i; while ((q = strchr(p, Quote)) && q[1] == Quote) p = q + 2; if (q && q[1] == ' ') { val = dStrndup(start, (uint_t)(q - start)); for (r = s = val; (*r = *s); ++r, ++s) if (s[0] == Quote && s[0] == s[1]) ++s; } } return val; }
/* * Extract multiple fields from the header. */ static Dlist *Cache_parse_multiple_fields(const char *header, const char *fieldname) { uint_t i, j; Dlist *fields = dList_new(8); char *field; for (i = 0; header[i]; i++) { /* Search fieldname */ for (j = 0; fieldname[j]; j++) if (D_ASCII_TOLOWER(fieldname[j]) != D_ASCII_TOLOWER(header[i + j])) break; if (fieldname[j]) { /* skip to next line */ for (i += j; header[i] != '\n'; i++); continue; } i += j; if (header[i] == ':') { /* Field found! */ while (header[++i] == ' ' || header[i] == '\t'); for (j = 0; header[i + j] != '\n'; j++); while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t')) j--; field = dStrndup(header + i, j); dList_append(fields, field); } else { while (header[i] != '\n') i++; } } if (dList_length(fields) == 0) { dList_free(fields); fields = NULL; } return fields; }
/* * Receive new data, update the reception buffer (for next read), update the * cache, and service the client queue. * * This function gets called whenever the IO has new data. * 'Op' is the operation to perform * 'VPtr' is a (void) pointer to the IO control structure */ void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, const DilloUrl *Url) { int offset, len; const char *str; Dstr *dstr1, *dstr2, *dstr3; CacheEntry_t *entry = Cache_entry_search(Url); /* Assert a valid entry (not aborted) */ dReturn_if_fail (entry != NULL); _MSG("__a_Cache_process_dbuf__\n"); if (Op == IORead) { /* * Cache_get_header() will set CA_GotHeader if it has a full header, and * Cache_parse_header() will unset it if the header ends being * merely an informational response from the server (i.e., 100 Continue) */ for (offset = 0; !(entry->Flags & CA_GotHeader) && (len = Cache_get_header(entry, buf + offset, buf_size - offset)); Cache_parse_header(entry) ) { offset += len; } if (entry->Flags & CA_GotHeader) { str = buf + offset; len = buf_size - offset; entry->TransferSize += len; dstr1 = dstr2 = dstr3 = NULL; /* Decode arrived data (<= 3 stages) */ if (entry->TransferDecoder) { dstr1 = a_Decode_process(entry->TransferDecoder, str, len); str = dstr1->str; len = dstr1->len; } if (entry->ContentDecoder) { dstr2 = a_Decode_process(entry->ContentDecoder, str, len); str = dstr2->str; len = dstr2->len; } dStr_append_l(entry->Data, str, len); if (entry->CharsetDecoder && entry->UTF8Data) { dstr3 = a_Decode_process(entry->CharsetDecoder, str, len); dStr_append_l(entry->UTF8Data, dstr3->str, dstr3->len); } dStr_free(dstr1, 1); dStr_free(dstr2, 1); dStr_free(dstr3, 1); if (entry->Data->len) entry->Flags &= ~CA_IsEmpty; entry = Cache_process_queue(entry); } } else if (Op == IOClose) { if ((entry->ExpectedSize || entry->TransferSize) && entry->TypeHdr == NULL) { MSG_HTTP("Message with a body lacked Content-Type header.\n"); } if ((entry->Flags & CA_GotLength) && (entry->ExpectedSize != entry->TransferSize)) { MSG_HTTP("Content-Length does NOT match message body,\n" " at: %s\n", URL_STR_(entry->Url)); MSG("entry->ExpectedSize = %d, entry->TransferSize = %d\n", entry->ExpectedSize, entry->TransferSize); } if (!entry->TransferSize && !(entry->Flags & CA_Redirect) && (entry->Flags & WEB_RootUrl)) { char *eol = strchr(entry->Header->str, '\n'); if (eol) { char *status_line = dStrndup(entry->Header->str, eol - entry->Header->str); MSG_HTTP("Body was empty. Server sent status: %s\n", status_line); dFree(status_line); } } entry->Flags |= CA_GotData; entry->Flags &= ~CA_Stopped; /* it may catch up! */ if (entry->TransferDecoder) { a_Decode_free(entry->TransferDecoder); entry->TransferDecoder = NULL; } if (entry->ContentDecoder) { a_Decode_free(entry->ContentDecoder); entry->ContentDecoder = NULL; } dStr_fit(entry->Data); /* fit buffer size! */ if ((entry = Cache_process_queue(entry))) { if (entry->Flags & CA_GotHeader) { Cache_unref_data(entry); } } } else if (Op == IOAbort) { /* unused */ MSG("a_Cache_process_dbuf Op = IOAbort; not implemented!\n"); } }