struct NntpServer *nntp_select_server(char *server, bool leave_lock)
{
  char file[PATH_MAX];
#ifdef USE_HCACHE
  char *p = NULL;
#endif
  int rc;
  struct Account acct;
  struct NntpServer *nserv = NULL;
  struct NntpData *nntp_data = NULL;
  struct Connection *conn = NULL;
  struct Url url;

  if (!server || !*server)
  {
    mutt_error(_("No news server defined!"));
    return NULL;
  }

  /* create account from news server url */
  acct.flags = 0;
  acct.port = NNTP_PORT;
  acct.type = MUTT_ACCT_TYPE_NNTP;
  snprintf(file, sizeof(file), "%s%s", strstr(server, "://") ? "" : "news://", server);
  if (url_parse(&url, file) < 0 || (url.path && *url.path) ||
      !(url.scheme == U_NNTP || url.scheme == U_NNTPS) || !url.host ||
      mutt_account_fromurl(&acct, &url) < 0)
  {
    url_free(&url);
    mutt_error(_("%s is an invalid news server specification!"), server);
    return NULL;
  }
  if (url.scheme == U_NNTPS)
  {
    acct.flags |= MUTT_ACCT_SSL;
    acct.port = NNTP_SSL_PORT;
  }
  url_free(&url);

  /* find connection by account */
  conn = mutt_conn_find(NULL, &acct);
  if (!conn)
    return NULL;
  if (!(conn->account.flags & MUTT_ACCT_USER) && acct.flags & MUTT_ACCT_USER)
  {
    conn->account.flags |= MUTT_ACCT_USER;
    conn->account.user[0] = '\0';
  }

  /* news server already exists */
  nserv = conn->data;
  if (nserv)
  {
    if (nserv->status == NNTP_BYE)
      nserv->status = NNTP_NONE;
    if (nntp_open_connection(nserv) < 0)
      return NULL;

    rc = nntp_newsrc_parse(nserv);
    if (rc < 0)
      return NULL;

    /* check for new newsgroups */
    if (!leave_lock && nntp_check_new_groups(nserv) < 0)
      rc = -1;

    /* .newsrc has been externally modified */
    if (rc > 0)
      nntp_clear_cache(nserv);
    if (rc < 0 || !leave_lock)
      nntp_newsrc_close(nserv);
    return (rc < 0) ? NULL : nserv;
  }

  /* new news server */
  nserv = mutt_mem_calloc(1, sizeof(struct NntpServer));
  nserv->conn = conn;
  nserv->groups_hash = mutt_hash_create(1009, 0);
  mutt_hash_set_destructor(nserv->groups_hash, nntp_hash_destructor, 0);
  nserv->groups_max = 16;
  nserv->groups_list = mutt_mem_malloc(nserv->groups_max * sizeof(nntp_data));

  rc = nntp_open_connection(nserv);

  /* try to create cache directory and enable caching */
  nserv->cacheable = false;
  if (rc >= 0 && NewsCacheDir && *NewsCacheDir)
  {
    cache_expand(file, sizeof(file), &conn->account, NULL);
    if (mutt_file_mkdir(file, S_IRWXU) < 0)
    {
      mutt_error(_("Can't create %s: %s."), file, strerror(errno));
    }
    nserv->cacheable = true;
  }

  /* load .newsrc */
  if (rc >= 0)
  {
    mutt_expando_format(file, sizeof(file), 0, MuttIndexWindow->cols,
                        NONULL(Newsrc), nntp_format_str, (unsigned long) nserv, 0);
    mutt_expand_path(file, sizeof(file));
    nserv->newsrc_file = mutt_str_strdup(file);
    rc = nntp_newsrc_parse(nserv);
  }
  if (rc >= 0)
  {
    /* try to load list of newsgroups from cache */
    if (nserv->cacheable && active_get_cache(nserv) == 0)
      rc = nntp_check_new_groups(nserv);

    /* load list of newsgroups from server */
    else
      rc = nntp_active_fetch(nserv, false);
  }

