mageni/mageni

View on GitHub
src/backend/api/src/api_tickets.c

Summary

Maintainability
Test Coverage
/**
 * SPDX-License-Identifier: GPL-2.0-or-later
 * SPDX-FileCopyrightText: Copyright 2018 Greenbone Networks GmbH
 * SPDX-FileComment: Base facilities.
 * SPDX-FileContributor: Matthew Mundell <matthew.mundell@greenbone.net>
 * SPDX-FileContributor: Mageni Security LLC
 * 
 */

#include "api_tickets.h"
#include "api_base.h"
#include "api_get.h"
#include "manage_tickets.h"

#include <glib.h>
#include "../../libraries/util/xmlutils.h"
#include <stdlib.h>
#include <string.h>

#undef G_LOG_DOMAIN
/**
 * @brief GLib log domain.
 */
#define G_LOG_DOMAIN "md    gmp"

/* GET_TICKETS. */

/**
 * @brief The get_tickets command.
 */
typedef struct
{
  get_data_t get; ///< Get args.
} get_tickets_t;

/**
 * @brief Parser callback data.
 *
 * This is initially 0 because it's a global variable.
 */
static get_tickets_t get_tickets_data;

/**
 * @brief Reset command data.
 */
static void
get_tickets_reset ()
{
  get_data_reset (&get_tickets_data.get);
  memset (&get_tickets_data, 0, sizeof (get_tickets_t));
}

/**
 * @brief Handle command start element.
 *
 * @param[in]  attribute_names   All attribute names.
 * @param[in]  attribute_values  All attribute values.
 */
void
get_tickets_start (const gchar **attribute_names,
                   const gchar **attribute_values)
{
  get_data_parse_attributes (
    &get_tickets_data.get, "ticket", attribute_names, attribute_values);
}

/**
 * @brief Handle end element.
 *
 * @param[in]  gmp_parser   GMP parser.
 * @param[in]  error        Error parameter.
 */
