pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Remove pidgin_dialogs_info and gtkdialogs.[ch] as it is now empty.
2 months ago, Gary Kramlich
ab244bae3b82
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
<hasl.h>
#include
"purpleircv3sasl.h"
#include
"purpleircv3capabilities.h"
#include
"purpleircv3connection.h"
#include
"purpleircv3constants.h"
#include
"purpleircv3core.h"
#define PURPLE_IRCV3_SASL_DATA_KEY ("sasl-data")
typedef
struct
{
PurpleConnection
*
connection
;
HaslContext
*
ctx
;
GString
*
server_in_buffer
;
}
PurpleIRCv3SASLData
;
/******************************************************************************
* Helpers
*****************************************************************************/
static
const
char
*
purple_ircv3_sasl_get_username
(
PurpleConnection
*
connection
)
{
PurpleAccount
*
account
=
NULL
;
const
char
*
username
=
NULL
;
account
=
purple_connection_get_account
(
connection
);
username
=
purple_account_get_string
(
account
,
"sasl-login-name"
,
""
);
if
(
username
!=
NULL
&&
username
[
0
]
!=
'\0'
)
{
return
username
;
}
return
purple_connection_get_display_name
(
connection
);
}
/******************************************************************************
* SASL Helpers
*****************************************************************************/
static
void
purple_ircv3_sasl_data_free
(
PurpleIRCv3SASLData
*
data
)
{
g_clear_object
(
&
data
->
ctx
);
g_string_free
(
data
->
server_in_buffer
,
TRUE
);
g_free
(
data
);
}
static
void
purple_ircv3_sasl_data_add
(
PurpleConnection
*
connection
,
HaslContext
*
ctx
)
{
PurpleIRCv3SASLData
*
data
=
NULL
;
data
=
g_new0
(
PurpleIRCv3SASLData
,
1
);
g_object_set_data_full
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
,
data
,
(
GDestroyNotify
)
purple_ircv3_sasl_data_free
);
/* We don't reference this because the life cycle of this data is tied
* directly to the connection and adding a reference to the connection
* would keep both alive forever.
*/
data
->
connection
=
connection
;
data
->
ctx
=
ctx
;
/* We truncate the server_in_buffer when we need to so that we can minimize
* allocations and simplify the logic involved with it.
*/
data
->
server_in_buffer
=
g_string_new
(
""
);
}
static
void
purple_ircv3_sasl_attempt
(
PurpleIRCv3Connection
*
connection
)
{
PurpleIRCv3SASLData
*
data
=
NULL
;
const
char
*
next_mechanism
=
NULL
;
const
char
*
current
=
NULL
;
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
current
=
hasl_context_get_current_mechanism
(
data
->
ctx
);
if
(
current
!=
NULL
)
{
g_message
(
"SASL '%s' mechanism failed"
,
current
);
}
next_mechanism
=
hasl_context_next
(
data
->
ctx
);
if
(
next_mechanism
==
NULL
)
{
GError
*
error
=
g_error_new
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
,
_
(
"No valid SASL mechanisms found"
));
purple_connection_take_error
(
PURPLE_CONNECTION
(
connection
),
error
);
return
;
}
g_message
(
"trying SASL '%s' mechanism"
,
next_mechanism
);
purple_ircv3_connection_writef
(
connection
,
"%s %s"
,
PURPLE_IRCV3_MSG_AUTHENTICATE
,
next_mechanism
);
}
static
void
purple_ircv3_sasl_start
(
PurpleIRCv3Capabilities
*
caps
)
{
PurpleIRCv3Connection
*
connection
=
NULL
;
PurpleAccount
*
account
=
NULL
;
PurpleConnection
*
purple_connection
=
NULL
;
HaslContext
*
ctx
=
NULL
;
const
char
*
mechanisms
=
NULL
;
gboolean
toggle
=
FALSE
;
connection
=
purple_ircv3_capabilities_get_connection
(
caps
);
purple_connection
=
PURPLE_CONNECTION
(
connection
);
account
=
purple_connection_get_account
(
purple_connection
);
ctx
=
hasl_context_new
();
/* At this point we are ready to start our SASL negotiation, so add a wait
* counter to the capabilities and start the negotiations!
*/
purple_ircv3_capabilities_add_wait
(
caps
);
/* Determine what mechanisms we're allowing and tell the context. */
mechanisms
=
purple_account_get_string
(
account
,
"sasl-mechanisms"
,
""
);
if
(
purple_strempty
(
mechanisms
))
{
/* If the user didn't specify any mechanisms, grab the mechanisms that
* the server advertised.
*/
mechanisms
=
purple_ircv3_capabilities_lookup
(
caps
,
"sasl"
,
NULL
);
}
hasl_context_set_allowed_mechanisms
(
ctx
,
mechanisms
);
/* Add the values we know to the context. */
hasl_context_set_username
(
ctx
,
purple_ircv3_sasl_get_username
(
purple_connection
));
hasl_context_set_password
(
ctx
,
purple_connection_get_password
(
purple_connection
));
toggle
=
purple_account_get_bool
(
account
,
"use-tls"
,
TRUE
);
hasl_context_set_tls
(
ctx
,
toggle
);
toggle
=
purple_account_get_bool
(
account
,
"plain-sasl-in-clear"
,
FALSE
);
hasl_context_set_allow_clear_text
(
ctx
,
toggle
);
/* Create our SASLData object, add it to the connection. */
purple_ircv3_sasl_data_add
(
purple_connection
,
ctx
);
/* Make it go! */
purple_ircv3_sasl_attempt
(
connection
);
}
/******************************************************************************
* Callbacks
*****************************************************************************/
static
void
purple_ircv3_sasl_ack_cb
(
PurpleIRCv3Capabilities
*
caps
,
G_GNUC_UNUSED
const
char
*
capability
,
G_GNUC_UNUSED
gpointer
data
)
{
purple_ircv3_sasl_start
(
caps
);
}
/******************************************************************************
* Internal API
*****************************************************************************/
void
purple_ircv3_sasl_request
(
PurpleIRCv3Capabilities
*
capabilities
)
{
purple_ircv3_capabilities_request
(
capabilities
,
PURPLE_IRCV3_CAPABILITY_SASL
);
g_signal_connect
(
capabilities
,
"ack::"
PURPLE_IRCV3_CAPABILITY_SASL
,
G_CALLBACK
(
purple_ircv3_sasl_ack_cb
),
NULL
);
}
gboolean
purple_ircv3_sasl_logged_in
(
G_GNUC_UNUSED
PurpleIRCv3Message
*
message
,
G_GNUC_UNUSED
GError
**
error
,
gpointer
user_data
)
{
PurpleIRCv3Connection
*
connection
=
user_data
;
PurpleIRCv3SASLData
*
data
=
NULL
;
PurpleAccount
*
account
=
NULL
;
const
char
*
sasl_name
=
NULL
;
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
if
(
data
==
NULL
)
{
g_set_error_literal
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"RPL_LOGGEDIN received with no SASL data "
"present"
);
return
FALSE
;
}
/* Check if the SASL login name is not set. If it is not set, set it to the
* current nick as it was successful.
*/
account
=
purple_connection_get_account
(
PURPLE_CONNECTION
(
connection
));
sasl_name
=
purple_account_get_string
(
account
,
"sasl-login-name"
,
""
);
if
(
purple_strempty
(
sasl_name
))
{
char
**
userparts
=
NULL
;
const
char
*
username
=
NULL
;
username
=
purple_contact_info_get_username
(
PURPLE_CONTACT_INFO
(
account
));
userparts
=
g_strsplit
(
username
,
"@"
,
2
);
purple_account_set_string
(
account
,
"sasl-login-name"
,
userparts
[
0
]);
g_strfreev
(
userparts
);
}
return
TRUE
;
}
gboolean
purple_ircv3_sasl_logged_out
(
G_GNUC_UNUSED
PurpleIRCv3Message
*
message
,
G_GNUC_UNUSED
GError
**
error
,
gpointer
user_data
)
{
PurpleIRCv3Connection
*
connection
=
user_data
;
PurpleIRCv3SASLData
*
data
=
NULL
;
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
if
(
data
==
NULL
)
{
g_set_error_literal
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"RPL_LOGGEDOUT received with no SASL data "
"present"
);
return
FALSE
;
}
/* Not sure how to trigger this or what we should do in this case to be
* honest, so just note it for now.
* -- GK 2023-01-12
*/
g_warning
(
"Server sent SASL logged out"
);
return
TRUE
;
}
gboolean
purple_ircv3_sasl_nick_locked
(
PurpleIRCv3Message
*
v3_message
,
GError
**
error
,
gpointer
user_data
)
{
PurpleIRCv3Connection
*
connection
=
user_data
;
PurpleIRCv3SASLData
*
data
=
NULL
;
GStrv
params
=
NULL
;
char
*
message
=
NULL
;
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
if
(
data
==
NULL
)
{
g_set_error_literal
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"ERR_NICKLOCKED received with no SASL data "
"present"
);
return
FALSE
;
}
params
=
purple_ircv3_message_get_params
(
v3_message
);
message
=
g_strjoinv
(
" "
,
params
);
g_set_error
(
error
,
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
,
_
(
"Nick name is locked: %s"
),
message
);
g_free
(
message
);
return
FALSE
;
}
gboolean
purple_ircv3_sasl_success
(
G_GNUC_UNUSED
PurpleIRCv3Message
*
message
,
GError
**
error
,
gpointer
user_data
)
{
PurpleIRCv3Capabilities
*
capabilities
=
NULL
;
PurpleIRCv3Connection
*
connection
=
user_data
;
PurpleIRCv3SASLData
*
data
=
NULL
;
capabilities
=
purple_ircv3_connection_get_capabilities
(
connection
);
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
if
(
data
==
NULL
)
{
g_set_error_literal
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"RPL_SASLSUCCESS received with no SASL data "
"present"
);
return
FALSE
;
}
/* This needs to be after we've checked the SASL data otherwise we might
* end up removing a wait that we don't own.
*/
purple_ircv3_capabilities_remove_wait
(
capabilities
);
g_message
(
"successfully authenticated with SASL '%s' mechanism."
,
hasl_context_get_current_mechanism
(
data
->
ctx
));
return
TRUE
;
}
gboolean
purple_ircv3_sasl_failed
(
G_GNUC_UNUSED
PurpleIRCv3Message
*
message
,
G_GNUC_UNUSED
GError
**
error
,
gpointer
user_data
)
{
PurpleIRCv3Connection
*
connection
=
user_data
;
PurpleIRCv3SASLData
*
data
=
NULL
;
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
if
(
data
==
NULL
)
{
g_set_error_literal
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"ERR_SASLFAIL received with no SASL data present"
);
return
FALSE
;
}
purple_ircv3_sasl_attempt
(
connection
);
return
TRUE
;
}
gboolean
purple_ircv3_sasl_message_too_long
(
G_GNUC_UNUSED
PurpleIRCv3Message
*
message
,
G_GNUC_UNUSED
GError
**
error
,
gpointer
user_data
)
{
PurpleIRCv3Connection
*
connection
=
user_data
;
PurpleIRCv3SASLData
*
data
=
NULL
;
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
if
(
data
==
NULL
)
{
g_set_error_literal
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"ERR_SASLTOOLONG received with no SASL data "
"present"
);
return
FALSE
;
}
return
TRUE
;
}
gboolean
purple_ircv3_sasl_aborted
(
G_GNUC_UNUSED
PurpleIRCv3Message
*
message
,
G_GNUC_UNUSED
GError
**
error
,
gpointer
user_data
)
{
PurpleIRCv3Connection
*
connection
=
user_data
;
PurpleIRCv3SASLData
*
data
=
NULL
;
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
if
(
data
==
NULL
)
{
g_set_error_literal
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"ERR_SASLABORTED received with no SASL data "
"present"
);
return
FALSE
;
}
/* This is supposed to get sent, when the client sends `AUTHENTICATE *`,
* but we don't do this, so I guess we'll note it for now...?
* --GK 2023-01-12
*/
g_warning
(
"The server claims we aborted SASL authentication."
);
return
TRUE
;
}
gboolean
purple_ircv3_sasl_already_authed
(
G_GNUC_UNUSED
PurpleIRCv3Message
*
message
,
G_GNUC_UNUSED
GError
**
error
,
gpointer
user_data
)
{
PurpleIRCv3Connection
*
connection
=
user_data
;
PurpleIRCv3SASLData
*
data
=
NULL
;
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
if
(
data
==
NULL
)
{
g_set_error_literal
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"ERR_SASLALREADY received with no SASL data "
"present"
);
return
FALSE
;
}
/* Similar to aborted above, we don't allow this, so just note that it
* happened.
* -- GK 2023-01-12
*/
g_warning
(
"Server claims we tried to SASL authenticate again."
);
return
TRUE
;
}
gboolean
purple_ircv3_sasl_mechanisms
(
PurpleIRCv3Message
*
message
,
G_GNUC_UNUSED
GError
**
error
,
gpointer
user_data
)
{
PurpleIRCv3Connection
*
connection
=
user_data
;
PurpleIRCv3SASLData
*
data
=
NULL
;
GStrv
params
=
NULL
;
guint
n_params
=
0
;
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
if
(
data
==
NULL
)
{
g_set_error_literal
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"RPL_SASLMECHS received with no SASL data "
"present"
);
return
FALSE
;
}
params
=
purple_ircv3_message_get_params
(
message
);
if
(
params
!=
NULL
)
{
n_params
=
g_strv_length
(
params
);
}
/* We need to find a server that sends this message. The specification says
* it _may_ be sent when the client sends AUTHENTICATE with an unknown
* mechanism, but ergo doesn't.
*
* We can't just blindly accept the new list either, as depending on how
* the server implements it, we'll need to remove mechanisms we've already
* tried in the event the server just dumps the entire list. As we're not
* currently tracking which mechanisms we've tried, this will have to be
* addressed as well.
*/
if
(
n_params
>
0
)
{
char
*
message
=
g_strjoinv
(
" "
,
params
);
g_message
(
"Server sent the following SASL mechanisms: %s"
,
message
);
g_free
(
message
);
}
else
{
g_message
(
"Server sent an empty list of SASL mechanisms"
);
}
return
TRUE
;
}
gboolean
purple_ircv3_sasl_authenticate
(
PurpleIRCv3Message
*
message
,
GError
**
error
,
gpointer
user_data
)
{
PurpleIRCv3Connection
*
connection
=
user_data
;
PurpleIRCv3SASLData
*
data
=
NULL
;
GStrv
params
=
NULL
;
char
*
payload
=
NULL
;
gboolean
done
=
FALSE
;
guint
n_params
=
0
;
params
=
purple_ircv3_message_get_params
(
message
);
if
(
params
!=
NULL
)
{
n_params
=
g_strv_length
(
params
);
}
if
(
n_params
!=
1
)
{
g_set_error
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"ignoring AUTHENTICATE with %d parameters"
,
n_params
);
return
FALSE
;
}
payload
=
params
[
0
];
data
=
g_object_get_data
(
G_OBJECT
(
connection
),
PURPLE_IRCV3_SASL_DATA_KEY
);
if
(
data
==
NULL
)
{
g_set_error_literal
(
error
,
PURPLE_IRCV3_DOMAIN
,
0
,
"AUTHENTICATE received with no SASL data present"
);
return
FALSE
;
}
/* If the server sent us a payload, combine the chunks. */
if
(
payload
[
0
]
!=
'+'
)
{
g_string_append
(
data
->
server_in_buffer
,
payload
);
if
(
strlen
(
payload
)
<
400
)
{
done
=
TRUE
;
}
}
else
{
/* The server sent a + which is an empty message or the final message
* ended on a 400 byte barrier. */
done
=
TRUE
;
}
if
(
done
)
{
HaslMechanismResult
res
=
0
;
GError
*
local_error
=
NULL
;
guint8
*
server_in
=
NULL
;
guint8
*
client_out
=
NULL
;
gsize
server_in_length
=
0
;
size_t
client_out_length
=
0
;
/* If we have a buffer, base64 decode it, and then truncate it. */
if
(
data
->
server_in_buffer
->
len
>
0
)
{
server_in
=
g_base64_decode
(
data
->
server_in_buffer
->
str
,
&
server_in_length
);
g_string_truncate
(
data
->
server_in_buffer
,
0
);
}
/* Try to move to the next step of the sasl client. */
res
=
hasl_context_step
(
data
->
ctx
,
server_in
,
server_in_length
,
&
client_out
,
&
client_out_length
,
&
local_error
);
/* We should be done with server_in, so free it.*/
g_clear_pointer
(
&
server_in
,
g_free
);
if
(
res
==
HASL_MECHANISM_RESULT_ERROR
)
{
g_propagate_error
(
error
,
local_error
);
return
FALSE
;
}
if
(
local_error
!=
NULL
)
{
g_warning
(
"hasl_context_step returned an error without an error "
"status: %s"
,
local_error
->
message
);
g_clear_error
(
&
local_error
);
}
/* If we got an output for the client, write it out. */
if
(
client_out_length
>
0
)
{
char
*
encoded
=
NULL
;
encoded
=
g_base64_encode
(
client_out
,
client_out_length
);
g_clear_pointer
(
&
client_out
,
g_free
);
purple_ircv3_connection_writef
(
connection
,
"%s %s"
,
PURPLE_IRCV3_MSG_AUTHENTICATE
,
encoded
);
g_free
(
encoded
);
}
else
{
purple_ircv3_connection_writef
(
connection
,
"%s +"
,
PURPLE_IRCV3_MSG_AUTHENTICATE
);
}
}
return
TRUE
;
}