4 * Copyright (C) 2004, Timothy T Ringenbach <omarvo@hotmail.com>
5 * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
30 /* receive properties */
35 PurpleNetworkListenData
*listen_data
;
42 G_DEFINE_DYNAMIC_TYPE(IrcXfer
, irc_xfer
, PURPLE_TYPE_XFER
);
44 /***************************************************************************
45 * Functions related to receiving files via DCC SEND
46 ***************************************************************************/
49 * This function is called whenever data is received.
50 * It sends the acknowledgement (in the form of a total byte count as an
51 * unsigned 4 byte integer in network byte order)
53 static void irc_dccsend_recv_ack(PurpleXfer
*xfer
, const guchar
*data
, size_t size
) {
57 if(purple_xfer_get_xfer_type(xfer
) != PURPLE_XFER_TYPE_RECEIVE
) {
61 l
= htonl(purple_xfer_get_bytes_sent(xfer
));
62 result
= purple_xfer_write(xfer
, (guchar
*)&l
, sizeof(l
));
63 if (result
!= sizeof(l
)) {
64 purple_debug_error("irc", "unable to send acknowledgement: %s\n", g_strerror(errno
));
65 /* TODO: We should probably close the connection here or something. */
69 static void irc_dccsend_recv_init(PurpleXfer
*xfer
) {
70 IrcXfer
*xd
= IRC_XFER(xfer
);
72 purple_xfer_start(xfer
, -1, xd
->ip
, xd
->remote_port
);
75 /* This function makes the necessary arrangements for receiving files */
76 void irc_dccsend_recv(struct irc_conn
*irc
, const char *from
, const char *msg
) {
84 token
= g_strsplit(msg
, " ", 0);
85 if (!token
[0] || !token
[1] || !token
[2]) {
90 filename
= g_string_new("");
91 if (token
[0][0] == '"') {
92 if (!strchr(&(token
[0][1]), '"')) {
93 g_string_append(filename
, &(token
[0][1]));
94 for (i
= 1; token
[i
]; i
++)
95 if (!strchr(token
[i
], '"')) {
96 g_string_append_printf(filename
, " %s", token
[i
]);
98 g_string_append_len(filename
, token
[i
], strlen(token
[i
]) - 1);
102 g_string_append_len(filename
, &(token
[0][1]), strlen(&(token
[0][1])) - 1);
105 g_string_append(filename
, token
[0]);
108 if (!token
[i
] || !token
[i
+1] || !token
[i
+2]) {
110 g_string_free(filename
, TRUE
);
117 "account", irc
->account
,
118 "type", PURPLE_XFER_TYPE_RECEIVE
,
123 purple_xfer_set_filename(PURPLE_XFER(xfer
), filename
->str
);
125 xfer
->remote_port
= atoi(token
[i
+1]);
127 nip
= strtoul(token
[i
], NULL
, 10);
129 addr
.s_addr
= htonl(nip
);
130 xfer
->ip
= g_strdup(inet_ntoa(addr
));
132 xfer
->ip
= g_strdup(token
[i
]);
135 purple_debug(PURPLE_DEBUG_INFO
, "irc", "Receiving file (%s) from %s\n",
136 filename
->str
, xfer
->ip
);
137 purple_xfer_set_size(PURPLE_XFER(xfer
), token
[i
+2] ? atoi(token
[i
+2]) : 0);
139 purple_xfer_request(PURPLE_XFER(xfer
));
142 g_string_free(filename
, TRUE
);
145 /*******************************************************************
146 * Functions related to sending files via DCC SEND
147 *******************************************************************/
149 /* just in case you were wondering, this is why DCC is crappy */
150 static void irc_dccsend_send_read(gpointer data
, int source
, PurpleInputCondition cond
)
152 PurpleXfer
*xfer
= PURPLE_XFER(data
);
153 IrcXfer
*xd
= IRC_XFER(xfer
);
157 len
= read(source
, buffer
, sizeof(buffer
));
159 if (len
< 0 && errno
== EAGAIN
)
162 /* XXX: Shouldn't this be canceling the transfer? */
163 purple_input_remove(xd
->inpa
);
168 xd
->rxqueue
= g_realloc(xd
->rxqueue
, len
+ xd
->rxlen
);
169 memcpy(xd
->rxqueue
+ xd
->rxlen
, buffer
, len
);
179 memcpy(&val
, xd
->rxqueue
, sizeof(val
));
184 unsigned char *tmp
= g_memdup(xd
->rxqueue
+ 4, xd
->rxlen
);
192 if ((goffset
)acked
>= purple_xfer_get_size(xfer
)) {
193 purple_input_remove(xd
->inpa
);
195 purple_xfer_set_completed(xfer
, TRUE
);
196 purple_xfer_end(xfer
);
202 static gssize
irc_dccsend_send_write(PurpleXfer
*xfer
, const guchar
*buffer
, size_t size
)
207 s
= MIN((gssize
)purple_xfer_get_bytes_remaining(xfer
), (gssize
)size
);
212 ret
= PURPLE_XFER_CLASS(irc_xfer_parent_class
)->write(xfer
, buffer
, s
);
214 if (ret
< 0 && errno
== EAGAIN
) {
221 static void irc_dccsend_send_connected(gpointer data
, int source
, PurpleInputCondition cond
) {
222 PurpleXfer
*xfer
= PURPLE_XFER(data
);
223 IrcXfer
*xd
= IRC_XFER(xfer
);
226 conn
= accept(xd
->fd
, NULL
, 0);
228 /* Accepting the connection failed. This could just be related
229 * to the nonblocking nature of the listening socket, so we'll
230 * just try again next time */
231 /* Let's print an error message anyway */
232 purple_debug_warning("irc", "accept: %s\n", g_strerror(errno
));
236 purple_input_remove(purple_xfer_get_watcher(xfer
));
237 purple_xfer_set_watcher(xfer
, 0);
241 _purple_network_set_common_socket_flags(conn
);
243 xd
->inpa
= purple_input_add(conn
, PURPLE_INPUT_READ
, irc_dccsend_send_read
, xfer
);
244 /* Start the transfer */
245 purple_xfer_start(xfer
, conn
, NULL
, 0);
249 irc_dccsend_network_listen_cb(int sock
, gpointer data
)
251 PurpleXfer
*xfer
= PURPLE_XFER(data
);
252 IrcXfer
*xd
= IRC_XFER(xfer
);
253 PurpleConnection
*gc
;
254 struct irc_conn
*irc
;
260 unsigned short int port
;
262 /* not sure what the deal is here, but it needs to be here.. gk 20190626 */
263 xd
->listen_data
= NULL
;
265 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_LOCAL
266 || purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_REMOTE
) {
267 g_object_unref(xfer
);
271 gc
= purple_account_get_connection(purple_xfer_get_account(xfer
));
272 irc
= purple_connection_get_protocol_data(gc
);
275 purple_notify_error(gc
, NULL
, _("File Transfer Failed"),
276 _("Unable to open a listening port."),
277 purple_request_cpar_from_connection(gc
));
278 purple_xfer_cancel_local(xfer
);
284 port
= purple_network_get_port_from_fd(sock
);
285 purple_debug_misc("irc", "port is %hu\n", port
);
286 /* Monitor the listening socket */
287 purple_xfer_set_watcher(
289 purple_input_add(sock
, PURPLE_INPUT_READ
, irc_dccsend_send_connected
, xfer
)
292 /* Send the intended recipient the DCC request */
293 arg
[0] = purple_xfer_get_remote_user(xfer
);
295 /* Fetching this fd here assumes it won't be modified */
296 gsock
= g_socket_connection_get_socket(irc
->conn
);
298 fd
= g_socket_get_fd(gsock
);
301 inet_aton(purple_network_get_my_ip(fd
), &addr
);
302 arg
[1] = tmp
= g_strdup_printf(
303 "\001DCC SEND \"%s\" %u %hu %" G_GOFFSET_FORMAT
"\001",
304 purple_xfer_get_filename(xfer
),
307 purple_xfer_get_size(xfer
)
310 irc_cmd_privmsg(purple_connection_get_protocol_data(gc
), "msg", NULL
, arg
);
315 * This function is called after the user has selected a file to send.
317 static void irc_dccsend_send_init(PurpleXfer
*xfer
) {
318 IrcXfer
*xd
= IRC_XFER(xfer
);
319 PurpleConnection
*gc
= purple_account_get_connection(purple_xfer_get_account(xfer
));
321 purple_xfer_set_filename(xfer
, g_path_get_basename(purple_xfer_get_local_filename(xfer
)));
323 /* Create a listening socket */
324 xd
->listen_data
= purple_network_listen_range(
330 irc_dccsend_network_listen_cb
,
333 if (xd
->listen_data
== NULL
) {
334 purple_notify_error(gc
, NULL
, _("File Transfer Failed"),
335 _("Unable to open a listening port."),
336 purple_request_cpar_from_connection(gc
));
337 purple_xfer_cancel_local(xfer
);
341 PurpleXfer
*irc_dccsend_new_xfer(PurpleProtocolXfer
*prplxfer
, PurpleConnection
*gc
, const char *who
) {
344 "account", purple_connection_get_account(gc
),
345 "type", PURPLE_XFER_TYPE_SEND
,
352 * Purple calls this function when the user selects Send File from the
354 * It sets up the PurpleXfer struct and tells Purple to go ahead
356 void irc_dccsend_send_file(PurpleProtocolXfer
*prplxfer
, PurpleConnection
*gc
, const char *who
, const char *file
) {
357 PurpleXfer
*xfer
= irc_dccsend_new_xfer(prplxfer
, gc
, who
);
359 /* Perform the request */
361 purple_xfer_request_accepted(xfer
, file
);
363 purple_xfer_request(xfer
);
366 /******************************************************************************
367 * PurpleXfer Implementation
368 *****************************************************************************/
370 irc_dccsend_init(PurpleXfer
*xfer
) {
371 PurpleXferType type
= purple_xfer_get_xfer_type(xfer
);
373 if(type
== PURPLE_XFER_TYPE_SEND
) {
374 irc_dccsend_send_init(xfer
);
375 } else if(type
== PURPLE_XFER_TYPE_RECEIVE
) {
376 irc_dccsend_recv_init(xfer
);
380 /******************************************************************************
381 * GObject Implementation
382 *****************************************************************************/
384 irc_xfer_init(IrcXfer
*xfer
) {
389 irc_xfer_finalize(GObject
*obj
) {
390 IrcXfer
*xfer
= IRC_XFER(obj
);
392 /* clean up the receiving proprties */
394 g_free(xfer
->rxqueue
);
396 /* clean up the sending properties */
397 g_clear_pointer(&xfer
->listen_data
, purple_network_listen_cancel
);
399 purple_input_remove(xfer
->inpa
);
405 G_OBJECT_CLASS(irc_xfer_parent_class
)->finalize(obj
);
409 irc_xfer_class_finalize(IrcXferClass
*klass
) {
414 irc_xfer_class_init(IrcXferClass
*klass
) {
415 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
416 PurpleXferClass
*xfer_class
= PURPLE_XFER_CLASS(klass
);
418 obj_class
->finalize
= irc_xfer_finalize
;
420 xfer_class
->init
= irc_dccsend_init
;
421 xfer_class
->ack
= irc_dccsend_recv_ack
;
422 xfer_class
->write
= irc_dccsend_send_write
;
426 irc_xfer_register(GTypeModule
*module
) {
427 irc_xfer_register_type(module
);