void
get_tickets_run (gmp_parser_t *gmp_parser, GError **error)
{
  iterator_t tickets;
  int count, filtered, ret, first;

  count = 0;

  ret = init_get ("get_tickets", &get_tickets_data.get, "Tickets", &first);
  if (ret)
    {
      switch (ret)
        {
        case 99:
          SEND_TO_CLIENT_OR_FAIL (
            XML_ERROR_SYNTAX ("get_tickets", "Permission denied"));
          break;
        default:
          internal_error_send_to_client (error);
          get_tickets_reset ();
          return;
        }
      get_tickets_reset ();
      return;
    }

  /* Setup the iterator. */

  ret = init_ticket_iterator (&tickets, &get_tickets_data.get);
  if (ret)
    {
      switch (ret)
        {
        case 1:
          if (send_find_error_to_client (
                "get_tickets", "ticket", get_tickets_data.get.id, gmp_parser))
            {
              error_send_to_client (error);
              get_tickets_reset ();
              return;
            }
          break;
        case 2:
          if (send_find_error_to_client ("get_tickets",
                                         "filter",
                                         get_tickets_data.get.filt_id,
                                         gmp_parser))
            {
              error_send_to_client (error);
              get_tickets_reset ();
              return;
            }
          break;
        case -1:
          SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("get_tickets"));
          break;
        }
      get_tickets_reset ();
      return;
    }

  /* Loop through tickets, sending XML. */

  SEND_GET_START ("ticket");
  while (1)
    {
      iterator_t results;
      int orphan;

      ret = get_next (
        &tickets, &get_tickets_data.get, &first, &count, init_ticket_iterator);
      if (ret == 1)
        break;
      if (ret == -1)
        {
          internal_error_send_to_client (error);
          get_tickets_reset ();
          return;
        }

      /* Send generic GET command elements. */

      SEND_GET_COMMON (ticket, &get_tickets_data.get, &tickets);

      /* Send ticket info. */

      SENDF_TO_CLIENT_OR_FAIL ("<assigned_to>"
                               "<user id=\"%s\">"
                               "<name>%s</name>"
                               "</user>"
                               "</assigned_to>"
                               "<severity>%1.1f</severity>"
                               "<host>%s</host>"
                               "<location>%s</location>"
                               "<solution_type>%s</solution_type>"
                               "<status>%s</status>"
                               "<open_time>%s</open_time>"
                               "<open_note>%s</open_note>"
                               "<nvt oid=\"%s\"/>",
                               ticket_iterator_user_id (&tickets),
                               ticket_iterator_user_name (&tickets),
                               ticket_iterator_severity (&tickets),
                               ticket_iterator_host (&tickets),
                               ticket_iterator_location (&tickets),
                               ticket_iterator_solution_type (&tickets),
                               ticket_iterator_status (&tickets),
                               ticket_iterator_open_time (&tickets),
                               ticket_iterator_open_note (&tickets),
                               ticket_iterator_nvt_oid (&tickets));

      if (ticket_iterator_task_id (&tickets))
        SENDF_TO_CLIENT_OR_FAIL (
          "<task id=\"%s\">"
          "<name>%s</name>"
          "<trash>%i</trash>"
          "</task>",
          ticket_iterator_task_id (&tickets),
          ticket_iterator_task_name (&tickets),
          task_in_trash_id (ticket_iterator_task_id (&tickets)));

      if (ticket_iterator_report_id (&tickets))
        {
          gchar *timestamp;

          if (report_timestamp (ticket_iterator_report_id (&tickets),
                                &timestamp))
            g_error ("%s: error getting timestamp of report, aborting",
                     __FUNCTION__);

          SENDF_TO_CLIENT_OR_FAIL ("<report id=\"%s\">"
                                   "<timestamp>%s</timestamp>"
                                   "</report>",
                                   ticket_iterator_report_id (&tickets),
                                   timestamp);
        }

      /* Send timestamps. */

      if (ticket_iterator_fixed_time (&tickets)
          && strlen (ticket_iterator_fixed_time (&tickets)))
        SENDF_TO_CLIENT_OR_FAIL ("<fixed_time>%s</fixed_time>",
                                 ticket_iterator_fixed_time (&tickets));

      if (ticket_iterator_fixed_note (&tickets))
        SENDF_TO_CLIENT_OR_FAIL ("<fixed_note>%s</fixed_note>",
                                 ticket_iterator_fixed_note (&tickets));

      if (ticket_iterator_closed_time (&tickets)
          && strlen (ticket_iterator_closed_time (&tickets)))
        SENDF_TO_CLIENT_OR_FAIL ("<closed_time>%s</closed_time>",
                                 ticket_iterator_closed_time (&tickets));

      if (ticket_iterator_closed_note (&tickets))
        SENDF_TO_CLIENT_OR_FAIL ("<closed_note>%s</closed_note>",
                                 ticket_iterator_closed_note (&tickets));

      if (ticket_iterator_fix_verified_time (&tickets)
          && strlen (ticket_iterator_fix_verified_time (&tickets)))
        {
          SENDF_TO_CLIENT_OR_FAIL (
            "<fix_verified_time>%s</fix_verified_time>",
            ticket_iterator_fix_verified_time (&tickets));
          if (ticket_iterator_fix_verified_report_id (&tickets))
            {
              gchar *timestamp;

              if (report_timestamp (
                    ticket_iterator_fix_verified_report_id (&tickets),
                    &timestamp))
                g_error ("%s: error getting timestamp of verified report,"
                         " aborting",
                         __FUNCTION__);

              SENDF_TO_CLIENT_OR_FAIL (
                "<fix_verified_report>"
                "<report id=\"%s\">"
                "<timestamp>%s</timestamp>"
                "</report>"
                "</fix_verified_report>",
                ticket_iterator_fix_verified_report_id (&tickets),
                timestamp);
            }
        }

      /* Send results that are linked to ticket. */

      if (init_ticket_result_iterator (
            &results, get_iterator_uuid (&tickets), get_tickets_data.get.trash))
        {
          internal_error_send_to_client (error);
          get_tickets_reset ();
          return;
        }
      orphan = 1;
      while (next (&results))
        {
          orphan = 0;
          SENDF_TO_CLIENT_OR_FAIL ("<result id=\"%s\"/>",
                                   ticket_result_iterator_result_id (&results));
        }
      cleanup_iterator (&results);

      SENDF_TO_CLIENT_OR_FAIL ("<orphan>%i</orphan>"
                               "</ticket>",
                               orphan);
      count++;
    }
  cleanup_iterator (&tickets);
  filtered = get_tickets_data.get.id ? 1 : ticket_count (&get_tickets_data.get);
  SEND_GET_END ("ticket", &get_tickets_data.get, count, filtered);

  get_tickets_reset ();
}

