/*
     This file is part of GNUnet.
     Copyright (C) 2003, 2004, 2006, 2009 GNUnet e.V.

     GNUnet is free software: you can redistribute it and/or modify it
     under the terms of the GNU Affero General Public License as published
     by the Free Software Foundation, either version 3 of the License,
     or (at your option) any later version.

     GNUnet is distributed in the hope that it will be useful, but
     WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     Affero General Public License for more details.

     You should have received a copy of the GNU Affero General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

     SPDX-License-Identifier: AGPL3.0-or-later
 */

/**
 * @file fs/fs_list_indexed.c
 * @author Christian Grothoff
 * @brief provide a list of all indexed files
 */

#include "platform.h"
#include "gnunet_constants.h"

#include "gnunet_fs_service.h"
#include "gnunet_protocols.h"
#include "fs_api.h"


/**
 * Context for #GNUNET_FS_get_indexed_files().
 */
struct GNUNET_FS_GetIndexedContext
{
  /**
   * Connection to the FS service.
   */
  struct GNUNET_MQ_Handle *mq;

  /**
   * Function to call for each indexed file.
   */
  GNUNET_FS_IndexedFileProcessor iterator;

  /**
   * Closure for @e iterator.
   */
  void *iterator_cls;

  /**
   * Continuation to trigger at the end.
   */
  GNUNET_SCHEDULER_TaskCallback cont;

  /**
   * Closure for @e cont.
   */
  void *cont_cls;
};


/**
 * Function called on each response from the FS
 * service with information about indexed files.
 *
 * @param cls closure (of type `struct GNUNET_FS_GetIndexedContext *`)
 * @param msg message with indexing information
 */
static void
handle_index_info_end (void *cls,
                       const struct GNUNET_MessageHeader *msg)
{
  struct GNUNET_FS_GetIndexedContext *gic = cls;

  (void) gic->iterator (gic->iterator_cls,
                        NULL,
                        NULL);
  GNUNET_FS_get_indexed_files_cancel (gic);
}


/**
 * Check validity of response from the FS
 * service with information about indexed files.
 *
 * @param cls closure (of type `struct GNUNET_FS_GetIndexedContext *`)
 * @param iim message with indexing information
 */
static int
check_index_info (void *cls,
                  const struct IndexInfoMessage *iim)
{
  uint16_t msize = ntohs (iim->header.size) - sizeof(*iim);
  const char *filename;

  filename = (const char *) &iim[1];
  if (filename[msize - 1] != '\0')
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}


/**
 * Function called on each response from the FS
 * service with information about indexed files.
 *
 * @param cls closure (of type `struct GNUNET_FS_GetIndexedContext *`)
 * @param iim message with indexing information
 */
static void
handle_index_info (void *cls,
                   const struct IndexInfoMessage *iim)
{
  struct GNUNET_FS_GetIndexedContext *gic = cls;
  const char *filename;

  filename = (const char *) &iim[1];
  if (GNUNET_OK !=
      gic->iterator (gic->iterator_cls,
                     filename,
                     &iim->file_id))
  {
    GNUNET_FS_get_indexed_files_cancel (gic);
    return;
  }
}


/**
 * Generic error handler, called with the appropriate error code and
 * the same closure specified at the creation of the message queue.
 * Not every message queue implementation supports an error handler.
 *
 * @param cls closure with the `struct GNUNET_FS_GetIndexedContent *`
 * @param error error code
 */
static void
mq_error_handler (void *cls,
                  enum GNUNET_MQ_Error error)
{
  struct GNUNET_FS_GetIndexedContext *gic = cls;

  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
              "Failed to receive response from `%s' service (error code is %d).\n",
              "fs",
              error);
  (void) gic->iterator (gic->iterator_cls,
                        NULL,
                        NULL);
  GNUNET_FS_get_indexed_files_cancel (gic);
}


struct GNUNET_FS_GetIndexedContext *
GNUNET_FS_get_indexed_files (struct GNUNET_FS_Handle *h,
                             GNUNET_FS_IndexedFileProcessor iterator,
                             void *iterator_cls)
{
  struct GNUNET_FS_GetIndexedContext *gic
    = GNUNET_new (struct GNUNET_FS_GetIndexedContext);
  struct GNUNET_MQ_MessageHandler handlers[] = {
    GNUNET_MQ_hd_fixed_size (index_info_end,
                             GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_END,
                             struct GNUNET_MessageHeader,
                             gic),
    GNUNET_MQ_hd_var_size (index_info,
                           GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY,
                           struct IndexInfoMessage,
                           gic),
    GNUNET_MQ_handler_end ()
  };
  struct GNUNET_MQ_Envelope *env;
  struct GNUNET_MessageHeader *msg;

  gic->mq = GNUNET_CLIENT_connect (h->cfg,
                                   "fs",
                                   handlers,
                                   &mq_error_handler,
                                   h);
  if (NULL == gic->mq)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                _ ("Failed to not connect to `%s' service.\n"),
                "fs");
    GNUNET_free (gic);
    return NULL;
  }
  gic->iterator = iterator;
  gic->iterator_cls = iterator_cls;
  env = GNUNET_MQ_msg (msg,
                       GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_GET);
  GNUNET_MQ_send (gic->mq,
                  env);
  return gic;
}


/**
 * Cancel iteration over all indexed files.
 *
 * @param gic operation to cancel
 */
void
GNUNET_FS_get_indexed_files_cancel (struct GNUNET_FS_GetIndexedContext *gic)
{
  GNUNET_MQ_destroy (gic->mq);
  GNUNET_free (gic);
}


/* end of fs_list_indexed.c */
