gaim/gaim
Clone
Summary
Browse
Changes
Graph
dave142 in patch #1193896 noticed that the SetDllDirectory magic probably never worked... This fixes it.
oldstatus
2005-05-03, Daniel Atallah
8e9c0e0ae15c
dave142 in patch #1193896 noticed that the SetDllDirectory magic probably never worked... This fixes it.
/**
* @file ft.c File Transfer API
*
* gaim
*
* Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include
"internal.h"
#include
"ft.h"
#include
"network.h"
#include
"notify.h"
#include
"prefs.h"
#include
"proxy.h"
#include
"request.h"
#include
"util.h"
static
GaimXferUiOps
*
xfer_ui_ops
=
NULL
;
GaimXfer
*
gaim_xfer_new
(
GaimAccount
*
account
,
GaimXferType
type
,
const
char
*
who
)
{
GaimXfer
*
xfer
;
GaimXferUiOps
*
ui_ops
;
g_return_val_if_fail
(
type
!=
GAIM_XFER_UNKNOWN
,
NULL
);
g_return_val_if_fail
(
account
!=
NULL
,
NULL
);
g_return_val_if_fail
(
who
!=
NULL
,
NULL
);
xfer
=
g_new0
(
GaimXfer
,
1
);
xfer
->
ref
=
1
;
xfer
->
type
=
type
;
xfer
->
account
=
account
;
xfer
->
who
=
g_strdup
(
who
);
xfer
->
ui_ops
=
gaim_xfers_get_ui_ops
();
xfer
->
message
=
NULL
;
ui_ops
=
gaim_xfer_get_ui_ops
(
xfer
);
if
(
ui_ops
!=
NULL
&&
ui_ops
->
new_xfer
!=
NULL
)
ui_ops
->
new_xfer
(
xfer
);
return
xfer
;
}
static
void
gaim_xfer_destroy
(
GaimXfer
*
xfer
)
{
GaimXferUiOps
*
ui_ops
;
g_return_if_fail
(
xfer
!=
NULL
);
/* Close the file browser, if it's open */
gaim_request_close_with_handle
(
xfer
);
if
(
gaim_xfer_get_status
(
xfer
)
==
GAIM_XFER_STATUS_STARTED
)
gaim_xfer_cancel_local
(
xfer
);
ui_ops
=
gaim_xfer_get_ui_ops
(
xfer
);
if
(
ui_ops
!=
NULL
&&
ui_ops
->
destroy
!=
NULL
)
ui_ops
->
destroy
(
xfer
);
g_free
(
xfer
->
who
);
g_free
(
xfer
->
filename
);
g_free
(
xfer
->
remote_ip
);
g_free
(
xfer
->
local_filename
);
g_free
(
xfer
);
}
void
gaim_xfer_ref
(
GaimXfer
*
xfer
)
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ref
++
;
}
void
gaim_xfer_unref
(
GaimXfer
*
xfer
)
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ref
--
;
if
(
xfer
->
ref
==
0
)
gaim_xfer_destroy
(
xfer
);
}
static
void
gaim_xfer_set_status
(
GaimXfer
*
xfer
,
GaimXferStatusType
status
)
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
status
=
status
;
}
static
void
gaim_xfer_conversation_write
(
GaimXfer
*
xfer
,
char
*
message
,
gboolean
is_error
)
{
GaimConversation
*
conv
=
NULL
;
GString
*
gs_message
=
NULL
;
GaimMessageFlags
flags
=
GAIM_MESSAGE_SYSTEM
;
g_return_if_fail
(
xfer
!=
NULL
);
g_return_if_fail
(
message
!=
NULL
);
conv
=
gaim_find_conversation_with_account
(
xfer
->
who
,
gaim_xfer_get_account
(
xfer
));
if
(
conv
==
NULL
)
return
;
gs_message
=
g_string_new
(
message
);
if
(
is_error
)
flags
=
GAIM_MESSAGE_ERROR
;
gaim_conversation_write
(
conv
,
NULL
,
gs_message
->
str
,
flags
,
time
(
NULL
));
g_string_free
(
gs_message
,
TRUE
);
}
static
void
gaim_xfer_show_file_error
(
GaimXfer
*
xfer
,
const
char
*
filename
)
{
gchar
*
msg
=
NULL
;
GaimXferType
xfer_type
=
gaim_xfer_get_type
(
xfer
);
switch
(
xfer_type
)
{
case
GAIM_XFER_SEND
:
msg
=
g_strdup_printf
(
_
(
"Error reading %s:
\n
%s.
\n
"
),
filename
,
strerror
(
errno
));
break
;
case
GAIM_XFER_RECEIVE
:
msg
=
g_strdup_printf
(
_
(
"Error writing %s:
\n
%s.
\n
"
),
filename
,
strerror
(
errno
));
break
;
default
:
msg
=
g_strdup_printf
(
_
(
"Error accessing %s:
\n
%s.
\n
"
),
filename
,
strerror
(
errno
));
break
;
}
gaim_xfer_conversation_write
(
xfer
,
msg
,
TRUE
);
gaim_xfer_error
(
xfer_type
,
xfer
->
who
,
msg
);
g_free
(
msg
);
}
static
void
gaim_xfer_choose_file_ok_cb
(
void
*
user_data
,
const
char
*
filename
)
{
GaimXfer
*
xfer
;
struct
stat
st
;
xfer
=
(
GaimXfer
*
)
user_data
;
if
(
g_stat
(
filename
,
&
st
)
!=
0
)
{
/* File not found. */
if
(
gaim_xfer_get_type
(
xfer
)
==
GAIM_XFER_RECEIVE
)
{
gaim_xfer_request_accepted
(
xfer
,
filename
);
}
else
{
gaim_xfer_show_file_error
(
xfer
,
filename
);
gaim_xfer_request_denied
(
xfer
);
}
}
else
if
((
gaim_xfer_get_type
(
xfer
)
==
GAIM_XFER_SEND
)
&&
(
st
.
st_size
==
0
))
{
gaim_notify_error
(
NULL
,
NULL
,
_
(
"Cannot send a file of 0 bytes."
),
NULL
);
gaim_xfer_request_denied
(
xfer
);
}
else
if
((
gaim_xfer_get_type
(
xfer
)
==
GAIM_XFER_SEND
)
&&
S_ISDIR
(
st
.
st_mode
))
{
/*
* XXX - Sending a directory should be valid for some protocols.
*/
gaim_notify_error
(
NULL
,
NULL
,
_
(
"Cannot send a directory."
),
NULL
);
gaim_xfer_request_denied
(
xfer
);
}
else
if
((
gaim_xfer_get_type
(
xfer
)
==
GAIM_XFER_RECEIVE
)
&&
S_ISDIR
(
st
.
st_mode
))
{
char
*
msg
=
g_strdup_printf
(
_
(
"%s is not a regular file. Cowardly refusing to overwrite it.
\n
"
),
filename
);
gaim_notify_error
(
NULL
,
NULL
,
msg
,
NULL
);
g_free
(
msg
);
gaim_xfer_request_denied
(
xfer
);
}
else
{
gaim_xfer_request_accepted
(
xfer
,
filename
);
}
gaim_xfer_unref
(
xfer
);
}
static
void
gaim_xfer_choose_file_cancel_cb
(
void
*
user_data
,
const
char
*
filename
)
{
GaimXfer
*
xfer
=
(
GaimXfer
*
)
user_data
;
gaim_xfer_set_status
(
xfer
,
GAIM_XFER_STATUS_CANCEL_LOCAL
);
gaim_xfer_request_denied
(
xfer
);
}
static
int
gaim_xfer_choose_file
(
GaimXfer
*
xfer
)
{
gaim_request_file
(
xfer
,
NULL
,
gaim_xfer_get_filename
(
xfer
),
(
gaim_xfer_get_type
(
xfer
)
==
GAIM_XFER_RECEIVE
),
G_CALLBACK
(
gaim_xfer_choose_file_ok_cb
),
G_CALLBACK
(
gaim_xfer_choose_file_cancel_cb
),
xfer
);
return
0
;
}
static
int
cancel_recv_cb
(
GaimXfer
*
xfer
)
{
gaim_xfer_set_status
(
xfer
,
GAIM_XFER_STATUS_CANCEL_LOCAL
);
gaim_xfer_request_denied
(
xfer
);
gaim_xfer_unref
(
xfer
);
return
0
;
}
static
void
gaim_xfer_ask_recv
(
GaimXfer
*
xfer
)
{
char
*
buf
,
*
size_buf
,
*
escaped
;
size_t
size
;
/* If we have already accepted the request, ask the destination file
name directly */
if
(
gaim_xfer_get_status
(
xfer
)
!=
GAIM_XFER_STATUS_ACCEPTED
)
{
if
(
gaim_xfer_get_filename
(
xfer
)
!=
NULL
)
{
size
=
gaim_xfer_get_size
(
xfer
);
size_buf
=
gaim_str_size_to_units
(
size
);
escaped
=
g_markup_escape_text
(
gaim_xfer_get_filename
(
xfer
),
-1
);
buf
=
g_strdup_printf
(
_
(
"%s wants to send you %s (%s)"
),
xfer
->
who
,
escaped
,
size_buf
);
g_free
(
size_buf
);
g_free
(
escaped
);
}
else
{
buf
=
g_strdup_printf
(
_
(
"%s wants to send you a file"
),
xfer
->
who
);
}
if
(
xfer
->
message
!=
NULL
)
serv_got_im
(
gaim_account_get_connection
(
xfer
->
account
),
xfer
->
who
,
xfer
->
message
,
0
,
time
(
NULL
));
gaim_request_accept_cancel
(
xfer
,
NULL
,
buf
,
NULL
,
GAIM_DEFAULT_ACTION_NONE
,
xfer
,
G_CALLBACK
(
gaim_xfer_choose_file
),
G_CALLBACK
(
cancel_recv_cb
));
g_free
(
buf
);
}
else
gaim_xfer_choose_file
(
xfer
);
}
static
int
ask_accept_ok
(
GaimXfer
*
xfer
)
{
gaim_xfer_request_accepted
(
xfer
,
NULL
);
return
0
;
}
static
int
ask_accept_cancel
(
GaimXfer
*
xfer
)
{
gaim_xfer_request_denied
(
xfer
);
gaim_xfer_unref
(
xfer
);
return
0
;
}
static
void
gaim_xfer_ask_accept
(
GaimXfer
*
xfer
)
{
char
*
buf
,
*
buf2
=
NULL
;
buf
=
g_strdup_printf
(
_
(
"Accept file transfer request from %s?"
),
xfer
->
who
);
if
(
gaim_xfer_get_remote_ip
(
xfer
)
&&
gaim_xfer_get_remote_port
(
xfer
))
buf2
=
g_strdup_printf
(
_
(
"A file is available for download from:
\n
"
"Remote host: %s
\n
Remote port: %d"
),
gaim_xfer_get_remote_ip
(
xfer
),
gaim_xfer_get_remote_port
(
xfer
));
gaim_request_accept_cancel
(
xfer
,
NULL
,
buf
,
buf2
,
GAIM_DEFAULT_ACTION_NONE
,
xfer
,
G_CALLBACK
(
ask_accept_ok
),
G_CALLBACK
(
ask_accept_cancel
));
g_free
(
buf
);
g_free
(
buf2
);
}
void
gaim_xfer_request
(
GaimXfer
*
xfer
)
{
g_return_if_fail
(
xfer
!=
NULL
);
g_return_if_fail
(
xfer
->
ops
.
init
!=
NULL
);
gaim_xfer_ref
(
xfer
);
if
(
gaim_xfer_get_type
(
xfer
)
==
GAIM_XFER_RECEIVE
)
{
if
(
gaim_xfer_get_filename
(
xfer
)
||
gaim_xfer_get_status
(
xfer
)
==
GAIM_XFER_STATUS_ACCEPTED
)
gaim_xfer_ask_recv
(
xfer
);
else
gaim_xfer_ask_accept
(
xfer
);
}
else
gaim_xfer_choose_file
(
xfer
);
}
void
gaim_xfer_request_accepted
(
GaimXfer
*
xfer
,
const
char
*
filename
)
{
GaimXferType
type
;
struct
stat
st
;
if
(
xfer
==
NULL
)
return
;
type
=
gaim_xfer_get_type
(
xfer
);
if
(
!
filename
&&
type
==
GAIM_XFER_RECEIVE
)
{
xfer
->
status
=
GAIM_XFER_STATUS_ACCEPTED
;
xfer
->
ops
.
init
(
xfer
);
return
;
}
if
(
type
==
GAIM_XFER_SEND
)
{
char
*
msg
;
/* Check the filename. */
if
(
g_strrstr
(
filename
,
".."
))
{
msg
=
g_strdup_printf
(
_
(
"%s is not a valid filename.
\n
"
),
filename
);
gaim_xfer_error
(
type
,
xfer
->
who
,
msg
);
g_free
(
msg
);
gaim_xfer_unref
(
xfer
);
return
;
}
if
(
g_stat
(
filename
,
&
st
)
==
-1
)
{
gaim_xfer_show_file_error
(
xfer
,
filename
);
gaim_xfer_unref
(
xfer
);
return
;
}
gaim_xfer_set_local_filename
(
xfer
,
filename
);
gaim_xfer_set_filename
(
xfer
,
g_basename
(
filename
));
gaim_xfer_set_size
(
xfer
,
st
.
st_size
);
msg
=
g_strdup_printf
(
_
(
"Offering to send %s to %s"
),
filename
,
xfer
->
who
);
gaim_xfer_conversation_write
(
xfer
,
msg
,
FALSE
);
g_free
(
msg
);
}
else
{
xfer
->
status
=
GAIM_XFER_STATUS_ACCEPTED
;
gaim_xfer_set_local_filename
(
xfer
,
filename
);
}
gaim_xfer_add
(
xfer
);
xfer
->
ops
.
init
(
xfer
);
}
void
gaim_xfer_request_denied
(
GaimXfer
*
xfer
)
{
g_return_if_fail
(
xfer
!=
NULL
);
if
(
xfer
->
ops
.
request_denied
!=
NULL
)
xfer
->
ops
.
request_denied
(
xfer
);
gaim_xfer_unref
(
xfer
);
}
GaimXferType
gaim_xfer_get_type
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
GAIM_XFER_UNKNOWN
);
return
xfer
->
type
;
}
GaimAccount
*
gaim_xfer_get_account
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
NULL
);
return
xfer
->
account
;
}
GaimXferStatusType
gaim_xfer_get_status
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
GAIM_XFER_STATUS_UNKNOWN
);
return
xfer
->
status
;
}
gboolean
gaim_xfer_is_canceled
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
TRUE
);
if
((
gaim_xfer_get_status
(
xfer
)
==
GAIM_XFER_STATUS_CANCEL_LOCAL
)
||
(
gaim_xfer_get_status
(
xfer
)
==
GAIM_XFER_STATUS_CANCEL_REMOTE
))
return
TRUE
;
else
return
FALSE
;
}
gboolean
gaim_xfer_is_completed
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
TRUE
);
return
(
gaim_xfer_get_status
(
xfer
)
==
GAIM_XFER_STATUS_DONE
);
}
const
char
*
gaim_xfer_get_filename
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
NULL
);
return
xfer
->
filename
;
}
const
char
*
gaim_xfer_get_local_filename
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
NULL
);
return
xfer
->
local_filename
;
}
size_t
gaim_xfer_get_bytes_sent
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
0
);
return
xfer
->
bytes_sent
;
}
size_t
gaim_xfer_get_bytes_remaining
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
0
);
return
xfer
->
bytes_remaining
;
}
size_t
gaim_xfer_get_size
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
0
);
return
xfer
->
size
;
}
double
gaim_xfer_get_progress
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
0.0
);
if
(
gaim_xfer_get_size
(
xfer
)
==
0
)
return
0.0
;
return
((
double
)
gaim_xfer_get_bytes_sent
(
xfer
)
/
(
double
)
gaim_xfer_get_size
(
xfer
));
}
unsigned
int
gaim_xfer_get_local_port
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
-1
);
return
xfer
->
local_port
;
}
const
char
*
gaim_xfer_get_remote_ip
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
NULL
);
return
xfer
->
remote_ip
;
}
unsigned
int
gaim_xfer_get_remote_port
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
-1
);
return
xfer
->
remote_port
;
}
void
gaim_xfer_set_completed
(
GaimXfer
*
xfer
,
gboolean
completed
)
{
GaimXferUiOps
*
ui_ops
;
g_return_if_fail
(
xfer
!=
NULL
);
if
(
completed
==
TRUE
)
gaim_xfer_set_status
(
xfer
,
GAIM_XFER_STATUS_DONE
);
ui_ops
=
gaim_xfer_get_ui_ops
(
xfer
);
if
(
ui_ops
!=
NULL
&&
ui_ops
->
update_progress
!=
NULL
)
ui_ops
->
update_progress
(
xfer
,
gaim_xfer_get_progress
(
xfer
));
}
void
gaim_xfer_set_message
(
GaimXfer
*
xfer
,
const
char
*
message
)
{
g_return_if_fail
(
xfer
!=
NULL
);
g_free
(
xfer
->
message
);
if
(
message
!=
NULL
)
xfer
->
message
=
g_strdup
(
message
);
else
xfer
->
message
=
NULL
;
}
void
gaim_xfer_set_filename
(
GaimXfer
*
xfer
,
const
char
*
filename
)
{
g_return_if_fail
(
xfer
!=
NULL
);
if
(
xfer
->
filename
!=
NULL
)
g_free
(
xfer
->
filename
);
xfer
->
filename
=
(
filename
==
NULL
?
NULL
:
g_strdup
(
filename
));
}
void
gaim_xfer_set_local_filename
(
GaimXfer
*
xfer
,
const
char
*
filename
)
{
g_return_if_fail
(
xfer
!=
NULL
);
if
(
xfer
->
local_filename
!=
NULL
)
g_free
(
xfer
->
local_filename
);
xfer
->
local_filename
=
(
filename
==
NULL
?
NULL
:
g_strdup
(
filename
));
}
void
gaim_xfer_set_size
(
GaimXfer
*
xfer
,
size_t
size
)
{
g_return_if_fail
(
xfer
!=
NULL
);
if
(
xfer
->
size
==
0
)
xfer
->
bytes_remaining
=
size
-
xfer
->
bytes_sent
;
xfer
->
size
=
size
;
}
GaimXferUiOps
*
gaim_xfer_get_ui_ops
(
const
GaimXfer
*
xfer
)
{
g_return_val_if_fail
(
xfer
!=
NULL
,
NULL
);
return
xfer
->
ui_ops
;
}
void
gaim_xfer_set_init_fnc
(
GaimXfer
*
xfer
,
void
(
*
fnc
)(
GaimXfer
*
))
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ops
.
init
=
fnc
;
}
void
gaim_xfer_set_request_denied_fnc
(
GaimXfer
*
xfer
,
void
(
*
fnc
)(
GaimXfer
*
))
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ops
.
request_denied
=
fnc
;
}
void
gaim_xfer_set_read_fnc
(
GaimXfer
*
xfer
,
ssize_t
(
*
fnc
)(
char
**
,
GaimXfer
*
))
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ops
.
read
=
fnc
;
}
void
gaim_xfer_set_write_fnc
(
GaimXfer
*
xfer
,
ssize_t
(
*
fnc
)(
const
char
*
,
size_t
,
GaimXfer
*
))
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ops
.
write
=
fnc
;
}
void
gaim_xfer_set_ack_fnc
(
GaimXfer
*
xfer
,
void
(
*
fnc
)(
GaimXfer
*
,
const
char
*
,
size_t
))
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ops
.
ack
=
fnc
;
}
void
gaim_xfer_set_start_fnc
(
GaimXfer
*
xfer
,
void
(
*
fnc
)(
GaimXfer
*
))
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ops
.
start
=
fnc
;
}
void
gaim_xfer_set_end_fnc
(
GaimXfer
*
xfer
,
void
(
*
fnc
)(
GaimXfer
*
))
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ops
.
end
=
fnc
;
}
void
gaim_xfer_set_cancel_send_fnc
(
GaimXfer
*
xfer
,
void
(
*
fnc
)(
GaimXfer
*
))
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ops
.
cancel_send
=
fnc
;
}
void
gaim_xfer_set_cancel_recv_fnc
(
GaimXfer
*
xfer
,
void
(
*
fnc
)(
GaimXfer
*
))
{
g_return_if_fail
(
xfer
!=
NULL
);
xfer
->
ops
.
cancel_recv
=
fnc
;
}
ssize_t
gaim_xfer_read
(
GaimXfer
*
xfer
,
char
**
buffer
)
{
ssize_t
s
,
r
;
g_return_val_if_fail
(
xfer
!=
NULL
,
0
);
g_return_val_if_fail
(
buffer
!=
NULL
,
0
);
if
(
gaim_xfer_get_size
(
xfer
)
==
0
)
s
=
4096
;
else
s
=
MIN
(
gaim_xfer_get_bytes_remaining
(
xfer
),
4096
);
if
(
xfer
->
ops
.
read
!=
NULL
)
r
=
(
xfer
->
ops
.
read
)(
buffer
,
xfer
);
else
{
*
buffer
=
g_malloc0
(
s
);
r
=
read
(
xfer
->
fd
,
*
buffer
,
s
);
if
((
gaim_xfer_get_size
(
xfer
)
>
0
)
&&
((
gaim_xfer_get_bytes_sent
(
xfer
)
+
r
)
>=
gaim_xfer_get_size
(
xfer
)))
gaim_xfer_set_completed
(
xfer
,
TRUE
);
else
if
(
r
<=
0
)
r
=
-1
;
}
return
r
;
}
ssize_t
gaim_xfer_write
(
GaimXfer
*
xfer
,
const
char
*
buffer
,
size_t
size
)
{
ssize_t
r
,
s
;
g_return_val_if_fail
(
xfer
!=
NULL
,
0
);
g_return_val_if_fail
(
buffer
!=
NULL
,
0
);
g_return_val_if_fail
(
size
!=
0
,
0
);
s
=
MIN
(
gaim_xfer_get_bytes_remaining
(
xfer
),
size
);
if
(
xfer
->
ops
.
write
!=
NULL
)
{
r
=
(
xfer
->
ops
.
write
)(
buffer
,
s
,
xfer
);
}
else
{
r
=
write
(
xfer
->
fd
,
buffer
,
s
);
if
((
gaim_xfer_get_bytes_sent
(
xfer
)
+
r
)
>=
gaim_xfer_get_size
(
xfer
))
gaim_xfer_set_completed
(
xfer
,
TRUE
);
}
return
r
;
}
static
void
transfer_cb
(
gpointer
data
,
gint
source
,
GaimInputCondition
condition
)
{
GaimXferUiOps
*
ui_ops
;
GaimXfer
*
xfer
=
(
GaimXfer
*
)
data
;
char
*
buffer
=
NULL
;
ssize_t
r
=
0
;
if
(
condition
&
GAIM_INPUT_READ
)
{
r
=
gaim_xfer_read
(
xfer
,
&
buffer
);
if
(
r
>
0
)
{
fwrite
(
buffer
,
1
,
r
,
xfer
->
dest_fp
);
}
else
if
(
r
<
0
)
{
gaim_xfer_cancel_remote
(
xfer
);
return
;
}
}
if
(
condition
&
GAIM_INPUT_WRITE
)
{
size_t
s
=
MIN
(
gaim_xfer_get_bytes_remaining
(
xfer
),
4096
);
/* this is so the prpl can keep the connection open
if it needs to for some odd reason. */
if
(
s
==
0
)
{
if
(
xfer
->
watcher
)
{
gaim_input_remove
(
xfer
->
watcher
);
xfer
->
watcher
=
0
;
}
return
;
}
buffer
=
g_malloc0
(
s
);
fread
(
buffer
,
1
,
s
,
xfer
->
dest_fp
);
/* Write as much as we're allowed to. */
r
=
gaim_xfer_write
(
xfer
,
buffer
,
s
);
if
(
r
==
-1
)
{
gaim_xfer_cancel_remote
(
xfer
);
g_free
(
buffer
);
return
;
}
else
if
(
r
<
s
)
{
/* We have to seek back in the file now. */
fseek
(
xfer
->
dest_fp
,
r
-
s
,
SEEK_CUR
);
}
}
if
(
gaim_xfer_get_size
(
xfer
)
>
0
)
xfer
->
bytes_remaining
-=
r
;
xfer
->
bytes_sent
+=
r
;
if
(
xfer
->
ops
.
ack
!=
NULL
)
xfer
->
ops
.
ack
(
xfer
,
buffer
,
r
);
g_free
(
buffer
);
ui_ops
=
gaim_xfer_get_ui_ops
(
xfer
);
if
(
ui_ops
!=
NULL
&&
ui_ops
->
update_progress
!=
NULL
)
ui_ops
->
update_progress
(
xfer
,
gaim_xfer_get_progress
(
xfer
));
if
(
gaim_xfer_is_completed
(
xfer
))
gaim_xfer_end
(
xfer
);
}
static
void
begin_transfer
(
GaimXfer
*
xfer
,
GaimInputCondition
cond
)
{
GaimXferType
type
=
gaim_xfer_get_type
(
xfer
);
xfer
->
dest_fp
=
g_fopen
(
gaim_xfer_get_local_filename
(
xfer
),
type
==
GAIM_XFER_RECEIVE
?
"wb"
:
"rb"
);
if
(
xfer
->
dest_fp
==
NULL
)
{
gaim_xfer_show_file_error
(
xfer
,
gaim_xfer_get_local_filename
(
xfer
));
gaim_xfer_cancel_local
(
xfer
);
return
;
}
xfer
->
watcher
=
gaim_input_add
(
xfer
->
fd
,
cond
,
transfer_cb
,
xfer
);
if
(
xfer
->
ops
.
start
!=
NULL
)
xfer
->
ops
.
start
(
xfer
);
}
static
void
connect_cb
(
gpointer
data
,
gint
source
,
GaimInputCondition
condition
)
{
GaimXfer
*
xfer
=
(
GaimXfer
*
)
data
;
xfer
->
fd
=
source
;
begin_transfer
(
xfer
,
condition
);
}
void
gaim_xfer_start
(
GaimXfer
*
xfer
,
int
fd
,
const
char
*
ip
,
unsigned
int
port
)
{
GaimInputCondition
cond
;
GaimXferType
type
;
g_return_if_fail
(
xfer
!=
NULL
);
g_return_if_fail
(
gaim_xfer_get_type
(
xfer
)
!=
GAIM_XFER_UNKNOWN
);
type
=
gaim_xfer_get_type
(
xfer
);
xfer
->
bytes_remaining
=
gaim_xfer_get_size
(
xfer
);
xfer
->
bytes_sent
=
0
;
gaim_xfer_set_status
(
xfer
,
GAIM_XFER_STATUS_STARTED
);
if
(
type
==
GAIM_XFER_RECEIVE
)
{
cond
=
GAIM_INPUT_READ
;
if
(
ip
!=
NULL
)
{
xfer
->
remote_ip
=
g_strdup
(
ip
);
xfer
->
remote_port
=
port
;
/* Establish a file descriptor. */
gaim_proxy_connect
(
xfer
->
account
,
xfer
->
remote_ip
,
xfer
->
remote_port
,
connect_cb
,
xfer
);
return
;
}
else
{
xfer
->
fd
=
fd
;
}
}
else
{
cond
=
GAIM_INPUT_WRITE
;
xfer
->
fd
=
fd
;
}
begin_transfer
(
xfer
,
cond
);
}
void
gaim_xfer_end
(
GaimXfer
*
xfer
)
{
char
*
msg
=
NULL
;
g_return_if_fail
(
xfer
!=
NULL
);
/* See if we are actually trying to cancel this. */
if
(
gaim_xfer_get_status
(
xfer
)
!=
GAIM_XFER_STATUS_DONE
)
{
gaim_xfer_cancel_local
(
xfer
);
return
;
}
if
(
gaim_xfer_get_filename
(
xfer
)
!=
NULL
)
msg
=
g_strdup_printf
(
_
(
"Transfer of file %s complete"
),
gaim_xfer_get_filename
(
xfer
));
else
msg
=
g_strdup_printf
(
_
(
"File transfer complete"
));
gaim_xfer_conversation_write
(
xfer
,
msg
,
FALSE
);
g_free
(
msg
);
if
(
xfer
->
ops
.
end
!=
NULL
)
xfer
->
ops
.
end
(
xfer
);
if
(
xfer
->
watcher
!=
0
)
{
gaim_input_remove
(
xfer
->
watcher
);
xfer
->
watcher
=
0
;
}
if
(
xfer
->
fd
!=
0
)
close
(
xfer
->
fd
);
if
(
xfer
->
dest_fp
!=
NULL
)
{
fclose
(
xfer
->
dest_fp
);
xfer
->
dest_fp
=
NULL
;
}
gaim_xfer_unref
(
xfer
);
}
void
gaim_xfer_add
(
GaimXfer
*
xfer
)
{
GaimXferUiOps
*
ui_ops
;
g_return_if_fail
(
xfer
!=
NULL
);
ui_ops
=
gaim_xfer_get_ui_ops
(
xfer
);
if
(
ui_ops
!=
NULL
&&
ui_ops
->
add_xfer
!=
NULL
)
ui_ops
->
add_xfer
(
xfer
);
}
void
gaim_xfer_cancel_local
(
GaimXfer
*
xfer
)
{
GaimXferUiOps
*
ui_ops
;
char
*
msg
=
NULL
,
*
escaped
;
g_return_if_fail
(
xfer
!=
NULL
);
gaim_xfer_set_status
(
xfer
,
GAIM_XFER_STATUS_CANCEL_LOCAL
);
if
(
gaim_xfer_get_filename
(
xfer
)
!=
NULL
)
{
escaped
=
g_markup_escape_text
(
gaim_xfer_get_filename
(
xfer
),
-1
);
msg
=
g_strdup_printf
(
_
(
"You canceled the transfer of %s"
),
escaped
);
g_free
(
escaped
);
}
else
{
msg
=
g_strdup_printf
(
_
(
"File transfer cancelled"
));
}
gaim_xfer_conversation_write
(
xfer
,
msg
,
FALSE
);
g_free
(
msg
);
if
(
gaim_xfer_get_type
(
xfer
)
==
GAIM_XFER_SEND
)
{
if
(
xfer
->
ops
.
cancel_send
!=
NULL
)
xfer
->
ops
.
cancel_send
(
xfer
);
}
else
{
if
(
xfer
->
ops
.
cancel_recv
!=
NULL
)
xfer
->
ops
.
cancel_recv
(
xfer
);
}
if
(
xfer
->
watcher
!=
0
)
{
gaim_input_remove
(
xfer
->
watcher
);
xfer
->
watcher
=
0
;
}
if
(
xfer
->
fd
!=
0
)
close
(
xfer
->
fd
);
if
(
xfer
->
dest_fp
!=
NULL
)
{
fclose
(
xfer
->
dest_fp
);
xfer
->
dest_fp
=
NULL
;
}
ui_ops
=
gaim_xfer_get_ui_ops
(
xfer
);
if
(
ui_ops
!=
NULL
&&
ui_ops
->
cancel_local
!=
NULL
)
ui_ops
->
cancel_local
(
xfer
);
xfer
->
bytes_remaining
=
0
;
gaim_xfer_unref
(
xfer
);
}
void
gaim_xfer_cancel_remote
(
GaimXfer
*
xfer
)
{
GaimXferUiOps
*
ui_ops
;
gchar
*
msg
,
*
escaped
;
g_return_if_fail
(
xfer
!=
NULL
);
gaim_request_close_with_handle
(
xfer
);
gaim_xfer_set_status
(
xfer
,
GAIM_XFER_STATUS_CANCEL_REMOTE
);
if
(
gaim_xfer_get_filename
(
xfer
)
!=
NULL
)
{
escaped
=
g_markup_escape_text
(
gaim_xfer_get_filename
(
xfer
),
-1
);
msg
=
g_strdup_printf
(
_
(
"%s canceled the transfer of %s"
),
xfer
->
who
,
escaped
);
g_free
(
escaped
);
}
else
{
msg
=
g_strdup_printf
(
_
(
"%s canceled the file transfer"
),
xfer
->
who
);
}
gaim_xfer_conversation_write
(
xfer
,
msg
,
TRUE
);
gaim_xfer_error
(
gaim_xfer_get_type
(
xfer
),
xfer
->
who
,
msg
);
g_free
(
msg
);
if
(
gaim_xfer_get_type
(
xfer
)
==
GAIM_XFER_SEND
)
{
if
(
xfer
->
ops
.
cancel_send
!=
NULL
)
xfer
->
ops
.
cancel_send
(
xfer
);
}
else
{
if
(
xfer
->
ops
.
cancel_recv
!=
NULL
)
xfer
->
ops
.
cancel_recv
(
xfer
);
}
if
(
xfer
->
watcher
!=
0
)
{
gaim_input_remove
(
xfer
->
watcher
);
xfer
->
watcher
=
0
;
}
if
(
xfer
->
fd
!=
0
)
close
(
xfer
->
fd
);
if
(
xfer
->
dest_fp
!=
NULL
)
{
fclose
(
xfer
->
dest_fp
);
xfer
->
dest_fp
=
NULL
;
}
ui_ops
=
gaim_xfer_get_ui_ops
(
xfer
);
if
(
ui_ops
!=
NULL
&&
ui_ops
->
cancel_remote
!=
NULL
)
ui_ops
->
cancel_remote
(
xfer
);
xfer
->
bytes_remaining
=
0
;
gaim_xfer_unref
(
xfer
);
}
void
gaim_xfer_error
(
GaimXferType
type
,
const
char
*
who
,
const
char
*
msg
)
{
char
*
title
;
g_return_if_fail
(
msg
!=
NULL
);
g_return_if_fail
(
type
!=
GAIM_XFER_UNKNOWN
);
if
(
type
==
GAIM_XFER_SEND
)
title
=
g_strdup_printf
(
_
(
"File transfer to %s aborted.
\n
"
),
who
);
else
title
=
g_strdup_printf
(
_
(
"File transfer from %s aborted.
\n
"
),
who
);
gaim_notify_error
(
NULL
,
NULL
,
title
,
msg
);
g_free
(
title
);
}
void
gaim_xfer_update_progress
(
GaimXfer
*
xfer
)
{
GaimXferUiOps
*
ui_ops
;
g_return_if_fail
(
xfer
!=
NULL
);
ui_ops
=
gaim_xfer_get_ui_ops
(
xfer
);
if
(
ui_ops
!=
NULL
&&
ui_ops
->
update_progress
!=
NULL
)
ui_ops
->
update_progress
(
xfer
,
gaim_xfer_get_progress
(
xfer
));
}
/**************************************************************************
* File Transfer Subsystem API
**************************************************************************/
void
gaim_xfers_set_ui_ops
(
GaimXferUiOps
*
ops
)
{
xfer_ui_ops
=
ops
;
}
GaimXferUiOps
*
gaim_xfers_get_ui_ops
(
void
)
{
return
xfer_ui_ops
;
}