  if (rc >= 0)
    nntp_clear_cache(nserv);

#ifdef USE_HCACHE
  /* check cache files */
  if (rc >= 0 && nserv->cacheable)
  {
    struct dirent *entry = NULL;
    DIR *dp = opendir(file);

    if (dp)
    {
      while ((entry = readdir(dp)))
      {
        header_cache_t *hc = NULL;
        void *hdata = NULL;
        char *group = entry->d_name;

        p = group + strlen(group) - 7;
        if (strlen(group) < 8 || (strcmp(p, ".hcache") != 0))
          continue;
        *p = '\0';
        nntp_data = mutt_hash_find(nserv->groups_hash, group);
        if (!nntp_data)
          continue;

        hc = nntp_hcache_open(nntp_data);
        if (!hc)
          continue;

        /* fetch previous values of first and last */
        hdata = mutt_hcache_fetch_raw(hc, "index", 5);
        if (hdata)
        {
          anum_t first, last;

          if (sscanf(hdata, ANUM " " ANUM, &first, &last) == 2)
          {
            if (nntp_data->deleted)
            {
              nntp_data->first_message = first;
              nntp_data->last_message = last;
            }
            if (last >= nntp_data->first_message && last <= nntp_data->last_message)
            {
              nntp_data->last_cached = last;
              mutt_debug(2, "%s last_cached=%u\n", nntp_data->group, last);
            }
          }
          mutt_hcache_free(hc, &hdata);
        }
        mutt_hcache_close(hc);
      }
      closedir(dp);
    }
  }
#endif

  if (rc < 0 || !leave_lock)
    nntp_newsrc_close(nserv);

  if (rc < 0)
  {
    mutt_hash_destroy(&nserv->groups_hash);
    FREE(&nserv->groups_list);
    FREE(&nserv->newsrc_file);
    FREE(&nserv->authenticators);
    FREE(&nserv);
    mutt_socket_close(conn);
    mutt_socket_free(conn);
    return NULL;
  }

  conn->data = nserv;
  return nserv;
}
struct NntpServer *nntp_select_server(char *server, bool leave_lock)
{
  char file[PATH_MAX];
#ifdef USE_HCACHE
  char *p = NULL;
#endif
  int rc;
  struct Account acct;
  struct NntpServer *nserv = NULL;
  struct NntpData *nntp_data = NULL;
  struct Connection *conn = NULL;
  struct Url url;

  if (!server || !*server)
  {
    mutt_error(_("No news server defined!"));
    return NULL;
  }

   
  acct.flags = 0;
  acct.port = NNTP_PORT;
  acct.type = MUTT_ACCT_TYPE_NNTP;
  snprintf(file, sizeof(file), "%s%s", strstr(server, "://") ? "" : "news://", server);
  if (url_parse(&url, file) < 0 || (url.path && *url.path) ||
      !(url.scheme == U_NNTP || url.scheme == U_NNTPS) || !url.host ||
      mutt_account_fromurl(&acct, &url) < 0)
  {
    url_free(&url);
    mutt_error(_("%s is an invalid news server specification!"), server);
    return NULL;
  }
  if (url.scheme == U_NNTPS)
  {
    acct.flags |= MUTT_ACCT_SSL;
    acct.port = NNTP_SSL_PORT;
  }
  url_free(&url);

   
  conn = mutt_conn_find(NULL, &acct);
  if (!conn)
    return NULL;
  if (!(conn->account.flags & MUTT_ACCT_USER) && acct.flags & MUTT_ACCT_USER)
  {
    conn->account.flags |= MUTT_ACCT_USER;
    conn->account.user[0] = '\0';
  }

   
  nserv = conn->data;
  if (nserv)
  {
    if (nserv->status == NNTP_BYE)
      nserv->status = NNTP_NONE;
    if (nntp_open_connection(nserv) < 0)
      return NULL;

    rc = nntp_newsrc_parse(nserv);
    if (rc < 0)
      return NULL;

     
    if (!leave_lock && nntp_check_new_groups(nserv) < 0)
      rc = -1;

     
    if (rc > 0)
      nntp_clear_cache(nserv);
    if (rc < 0 || !leave_lock)
      nntp_newsrc_close(nserv);
    return (rc < 0) ? NULL : nserv;
  }

   
  nserv = mutt_mem_calloc(1, sizeof(struct NntpServer));
  nserv->conn = conn;
  nserv->groups_hash = mutt_hash_create(1009, 0);
  mutt_hash_set_destructor(nserv->groups_hash, nntp_hash_destructor, 0);
  nserv->groups_max = 16;
  nserv->groups_list = mutt_mem_malloc(nserv->groups_max * sizeof(nntp_data));

