2 * @file dcc_send.c Functions used in sending files with DCC SEND
6 * Copyright (C) 2004, Timothy T Ringenbach <omarvo@hotmail.com>
7 * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
34 /* receive properties */
39 PurpleNetworkListenData
*listen_data
;
46 G_DEFINE_DYNAMIC_TYPE(IrcXfer
, irc_xfer
, PURPLE_TYPE_XFER
);
48 /***************************************************************************
49 * Functions related to receiving files via DCC SEND
50 ***************************************************************************/
53 * This function is called whenever data is received.
54 * It sends the acknowledgement (in the form of a total byte count as an
55 * unsigned 4 byte integer in network byte order)
57 static void irc_dccsend_recv_ack(PurpleXfer
*xfer
, const guchar
*data
, size_t size
) {
61 if(purple_xfer_get_xfer_type(xfer
) != PURPLE_XFER_TYPE_RECEIVE
) {
65 l
= htonl(purple_xfer_get_bytes_sent(xfer
));
66 result
= purple_xfer_write(xfer
, (guchar
*)&l
, sizeof(l
));
67 if (result
!= sizeof(l
)) {
68 purple_debug_error("irc", "unable to send acknowledgement: %s\n", g_strerror(errno
));
69 /* TODO: We should probably close the connection here or something. */
73 static void irc_dccsend_recv_init(PurpleXfer
*xfer
) {
74 IrcXfer
*xd
= IRC_XFER(xfer
);
76 purple_xfer_start(xfer
, -1, xd
->ip
, xd
->remote_port
);
79 /* This function makes the necessary arrangements for receiving files */
80 void irc_dccsend_recv(struct irc_conn
*irc
, const char *from
, const char *msg
) {
88 token
= g_strsplit(msg
, " ", 0);
89 if (!token
[0] || !token
[1] || !token
[2]) {
94 filename
= g_string_new("");
95 if (token
[0][0] == '"') {
96 if (!strchr(&(token
[0][1]), '"')) {
97 g_string_append(filename
, &(token
[0][1]));
98 for (i
= 1; token
[i
]; i
++)
99 if (!strchr(token
[i
], '"')) {
100 g_string_append_printf(filename
, " %s", token
[i
]);
102 g_string_append_len(filename
, token
[i
], strlen(token
[i
]) - 1);
106 g_string_append_len(filename
, &(token
[0][1]), strlen(&(token
[0][1])) - 1);
109 g_string_append(filename
, token
[0]);
112 if (!token
[i
] || !token
[i
+1] || !token
[i
+2]) {
114 g_string_free(filename
, TRUE
);
121 "account", irc
->account
,
122 "type", PURPLE_XFER_TYPE_RECEIVE
,
127 purple_xfer_set_filename(PURPLE_XFER(xfer
), filename
->str
);
129 xfer
->remote_port
= atoi(token
[i
+1]);
131 nip
= strtoul(token
[i
], NULL
, 10);
133 addr
.s_addr
= htonl(nip
);
134 xfer
->ip
= g_strdup(inet_ntoa(addr
));
136 xfer
->ip
= g_strdup(token
[i
]);
139 purple_debug(PURPLE_DEBUG_INFO
, "irc", "Receiving file (%s) from %s\n",
140 filename
->str
, xfer
->ip
);
141 purple_xfer_set_size(PURPLE_XFER(xfer
), token
[i
+2] ? atoi(token
[i
+2]) : 0);
143 purple_xfer_request(PURPLE_XFER(xfer
));
146 g_string_free(filename
, TRUE
);
149 /*******************************************************************
150 * Functions related to sending files via DCC SEND
151 *******************************************************************/
153 /* just in case you were wondering, this is why DCC is crappy */
154 static void irc_dccsend_send_read(gpointer data
, int source
, PurpleInputCondition cond
)
156 PurpleXfer
*xfer
= PURPLE_XFER(data
);
157 IrcXfer
*xd
= IRC_XFER(xfer
);
161 len
= read(source
, buffer
, sizeof(buffer
));
163 if (len
< 0 && errno
== EAGAIN
)
166 /* XXX: Shouldn't this be canceling the transfer? */
167 purple_input_remove(xd
->inpa
);
172 xd
->rxqueue
= g_realloc(xd
->rxqueue
, len
+ xd
->rxlen
);
173 memcpy(xd
->rxqueue
+ xd
->rxlen
, buffer
, len
);
183 memcpy(&val
, xd
->rxqueue
, sizeof(val
));
188 unsigned char *tmp
= g_memdup(xd
->rxqueue
+ 4, xd
->rxlen
);
196 if ((goffset
)acked
>= purple_xfer_get_size(xfer
)) {
197 purple_input_remove(xd
->inpa
);
199 purple_xfer_set_completed(xfer
, TRUE
);
200 purple_xfer_end(xfer
);
206 static gssize
irc_dccsend_send_write(PurpleXfer
*xfer
, const guchar
*buffer
, size_t size
)
211 s
= MIN((gssize
)purple_xfer_get_bytes_remaining(xfer
), (gssize
)size
);
216 ret
= PURPLE_XFER_CLASS(irc_xfer_parent_class
)->write(xfer
, buffer
, s
);
218 if (ret
< 0 && errno
== EAGAIN
) {
225 static void irc_dccsend_send_connected(gpointer data
, int source
, PurpleInputCondition cond
) {
226 PurpleXfer
*xfer
= PURPLE_XFER(data
);
227 IrcXfer
*xd
= IRC_XFER(xfer
);
230 conn
= accept(xd
->fd
, NULL
, 0);
232 /* Accepting the connection failed. This could just be related
233 * to the nonblocking nature of the listening socket, so we'll
234 * just try again next time */
235 /* Let's print an error message anyway */
236 purple_debug_warning("irc", "accept: %s\n", g_strerror(errno
));
240 purple_input_remove(purple_xfer_get_watcher(xfer
));
241 purple_xfer_set_watcher(xfer
, 0);
245 _purple_network_set_common_socket_flags(conn
);
247 xd
->inpa
= purple_input_add(conn
, PURPLE_INPUT_READ
, irc_dccsend_send_read
, xfer
);
248 /* Start the transfer */
249 purple_xfer_start(xfer
, conn
, NULL
, 0);
253 irc_dccsend_network_listen_cb(int sock
, gpointer data
)
255 PurpleXfer
*xfer
= PURPLE_XFER(data
);
256 IrcXfer
*xd
= IRC_XFER(xfer
);
257 PurpleConnection
*gc
;
258 struct irc_conn
*irc
;
264 unsigned short int port
;
266 /* not sure what the deal is here, but it needs to be here.. gk 20190626 */
267 xd
->listen_data
= NULL
;
269 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_LOCAL
270 || purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_REMOTE
) {
271 g_object_unref(xfer
);
275 gc
= purple_account_get_connection(purple_xfer_get_account(xfer
));
276 irc
= purple_connection_get_protocol_data(gc
);
279 purple_notify_error(gc
, NULL
, _("File Transfer Failed"),
280 _("Unable to open a listening port."),
281 purple_request_cpar_from_connection(gc
));
282 purple_xfer_cancel_local(xfer
);
288 port
= purple_network_get_port_from_fd(sock
);
289 purple_debug_misc("irc", "port is %hu\n", port
);
290 /* Monitor the listening socket */
291 purple_xfer_set_watcher(
293 purple_input_add(sock
, PURPLE_INPUT_READ
, irc_dccsend_send_connected
, xfer
)
296 /* Send the intended recipient the DCC request */
297 arg
[0] = purple_xfer_get_remote_user(xfer
);
299 /* Fetching this fd here assumes it won't be modified */
300 gsock
= g_socket_connection_get_socket(irc
->conn
);
302 fd
= g_socket_get_fd(gsock
);
305 inet_aton(purple_network_get_my_ip(fd
), &addr
);
306 arg
[1] = tmp
= g_strdup_printf(
307 "\001DCC SEND \"%s\" %u %hu %" G_GOFFSET_FORMAT
"\001",
308 purple_xfer_get_filename(xfer
),
311 purple_xfer_get_size(xfer
)
314 irc_cmd_privmsg(purple_connection_get_protocol_data(gc
), "msg", NULL
, arg
);
319 * This function is called after the user has selected a file to send.
321 static void irc_dccsend_send_init(PurpleXfer
*xfer
) {
322 IrcXfer
*xd
= IRC_XFER(xfer
);
323 PurpleConnection
*gc
= purple_account_get_connection(purple_xfer_get_account(xfer
));
325 purple_xfer_set_filename(xfer
, g_path_get_basename(purple_xfer_get_local_filename(xfer
)));
327 /* Create a listening socket */
328 xd
->listen_data
= purple_network_listen_range(
334 irc_dccsend_network_listen_cb
,
337 if (xd
->listen_data
== NULL
) {
338 purple_notify_error(gc
, NULL
, _("File Transfer Failed"),
339 _("Unable to open a listening port."),
340 purple_request_cpar_from_connection(gc
));
341 purple_xfer_cancel_local(xfer
);
345 PurpleXfer
*irc_dccsend_new_xfer(PurpleProtocolXfer
*prplxfer
, PurpleConnection
*gc
, const char *who
) {
348 "account", purple_connection_get_account(gc
),
349 "type", PURPLE_XFER_TYPE_SEND
,
356 * Purple calls this function when the user selects Send File from the
358 * It sets up the PurpleXfer struct and tells Purple to go ahead
360 void irc_dccsend_send_file(PurpleProtocolXfer
*prplxfer
, PurpleConnection
*gc
, const char *who
, const char *file
) {
361 PurpleXfer
*xfer
= irc_dccsend_new_xfer(prplxfer
, gc
, who
);
363 /* Perform the request */
365 purple_xfer_request_accepted(xfer
, file
);
367 purple_xfer_request(xfer
);
370 /******************************************************************************
371 * PurpleXfer Implementation
372 *****************************************************************************/
374 irc_dccsend_init(PurpleXfer
*xfer
) {
375 PurpleXferType type
= purple_xfer_get_xfer_type(xfer
);
377 if(type
== PURPLE_XFER_TYPE_SEND
) {
378 irc_dccsend_send_init(xfer
);
379 } else if(type
== PURPLE_XFER_TYPE_RECEIVE
) {
380 irc_dccsend_recv_init(xfer
);
384 /******************************************************************************
385 * GObject Implementation
386 *****************************************************************************/
388 irc_xfer_init(IrcXfer
*xfer
) {
393 irc_xfer_finalize(GObject
*obj
) {
394 IrcXfer
*xfer
= IRC_XFER(obj
);
396 /* clean up the receiving proprties */
398 g_free(xfer
->rxqueue
);
400 /* clean up the sending properties */
401 g_clear_pointer(&xfer
->listen_data
, purple_network_listen_cancel
);
403 purple_input_remove(xfer
->inpa
);
409 G_OBJECT_CLASS(irc_xfer_parent_class
)->finalize(obj
);
413 irc_xfer_class_finalize(IrcXferClass
*klass
) {
418 irc_xfer_class_init(IrcXferClass
*klass
) {
419 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
420 PurpleXferClass
*xfer_class
= PURPLE_XFER_CLASS(klass
);
422 obj_class
->finalize
= irc_xfer_finalize
;
424 xfer_class
->init
= irc_dccsend_init
;
425 xfer_class
->ack
= irc_dccsend_recv_ack
;
426 xfer_class
->write
= irc_dccsend_send_write
;
430 irc_xfer_register(GTypeModule
*module
) {
431 irc_xfer_register_type(module
);