Example #1
0
/*
 * 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;
}
Example #2
0
/*
 * Decode gzipped data
 */
static Dstr *Decode_gzip(Decode *dc, const char *instr, int inlen)
{
   int rc = Z_OK;

   z_stream *zs = (z_stream *)dc->state;

   int inputConsumed = 0;
   Dstr *output = dStr_new("");

   while ((rc == Z_OK) && (inputConsumed < inlen)) {
      zs->next_in = (Bytef *)instr + inputConsumed;
      zs->avail_in = inlen - inputConsumed;

      zs->next_out = (Bytef *)dc->buffer;
      zs->avail_out = bufsize;

      rc = inflate(zs, Z_SYNC_FLUSH);

      dStr_append_l(output, dc->buffer, zs->total_out);

      if ((rc == Z_OK) || (rc == Z_STREAM_END)) {
         // Z_STREAM_END at end of file

         inputConsumed += zs->total_in;
         zs->total_out = 0;
         zs->total_in = 0;
      } else if (rc == Z_DATA_ERROR) {
         MSG_ERR("gzip decompression error\n");
      }
   }
   return output;
}
Example #3
0
/*
 * 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;
}
Example #4
0
/*
 * 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);
}
Example #5
0
File: dpip.c Project: epitron/dillo
/*
 * Return value: 1..DataSize sent, -1 eagain, or -3 on big Error
 */
int a_Dpip_dsh_trywrite(Dsh *dsh, const char *Data, int DataSize)
{
   int st;

   if ((st = Dpip_dsh_write(dsh, 1, Data, DataSize)) > 0) {
      /* update internal buffer */
      if (st < DataSize)
         dStr_append_l(dsh->wrbuf, Data + st, DataSize - st);
   }
   return st;
}
Example #6
0
/*
 * Translate to desired character set (UTF-8)
 */
static Dstr *Decode_charset(Decode *dc, const char *instr, int inlen)
{
   inbuf_t *inPtr;
   char *outPtr;
   size_t inLeft, outRoom;

   Dstr *output = dStr_new("");
   int rc = 0;

   dStr_append_l(dc->leftover, instr, inlen);
   inPtr = dc->leftover->str;
   inLeft = dc->leftover->len;

   while ((rc != EINVAL) && (inLeft > 0)) {

      outPtr = dc->buffer;
      outRoom = bufsize;

      rc = iconv((iconv_t)dc->state, &inPtr, &inLeft, &outPtr, &outRoom);

      // iconv() on success, number of bytes converted
      //         -1, errno == EILSEQ illegal byte sequence found
      //                      EINVAL partial character ends source buffer
      //                      E2BIG  destination buffer is full

      dStr_append_l(output, dc->buffer, bufsize - outRoom);

      if (rc == -1)
         rc = errno;
      if (rc == EILSEQ){
         inPtr++;
         inLeft--;
         dStr_append_l(output, utf8_replacement_char,
                       sizeof(utf8_replacement_char) - 1);
      }
   }
   dStr_erase(dc->leftover, 0, dc->leftover->len - inLeft);

   return output;
}
Example #7
0
/*
 * Inject full page content directly into the cache.
 * Used for "about:splash". May be used for "about:cache" too.
 */
static void Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds)
{
   CacheEntry_t *entry;

   if (!(entry = Cache_entry_search(Url)))
      entry = Cache_entry_add(Url);
   entry->Flags |= CA_GotData + CA_GotHeader + CA_GotLength + CA_InternalUrl;
   if (data_ds->len)
      entry->Flags &= ~CA_IsEmpty;
   dStr_truncate(entry->Data, 0);
   dStr_append_l(entry->Data, data_ds->str, data_ds->len);
   dStr_fit(entry->Data);
   entry->ExpectedSize = entry->TransferSize = entry->Data->len;
}
Example #8
0
/*
 * Read raw data from the socket into our buffer in
 * either BLOCKING or NONBLOCKING mode.
 */