/* CREATE_TICKET. */

/**
 * @brief The create_ticket command.
 */
typedef struct
{
  context_data_t *context; ///< XML parser context.
} create_ticket_t;

/**
 * @brief Parser callback data.
 *
 * This is initially 0 because it's a global variable.
 */
static create_ticket_t create_ticket_data;

/**
 * @brief Reset command data.
 */
static void
create_ticket_reset ()
{
  if (create_ticket_data.context->first)
    {
      free_entity (create_ticket_data.context->first->data);
      g_slist_free_1 (create_ticket_data.context->first);
    }
  g_free (create_ticket_data.context);
  memset (&create_ticket_data, 0, sizeof (create_ticket_t));
}

/**
 * @brief Start a command.
 *
 * @param[in]  gmp_parser        GMP parser.
 * @param[in]  attribute_names   All attribute names.
 * @param[in]  attribute_values  All attribute values.
 */
void
create_ticket_start (gmp_parser_t *gmp_parser,
                     const gchar **attribute_names,
                     const gchar **attribute_values)
{
  memset (&create_ticket_data, 0, sizeof (create_ticket_t));
  create_ticket_data.context = g_malloc0 (sizeof (context_data_t));
  create_ticket_element_start (
    gmp_parser, "create_ticket", attribute_names, attribute_values);
}

/**
 * @brief Start element.
 *
 * @param[in]  gmp_parser        GMP parser.
 * @param[in]  name              Element name.
 * @param[in]  attribute_names   All attribute names.
 * @param[in]  attribute_values  All attribute values.
 */
void
create_ticket_element_start (gmp_parser_t *gmp_parser,
                             const gchar *name,
                             const gchar **attribute_names,
                             const gchar **attribute_values)
{
  xml_handle_start_element (
    create_ticket_data.context, name, attribute_names, attribute_values);
}

/**
 * @brief Execute command.
 *
 * @param[in]  gmp_parser   GMP parser.
 * @param[in]  error        Error parameter.
 */
