Esempio n. 1
0
File: prefs.c Progetto: dimkr/dillo
/*
 *  Preferences memory-deallocation
 *  (Call this one at exit time)
 */
void a_Prefs_freeall(void)
{
   g_free(prefs.http_proxyuser);
   g_free(prefs.no_proxy);
   if (prefs.no_proxy_vec)
      g_strfreev(prefs.no_proxy_vec);
   a_Url_free(prefs.http_proxy);
   g_free(prefs.fw_fontname);
   g_free(prefs.vw_fontname);
   a_Url_free(prefs.start_page);
   a_Url_free(prefs.home);
   g_free(prefs.search_url);
}
Esempio n. 2
0
/*
 * Deallocate memory used by http module
 * (Call this one at exit time)
 */
void a_Http_freeall(void)
{
   Http_host_connection_remove_all();
   a_Klist_free(&ValidSocks);
   a_Url_free(HTTP_Proxy);
   dFree(HTTP_Proxy_Auth_base64);
   dFree(HTTP_Language_hdr);
}
Esempio n. 3
0
/*
 * Save link from behind a timeout so that Cache_process_queue() can
 * get on with its work.
 */
static void Cache_savelink_cb(void *vdata)
{
   Cache_savelink_t *data = (Cache_savelink_t*) vdata;

   a_UIcmd_save_link(data->bw, data->url);
   a_Url_free(data->url);
   dFree(data);
}
Esempio n. 4
0
File: web.c Progetto: dimkr/dillo
/*
 * Deallocate a DilloWeb structure
 */
void a_Web_free(DilloWeb *web)
{
   if (!web) return;
   if (web->url)
      a_Url_free(web->url);
   if (web->Image)
      a_Image_unref(web->Image);
   ValidWebs = g_slist_remove(ValidWebs, (gpointer)web);
   g_free(web);
}
Esempio n. 5
0
/*
 *  Free the components of a CacheEntry_t struct.
 */
static void Cache_entry_free(CacheEntry_t *entry)
{
   a_Url_free((DilloUrl *)entry->Url);
   dFree(entry->TypeDet);
   dFree(entry->TypeHdr);
   dFree(entry->TypeMeta);
   dFree(entry->TypeNorm);
   dStr_free(entry->Header, TRUE);
   a_Url_free((DilloUrl *)entry->Location);
   Cache_auth_free(entry->Auth);
   dStr_free(entry->Data, 1);
   dStr_free(entry->UTF8Data, 1);
   if (entry->CharsetDecoder)
      a_Decode_free(entry->CharsetDecoder);
   if (entry->TransferDecoder)
      a_Decode_free(entry->TransferDecoder);
   if (entry->ContentDecoder)
      a_Decode_free(entry->ContentDecoder);
   dFree(entry);
}
Esempio n. 6
0
/*
 * Ask for user/password and reload the page.
 */
static void Cache_auth_callback(void *vdata)
{
   CacheAuthData_t *data = (CacheAuthData_t *)vdata;
   if (a_Auth_do_auth(data->auth, data->url))
      a_Nav_reload(data->bw);
   Cache_auth_free(data->auth);
   a_Url_free(data->url);
   dFree(data);
   Cache_auth_entry(NULL, NULL);
   a_Timeout_remove();
}
Esempio n. 7
0
/*
 * Initialize dicache data
 */
void a_Cache_init(void)
{
   ClientQueue = dList_new(32);
   DelayedQueue = dList_new(32);
   CachedURLs = dList_new(256);

   /* inject the splash screen in the cache */
   {
      DilloUrl *url = a_Url_new("about:splash", NULL);
      Dstr *ds = dStr_new(AboutSplash);
      Cache_entry_inject(url, ds);
      dStr_free(ds, 1);
      a_Url_free(url);
   }
}
Esempio n. 8
0
/*
 * Process redirections (HTTP 30x answers)
 * (This is a work in progress --not finished yet)
 */