static void Dpip_dsh_read(Dsh *dsh, int blocking)
{
   char buf[RBUF_SZ];
   int req_mode, old_flags = 0, st, ret = -3, nb = !blocking;

   dReturn_if (dsh->status == DPIP_ERROR || dsh->status == DPIP_EOF);

   req_mode = (nb) ? DPIP_NONBLOCK : 0;
   if ((dsh->mode & DPIP_NONBLOCK) != req_mode) {
      /* change mode temporarily... */
      old_flags = fcntl(dsh->fd_in, F_GETFL);
      fcntl(dsh->fd_in, F_SETFL,
            (nb) ? O_NONBLOCK | old_flags : old_flags & ~O_NONBLOCK);
   }

   while (1) {
      st = read(dsh->fd_in, buf, RBUF_SZ);
      if (st < 0) {
         if (errno == EINTR) {
            continue;
         } else if (errno == EAGAIN) {
            dsh->status = DPIP_EAGAIN;
            ret = -1;
            break;
         } else {
            MSG_ERR("[Dpip_dsh_read] %s\n", dStrerror(errno));
            dsh->status = DPIP_ERROR;
            break;
         }
      } else if (st == 0) {
         dsh->status = DPIP_EOF;
         break;
      } else {
         /* append to buf */
         dStr_append_l(dsh->rdbuf, buf, st);
         if (blocking)
            break;
      }
   }

   if ((dsh->mode & DPIP_NONBLOCK) != req_mode) {
      /* restore old mode */
      fcntl(dsh->fd_out, F_SETFL, old_flags);
   }

   /* assert there's no more data in the wire...
    * (st < buf upon interrupt || st == buf and no more data) */
   if (blocking)
      Dpip_dsh_read(dsh, 0);
}
Example #9
0
File: dpip.c Project: epitron/dillo
/*
 * Streamed write to socket
 * Return: 0 on success, 1 on error.
 */
int a_Dpip_dsh_write(Dsh *dsh, int flush, const char *Data, int DataSize)
{
   int ret = 1;

   /* append to buf */
   dStr_append_l(dsh->wrbuf, Data, DataSize);

   if (!flush || dsh->wrbuf->len == 0)
      return 0;

   ret = Dpip_dsh_write(dsh, 0, dsh->wrbuf->str, dsh->wrbuf->len);
   if (ret == dsh->wrbuf->len) {
      dStr_truncate(dsh->wrbuf, 0);
      ret = 0;
   }

   return ret;
}
Example #10
0
/*
 * Generate Content-Type header value for a POST query.
 */
