qulogic/pidgin
Clone
Summary
Browse
Changes
Graph
Use g_slist_free_full and g_list_free_full in a few places.
2014-01-20, Mark Doliner
2561402bb3fd
Use g_slist_free_full and g_list_free_full in a few places.
/**
* @file purple-socket.c Generic sockets
* @ingroup core
*/
/* purple
*
* 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 program 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 program 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 program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include
"purple-socket.h"
#include
"internal.h"
#include
"debug.h"
#include
"proxy.h"
#include
"sslconn.h"
typedef
enum
{
PURPLE_SOCKET_STATE_DISCONNECTED
=
0
,
PURPLE_SOCKET_STATE_CONNECTING
,
PURPLE_SOCKET_STATE_CONNECTED
,
PURPLE_SOCKET_STATE_ERROR
}
PurpleSocketState
;
struct
_PurpleSocket
{
PurpleConnection
*
gc
;
gchar
*
host
;
int
port
;
gboolean
is_tls
;
GHashTable
*
data
;
PurpleSocketState
state
;
PurpleSslConnection
*
tls_connection
;
PurpleProxyConnectData
*
raw_connection
;
int
fd
;
guint
inpa
;
PurpleSocketConnectCb
cb
;
gpointer
cb_data
;
};
PurpleSocket
*
purple_socket_new
(
PurpleConnection
*
gc
)
{
PurpleSocket
*
ps
=
g_new0
(
PurpleSocket
,
1
);
ps
->
gc
=
gc
;
ps
->
fd
=
-1
;
ps
->
port
=
-1
;
ps
->
data
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
NULL
);
return
ps
;
}
PurpleConnection
*
purple_socket_get_connection
(
PurpleSocket
*
ps
)
{
g_return_val_if_fail
(
ps
!=
NULL
,
NULL
);
return
ps
->
gc
;
}
static
gboolean
purple_socket_check_state
(
PurpleSocket
*
ps
,
PurpleSocketState
wanted_state
)
{
g_return_val_if_fail
(
ps
!=
NULL
,
FALSE
);
if
(
ps
->
state
==
wanted_state
)
return
TRUE
;
purple_debug_error
(
"socket"
,
"invalid state: %d (should be: %d)"
,
ps
->
state
,
wanted_state
);
ps
->
state
=
PURPLE_SOCKET_STATE_ERROR
;
return
FALSE
;
}
void
purple_socket_set_tls
(
PurpleSocket
*
ps
,
gboolean
is_tls
)
{
g_return_if_fail
(
ps
!=
NULL
);
if
(
!
purple_socket_check_state
(
ps
,
PURPLE_SOCKET_STATE_DISCONNECTED
))
return
;
ps
->
is_tls
=
is_tls
;
}
void
purple_socket_set_host
(
PurpleSocket
*
ps
,
const
gchar
*
host
)
{
g_return_if_fail
(
ps
!=
NULL
);
if
(
!
purple_socket_check_state
(
ps
,
PURPLE_SOCKET_STATE_DISCONNECTED
))
return
;
g_free
(
ps
->
host
);
ps
->
host
=
g_strdup
(
host
);
}
void
purple_socket_set_port
(
PurpleSocket
*
ps
,
int
port
)
{
g_return_if_fail
(
ps
!=
NULL
);
g_return_if_fail
(
port
>=
0
);
g_return_if_fail
(
port
<=
65535
);
if
(
!
purple_socket_check_state
(
ps
,
PURPLE_SOCKET_STATE_DISCONNECTED
))
return
;
ps
->
port
=
port
;
}
static
void
_purple_socket_connected_raw
(
gpointer
_ps
,
gint
fd
,
const
gchar
*
error_message
)
{
PurpleSocket
*
ps
=
_ps
;
ps
->
raw_connection
=
NULL
;
if
(
!
purple_socket_check_state
(
ps
,
PURPLE_SOCKET_STATE_CONNECTING
))
{
if
(
fd
>
0
)
close
(
fd
);
ps
->
cb
(
ps
,
_
(
"Invalid socket state"
),
ps
->
cb_data
);
return
;
}
if
(
fd
<=
0
||
error_message
!=
NULL
)
{
if
(
error_message
==
NULL
)
error_message
=
_
(
"Unknown error"
);
ps
->
fd
=
-1
;
ps
->
state
=
PURPLE_SOCKET_STATE_ERROR
;
ps
->
cb
(
ps
,
error_message
,
ps
->
cb_data
);
return
;
}
ps
->
state
=
PURPLE_SOCKET_STATE_CONNECTED
;
ps
->
fd
=
fd
;
ps
->
cb
(
ps
,
NULL
,
ps
->
cb_data
);
}
static
void
_purple_socket_connected_tls
(
gpointer
_ps
,
PurpleSslConnection
*
tls_connection
,
PurpleInputCondition
cond
)
{
PurpleSocket
*
ps
=
_ps
;
if
(
!
purple_socket_check_state
(
ps
,
PURPLE_SOCKET_STATE_CONNECTING
))
{
purple_ssl_close
(
tls_connection
);
ps
->
tls_connection
=
NULL
;
ps
->
cb
(
ps
,
_
(
"Invalid socket state"
),
ps
->
cb_data
);
return
;
}
if
(
ps
->
tls_connection
->
fd
<=
0
)
{
ps
->
state
=
PURPLE_SOCKET_STATE_ERROR
;
purple_ssl_close
(
tls_connection
);
ps
->
tls_connection
=
NULL
;
ps
->
cb
(
ps
,
_
(
"Invalid file descriptor"
),
ps
->
cb_data
);
return
;
}
ps
->
state
=
PURPLE_SOCKET_STATE_CONNECTED
;
ps
->
fd
=
ps
->
tls_connection
->
fd
;
ps
->
cb
(
ps
,
NULL
,
ps
->
cb_data
);
}
static
void
_purple_socket_connected_tls_error
(
PurpleSslConnection
*
ssl_connection
,
PurpleSslErrorType
error
,
gpointer
_ps
)
{
PurpleSocket
*
ps
=
_ps
;
ps
->
state
=
PURPLE_SOCKET_STATE_ERROR
;
ps
->
tls_connection
=
NULL
;
ps
->
cb
(
ps
,
purple_ssl_strerror
(
error
),
ps
->
cb_data
);
}
gboolean
purple_socket_connect
(
PurpleSocket
*
ps
,
PurpleSocketConnectCb
cb
,
gpointer
user_data
)
{
PurpleAccount
*
account
=
NULL
;
g_return_val_if_fail
(
ps
!=
NULL
,
FALSE
);
if
(
!
purple_socket_check_state
(
ps
,
PURPLE_SOCKET_STATE_DISCONNECTED
))
return
FALSE
;
ps
->
state
=
PURPLE_SOCKET_STATE_CONNECTING
;
if
(
ps
->
host
==
NULL
||
ps
->
port
<
0
)
{
purple_debug_error
(
"socket"
,
"Host or port is not specified"
);
ps
->
state
=
PURPLE_SOCKET_STATE_ERROR
;
return
FALSE
;
}
if
(
ps
->
gc
!=
NULL
)
account
=
purple_connection_get_account
(
ps
->
gc
);
ps
->
cb
=
cb
;
ps
->
cb_data
=
user_data
;
if
(
ps
->
is_tls
)
{
if
(
!
purple_ssl_is_supported
())
{
purple_debug_error
(
"socket"
,
"TLS is not supported"
);
ps
->
state
=
PURPLE_SOCKET_STATE_ERROR
;
return
FALSE
;
}
ps
->
tls_connection
=
purple_ssl_connect
(
account
,
ps
->
host
,
ps
->
port
,
_purple_socket_connected_tls
,
_purple_socket_connected_tls_error
,
ps
);
}
else
{
ps
->
raw_connection
=
purple_proxy_connect
(
ps
->
gc
,
account
,
ps
->
host
,
ps
->
port
,
_purple_socket_connected_raw
,
ps
);
}
if
(
ps
->
tls_connection
==
NULL
&&
ps
->
raw_connection
==
NULL
)
{
ps
->
state
=
PURPLE_SOCKET_STATE_ERROR
;
return
FALSE
;
}
return
TRUE
;
}
gssize
purple_socket_read
(
PurpleSocket
*
ps
,
guchar
*
buf
,
size_t
len
)
{
g_return_val_if_fail
(
ps
!=
NULL
,
-1
);
g_return_val_if_fail
(
buf
!=
NULL
,
-1
);
if
(
!
purple_socket_check_state
(
ps
,
PURPLE_SOCKET_STATE_CONNECTED
))
return
-1
;
if
(
ps
->
is_tls
)
return
purple_ssl_read
(
ps
->
tls_connection
,
buf
,
len
);
else
return
read
(
ps
->
fd
,
buf
,
len
);
}
gssize
purple_socket_write
(
PurpleSocket
*
ps
,
const
guchar
*
buf
,
size_t
len
)
{
g_return_val_if_fail
(
ps
!=
NULL
,
-1
);
g_return_val_if_fail
(
buf
!=
NULL
,
-1
);
if
(
!
purple_socket_check_state
(
ps
,
PURPLE_SOCKET_STATE_CONNECTED
))
return
-1
;
if
(
ps
->
is_tls
)
return
purple_ssl_write
(
ps
->
tls_connection
,
buf
,
len
);
else
return
write
(
ps
->
fd
,
buf
,
len
);
}
void
purple_socket_watch
(
PurpleSocket
*
ps
,
PurpleInputCondition
cond
,
PurpleInputFunction
func
,
gpointer
user_data
)
{
g_return_if_fail
(
ps
!=
NULL
);
if
(
!
purple_socket_check_state
(
ps
,
PURPLE_SOCKET_STATE_CONNECTED
))
return
;
if
(
ps
->
inpa
>
0
)
purple_input_remove
(
ps
->
inpa
);
ps
->
inpa
=
0
;
g_return_if_fail
(
ps
->
fd
>
0
);
if
(
func
!=
NULL
)
ps
->
inpa
=
purple_input_add
(
ps
->
fd
,
cond
,
func
,
user_data
);
}
int
purple_socket_get_fd
(
PurpleSocket
*
ps
)
{
g_return_val_if_fail
(
ps
!=
NULL
,
-1
);
if
(
!
purple_socket_check_state
(
ps
,
PURPLE_SOCKET_STATE_CONNECTED
))
return
-1
;
g_return_val_if_fail
(
ps
->
fd
>
0
,
-1
);
return
ps
->
fd
;
}
void
purple_socket_set_data
(
PurpleSocket
*
ps
,
const
gchar
*
key
,
gpointer
data
)
{
g_return_if_fail
(
ps
!=
NULL
);
g_return_if_fail
(
key
!=
NULL
);
if
(
data
==
NULL
)
g_hash_table_remove
(
ps
->
data
,
key
);
else
g_hash_table_insert
(
ps
->
data
,
g_strdup
(
key
),
data
);
}
gpointer
purple_socket_get_data
(
PurpleSocket
*
ps
,
const
gchar
*
key
)
{
g_return_val_if_fail
(
ps
!=
NULL
,
NULL
);
g_return_val_if_fail
(
key
!=
NULL
,
NULL
);
return
g_hash_table_lookup
(
ps
->
data
,
key
);
}
void
purple_socket_destroy
(
PurpleSocket
*
ps
)
{
if
(
ps
==
NULL
)
return
;
g_free
(
ps
->
host
);
if
(
ps
->
inpa
>
0
)
purple_input_remove
(
ps
->
inpa
);
ps
->
inpa
=
0
;
if
(
ps
->
tls_connection
!=
NULL
)
{
purple_ssl_close
(
ps
->
tls_connection
);
ps
->
fd
=
-1
;
}
if
(
ps
->
raw_connection
!=
NULL
)
purple_proxy_connect_cancel
(
ps
->
raw_connection
);
if
(
ps
->
fd
>
0
)
close
(
ps
->
fd
);
g_hash_table_destroy
(
ps
->
data
);
g_free
(
ps
);
}