void
create_ticket_run (gmp_parser_t *gmp_parser, GError **error)
{
  entity_t entity, copy, comment, result, assigned_to, user, open_note;
  ticket_t new_ticket;
  const char *result_id, *user_id;

  entity = (entity_t) create_ticket_data.context->first->data;

  copy = entity_child (entity, "copy");

  if (copy)
    {
      /* Copy from an existing ticket and exit. */

      comment = entity_child (entity, "comment");
      switch (copy_ticket (
        comment ? entity_text (comment) : "", entity_text (copy), &new_ticket))
        {
        case 0:
          {
            char *uuid;
            uuid = ticket_uuid (new_ticket);
            SENDF_TO_CLIENT_OR_FAIL (XML_OK_CREATED_ID ("create_ticket"), uuid);
            log_event ("ticket", "Ticket", uuid, "created");
            free (uuid);
            break;
          }
        case 1:
          SEND_TO_CLIENT_OR_FAIL (
            XML_ERROR_SYNTAX ("create_ticket", "Ticket exists already"));
          log_event_fail ("ticket", "Ticket", NULL, "created");
          break;
        case 2:
          if (send_find_error_to_client (
                "create_ticket", "ticket", entity_text (copy), gmp_parser))
            {
              error_send_to_client (error);
              return;
            }
          log_event_fail ("ticket", "Ticket", NULL, "created");
          break;
        case 99:
          SEND_TO_CLIENT_OR_FAIL (
            XML_ERROR_SYNTAX ("create_ticket", "Permission denied"));
          log_event_fail ("ticket", "Ticket", NULL, "created");
          break;
        case -1:
        default:
          SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("create_ticket"));
          log_event_fail ("ticket", "Ticket", NULL, "created");
          break;
        }
      create_ticket_reset ();
      return;
    }

  /* Check given info. */

  comment = entity_child (entity, "comment");

  open_note = entity_child (entity, "open_note");
  if (open_note == NULL)
    {
      SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX (
        "create_ticket", "CREATE_TICKET requires an OPEN_NOTE"));
      create_ticket_reset ();
      return;
    }

  result = entity_child (entity, "result");
  if (result == NULL)
    {
      SEND_TO_CLIENT_OR_FAIL (
        XML_ERROR_SYNTAX ("create_ticket", "CREATE_TICKET requires a RESULT"));
      create_ticket_reset ();
      return;
    }

  assigned_to = entity_child (entity, "assigned_to");
  if (assigned_to == NULL)
    {
      SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX (
        "create_ticket", "CREATE_TICKET requires an ASSIGNED_TO element"));
      create_ticket_reset ();
      return;
    }

  user = entity_child (assigned_to, "user");
  if (user == NULL)
    {
      SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX (
        "create_ticket", "CREATE_TICKET requires USER in ASSIGNED_TO"));
      create_ticket_reset ();
      return;
    }

  /* Create ticket from given info. */

  result_id = entity_attribute (result, "id");
  user_id = entity_attribute (user, "id");

  if ((result_id == NULL) || (strlen (result_id) == 0))
    SEND_TO_CLIENT_OR_FAIL (
      XML_ERROR_SYNTAX ("create_ticket",
                        "CREATE_TICKET RESULT must have an id"
                        " attribute"));
  else if ((user_id == NULL) || (strlen (user_id) == 0))
    SEND_TO_CLIENT_OR_FAIL (
      XML_ERROR_SYNTAX ("create_ticket",
                        "CREATE_TICKET USER must have an id"
                        " attribute"));
  else if (strlen (entity_text (open_note)) == 0)
    SEND_TO_CLIENT_OR_FAIL (
      XML_ERROR_SYNTAX ("create_ticket", "CREATE_TICKET OPEN_NOTE is empty"));
  else
    switch (create_ticket (comment ? entity_text (comment) : "",
                           result_id,
                           user_id,
                           entity_text (open_note),
                           &new_ticket))
      {
      case 0:
        {
          char *uuid = ticket_uuid (new_ticket);
          SENDF_TO_CLIENT_OR_FAIL (XML_OK_CREATED_ID ("create_ticket"), uuid);
          log_event ("ticket", "Ticket", uuid, "created");
          free (uuid);
          break;
        }
      case 1:
        log_event_fail ("ticket", "Ticket", NULL, "created");
        if (send_find_error_to_client (
              "create_ticket", "user", user_id, gmp_parser))
          {
            error_send_to_client (error);
            return;
          }
        break;
      case 2:
        log_event_fail ("ticket", "Ticket", NULL, "created");
        if (send_find_error_to_client (
              "create_ticket", "result", result_id, gmp_parser))
          {
            error_send_to_client (error);
            return;
          }
        break;
      case 99:
        SEND_TO_CLIENT_OR_FAIL (
          XML_ERROR_SYNTAX ("create_ticket", "Permission denied"));
        log_event_fail ("ticket", "Ticket", NULL, "created");
        break;
      case -1:
      default:
        SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("create_ticket"));
        log_event_fail ("ticket", "Ticket", NULL, "created");
        break;
      }

  create_ticket_reset ();
}

/**
 * @brief End element.
 *
 * @param[in]  gmp_parser   GMP parser.
 * @param[in]  error        Error parameter.
 * @param[in]  name         Element name.
 *
 * @return 0 success, 1 command finished.
 */
int
create_ticket_element_end (gmp_parser_t *gmp_parser,
                           GError **error,
                           const gchar *name)
{
  xml_handle_end_element (create_ticket_data.context, name);
  if (create_ticket_data.context->done)
    {
      create_ticket_run (gmp_parser, error);
      return 1;
    }
  return 0;
}