  rc = nntp_open_connection(nserv);

   
  nserv->cacheable = false;
  if (rc >= 0 && NewsCacheDir && *NewsCacheDir)
  {
    cache_expand(file, sizeof(file), &conn->account, NULL);
    if (mutt_file_mkdir(file, S_IRWXU) < 0)
    {
      mutt_error(_("Can't create %s: %s."), file, strerror(errno));
    }
    nserv->cacheable = true;
  }

   
  if (rc >= 0)
  {
    mutt_expando_format(file, sizeof(file), 0, MuttIndexWindow->cols,
                        NONULL(Newsrc), nntp_format_str, (unsigned long) nserv, 0);
    mutt_expand_path(file, sizeof(file));
    nserv->newsrc_file = mutt_str_strdup(file);
    rc = nntp_newsrc_parse(nserv);
  }
  if (rc >= 0)
  {
     
    if (nserv->cacheable && active_get_cache(nserv) == 0)
      rc = nntp_check_new_groups(nserv);

     
    else
      rc = nntp_active_fetch(nserv, false);
  }

  if (rc >= 0)
    nntp_clear_cache(nserv);

#ifdef USE_HCACHE
   
  if (rc >= 0 && nserv->cacheable)
  {
    struct dirent *entry = NULL;
    DIR *dp = opendir(file);

    if (dp)
    {
      while ((entry = readdir(dp)))
      {
        header_cache_t *hc = NULL;
        void *hdata = NULL;
        char *group = entry->d_name;

        p = group + strlen(group) - 7;
        if (strlen(group) < 8 || (strcmp(p, ".hcache") != 0))
          continue;
        *p = '\0';
        nntp_data = mutt_hash_find(nserv->groups_hash, group);
        if (!nntp_data)
          continue;

        hc = nntp_hcache_open(nntp_data);
        if (!hc)
          continue;

         
        hdata = mutt_hcache_fetch_raw(hc, "index", 5);
        if (hdata)
        {
          anum_t first, last;

          if (sscanf(hdata, ANUM " " ANUM, &first, &last) == 2)
          {
            if (nntp_data->deleted)
            {
              nntp_data->first_message = first;
              nntp_data->last_message = last;
            }
            if (last >= nntp_data->first_message && last <= nntp_data->last_message)
            {
              nntp_data->last_cached = last;
              mutt_debug(2, "%s last_cached=%u\n", nntp_data->group, last);
            }
          }
          mutt_hcache_free(hc, &hdata);
        }
        mutt_hcache_close(hc);
      }
      closedir(dp);
    }
  }
#endif

  if (rc < 0 || !leave_lock)
    nntp_newsrc_close(nserv);

  if (rc < 0)
  {
    mutt_hash_destroy(&nserv->groups_hash);
    FREE(&nserv->groups_list);
    FREE(&nserv->newsrc_file);
    FREE(&nserv->authenticators);
    FREE(&nserv);
    mutt_socket_close(conn);
    mutt_socket_free(conn);
    return NULL;
  }

  conn->data = nserv;
  return nserv;
}