static int Cache_redirect(CacheEntry_t *entry, int Flags, BrowserWindow *bw)
{
   DilloUrl *NewUrl;

   _MSG(" Cache_redirect: redirect_level = %d\n", bw->redirect_level);

   /* Don't allow redirection for SpamSafe/local URLs */
   if (URL_FLAGS(entry->Url) & URL_SpamSafe) {
      a_UIcmd_set_msg(bw, "WARNING: local URL with redirection.  Aborting.");
      return 0;
   }

   /* if there's a redirect loop, stop now */
   if (bw->redirect_level >= 5)
      entry->Flags |= CA_RedirectLoop;

   if (entry->Flags & CA_RedirectLoop) {
      a_UIcmd_set_msg(bw, "ERROR: redirect loop for: %s", URL_STR_(entry->Url));
      bw->redirect_level = 0;
      return 0;
   }

   if ((entry->Flags & CA_Redirect && entry->Location) &&
       (entry->Flags & CA_ForceRedirect || entry->Flags & CA_TempRedirect ||
        !entry->Data->len || entry->Data->len < 1024)) {

      _MSG(">>>> Redirect from: %s\n to %s <<<<\n",
           URL_STR_(entry->Url), URL_STR_(entry->Location));
      _MSG("%s", entry->Header->str);

      if (Flags & WEB_RootUrl) {
         /* Redirection of the main page */
         NewUrl = a_Url_new(URL_STR_(entry->Location), URL_STR_(entry->Url));
         if (entry->Flags & CA_TempRedirect)
            a_Url_set_flags(NewUrl, URL_FLAGS(NewUrl) | URL_E2EQuery);
         a_Nav_push(bw, NewUrl, entry->Url);
         a_Url_free(NewUrl);
      } else {
         /* Sub entity redirection (most probably an image) */
         if (!entry->Data->len) {
            _MSG(">>>> Image redirection without entity-content <<<<\n");
         } else {
            _MSG(">>>> Image redirection with entity-content <<<<\n");
         }
      }
   }
   return 0;
}
Esempio n. 9
0
/*
 * Decrement the reference count (and remove from list when zero)
 */
static void Capi_conn_unref(capi_conn_t *conn)
{
   _MSG(" Capi_conn_unref #%d %p\n", conn->Ref - 1, conn);

   /* We may validate conn here, but it doesn't *seem* necessary */
   if (--conn->Ref == 0) {
      /* remove conn preserving the list order */
      dList_remove(CapiConns, (void *)conn);
      /* free dynamic memory */
      a_Url_free(conn->url);
      dFree(conn->server);
      dFree(conn->datastr);
      dFree(conn);
   }
   _MSG(" Capi_conn_unref CapiConns=%d\n", dList_length(CapiConns));
}
Esempio n. 10
0
File: auth.c Progetto: epitron/dillo
/*
 * Return: Nonzero if we got new credentials from the user and everything
 * seems fine.
 */
static int Auth_do_auth_dialog(const AuthParse_t *auth_parse,
                               const DilloUrl *url)
{
   int ret;
   char *title, *msg;
   AuthDialogData_t *data;
   const char *typestr = auth_parse->type == DIGEST ? "Digest" : "Basic";

   _MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", auth_parse->realm);

   title = dStrconcat("Dillo: Password for ", auth_parse->realm, NULL);
   msg = dStrconcat("The server at ", URL_HOST(url), " requires a username"
                    " and password for  \"", auth_parse->realm, "\".\n\n"
                    "Authentication scheme: ", typestr, NULL);
   data = dNew(AuthDialogData_t, 1);
   data->auth_parse = auth_parse;
   data->url = a_Url_dup(url);
   ret = a_Dialog_user_password(title, msg, Auth_do_auth_dialog_cb, data);
   dFree(title); dFree(msg);
   a_Url_free((void *)data->url);
   dFree(data);
   return ret;
}
Esempio n. 11
0
File: prefs.c Progetto: dimkr/dillo
/*
 * Read tokens from dillorc and set values in the prefs structure.
 * (Although this function can be called several times, and not leak,
 *  preferences aren't yet enabled for on-the-fly changes).
 */