/**
 * @brief Add text to element.
 *
 * @param[in]  text         Text.
 * @param[in]  text_len     Text length.
 */
void
create_ticket_element_text (const gchar *text, gsize text_len)
{
  xml_handle_text (create_ticket_data.context, text, text_len);
}

/* MODIFY_TICKET. */

/**
 * @brief The modify_ticket command.
 */
typedef struct
{
  context_data_t *context; ///< XML parser context.
} modify_ticket_t;

/**
 * @brief Parser callback data.
 *
 * This is initially 0 because it's a global variable.
 */
static modify_ticket_t modify_ticket_data;

/**
 * @brief Reset command data.
 */
static void
modify_ticket_reset ()
{
  if (modify_ticket_data.context->first)
    {
      free_entity (modify_ticket_data.context->first->data);
      g_slist_free_1 (modify_ticket_data.context->first);
    }
  g_free (modify_ticket_data.context);
  memset (&modify_ticket_data, 0, sizeof (modify_ticket_t));
}

/**
 * @brief Start a command.
 *
 * @param[in]  gmp_parser        GMP parser.
 * @param[in]  attribute_names   All attribute names.
 * @param[in]  attribute_values  All attribute values.
 */
void
modify_ticket_start (gmp_parser_t *gmp_parser,
                     const gchar **attribute_names,
                     const gchar **attribute_values)
{
  memset (&modify_ticket_data, 0, sizeof (modify_ticket_t));
  modify_ticket_data.context = g_malloc0 (sizeof (context_data_t));
  modify_ticket_element_start (
    gmp_parser, "modify_ticket", attribute_names, attribute_values);
}

/**
 * @brief Start element.
 *
 * @param[in]  gmp_parser        GMP parser.
 * @param[in]  name              Element name.
 * @param[in]  attribute_names   All attribute names.
 * @param[in]  attribute_values  All attribute values.
 */
void
modify_ticket_element_start (gmp_parser_t *gmp_parser,
                             const gchar *name,
                             const gchar **attribute_names,
                             const gchar **attribute_values)
{
  xml_handle_start_element (
    modify_ticket_data.context, name, attribute_names, attribute_values);
}

/**
 * @brief Execute command.
 *
 * @param[in]  gmp_parser   GMP parser.
 * @param[in]  error        Error parameter.
 */