static Dstr *Http_make_content_type(const DilloUrl *url)
{
   Dstr *dstr;

   if (URL_FLAGS(url) & URL_MultipartEnc) {
      MSG("submitting multipart/form-data!\n");
      dstr = dStr_new("multipart/form-data; boundary=\"");
      if (URL_DATA(url)->len > 2) {
         /* boundary lines have "--" prepended. Skip that. */
         const char *start = URL_DATA(url)->str + 2;
         char *eol = strchr(start, '\r');
         if (eol)
            dStr_append_l(dstr, start, eol - start);
      } else {
         /* Zero parts; arbitrary boundary */
         dStr_append_c(dstr, '0');
      }
      dStr_append_c(dstr,'"');
   } else {
      dstr = dStr_new("application/x-www-form-urlencoded");
   }
   return dstr;
}
Example #11
0
/*
 * Make the http query string
 */
Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester,
                            bool_t use_proxy)
{
   char *ptr, *cookies, *referer, *auth;
   Dstr *query      = dStr_new(""),
        *request_uri = dStr_new(""),
        *proxy_auth = dStr_new("");

   if (use_proxy) {
      dStr_sprintfa(request_uri, "%s%s",
                    URL_STR(url),
                    (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
      if ((ptr = strrchr(request_uri->str, '#')))
         dStr_truncate(request_uri, ptr - request_uri->str);
      if (HTTP_Proxy_Auth_base64)
         dStr_sprintf(proxy_auth, "Proxy-Authorization: Basic %s\r\n",
                      HTTP_Proxy_Auth_base64);
   } else {
      dStr_sprintfa(request_uri, "%s%s%s%s",
                    URL_PATH(url),
                    URL_QUERY_(url) ? "?" : "",
                    URL_QUERY(url),
                    (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/");
   }

   cookies = a_Cookies_get_query(url, requester);
   auth = a_Auth_get_auth_str(url, request_uri->str);
   referer = Http_get_referer(url);
   if (URL_FLAGS(url) & URL_Post) {
      Dstr *content_type = Http_make_content_type(url);
      dStr_sprintfa(
         query,
         "POST %s HTTP/1.1\r\n"
         "Connection: close\r\n"
         "Accept: text/*,image/*,*/*;q=0.2\r\n"
         "Accept-Charset: utf-8,*;q=0.8\r\n"
         "Accept-Encoding: gzip\r\n"
         "%s" /* language */
         "%s" /* auth */
         "Host: %s\r\n"
         "%s"
         "%s"
         "User-Agent: %s\r\n"
         "Content-Length: %ld\r\n"
         "Content-Type: %s\r\n"
         "%s" /* cookies */
         "\r\n",
         request_uri->str, HTTP_Language_hdr, auth ? auth : "",
         URL_AUTHORITY(url), proxy_auth->str, referer, prefs.http_user_agent,
         (long)URL_DATA(url)->len, content_type->str,
         cookies);
      dStr_append_l(query, URL_DATA(url)->str, URL_DATA(url)->len);
      dStr_free(content_type, TRUE);
   } else {
      dStr_sprintfa(
         query,
         "GET %s HTTP/1.1\r\n"
         "%s"
         "Connection: close\r\n"
         "Accept: text/*,image/*,*/*;q=0.2\r\n"
         "Accept-Charset: utf-8,*;q=0.8\r\n"
         "Accept-Encoding: gzip\r\n"
         "%s" /* language */
         "%s" /* auth */
         "Host: %s\r\n"
         "%s"
         "%s"
         "User-Agent: %s\r\n"
         "%s" /* cookies */
         "\r\n",
         request_uri->str,
         (URL_FLAGS(url) & URL_E2EQuery) ?
            "Cache-Control: no-cache\r\nPragma: no-cache\r\n" : "",
         HTTP_Language_hdr, auth ? auth : "", URL_AUTHORITY(url),
         proxy_auth->str, referer, prefs.http_user_agent, cookies);
   }
   dFree(referer);
   dFree(cookies);
   dFree(auth);

   dStr_free(request_uri, TRUE);
   dStr_free(proxy_auth, TRUE);
   _MSG("Query: {%s}\n", dStr_printable(query, 8192));
   return query;
}
Example #12
0
/*
 * 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");
   }
}
Example #13
0
/*
 * 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
 */
bool_t 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;
   bool_t done = FALSE;
   CacheEntry_t *entry = Cache_entry_search(Url);

   /* Assert a valid entry (not aborted) */
   dReturn_val_if_fail (entry != NULL, FALSE);

   _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_transfer_process(entry->TransferDecoder, str,len);
            done = a_Decode_transfer_finished(entry->TransferDecoder);
            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;

         if ((entry->Flags & CA_GotLength) &&
             (entry->TransferSize >= entry->ExpectedSize)) {
            done = TRUE;
         }
         if (!(entry->Flags & CA_KeepAlive)) {
            /* Let IOClose finish it later */
            done = FALSE;
         }

         entry = Cache_process_queue(entry);

         if (entry && done)
            Cache_finish_msg(entry);
      }
   } else if (Op == IOClose) {
      Cache_finish_msg(entry);
   } else if (Op == IOAbort) {
      int i;
      CacheClient_t *Client;

      for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) {
         if (Client->Url == entry->Url) {
            DilloWeb *web = (DilloWeb *)Client->Web;

            a_Bw_remove_client(web->bw, Client->Key);
            Cache_client_dequeue(Client);
            --i; /* Keep the index value in the next iteration */
         }
      }
   }
   return done;
}