static gboolean
infinoted_plugin_document_stream_process_send_chat_message(
  InfinotedPluginDocumentStreamStream* stream,
  const gchar** data,
  gsize* len)
{
  guint16 text_len;
  const gchar* text;

  if(*len < 2) return FALSE;
  text_len = *(guint16*)(*data);
  *data += 2; *len -= 2;

  if(*len < text_len) return FALSE;
  text = *data;
  *data += text_len; *len -= text_len;

  if(!INF_IS_CHAT_BUFFER(stream->buffer))
  {
    infinoted_plugin_document_stream_send_error(
      stream,
      "Not a chat session"
    );
  }
  else
  {
    infinoted_plugin_document_stream_chat_add_message(stream, text, text_len);
  }

  return TRUE;
}
/**
 * infd_chat_filesystem_format_write:
 * @storage: A #InfdFilesystemStorage.
 * @path: Storage path where to write the session to.
 * @buffer: The #InfChatBuffer to write.
 * @error: Location to store error information, if any, or %NULL.
 *
 * Writes the given buffer into the filesystem storage at @path. If
 * successful, the session can then be read back with
 * infd_chat_filesystem_format_read(). If the function fails, %FALSE is
 * returned and @error is set.
 *
 * Returns: %TRUE on success or %FALSE on error.
 */
gboolean
infd_chat_filesystem_format_write(InfdFilesystemStorage* storage,
                                  const gchar* path,
                                  InfChatBuffer* buffer,
                                  GError** error)
{
  FILE* stream;
  xmlDocPtr doc;
  xmlNodePtr root;
  xmlErrorPtr xmlerror;

  g_return_val_if_fail(INFD_IS_FILESYSTEM_STORAGE(storage), FALSE);
  g_return_val_if_fail(path != NULL, FALSE);
  g_return_val_if_fail(INF_IS_CHAT_BUFFER(buffer), FALSE);
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);

  /* Open stream before exporting buffer to XML so possible errors are
   * catched earlier. */
  stream = infd_filesystem_storage_open(
    INFD_FILESYSTEM_STORAGE(storage),
    "InfChat",
    path,
    "w",
    NULL,
    error
  );

  if(stream == NULL)
    return FALSE;

  root = xmlNewNode(NULL, (const xmlChar*)"inf-chat-session");

  doc = xmlNewDoc((const xmlChar*)"1.0");
  xmlDocSetRootElement(doc, root);

  /* TODO: At this point, we should tell libxml2 to use
   * infd_filesystem_storage_stream_write() instead of fwrite(),
   * to prevent C runtime mixups. */
  if(xmlDocFormatDump(stream, doc, 1) == -1)
  {
    xmlerror = xmlGetLastError();
    infd_filesystem_storage_stream_close(stream);
    xmlFreeDoc(doc);

    g_set_error_literal(
      error,
      g_quark_from_static_string("LIBXML2_OUTPUT_ERROR"),
      xmlerror->code,
      xmlerror->message
    );

    return FALSE;
  }

  infd_filesystem_storage_stream_close(stream);
  xmlFreeDoc(doc);
  return TRUE;
}
Пример #3
0
/**
 * inf_chat_buffer_get_message:
 * @buffer: A #InfChatBuffer.
 * @n: The index of the message to obtain.
 *
 * Returns the message with the given index from the buffer. The oldest
 * message in the buffer has index 0, and the most recent one has index
 * inf_chat_buffer_get_n_messages() - 1.
 *
 * Returns: The #InfChatBufferMessage with the given index.
 */
const InfChatBufferMessage*
inf_chat_buffer_get_message(InfChatBuffer* buffer,
                            guint n)
{
  InfChatBufferPrivate* priv;

  g_return_val_if_fail(INF_IS_CHAT_BUFFER(buffer), NULL);
  g_return_val_if_fail(n < inf_chat_buffer_get_n_messages(buffer), NULL);

  priv = INF_CHAT_BUFFER_PRIVATE(buffer);
  return &priv->messages[
    (priv->first_message + n) % priv->size
  ];
}
static void
infinoted_plugin_document_stream_sync_chat(
  InfinotedPluginDocumentStreamStream* stream)
{
  InfChatBuffer* buffer;
  guint n_messages;
  guint i;
  const InfChatBufferMessage* message;

  g_assert(INF_IS_CHAT_BUFFER(stream->buffer));
  buffer = INF_CHAT_BUFFER(stream->buffer);
  n_messages = inf_chat_buffer_get_n_messages(buffer);

  for(i = 0; i < n_messages; ++i)
  {
    message = inf_chat_buffer_get_message(buffer, i);
    infinoted_plugin_document_stream_chat_send_message(stream, message);
  }
}
Пример #5
0
/**
 * inf_chat_buffer_add_userpart_message:
 * @buffer: A #InfChatBuffer.
 * @user: A #InfUser who wrote the message.
 * @time: The time at which the user has written the message.
 * @flags: Flags to set for the message to add.
 *
 * Adds a new userpart message to the chat buffer. If the buffer is full
 * (meaning the number of messages in the buffer equals its size), then an
 * old message will get discarded. If the message to be added is older than
 * all other messages in the buffer, then it will not be added at all.
 */