void
modify_ticket_run (gmp_parser_t *gmp_parser, GError **error)
{
  entity_t entity, comment, status, open_note, fixed_note, closed_note;
  entity_t assigned_to;
  const char *ticket_id, *user_id;

  entity = (entity_t) modify_ticket_data.context->first->data;

  ticket_id = entity_attribute (entity, "ticket_id");

  /* Check the given info. */

  comment = entity_child (entity, "comment");
  status = entity_child (entity, "status");
  open_note = entity_child (entity, "open_note");
  fixed_note = entity_child (entity, "fixed_note");
  closed_note = entity_child (entity, "closed_note");

  assigned_to = entity_child (entity, "assigned_to");
  if (assigned_to)
    {
      entity_t user;

      user = entity_child (assigned_to, "user");
      if (user == NULL)
        {
          SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX (
            "modify_ticket", "MODIFY_TICKET requires USER in ASSIGNED_TO"));
          modify_ticket_reset ();
          return;
        }

      user_id = entity_attribute (user, "id");
      if ((user_id == NULL) || (strlen (user_id) == 0))
        {
          SEND_TO_CLIENT_OR_FAIL (
            XML_ERROR_SYNTAX ("modify_ticket",
                              "MODIFY_TICKET USER must have an id"
                              " attribute"));
          modify_ticket_reset ();
          return;
        }
    }
  else
    user_id = NULL;

  /* Modify the ticket. */

  if (ticket_id == NULL)
    SEND_TO_CLIENT_OR_FAIL (
      XML_ERROR_SYNTAX ("modify_ticket",
                        "MODIFY_TICKET requires a ticket_id"
                        " attribute"));
  else
    switch (modify_ticket (ticket_id,
                           comment ? entity_text (comment) : NULL,
                           status ? entity_text (status) : NULL,
                           open_note ? entity_text (open_note) : NULL,
                           fixed_note ? entity_text (fixed_note) : NULL,
                           closed_note ? entity_text (closed_note) : NULL,
                           user_id))
      {
      case 0:
        SENDF_TO_CLIENT_OR_FAIL (XML_OK ("modify_ticket"));
        log_event ("ticket", "Ticket", ticket_id, "modified");
        break;
      case 1:
        SEND_TO_CLIENT_OR_FAIL (
          XML_ERROR_SYNTAX ("modify_ticket", "Ticket exists already"));
        log_event_fail ("ticket", "Ticket", ticket_id, "modified");
        break;
      case 2:
        log_event_fail ("ticket", "Ticket", ticket_id, "modified");
        if (send_find_error_to_client (
              "modify_ticket", "ticket", ticket_id, gmp_parser))
          {
            error_send_to_client (error);
            return;
          }
        break;
      case 3:
        log_event_fail ("ticket", "Ticket", ticket_id, "modified");
        if (send_find_error_to_client (
              "modify_ticket", "user", user_id, gmp_parser))
          {
            error_send_to_client (error);
            return;
          }
        break;
      case 4:
        SEND_TO_CLIENT_OR_FAIL (
          XML_ERROR_SYNTAX ("modify_ticket", "Error in status"));
        log_event_fail ("ticket", "Ticket", ticket_id, "modified");
        break;
      case 5:
        SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX (
          "modify_ticket", "Fixed STATUS requires a FIXED_NOTE"));
        log_event_fail ("ticket", "Ticket", ticket_id, "modified");
        break;
      case 6:
        SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX (
          "modify_ticket", "Closed STATUS requires a CLOSED_NOTE"));
        log_event_fail ("ticket", "Ticket", ticket_id, "modified");
        break;
      case 7:
        SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX (
          "modify_ticket", "Open STATUS requires an OPEN_NOTE"));
        log_event_fail ("ticket", "Ticket", ticket_id, "modified");
        break;
      case 99:
        SEND_TO_CLIENT_OR_FAIL (
          XML_ERROR_SYNTAX ("modify_ticket", "Permission denied"));
        log_event_fail ("ticket", "Ticket", ticket_id, "modified");
        break;
      case -1:
      default:
        SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_ticket"));
        log_event_fail ("ticket", "Ticket", ticket_id, "modified");
        break;
      }

  modify_ticket_reset ();
}

/**
 * @brief End element.
 *
 * @param[in]  gmp_parser   GMP parser.
 * @param[in]  error        Error parameter.
 * @param[in]  name         Element name.
 *
 * @return 0 success, 1 command finished.
 */
int
modify_ticket_element_end (gmp_parser_t *gmp_parser,
                           GError **error,
                           const gchar *name)
{
  xml_handle_end_element (modify_ticket_data.context, name);
  if (modify_ticket_data.context->done)
    {
      modify_ticket_run (gmp_parser, error);
      return 1;
    }
  return 0;
}

/**
 * @brief Add text to element.
 *
 * @param[in]  text         Text.
 * @param[in]  text_len     Text length.
 */
void
modify_ticket_element_text (const gchar *text, gsize text_len)
{
  xml_handle_text (modify_ticket_data.context, text, text_len);
}

/* Result ticket support. */

/**
 * @brief Buffer ticket XML for a result.
 *
 * @param[in]  buffer   Buffer.
 * @param[in]  result   Result.
 *
 * @return 0 success, -1 internal error.
 */
int
buffer_result_tickets_xml (GString *buffer, result_t result)
{
  iterator_t tickets;
  int ret;

  ret = init_result_ticket_iterator (&tickets, result);

  if (ret == 0)
    {
      buffer_xml_append_printf (buffer, "<tickets>");
      while (next (&tickets))
        buffer_xml_append_printf (buffer,
                                  "<ticket id=\"%s\"/>",
                                  result_ticket_iterator_ticket_id (&tickets));
      buffer_xml_append_printf (buffer, "</tickets>");
      cleanup_iterator (&tickets);
    }

  return ret;
}