mark PurpleImageClass as private
[pidgin-git.git] / libpurple / protocols / irc / dcc_send.c
blobd232d47cb1d8344e2c8cd67720b76b64ddf2831a
1 /**
2 * @file dcc_send.c Functions used in sending files with DCC SEND
4 * purple
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
24 #include "internal.h"
25 #include "irc.h"
26 #include "debug.h"
27 #include "xfer.h"
28 #include "notify.h"
29 #include "network.h"
31 struct _IrcXfer {
32 PurpleXfer parent;
34 /* receive properties */
35 gchar *ip;
36 guint remote_port;
38 /* send properties */
39 PurpleNetworkListenData *listen_data;
40 gint inpa;
41 gint fd;
42 guchar *rxqueue;
43 guint rxlen;
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) {
58 guint32 l;
59 gssize result;
61 if(purple_xfer_get_xfer_type(xfer) != PURPLE_XFER_TYPE_RECEIVE) {
62 return;
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) {
81 IrcXfer *xfer;
82 gchar **token;
83 struct in_addr addr;
84 GString *filename;
85 int i = 0;
86 guint32 nip;
88 token = g_strsplit(msg, " ", 0);
89 if (!token[0] || !token[1] || !token[2]) {
90 g_strfreev(token);
91 return;
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]);
101 } else {
102 g_string_append_len(filename, token[i], strlen(token[i]) - 1);
103 break;
105 } else {
106 g_string_append_len(filename, &(token[0][1]), strlen(&(token[0][1])) - 1);
108 } else {
109 g_string_append(filename, token[0]);
112 if (!token[i] || !token[i+1] || !token[i+2]) {
113 g_strfreev(token);
114 g_string_free(filename, TRUE);
115 return;
117 i++;
119 xfer = g_object_new(
120 IRC_TYPE_XFER,
121 "account", irc->account,
122 "type", PURPLE_XFER_TYPE_RECEIVE,
123 "remote-user", from,
124 NULL
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);
132 if (nip) {
133 addr.s_addr = htonl(nip);
134 xfer->ip = g_strdup(inet_ntoa(addr));
135 } else {
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));
145 g_strfreev(token);
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);
158 char buffer[64];
159 int len;
161 len = read(source, buffer, sizeof(buffer));
163 if (len < 0 && errno == EAGAIN)
164 return;
165 else if (len <= 0) {
166 /* XXX: Shouldn't this be canceling the transfer? */
167 purple_input_remove(xd->inpa);
168 xd->inpa = 0;
169 return;
172 xd->rxqueue = g_realloc(xd->rxqueue, len + xd->rxlen);
173 memcpy(xd->rxqueue + xd->rxlen, buffer, len);
174 xd->rxlen += len;
176 while (1) {
177 gint32 val;
178 size_t acked;
180 if (xd->rxlen < 4)
181 break;
183 memcpy(&val, xd->rxqueue, sizeof(val));
184 acked = ntohl(val);
186 xd->rxlen -= 4;
187 if (xd->rxlen) {
188 unsigned char *tmp = g_memdup(xd->rxqueue + 4, xd->rxlen);
189 g_free(xd->rxqueue);
190 xd->rxqueue = tmp;
191 } else {
192 g_free(xd->rxqueue);
193 xd->rxqueue = NULL;
196 if ((goffset)acked >= purple_xfer_get_size(xfer)) {
197 purple_input_remove(xd->inpa);
198 xd->inpa = 0;
199 purple_xfer_set_completed(xfer, TRUE);
200 purple_xfer_end(xfer);
201 return;
206 static gssize irc_dccsend_send_write(PurpleXfer *xfer, const guchar *buffer, size_t size)
208 gssize s;
209 gssize ret;
211 s = MIN((gssize)purple_xfer_get_bytes_remaining(xfer), (gssize)size);
212 if (!s) {
213 return 0;
216 ret = PURPLE_XFER_CLASS(irc_xfer_parent_class)->write(xfer, buffer, s);
218 if (ret < 0 && errno == EAGAIN) {
219 ret = 0;
222 return ret;
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);
228 int conn;
230 conn = accept(xd->fd, NULL, 0);
231 if (conn == -1) {
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));
237 return;
240 purple_input_remove(purple_xfer_get_watcher(xfer));
241 purple_xfer_set_watcher(xfer, 0);
242 close(xd->fd);
243 xd->fd = -1;
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);
252 static void
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;
259 GSocket *gsock;
260 int fd = -1;
261 const char *arg[2];
262 char *tmp;
263 struct in_addr addr;
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);
272 return;
275 gc = purple_account_get_connection(purple_xfer_get_account(xfer));
276 irc = purple_connection_get_protocol_data(gc);
278 if (sock < 0) {
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);
283 return;
286 xd->fd = sock;
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(
292 xfer,
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);
301 if (gsock != NULL) {
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),
309 ntohl(addr.s_addr),
310 port,
311 purple_xfer_get_size(xfer)
314 irc_cmd_privmsg(purple_connection_get_protocol_data(gc), "msg", NULL, arg);
315 g_free(tmp);
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(
331 AF_UNSPEC,
332 SOCK_STREAM,
333 TRUE,
334 irc_dccsend_network_listen_cb,
335 xfer
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) {
346 return g_object_new(
347 IRC_TYPE_XFER,
348 "account", purple_connection_get_account(gc),
349 "type", PURPLE_XFER_TYPE_SEND,
350 "remote-user", who,
351 NULL
356 * Purple calls this function when the user selects Send File from the
357 * buddy menu
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 */
364 if (file)
365 purple_xfer_request_accepted(xfer, file);
366 else
367 purple_xfer_request(xfer);
370 /******************************************************************************
371 * PurpleXfer Implementation
372 *****************************************************************************/
373 static void
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 *****************************************************************************/
387 static void
388 irc_xfer_init(IrcXfer *xfer) {
389 xfer->fd = -1;
392 static void
393 irc_xfer_finalize(GObject *obj) {
394 IrcXfer *xfer = IRC_XFER(obj);
396 /* clean up the receiving proprties */
397 g_free(xfer->ip);
398 g_free(xfer->rxqueue);
400 /* clean up the sending properties */
401 g_clear_pointer(&xfer->listen_data, purple_network_listen_cancel);
402 if(xfer->inpa > 0) {
403 purple_input_remove(xfer->inpa);
405 if(xfer->fd != -1) {
406 close(xfer->fd);
409 G_OBJECT_CLASS(irc_xfer_parent_class)->finalize(obj);
412 static void
413 irc_xfer_class_finalize(IrcXferClass *klass) {
417 static void
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;
429 void
430 irc_xfer_register(GTypeModule *module) {
431 irc_xfer_register_type(module);