void
inf_chat_buffer_add_userpart_message(InfChatBuffer* buffer,
                                     InfUser* user,
                                     time_t time,
                                     InfChatBufferMessageFlags flags)
{
  InfChatBufferMessage msg;

  g_return_if_fail(INF_IS_CHAT_BUFFER(buffer));
  g_return_if_fail(INF_IS_USER(user));

  msg.type = INF_CHAT_BUFFER_MESSAGE_USERPART;
  msg.user = user;
  msg.text = NULL;
  msg.length = 0;
  msg.time = time;
  msg.flags = flags;

  g_signal_emit(buffer, chat_buffer_signals[ADD_MESSAGE], 0, &msg);
}
Пример #6
0
/**
 * inf_chat_buffer_add_emote_message:
 * @buffer: A #InfChatBuffer.
 * @by: A #InfUser who wrote the message.
 * @message: The message text.
 * @length: The length of @message, in bytes.
 * @time: The time at which the user has written the message.
 * @flags: Flags to set for the message to add.
 *
 * Adds a new emote message to the chat buffer. If the buffer is full
 * (meaning the number of messages in the buffer equals its size), then an
 * old message will get discarded. If the message to be added is older than
 * all other messages in the buffer, then it will not be added at all.
 */
void
inf_chat_buffer_add_emote_message(InfChatBuffer* buffer,
                                  InfUser* by,
                                  const gchar* message,
                                  gsize length,
                                  time_t time,
                                  InfChatBufferMessageFlags flags)
{
  InfChatBufferMessage msg;

  g_return_if_fail(INF_IS_CHAT_BUFFER(buffer));
  g_return_if_fail(INF_IS_USER(by));
  g_return_if_fail(message != NULL);

  msg.type = INF_CHAT_BUFFER_MESSAGE_EMOTE;
  msg.user = by;
  /* cast const away without warning */
  msg.text = *(gchar**) (gpointer) &message;
  msg.length = length;
  msg.time = time;
  msg.flags = flags;

  g_signal_emit(buffer, chat_buffer_signals[ADD_MESSAGE], 0, &msg);
}
Пример #7
0
/**
 * inf_chat_buffer_get_size:
 * @buffer: A #InfChatBuffer.
 *
 * Returns the size of the chat buffer, which is the maximum number of
 * messages that can be stored in the buffer.
 *
 * Returns: The number of messages in the chat buffer.
 */
guint
inf_chat_buffer_get_size(InfChatBuffer* buffer)
{
  g_return_val_if_fail(INF_IS_CHAT_BUFFER(buffer), 0);
  return INF_CHAT_BUFFER_PRIVATE(buffer)->size;
}
Пример #8
0
/**
 * inf_chat_buffer_get_n_messages:
 * @buffer: A #InfChatBuffer.
 *
 * Returns the number of messages in the buffer.
 *
 * Returns: The number of messages in the buffer.
 */
