pidgin/pidgin

Remove pidgin_dialogs_info and gtkdialogs.[ch] as it is now empty.

We have a new API for this and the old dialog just wasn't to fit in there.

Testing Done:
Ran with the turtles and ran the app.

Reviewed at https://reviews.imfreedom.org/r/3109/
/*
* Purple - Internet Messaging Library
* Copyright (C) Pidgin Developers <devel@pidgin.im>
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This library 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, see <https://www.gnu.org/licenses/>.
*/
#include <glib/gi18n-lib.h>
#include <birb.h>
#include "purpleircv3connection.h"
#include "purpleircv3constants.h"
#include "purpleircv3core.h"
#include "purpleircv3ctcp.h"
#include "purpleircv3formatting.h"
#include "purpleircv3parser.h"
enum {
PROP_0,
PROP_CAPABILITIES,
PROP_REGISTERED,
N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };
enum {
SIG_REGISTRATION_COMPLETE,
SIG_CTCP_REQUEST,
SIG_CTCP_RESPONSE,
N_SIGNALS,
};
static guint signals[N_SIGNALS] = {0, };
typedef struct {
GSocketConnection *connection;
gchar *server_name;
gboolean registered;
GDataInputStream *input;
GOutputStream *output;
PurpleIRCv3Parser *parser;
PurpleIRCv3Capabilities *capabilities;
PurpleConversation *status_conversation;
} PurpleIRCv3ConnectionPrivate;
G_DEFINE_DYNAMIC_TYPE_EXTENDED(PurpleIRCv3Connection,
purple_ircv3_connection,
PURPLE_TYPE_CONNECTION,
0,
G_ADD_PRIVATE_DYNAMIC(PurpleIRCv3Connection))
/******************************************************************************
* Helpers
*****************************************************************************/
static void
purple_ircv3_connection_send_pass_command(PurpleIRCv3Connection *connection) {
PurpleAccount *account = NULL;
const char *password = NULL;
account = purple_connection_get_account(PURPLE_CONNECTION(connection));
password = purple_account_get_string(account, "server-password", "");
if(password != NULL && *password != '\0') {
purple_ircv3_connection_writef(connection, "PASS %s", password);
}
}
static void
purple_ircv3_connection_send_user_command(PurpleIRCv3Connection *connection) {
PurpleAccount *account = NULL;
const char *identname = NULL;
const char *nickname = NULL;
const char *realname = NULL;
nickname =
purple_connection_get_display_name(PURPLE_CONNECTION(connection));
account = purple_connection_get_account(PURPLE_CONNECTION(connection));
/* The stored value could be an empty string, so pass a default of empty
* string and then if it was empty, set our correct fallback.
*/
identname = purple_account_get_string(account, "ident", "");
if(identname == NULL || *identname == '\0') {
identname = nickname;
}
realname = purple_account_get_string(account, "real-name", "");
if(realname == NULL || *realname == '\0') {
realname = nickname;
}
purple_ircv3_connection_writef(connection, "USER %s 0 * :%s", identname,
realname);
}
static void
purple_ircv3_connection_send_nick_command(PurpleIRCv3Connection *connection) {
const char *nickname = NULL;
nickname =
purple_connection_get_display_name(PURPLE_CONNECTION(connection));
purple_ircv3_connection_writef(connection, "NICK %s", nickname);
}
static void
purple_ircv3_connection_rejoin_channels(PurpleIRCv3Connection *connection) {
PurpleAccount *account = NULL;
PurpleConversationManager *manager = NULL;
GList *conversations = NULL;
account = purple_connection_get_account(PURPLE_CONNECTION(connection));
manager = purple_conversation_manager_get_default();
conversations = purple_conversation_manager_get_all(manager);
while(conversations != NULL) {
PurpleConversation *conversation = conversations->data;
PurpleAccount *conv_account = NULL;
conv_account = purple_conversation_get_account(conversation);
if(conv_account == account) {
const char *id = purple_conversation_get_id(conversation);
purple_ircv3_connection_writef(connection, "%s %s",
PURPLE_IRCV3_MSG_JOIN, id);
}
conversations = g_list_delete_link(conversations, conversations);
}
}
/******************************************************************************
* Callbacks
*****************************************************************************/
static void
purple_ircv3_connection_read_cb(GObject *source, GAsyncResult *result,
gpointer data)
{
PurpleIRCv3Connection *connection = data;
PurpleIRCv3ConnectionPrivate *priv = NULL;
GCancellable *cancellable = NULL;
GDataInputStream *istream = G_DATA_INPUT_STREAM(source);
GError *error = NULL;
gchar *line = NULL;
gsize length;
gboolean parsed = FALSE;
line = g_data_input_stream_read_line_finish(istream, result, &length,
&error);
if(line == NULL || error != NULL) {
if(PURPLE_IS_CONNECTION(connection)) {
if(error == NULL) {
g_set_error_literal(&error, PURPLE_CONNECTION_ERROR,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Server closed the connection"));
} else {
g_prefix_error(&error, "%s", _("Lost connection with server: "));
}
purple_connection_take_error(PURPLE_CONNECTION(connection), error);
}
/* In the off chance that line was returned, make sure we free it. */
g_free(line);
return;
}
priv = purple_ircv3_connection_get_instance_private(connection);
parsed = purple_ircv3_parser_parse(priv->parser, line, &error,
connection);
if(!parsed) {
g_warning("failed to handle '%s': %s", line,
error != NULL ? error->message : "unknown error");
}
g_clear_error(&error);
g_free(line);
/* Call read_line_async again to continue reading lines. */
cancellable = purple_connection_get_cancellable(PURPLE_CONNECTION(connection));
g_data_input_stream_read_line_async(priv->input,
G_PRIORITY_DEFAULT,
cancellable,
purple_ircv3_connection_read_cb,
connection);
}
static void
purple_ircv3_connection_write_cb(GObject *source, GAsyncResult *result,
gpointer data)
{
PurpleIRCv3Connection *connection = data;
BirbQueuedOutputStream *stream = BIRB_QUEUED_OUTPUT_STREAM(source);
GError *error = NULL;
gboolean success = FALSE;
success = birb_queued_output_stream_push_bytes_finish(stream, result,
&error);
if(!success) {
birb_queued_output_stream_clear_queue(stream);
g_prefix_error(&error, "%s", _("Lost connection with server: "));
purple_connection_take_error(PURPLE_CONNECTION(connection), error);
return;
}
}
static void
purple_ircv3_connection_connected_cb(GObject *source, GAsyncResult *result,
gpointer data)
{
PurpleIRCv3Connection *connection = data;
PurpleIRCv3ConnectionPrivate *priv = NULL;
GCancellable *cancellable = NULL;
GError *error = NULL;
GInputStream *istream = NULL;
GOutputStream *ostream = NULL;
GSocketClient *client = G_SOCKET_CLIENT(source);
GSocketConnection *conn = NULL;
priv = purple_ircv3_connection_get_instance_private(connection);
/* Finish the async method. */
conn = g_socket_client_connect_to_host_finish(client, result, &error);
if(conn == NULL || error != NULL) {
g_prefix_error(&error, "%s", _("Unable to connect: "));
purple_connection_take_error(PURPLE_CONNECTION(connection), error);
return;
}
g_message("Successfully connected to %s", priv->server_name);
/* Save our connection and setup our input and outputs. */
priv->connection = conn;
/* Create our parser. */
priv->parser = purple_ircv3_parser_new();
purple_ircv3_parser_add_default_handlers(priv->parser);
ostream = g_io_stream_get_output_stream(G_IO_STREAM(conn));
priv->output = birb_queued_output_stream_new(ostream);
istream = g_io_stream_get_input_stream(G_IO_STREAM(conn));
priv->input = g_data_input_stream_new(istream);
g_data_input_stream_set_newline_type(G_DATA_INPUT_STREAM(priv->input),
G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
cancellable = purple_connection_get_cancellable(PURPLE_CONNECTION(connection));
/* Add our read callback. */
g_data_input_stream_read_line_async(priv->input,
G_PRIORITY_DEFAULT,
cancellable,
purple_ircv3_connection_read_cb,
connection);
/* Send our registration commands. */
purple_ircv3_capabilities_start(priv->capabilities);
purple_ircv3_connection_send_pass_command(connection);
purple_ircv3_connection_send_user_command(connection);
purple_ircv3_connection_send_nick_command(connection);
}
static void
purple_ircv3_connection_caps_done_cb(G_GNUC_UNUSED PurpleIRCv3Capabilities *caps,
gpointer data)
{
PurpleIRCv3Connection *connection = data;
PurpleIRCv3ConnectionPrivate *priv = NULL;
priv = purple_ircv3_connection_get_instance_private(connection);
priv->registered = TRUE;
g_signal_emit(connection, signals[SIG_REGISTRATION_COMPLETE], 0);
/* Add our supported CTCP commands. */
purple_ircv3_ctcp_add_default_handlers(connection);
/* Now that registration is complete, rejoin any channels that the
* conversation manager has for us.
*/
purple_ircv3_connection_rejoin_channels(connection);
}
/******************************************************************************
* PurpleConnection Implementation
*****************************************************************************/
static gboolean
purple_ircv3_connection_connect(PurpleConnection *purple_connection,
GError **error)
{
PurpleIRCv3Connection *connection = NULL;
PurpleIRCv3ConnectionPrivate *priv = NULL;
PurpleAccount *account = NULL;
GCancellable *cancellable = NULL;
GSocketClient *client = NULL;
gint default_port = PURPLE_IRCV3_DEFAULT_TLS_PORT;
gint port = 0;
gboolean use_tls = TRUE;
g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(purple_connection), FALSE);
connection = PURPLE_IRCV3_CONNECTION(purple_connection);
priv = purple_ircv3_connection_get_instance_private(connection);
account = purple_connection_get_account(purple_connection);
client = purple_gio_socket_client_new(account, error);
if(!G_IS_SOCKET_CLIENT(client)) {
if(error != NULL && *error != NULL) {
purple_connection_take_error(purple_connection, *error);
}
return FALSE;
}
/* Turn on TLS if requested. */
use_tls = purple_account_get_bool(account, "use-tls", TRUE);
g_socket_client_set_tls(client, use_tls);
/* If TLS is not being used, set the default port to the plain port. */
if(!use_tls) {
default_port = PURPLE_IRCV3_DEFAULT_PLAIN_PORT;
}
port = purple_account_get_int(account, "port", default_port);
cancellable = purple_connection_get_cancellable(purple_connection);
/* Finally start the async connection. */
g_socket_client_connect_to_host_async(client, priv->server_name,
port, cancellable,
purple_ircv3_connection_connected_cb,
connection);
g_clear_object(&client);
return TRUE;
}
static gboolean
purple_ircv3_connection_disconnect(PurpleConnection *purple_connection,
GError **error)
{
PurpleIRCv3Connection *connection = NULL;
PurpleIRCv3ConnectionPrivate *priv = NULL;
PurpleConnectionClass *parent_class = NULL;
g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(purple_connection), FALSE);
/* Chain up to our parent's disconnect to do initial clean up. */
parent_class = PURPLE_CONNECTION_CLASS(purple_ircv3_connection_parent_class);
parent_class->disconnect(purple_connection, error);
connection = PURPLE_IRCV3_CONNECTION(purple_connection);
priv = purple_ircv3_connection_get_instance_private(connection);
/* TODO: send QUIT command. */
if(G_IS_SOCKET_CONNECTION(priv->connection)) {
GInputStream *istream = G_INPUT_STREAM(priv->input);
GOutputStream *ostream = G_OUTPUT_STREAM(priv->output);
purple_gio_graceful_close(G_IO_STREAM(priv->connection),
istream, ostream);
}
g_clear_object(&priv->input);
g_clear_object(&priv->output);
g_clear_object(&priv->connection);
return TRUE;
}
static void
purple_ircv3_connection_registration_complete_cb(PurpleIRCv3Connection *connection) {
/* Don't set our connection state to connected until we've completed
* registration as connected implies that we can start chatting or join
* rooms and other "online" activities.
*/
purple_connection_set_state(PURPLE_CONNECTION(connection),
PURPLE_CONNECTION_STATE_CONNECTED);
}
/******************************************************************************
* Default Handlers
*****************************************************************************/
static gboolean
purple_ircv3_connection_ctcp_request_default_handler(G_GNUC_UNUSED PurpleIRCv3Connection *connection,
G_GNUC_UNUSED PurpleConversation *conversation,
PurpleMessage *message,
const char *command,
const char *params)
{
char *contents = NULL;
if(!purple_strempty(params)) {
contents = g_strdup_printf(_("requested CTCP %s: %s"), command,
params);
} else {
contents = g_strdup_printf(_("requested CTCP %s"),
command);
}
purple_message_set_contents(message, contents);
g_clear_pointer(&contents, g_free);
return FALSE;
}
static gboolean
purple_ircv3_connection_ctcp_response_default_handler(G_GNUC_UNUSED PurpleIRCv3Connection *connection,
G_GNUC_UNUSED PurpleConversation *conversation,
PurpleMessage *message,
const char *command,
const char *params)
{
char *contents = NULL;
if(!purple_strempty(params)) {
contents = g_strdup_printf(_("CTCP %s response: %s"), command,
params);
} else {
contents = g_strdup_printf(_("CTCP %s response was empty"),
command);
}
purple_message_set_contents(message, contents);
g_clear_pointer(&contents, g_free);
return FALSE;
}
/******************************************************************************
* GObject Implementation
*****************************************************************************/
static void
purple_ircv3_connection_get_property(GObject *obj, guint param_id,
GValue *value, GParamSpec *pspec)
{
PurpleIRCv3Connection *connection = PURPLE_IRCV3_CONNECTION(obj);
switch(param_id) {
case PROP_CAPABILITIES:
g_value_set_object(value,
purple_ircv3_connection_get_capabilities(connection));
break;
case PROP_REGISTERED:
g_value_set_boolean(value,
purple_ircv3_connection_get_registered(connection));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
purple_ircv3_connection_dispose(GObject *obj) {
PurpleIRCv3Connection *connection = PURPLE_IRCV3_CONNECTION(obj);
PurpleIRCv3ConnectionPrivate *priv = NULL;
priv = purple_ircv3_connection_get_instance_private(connection);
g_clear_object(&priv->input);
g_clear_object(&priv->output);
g_clear_object(&priv->connection);
g_clear_object(&priv->capabilities);
g_clear_object(&priv->parser);
g_clear_object(&priv->status_conversation);
G_OBJECT_CLASS(purple_ircv3_connection_parent_class)->dispose(obj);
}
static void
purple_ircv3_connection_finalize(GObject *obj) {
PurpleIRCv3Connection *connection = PURPLE_IRCV3_CONNECTION(obj);
PurpleIRCv3ConnectionPrivate *priv = NULL;
priv = purple_ircv3_connection_get_instance_private(connection);
g_clear_pointer(&priv->server_name, g_free);
G_OBJECT_CLASS(purple_ircv3_connection_parent_class)->finalize(obj);
}
static void
purple_ircv3_connection_constructed(GObject *obj) {
PurpleIRCv3Connection *connection = PURPLE_IRCV3_CONNECTION(obj);
PurpleIRCv3ConnectionPrivate *priv = NULL;
PurpleAccount *account = NULL;
PurpleContactInfo *info = NULL;
PurpleConversationManager *conversation_manager = NULL;
char **userparts = NULL;
const char *username = NULL;
char *title = NULL;
G_OBJECT_CLASS(purple_ircv3_connection_parent_class)->constructed(obj);
priv = purple_ircv3_connection_get_instance_private(connection);
account = purple_connection_get_account(PURPLE_CONNECTION(connection));
info = PURPLE_CONTACT_INFO(account);
title = g_strdup_printf(_("status for %s"),
purple_contact_info_get_username(info));
/* Split the username into nick and server and store the values. */
username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(account));
userparts = g_strsplit(username, "@", 2);
purple_connection_set_display_name(PURPLE_CONNECTION(connection),
userparts[0]);
priv->server_name = g_strdup(userparts[1]);
/* Free the userparts vector. */
g_strfreev(userparts);
/* Check if we have an existing status conversation. */
conversation_manager = purple_conversation_manager_get_default();
priv->status_conversation = purple_conversation_manager_find_with_id(conversation_manager,
account,
priv->server_name);
if(!PURPLE_IS_CONVERSATION(priv->status_conversation)) {
/* Create our status conversation. */
priv->status_conversation = g_object_new(
PURPLE_TYPE_CONVERSATION,
"account", account,
"id", priv->server_name,
"name", priv->server_name,
"title", title,
NULL);
purple_conversation_manager_register(conversation_manager,
priv->status_conversation);
} else {
/* The conversation existed, so add a reference to it. */
g_object_ref(priv->status_conversation);
}
g_clear_pointer(&title, g_free);
/* Finally create our objects. */
priv->capabilities = purple_ircv3_capabilities_new(connection);
g_signal_connect_object(priv->capabilities, "done",
G_CALLBACK(purple_ircv3_connection_caps_done_cb),
connection, 0);
}
static void
purple_ircv3_connection_init(G_GNUC_UNUSED PurpleIRCv3Connection *connection) {
}
static void
purple_ircv3_connection_class_finalize(G_GNUC_UNUSED PurpleIRCv3ConnectionClass *klass) {
}
static void
purple_ircv3_connection_class_init(PurpleIRCv3ConnectionClass *klass) {
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
PurpleConnectionClass *connection_class = PURPLE_CONNECTION_CLASS(klass);
obj_class->get_property = purple_ircv3_connection_get_property;
obj_class->constructed = purple_ircv3_connection_constructed;
obj_class->dispose = purple_ircv3_connection_dispose;
obj_class->finalize = purple_ircv3_connection_finalize;
connection_class->connect = purple_ircv3_connection_connect;
connection_class->disconnect = purple_ircv3_connection_disconnect;
/**
* PurpleIRCv3Connection:capabilities:
*
* The capabilities that the server supports.
*
* This is created during registration of the connection and is useful for
* troubleshooting or just reporting them to end users.
*
* Since: 3.0
*/
properties[PROP_CAPABILITIES] = g_param_spec_object(
"capabilities", "capabilities",
"The capabilities that the server supports",
PURPLE_IRCV3_TYPE_CAPABILITIES,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* PurpleIRCv3Connection:registered:
*
* Whether or not the connection has finished the registration portion of
* the connection.
*
* Since: 3.0
*/
properties[PROP_REGISTERED] = g_param_spec_boolean(
"registered", "registered",
"Whether or not the connection has finished registration.",
FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
/* Signals */
/**
* PurpleIRCv3Connection::registration-complete:
* @connection: The instance.
*
* This signal is emitted after the registration process has been
* completed. Plugins can use this to perform additional actions before
* any channels are auto joined or similar.
*
* Since: 3.0
*/
signals[SIG_REGISTRATION_COMPLETE] = g_signal_new_class_handler(
"registration-complete",
G_OBJECT_CLASS_TYPE(klass),
G_SIGNAL_RUN_LAST,
G_CALLBACK(purple_ircv3_connection_registration_complete_cb),
NULL,
NULL,
NULL,
G_TYPE_NONE,
0);
/**
* PurpleIRCv3Connection::ctcp-request:
* @connection: The instance.
* @conversation: The conversation.
* @message: The message.
* @command: The CTCP command.
* @params: (nullable): The CTCP parameters.
*
* This signal is emitted after a CTCP request has been received.
*
* Signal handlers should return TRUE if they fully handled the request and
* do not want it echoed out to the user.
*
* Handlers may also modify the message. For example, the CTCP ACTION
* command has its message contents replaced with just the CTCP parameters
* and sets the [property@Message:action] to %TRUE and then returns %TRUE.
*
* Since: 3.0
*/
signals[SIG_CTCP_REQUEST] = g_signal_new_class_handler(
"ctcp-request",
G_OBJECT_CLASS_TYPE(klass),
G_SIGNAL_RUN_LAST,
G_CALLBACK(purple_ircv3_connection_ctcp_request_default_handler),
g_signal_accumulator_true_handled,
NULL,
NULL,
G_TYPE_BOOLEAN,
4,
PURPLE_TYPE_CONVERSATION,
PURPLE_TYPE_MESSAGE,
G_TYPE_STRING,
G_TYPE_STRING);
/**
* PurpleIRCv3Connection::ctcp-response:
* @connection: The instance.
* @conversation: The conversation.
* @message: The message.
* @command: The CTCP command.
* @params: (nullable): The CTCP parameters.
*
* This signal is emitted after a CTCP response has been received.
*
* Signal handlers should return TRUE if they fully handled the response
* and do not want it echoed out to the user.
*
* Handlers may modify @conversation or @message to depending on their
* needs.
*
* Since: 3.0
*/
signals[SIG_CTCP_RESPONSE] = g_signal_new_class_handler(
"ctcp-response",
G_OBJECT_CLASS_TYPE(klass),
G_SIGNAL_RUN_LAST,
G_CALLBACK(purple_ircv3_connection_ctcp_response_default_handler),
g_signal_accumulator_true_handled,
NULL,
NULL,
G_TYPE_BOOLEAN,
4,
PURPLE_TYPE_CONVERSATION,
PURPLE_TYPE_MESSAGE,
G_TYPE_STRING,
G_TYPE_STRING);
}
/******************************************************************************
* Internal API
*****************************************************************************/
void
purple_ircv3_connection_register(GPluginNativePlugin *plugin) {
GObjectClass *hack = NULL;
purple_ircv3_connection_register_type(G_TYPE_MODULE(plugin));
/* Without this hack we get some warnings about no reference on this type
* when generating the gir file. However, there are no changes to the
* generated gir file with or without this hack, so this is purely to get
* rid of the warnings.
*
* - GK 2023-08-21
*/
hack = g_type_class_ref(purple_ircv3_connection_get_type());
g_type_class_unref(hack);
}
gboolean
purple_ircv3_connection_emit_ctcp_request(PurpleIRCv3Connection *connection,
PurpleConversation *conversation,
PurpleMessage *message,
const char *command,
const char *parameters)
{
gboolean ret = FALSE;
g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection), FALSE);
g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
g_return_val_if_fail(!purple_strempty(command), FALSE);
g_signal_emit(connection, signals[SIG_CTCP_REQUEST], 0, conversation,
message, command, parameters, &ret);
return ret;
}
gboolean
purple_ircv3_connection_emit_ctcp_response(PurpleIRCv3Connection *connection,
PurpleConversation *conversation,
PurpleMessage *message,
const char *command,
const char *parameters)
{
gboolean ret = FALSE;
g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection), FALSE);
g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE);
g_return_val_if_fail(!purple_strempty(command), FALSE);
g_signal_emit(connection, signals[SIG_CTCP_RESPONSE], 0, conversation,
message, command, parameters, &ret);
return ret;
}
/******************************************************************************
* Public API
*****************************************************************************/
void
purple_ircv3_connection_writef(PurpleIRCv3Connection *connection,
const char *format, ...)
{
PurpleIRCv3ConnectionPrivate *priv = NULL;
GBytes *bytes = NULL;
GCancellable *cancellable = NULL;
GString *msg = NULL;
va_list vargs;
g_return_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection));
g_return_if_fail(format != NULL);
priv = purple_ircv3_connection_get_instance_private(connection);
/* Create our string and append our format to it. */
msg = g_string_new("");
va_start(vargs, format);
g_string_vprintf(msg, format, vargs);
va_end(vargs);
/* Next add the trailing carriage return line feed. */
g_string_append(msg, "\r\n");
/* Finally turn the string into bytes and send it! */
bytes = g_string_free_to_bytes(msg);
cancellable = purple_connection_get_cancellable(PURPLE_CONNECTION(connection));
birb_queued_output_stream_push_bytes_async(BIRB_QUEUED_OUTPUT_STREAM(priv->output),
bytes,
G_PRIORITY_DEFAULT,
cancellable,
purple_ircv3_connection_write_cb,
connection);
g_bytes_unref(bytes);
}
PurpleIRCv3Capabilities *
purple_ircv3_connection_get_capabilities(PurpleIRCv3Connection *connection) {
PurpleIRCv3ConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection), NULL);
priv = purple_ircv3_connection_get_instance_private(connection);
return priv->capabilities;
}
gboolean
purple_ircv3_connection_get_registered(PurpleIRCv3Connection *connection) {
PurpleIRCv3ConnectionPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection), FALSE);
priv = purple_ircv3_connection_get_instance_private(connection);
return priv->registered;
}
void
purple_ircv3_connection_add_status_message(PurpleIRCv3Connection *connection,
PurpleIRCv3Message *v3_message)
{
PurpleIRCv3ConnectionPrivate *priv = NULL;
PurpleMessage *message = NULL;
GString *str = NULL;
GStrv params = NULL;
char *stripped = NULL;
const char *command = NULL;
g_return_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection));
g_return_if_fail(PURPLE_IRCV3_IS_MESSAGE(v3_message));
priv = purple_ircv3_connection_get_instance_private(connection);
command = purple_ircv3_message_get_command(v3_message);
str = g_string_new(command);
params = purple_ircv3_message_get_params(v3_message);
if(params != NULL && params[0] != NULL) {
char *joined = g_strjoinv(" ", params);
g_string_append_printf(str, " %s", joined);
g_free(joined);
}
stripped = purple_ircv3_formatting_strip(str->str);
g_string_free(str, TRUE);
message = g_object_new(
PURPLE_TYPE_MESSAGE,
"author", purple_ircv3_message_get_source(v3_message),
"contents", stripped,
NULL);
g_free(stripped);
purple_conversation_write_message(priv->status_conversation, message);
g_clear_object(&message);
}
gboolean
purple_ircv3_connection_is_channel(PurpleIRCv3Connection *connection,
const char *id)
{
g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection), FALSE);
g_return_val_if_fail(id != NULL, FALSE);
return (id[0] == '#');
}
PurpleConversation *
purple_ircv3_connection_find_or_create_conversation(PurpleIRCv3Connection *connection,
const char *id)
{
PurpleAccount *account = NULL;
PurpleConversation *conversation = NULL;
PurpleConversationManager *manager = NULL;
g_return_val_if_fail(PURPLE_IRCV3_IS_CONNECTION(connection), NULL);
g_return_val_if_fail(id != NULL, NULL);
account = purple_connection_get_account(PURPLE_CONNECTION(connection));
manager = purple_conversation_manager_get_default();
conversation = purple_conversation_manager_find_with_id(manager, account,
id);
if(!PURPLE_IS_CONVERSATION(conversation)) {
PurpleConversationType type = PURPLE_CONVERSATION_TYPE_DM;
if(purple_ircv3_connection_is_channel(connection, id)) {
type = PURPLE_CONVERSATION_TYPE_CHANNEL;
}
conversation = g_object_new(
PURPLE_TYPE_CONVERSATION,
"account", account,
"id", id,
"name", id,
"type", type,
NULL);
purple_conversation_manager_register(manager, conversation);
/* The manager creates its own reference on our new conversation, so we
* borrow it like we do above if it already exists.
*/
g_object_unref(conversation);
}
return conversation;
}
PurpleContact *
purple_ircv3_connection_find_or_create_contact(PurpleIRCv3Connection *connection,
const char *nick)
{
PurpleAccount *account = NULL;
PurpleContact *contact = NULL;
PurpleContactManager *manager = NULL;
account = purple_connection_get_account(PURPLE_CONNECTION(connection));
manager = purple_contact_manager_get_default();
contact = purple_contact_manager_find_with_id(manager, account, nick);
if(!PURPLE_IS_CONTACT(contact)) {
contact = purple_contact_new(account, nick);
purple_contact_info_set_username(PURPLE_CONTACT_INFO(contact), nick);
purple_contact_manager_add(manager, contact);
}
return contact;
}