static guint Prefs_parser(GScanner *scanner)
{
   gint st;
   guint symbol;

   /* expect a valid symbol */
   g_scanner_get_next_token(scanner);
   symbol = scanner->token;
   if (scanner->token == G_TOKEN_EQUAL_SIGN) {
      g_scanner_get_next_token (scanner);
      return G_TOKEN_NONE;
   } else if (symbol < DRC_TOKEN_FIRST || symbol > DRC_TOKEN_LAST )
      return G_TOKEN_SYMBOL;

   /* expect '=' */
   g_scanner_get_next_token(scanner);
   if (scanner->token != G_TOKEN_EQUAL_SIGN)
      return G_TOKEN_EQUAL_SIGN;

   /* expect a string */
   g_scanner_get_next_token(scanner);
   if (scanner->token != G_TOKEN_STRING)
      return G_TOKEN_STRING;

   /* assign value and exit successfully */
   switch (symbol) {
   case DRC_TOKEN_GEOMETRY:
      a_Misc_parse_geometry(scanner->value.v_string, &prefs.xpos, &prefs.ypos,
                            &prefs.width, &prefs.height);
      break;
   case DRC_TOKEN_PROXY:
      a_Url_free(prefs.http_proxy);
      prefs.http_proxy = a_Url_new(scanner->value.v_string, NULL, 0, 0, 0);
      break;
   case DRC_TOKEN_PROXYUSER:
      g_free(prefs.http_proxyuser);
      prefs.http_proxyuser = g_strdup(scanner->value.v_string);
      break;
   case DRC_TOKEN_NOPROXY:
      g_free(prefs.no_proxy);
      prefs.no_proxy = g_strdup(scanner->value.v_string);
      prefs.no_proxy_vec = g_strsplit(prefs.no_proxy, " ", 0);
      break;
   case DRC_TOKEN_LINK_COLOR:
      prefs.link_color = a_Color_parse(scanner->value.v_string,
                                       prefs.link_color, &st);
      break;
   case DRC_TOKEN_VISITED_COLOR:
      prefs.visited_color = a_Color_parse(scanner->value.v_string,
                                          prefs.visited_color, &st);
      break;
   case DRC_TOKEN_TEXT_COLOR:
      prefs.text_color = a_Color_parse(scanner->value.v_string,
                                       prefs.text_color, &st);
      break;
   case DRC_TOKEN_BG_COLOR:
      prefs.bg_color = a_Color_parse(scanner->value.v_string,
                                     prefs.bg_color, &st);
      break;
   case DRC_TOKEN_ALLOW_WHITE_BG:
      prefs.allow_white_bg = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_FORCE_MY_COLORS:
      prefs.force_my_colors = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_CONTRAST_VISITED_COLOR:
      prefs.contrast_visited_color =
         (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_USE_OBLIQUE:
      prefs.use_oblique = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_PANEL_SIZE:
      if (!g_strcasecmp(scanner->value.v_string, "tiny"))
         prefs.panel_size = 1;
      else if (!g_strcasecmp(scanner->value.v_string, "small"))
         prefs.panel_size = 2;
      else if (!g_strcasecmp(scanner->value.v_string, "medium"))
         prefs.panel_size = 3;
      else /* default to "large" */
         prefs.panel_size = 4;
      break;
   case DRC_TOKEN_SMALL_ICONS:
      prefs.small_icons = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_START_PAGE:
      a_Url_free(prefs.start_page);
      prefs.start_page = a_Url_new(scanner->value.v_string, NULL, 0, 0, 0);
      break;
   case DRC_TOKEN_HOME:
      a_Url_free(prefs.home);
      prefs.home = a_Url_new(scanner->value.v_string, NULL, 0, 0, 0);
      break;
   case DRC_TOKEN_SHOW_TOOLTIP:
      prefs.show_tooltip = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_FONT_FACTOR:
      prefs.font_factor = strtod(scanner->value.v_string, NULL);
      break;
   case DRC_TOKEN_LIMIT_TEXT_WIDTH:
      prefs.limit_text_width = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_W3C_PLUS_HEURISTICS:
      prefs.w3c_plus_heuristics = (strcmp(scanner->value.v_string,"YES") == 0);
      break;
   case DRC_TOKEN_USE_DICACHE:
      prefs.use_dicache = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_BACK:
      prefs.show_back = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_FORW:
      prefs.show_forw = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_HOME:
      prefs.show_home = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_RELOAD:
      prefs.show_reload = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_SAVE:
      prefs.show_save = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_STOP:
      prefs.show_stop = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_BOOKMARKS:
      prefs.show_bookmarks = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_MENUBAR:
      prefs.show_menubar = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_CLEAR_URL:
      prefs.show_clear_url = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_URL:
      prefs.show_url = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_SEARCH:
      prefs.show_search = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_PROGRESS_BOX:
      prefs.show_progress_box = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_FULLWINDOW_START:
      prefs.fullwindow_start = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_TRANSIENT_DIALOGS:
      prefs.transient_dialogs = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_FW_FONT:
      g_free(prefs.fw_fontname);
      prefs.fw_fontname = g_strdup(scanner->value.v_string);
      break;
   case DRC_TOKEN_VW_FONT:
      g_free(prefs.vw_fontname);
      prefs.vw_fontname = g_strdup(scanner->value.v_string);
      break;
   case DRC_TOKEN_GENERATE_SUBMIT:
      prefs.generate_submit = (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT:
      prefs.enterpress_forces_submit =
         (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SEARCH_URL:
      g_free(prefs.search_url);
      prefs.search_url = g_strdup(scanner->value.v_string);
      break;
   case DRC_TOKEN_SHOW_MSG:
      prefs.show_msg =
         (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   case DRC_TOKEN_SHOW_EXTRA_WARNINGS:
      prefs.show_extra_warnings =
         (strcmp(scanner->value.v_string, "YES") == 0);
      break;
   default:
      break;   /* Not reached */
   }
   return G_TOKEN_NONE;
}
Esempio n. 12
0
/*
 * 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);
}
Esempio n. 13
0
/*
 * Update cache clients for a single cache-entry
 * Tasks:
 *   - Set the client function (if not already set)
 *   - Look if new data is available and pass it to client functions
 *   - Remove clients when done
 *   - Call redirect handler
 *
 * Return: Cache entry, which may be NULL if it has been removed.
 *
 * TODO: Implement CA_Abort Op in client callback
 */
static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry)
{
   uint_t i;
   int st;
   const char *Type;
   Dstr *data;
   CacheClient_t *Client;
   DilloWeb *ClientWeb;
   BrowserWindow *Client_bw = NULL;
   static bool_t Busy = FALSE;
   bool_t AbortEntry = FALSE;
   bool_t OfferDownload = FALSE;
   bool_t TypeMismatch = FALSE;

   if (Busy)
      MSG_ERR("FATAL!: >>>> Cache_process_queue Caught busy!!! <<<<\n");
   if (!(entry->Flags & CA_GotHeader))
      return entry;
   if (!(entry->Flags & CA_GotContentType)) {
      st = a_Misc_get_content_type_from_data(
              entry->Data->str, entry->Data->len, &Type);
      _MSG("Cache: detected Content-Type '%s'\n", Type);
      if (st == 0 || entry->Flags & CA_GotData) {
         if (a_Misc_content_type_check(entry->TypeHdr, Type) < 0) {
            MSG_HTTP("Content-Type '%s' doesn't match the real data.\n",
                     entry->TypeHdr);
            TypeMismatch = TRUE;
         }
         entry->TypeDet = dStrdup(Type);
         entry->Flags |= CA_GotContentType;
      } else
         return entry;  /* i.e., wait for more data */
   }

   Busy = TRUE;
   for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) {
      if (Client->Url == entry->Url) {
         ClientWeb = Client->Web;    /* It was a (void*) */
         Client_bw = ClientWeb->bw;  /* 'bw' in a local var */

         if (ClientWeb->flags & WEB_RootUrl) {
            if (!(entry->Flags & CA_MsgErased)) {
               /* clear the "expecting for reply..." message */
               a_UIcmd_set_msg(Client_bw, "");
               entry->Flags |= CA_MsgErased;
            }
            if (TypeMismatch) {
               a_UIcmd_set_msg(Client_bw,"HTTP warning: Content-Type '%s' "
                               "doesn't match the real data.", entry->TypeHdr);
               OfferDownload = TRUE;
            }
            if (entry->Flags & CA_Redirect) {
               if (!Client->Callback) {
                  Client->Callback = Cache_null_client;
                  Client_bw->redirect_level++;
               }
            } else {
               Client_bw->redirect_level = 0;
            }
            if (entry->Flags & CA_HugeFile) {
               a_UIcmd_set_msg(Client_bw,"Huge file! (%dMB)",
                               entry->ExpectedSize / (1024*1024));
               AbortEntry = OfferDownload = TRUE;
            }
         } else {
            /* For non root URLs, ignore redirections and 404 answers */
            if (entry->Flags & CA_Redirect || entry->Flags & CA_NotFound)
               Client->Callback = Cache_null_client;
         }

         /* Set the client function */
         if (!Client->Callback) {
            Client->Callback = Cache_null_client;
            if (TypeMismatch) {
               AbortEntry = TRUE;
            } else {
               const char *curr_type = Cache_current_content_type(entry);
               st = a_Web_dispatch_by_type(curr_type, ClientWeb,
                                           &Client->Callback, &Client->CbData);
               if (st == -1) {
                  /* MIME type is not viewable */
                  if (ClientWeb->flags & WEB_RootUrl) {
                     MSG("Content-Type '%s' not viewable.\n", curr_type);
                     /* prepare a download offer... */
                     AbortEntry = OfferDownload = TRUE;
                  } else {
                     /* TODO: Resource Type not handled.
                      * Not aborted to avoid multiple connections on the same
                      * resource. A better idea is to abort the connection and
                      * to keep a failed-resource flag in the cache entry. */
                  }
               }
            }
            if (AbortEntry) {
               if (ClientWeb->flags & WEB_RootUrl)
                  a_Nav_cancel_expect_if_eq(Client_bw, Client->Url);
               a_Bw_remove_client(Client_bw, Client->Key);
               Cache_client_dequeue(Client, NULLKey);
               --i; /* Keep the index value in the next iteration */
               continue;
            }
         }

         /* Send data to our client */
         if (ClientWeb->flags & WEB_Download) {
            /* for download, always provide original data, not translated */
            data = entry->Data;
         } else {
            data = Cache_data(entry);
         }
         if ((Client->BufSize = data->len) > 0) {
            Client->Buf = data->str;
            (Client->Callback)(CA_Send, Client);
            if (ClientWeb->flags & WEB_RootUrl) {
               /* show size of page received */
               a_UIcmd_set_page_prog(Client_bw, entry->Data->len, 1);
            }
         }

         /* Remove client when done */
         if (entry->Flags & CA_GotData) {
            /* Copy flags to a local var */
            int flags = ClientWeb->flags;
            /* We finished sending data, let the client know */
            (Client->Callback)(CA_Close, Client);
            if (ClientWeb->flags & WEB_RootUrl)
               a_UIcmd_set_page_prog(Client_bw, 0, 0);
            Cache_client_dequeue(Client, NULLKey);
            --i; /* Keep the index value in the next iteration */

            /* within CA_GotData, we assert just one redirect call */
            if (entry->Flags & CA_Redirect)
               Cache_redirect(entry, flags, Client_bw);
         }
      }
   } /* for */

   if (AbortEntry) {
      /* Abort the entry, remove it from cache, and maybe offer download. */
      DilloUrl *url = a_Url_dup(entry->Url);
      a_Capi_conn_abort_by_url(url);
      entry = NULL;
      if (OfferDownload) {
         /* Remove entry when 'conn' is already done */
         Cache_entry_remove(NULL, url);
         if (a_Cache_download_enabled(url)) {
            Cache_savelink_t *data = dNew(Cache_savelink_t, 1);
            data->bw = Client_bw;
            data->url = a_Url_dup(url);
            a_Timeout_add(0.0, Cache_savelink_cb, data);
         }
      }
      a_Url_free(url);
   } else if (entry->Auth && (entry->Flags & CA_GotData)) {
      Cache_auth_entry(entry, Client_bw);
   }

   /* Trigger cleanup when there are no cache clients */
   if (dList_length(ClientQueue) == 0) {
      _MSG("  a_Dicache_cleanup()\n");
      a_Dicache_cleanup();
   }

   Busy = FALSE;
   _MSG("QueueSize ====> %d\n", dList_length(ClientQueue));
   return entry;
}