guint
inf_chat_buffer_get_n_messages(InfChatBuffer* buffer)
{
  g_return_val_if_fail(INF_IS_CHAT_BUFFER(buffer), 0);
  return INF_CHAT_BUFFER_PRIVATE(buffer)->num_messages;
}
static void
infinoted_plugin_document_stream_stop(
  InfinotedPluginDocumentStreamStream* stream,
  gboolean send_stop)
{
  guint32 comm;
  InfSession* session;

  if(send_stop)
  {
    comm = 5; /* STOP */
    if(!infinoted_plugin_document_stream_send(stream, &comm, 4))
      return;
  }

  if(stream->user != NULL)
  {
    g_assert(stream->proxy != NULL);
    g_object_get(G_OBJECT(stream->proxy), "session", &session, NULL);
    inf_session_set_user_status(session, stream->user, INF_USER_UNAVAILABLE);
    g_object_unref(session);

    g_object_unref(stream->user);
    stream->user = NULL;
  }

  if(stream->proxy != NULL)
  {
    g_object_unref(stream->proxy);
    stream->proxy = NULL;
  }

  if(stream->buffer != NULL)
  {
    if(INF_TEXT_IS_BUFFER(stream->buffer))
    {
      inf_signal_handlers_disconnect_by_func(
        G_OBJECT(stream->buffer),
        G_CALLBACK(infinoted_plugin_document_stream_text_inserted_cb),
        stream
      );

      inf_signal_handlers_disconnect_by_func(
        G_OBJECT(stream->buffer),
        G_CALLBACK(infinoted_plugin_document_stream_text_erased_cb),
        stream
      );
    }
    else if(INF_IS_CHAT_BUFFER(stream->buffer))
    {
      inf_signal_handlers_disconnect_by_func(
        G_OBJECT(stream->buffer),
        G_CALLBACK(infinoted_plugin_document_stream_chat_add_message_cb),
        stream
      );
    }

    g_object_unref(stream->buffer);
    stream->buffer = NULL;
  }

  if(stream->subscribe_request != NULL)
  {
    inf_signal_handlers_disconnect_by_func(
      G_OBJECT(stream->subscribe_request),
      G_CALLBACK(infinoted_plugin_document_stream_subscribe_func),
      stream
    );

    stream->subscribe_request = NULL;
  }

  if(stream->user_request != NULL)
  {
    inf_signal_handlers_disconnect_by_func(
      G_OBJECT(stream->user_request),
      G_CALLBACK(infinoted_plugin_document_stream_user_join_func),
      stream
    );

    stream->user_request = NULL;
  }
}
/**
 * infd_chat_filesystem_format_read:
 * @storage: A #InfdFilesystemStorage.
 * @path: Storage path to retrieve the session from.
 * @buffer: An empty #InfTextBuffer to use as the new session's buffer.
 * @error: Location to store error information, if any, or %NULL.
 *
 * Reads a chat session from @path in @storage. The file is expected to have
 * been saved with infd_chat_filesystem_format_write() before. The @buffer
 * parameter should be an empty #InfChatBuffer, and the document will be
 * written into this buffer. If the function succeeds, the buffer can be used
 * to create an #InfChatSession with inf_chat_session_new(). If the function 
 * fails, %FALSE is returned and @error is set.
 *
 * Returns: %TRUE on success or %FALSE on error.
 */
gboolean
infd_chat_filesystem_format_read(InfdFilesystemStorage* storage,
                                 const gchar* path,
                                 InfChatBuffer* buffer,
                                 GError** error)
{
  FILE* stream;
  gchar* full_path;
  gchar* uri;

  xmlDocPtr doc;
  xmlErrorPtr xmlerror;
  xmlNodePtr root;
  xmlNodePtr child;
  gboolean result;

  g_return_val_if_fail(INFD_IS_FILESYSTEM_STORAGE(storage), FALSE);
  g_return_val_if_fail(path != NULL, FALSE);
  g_return_val_if_fail(INF_IS_CHAT_BUFFER(buffer), FALSE);
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);

  /* TODO: Use a SAX parser for better performance */
  full_path = NULL;
  stream = infd_filesystem_storage_open(
    INFD_FILESYSTEM_STORAGE(storage),
    "InfChat",
    path,
    "r",
    &full_path,
    error
  );

  if(stream == NULL)
  {
    g_free(full_path);
    return FALSE;
  }

  uri = g_filename_to_uri(full_path, NULL, error);
  g_free(full_path);

  if(uri == NULL)
    return FALSE;

  doc = xmlReadIO(
    infd_chat_filesystem_format_read_read_func,
    infd_chat_filesystem_format_read_close_func,
    stream,
    uri,
    "UTF-8",
    XML_PARSE_NOWARNING | XML_PARSE_NOERROR
  );

  g_free(uri);

  if(doc == NULL)
  {
    xmlerror = xmlGetLastError();

    g_set_error(
      error,
      g_quark_from_static_string("LIBXML2_PARSER_ERROR"),
      xmlerror->code,
      _("Error parsing XML in file \"%s\": [%d]: %s"),
      path,
      xmlerror->line,
      xmlerror->message
    );

    result = FALSE;
  }
  else
  {
    root = xmlDocGetRootElement(doc);
    if(strcmp((const char*)root->name, "inf-chat-session") != 0)
    {
      g_set_error(
        error,
        infd_chat_filesystem_format_error_quark(),
        INFD_CHAT_FILESYSTEM_FORMAT_ERROR_NOT_A_CHAT_SESSION,
        _("Error processing file \"%s\": %s"),
        path,
        _("The document is not a chat session")
      );

      result = FALSE;
    }
    else
    {
      result = TRUE;
    }

    xmlFreeDoc(doc);
  }

  if(result == FALSE)
    return FALSE;

  return TRUE;
}