Mercurial > pidgin
annotate src/protocols/irc/dcc_send.c @ 12645:fc28451f5d96
[gaim-migrate @ 14983]
SF Patch #1314512 from Sadrul (who has a patch for everything)
"This patch introduces a flag for protocol plugins that
support offline messages (like Y!M and ICQ). This was
encouraged by the following conversation:
<sadrul> should offline buddies be listed/enabled in
the send-to menu?
<rekkanoryo> i would think only for protocols that
support offline messaging, if it's indicated that the
buddy is offline
-- <snip> --
<Bleeter> sadrul: personally, I'd like to see a
'supports offline' flag of some description
<Bleeter> one could then redirect (via plugins) through
email or alternative methods
<Bleeter> just a thought
<Paco-Paco> yeah, that sounds like a reasonble thing to have
This patch uses this flag to disable the buddies in the
send-to menu who are offline and the protocol doesn't
support offline messages."
I made this make the label insensitive instead of the whole menuitem. This
should address SimGuy's concerns about inconsistency (i.e. you could create a
conversation with someone via the buddy list that you couldn't create via the
Send To menu). I also hacked up some voodoo to show the label as sensitive when
moused-over, as that looks better (given the label-insensitive thing is itself a
hack). I think this works quite well.
BUG NOTE:
This makes more obvious an existing bug. The Send To menu isn't updated when
buddies sign on or off or change status (at least under some circumstances).
We need to fix that anyway, so I'm not going to let it hold up this commit.
Switching tabs will clear it up. I'm thinking we just might want to build the
contents of that menu when it is selected. That would save us a mess of
inefficient signal callbacks that update the Send To menus in open windows all
the time.
AIM NOTE:
This assumes that AIM can't offline message. That's not strictly true. You can
message invisible users on AIM. However, by design, we can't tell when a user
is invisible without resorting to dirty hackery. In practice, this isn't a
problem, as you can still select the AIM user from the menu. And really, how
often will you be choosing the Invisible contact, rather than the user going
Invisible in the middle of a conversation or IMing you while they're Invisible?
JABBER NOTE:
This assumes that Jabber can always offline message. This isn't strictly true.
Sadrul said:
I have updated Jabber according to this link which seems to
talk about how to determine the existence offline-message
support in a server:
http://www.jabber.org/jeps/jep-0013.html#discover
However, jabber.org doesn't seem to send the required
info. So I am not sure about it.
He later said:
I talked to Nathan and he said offline message support is
mostly assumed for most jabber servers. GTalk doesn't yet
support it, but they are working on it. So I have made
jabber to always return TRUE.
If there is truly no way to detect offline messaging capability, then this is
an acceptable solution. We could special case Google Talk because of its
popularity, and remove that later. It's probably not worth it though.
MSN NOTE:
This assumes that MSN can never offline message. That's effectively true, but
to be technically correct, MSN can offline message if there's already a
switchboard conversation open with a user. We could write an offline_message
function in the MSN prpl to detect that, but it'd be of limited usefulness,
especially given that under most circumstances (where this might matter), the
switchboard connection will be closed almost immediately.
CVS NOTE:
I'm writing to share a tragic little story.
I have a PC that I use for Gaim development. One day, I was writing a commit
message on it, when all of a suddent it went berserk. The screen started
flashing, and the whole commit message just disappeared. All of it. And it was
a good commit message! I had to cram and rewrite it really quickly. Needless to
say, my rushed commit message wasn't nearly as good, and I blame the PC for that.
Seriously, though, what kind of version control system loses your commit
message on a broken connection to the server? Stupid!
committer: Tailor Script <tailor@pidgin.im>
| author | Richard Laager <rlaager@wiktel.com> |
|---|---|
| date | Fri, 23 Dec 2005 19:26:04 +0000 |
| parents | fc464a0abccc |
| children | d5b8f4dc1622 |
| rev | line source |
|---|---|
| 8351 | 1 /** |
| 2 * @file dcc_send.c Functions used in sending files with DCC SEND | |
| 3 * | |
| 4 * gaim | |
| 5 * | |
| 6 * Copyright (C) 2004, Timothy T Ringenbach <omarvo@hotmail.com> | |
| 7 * Copyright (C) 2003, Robbert Haarman <gaim@inglorion.net> | |
| 8 * | |
| 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. | |
| 13 * | |
| 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. | |
| 18 * | |
| 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 22 */ | |
| 23 | |
| 24 #include "internal.h" | |
| 25 #include "irc.h" | |
| 26 #include "debug.h" | |
| 27 #include "ft.h" | |
| 28 #include "notify.h" | |
| 29 #include "network.h" | |
| 30 | |
| 31 /*************************************************************************** | |
| 32 * Functions related to receiving files via DCC SEND | |
| 33 ***************************************************************************/ | |
| 34 | |
| 35 struct irc_xfer_rx_data { | |
| 36 gchar *ip; | |
| 37 }; | |
| 38 | |
| 39 static void irc_dccsend_recv_destroy(GaimXfer *xfer) | |
| 40 { | |
| 41 struct irc_xfer_rx_data *xd = xfer->data; | |
| 42 | |
| 43 if (xd->ip != NULL) | |
| 44 g_free(xd->ip); | |
| 45 | |
| 46 g_free(xd); | |
| 47 } | |
| 48 | |
| 49 /* | |
| 50 * This function is called whenever data is received. | |
| 51 * It sends the acknowledgement (in the form of a total byte count as an | |
| 52 * unsigned 4 byte integer in network byte order) | |
| 53 */ | |
| 11159 | 54 static void irc_dccsend_recv_ack(GaimXfer *xfer, const guchar *data, size_t size) { |
| 8351 | 55 unsigned long l; |
| 56 | |
| 57 l = htonl(xfer->bytes_sent); | |
| 58 write(xfer->fd, &l, sizeof(l)); | |
| 59 } | |
| 60 | |
| 61 static void irc_dccsend_recv_init(GaimXfer *xfer) { | |
| 62 struct irc_xfer_rx_data *xd = xfer->data; | |
| 63 | |
| 64 gaim_xfer_start(xfer, -1, xd->ip, xfer->remote_port); | |
| 65 g_free(xd->ip); | |
| 66 xd->ip = NULL; | |
| 67 } | |
| 68 | |
| 69 /* This function makes the necessary arrangements for receiving files */ | |
| 70 void irc_dccsend_recv(struct irc_conn *irc, const char *from, const char *msg) { | |
| 71 GaimXfer *xfer; | |
| 72 struct irc_xfer_rx_data *xd; | |
| 73 gchar **token; | |
| 74 struct in_addr addr; | |
| 75 GString *filename; | |
| 76 int i = 0; | |
| 77 guint32 nip; | |
| 78 | |
| 79 token = g_strsplit(msg, " ", 0); | |
| 80 if (!token[0] || !token[1] || !token[2]) { | |
| 81 g_strfreev(token); | |
| 82 return; | |
| 83 } | |
| 84 | |
| 85 filename = g_string_new(""); | |
| 86 if (token[0][0] == '"') { | |
| 87 if (!strchr(&(token[0][1]), '"')) { | |
| 88 g_string_append(filename, &(token[0][1])); | |
| 89 for (i = 1; token[i]; i++) | |
| 90 if (!strchr(token[i], '"')) { | |
| 91 g_string_append_printf(filename, " %s", token[i]); | |
| 92 } else { | |
| 93 g_string_append_len(filename, token[i], strlen(token[i]) - 1); | |
| 94 break; | |
| 95 } | |
| 96 } else { | |
| 97 g_string_append_len(filename, &(token[0][1]), strlen(&(token[0][1])) - 1); | |
| 98 } | |
| 99 } else { | |
| 100 g_string_append(filename, token[0]); | |
| 101 } | |
| 102 | |
| 103 if (!token[i] || !token[i+1] || !token[i+2]) { | |
| 104 g_strfreev(token); | |
| 105 g_string_free(filename, TRUE); | |
| 106 return; | |
| 107 } | |
| 108 i++; | |
| 109 | |
| 110 xfer = gaim_xfer_new(irc->account, GAIM_XFER_RECEIVE, from); | |
| 111 xd = g_new0(struct irc_xfer_rx_data, 1); | |
| 112 xfer->data = xd; | |
| 113 | |
| 114 gaim_xfer_set_filename(xfer, filename->str); | |
| 115 xfer->remote_port = atoi(token[i+1]); | |
| 116 | |
| 117 nip = strtoul(token[i], NULL, 10); | |
| 118 if (nip) { | |
| 119 addr.s_addr = htonl(nip); | |
| 120 xd->ip = g_strdup(inet_ntoa(addr)); | |
| 121 } else { | |
| 122 xd->ip = g_strdup(token[i]); | |
| 123 } | |
| 124 gaim_debug(GAIM_DEBUG_INFO, "irc", "Receiving file from %s\n", | |
| 125 xd->ip); | |
| 126 gaim_xfer_set_size(xfer, token[i+2] ? atoi(token[i+2]) : 0); | |
| 127 | |
| 128 gaim_xfer_set_init_fnc(xfer, irc_dccsend_recv_init); | |
| 129 gaim_xfer_set_ack_fnc(xfer, irc_dccsend_recv_ack); | |
| 130 | |
| 131 gaim_xfer_set_end_fnc(xfer, irc_dccsend_recv_destroy); | |
| 132 gaim_xfer_set_request_denied_fnc(xfer, irc_dccsend_recv_destroy); | |
| 133 gaim_xfer_set_cancel_send_fnc(xfer, irc_dccsend_recv_destroy); | |
| 134 | |
| 135 gaim_xfer_request(xfer); | |
| 136 g_strfreev(token); | |
| 137 g_string_free(filename, TRUE); | |
| 138 } | |
| 139 | |
| 140 /******************************************************************* | |
| 141 * Functions related to sending files via DCC SEND | |
| 142 *******************************************************************/ | |
| 143 | |
| 144 struct irc_xfer_send_data { | |
| 145 gint inpa; | |
| 146 int fd; | |
| 147 guchar *rxqueue; | |
| 148 guint rxlen; | |
| 149 }; | |
| 150 | |
| 151 static void irc_dccsend_send_destroy(GaimXfer *xfer) | |
| 152 { | |
| 153 struct irc_xfer_send_data *xd = xfer->data; | |
| 154 | |
| 155 if (xd == NULL) | |
| 156 return; | |
| 157 | |
| 158 if (xd->inpa > 0) | |
| 159 gaim_input_remove(xd->inpa); | |
| 160 if (xd->fd != -1) | |
| 161 close(xd->fd); | |
| 162 | |
| 163 if (xd->rxqueue) | |
| 164 g_free(xd->rxqueue); | |
| 165 | |
| 166 g_free(xd); | |
| 167 } | |
| 168 | |
| 169 /* just in case you were wondering, this is why DCC is gay */ | |
| 170 static void irc_dccsend_send_read(gpointer data, int source, GaimInputCondition cond) | |
| 171 { | |
| 172 GaimXfer *xfer = data; | |
| 173 struct irc_xfer_send_data *xd = xfer->data; | |
| 174 char *buffer[16]; | |
| 175 int len; | |
| 176 | |
| 177 if ((len = read(source, buffer, sizeof(buffer))) <= 0) { | |
| 178 gaim_input_remove(xd->inpa); | |
| 179 xd->inpa = 0; | |
| 180 return; | |
| 181 } | |
| 182 | |
| 183 xd->rxqueue = g_realloc(xd->rxqueue, len + xd->rxlen); | |
| 184 memcpy(xd->rxqueue + xd->rxlen, buffer, len); | |
| 185 xd->rxlen += len; | |
| 186 | |
| 187 while (1) { | |
| 11318 | 188 size_t acked; |
| 8351 | 189 |
| 190 if (xd->rxlen < 4) | |
| 191 break; | |
| 192 | |
| 193 acked = ntohl(*((gint32 *)xd->rxqueue)); | |
| 194 | |
| 195 xd->rxlen -= 4; | |
| 196 if (xd->rxlen) { | |
| 10388 | 197 unsigned char *tmp = g_memdup(xd->rxqueue + 4, xd->rxlen); |
| 8351 | 198 g_free(xd->rxqueue); |
| 199 xd->rxqueue = tmp; | |
| 200 } else { | |
| 201 g_free(xd->rxqueue); | |
| 202 xd->rxqueue = NULL; | |
| 203 } | |
| 204 | |
| 205 if (acked >= gaim_xfer_get_size(xfer)) { | |
| 206 gaim_input_remove(xd->inpa); | |
| 207 xd->inpa = 0; | |
| 208 gaim_xfer_set_completed(xfer, TRUE); | |
| 209 gaim_xfer_end(xfer); | |
| 210 return; | |
| 211 } | |
| 212 | |
| 213 | |
| 214 } | |
| 215 } | |
| 216 | |
|
12323
fc464a0abccc
[gaim-migrate @ 14627]
Richard Laager <rlaager@wiktel.com>
parents:
12151
diff
changeset
|
217 static gssize irc_dccsend_send_write(const guchar *buffer, size_t size, GaimXfer *xfer) |
| 8351 | 218 { |
|
12151
de798f2f4bf1
[gaim-migrate @ 14452]
Richard Laager <rlaager@wiktel.com>
parents:
12143
diff
changeset
|
219 gssize s; |
| 8351 | 220 |
| 221 s = MIN(gaim_xfer_get_bytes_remaining(xfer), size); | |
| 222 if (!s) | |
| 223 return 0; | |
| 224 | |
| 225 return write(xfer->fd, buffer, s); | |
| 226 } | |
| 227 | |
| 228 static void irc_dccsend_send_connected(gpointer data, int source, GaimInputCondition cond) { | |
| 229 GaimXfer *xfer = (GaimXfer *) data; | |
| 230 struct irc_xfer_send_data *xd = xfer->data; | |
| 231 int conn; | |
| 232 | |
| 233 conn = accept(xd->fd, NULL, 0); | |
| 234 if (conn == -1) { | |
| 235 /* Accepting the connection failed. This could just be related | |
| 236 * to the nonblocking nature of the listening socket, so we'll | |
| 237 * just try again next time */ | |
| 238 /* Let's print an error message anyway */ | |
| 239 gaim_debug_warning("irc", "accept: %s\n", strerror(errno)); | |
| 240 return; | |
| 241 } | |
| 242 | |
| 243 gaim_input_remove(xfer->watcher); | |
| 244 xfer->watcher = 0; | |
| 245 close(xd->fd); | |
| 246 xd->fd = -1; | |
| 247 | |
| 248 xd->inpa = gaim_input_add(conn, GAIM_INPUT_READ, irc_dccsend_send_read, xfer); | |
| 249 /* Start the transfer */ | |
| 250 gaim_xfer_start(xfer, conn, NULL, 0); | |
| 251 } | |
| 252 | |
| 253 /* | |
| 254 * This function is called after the user has selected a file to send. | |
| 255 */ | |
| 256 static void irc_dccsend_send_init(GaimXfer *xfer) { | |
| 257 struct irc_xfer_send_data *xd = xfer->data; | |
| 258 GaimConnection *gc = gaim_account_get_connection(gaim_xfer_get_account(xfer)); | |
| 259 struct irc_conn *irc = gc->proto_data; | |
| 260 int sock; | |
| 261 const char *arg[2]; | |
| 262 char *tmp; | |
| 263 struct in_addr addr; | |
| 264 unsigned short int port; | |
| 265 | |
| 266 xfer->filename = g_path_get_basename(xfer->local_filename); | |
| 267 | |
| 268 /* Create a listening socket */ | |
| 269 sock = gaim_network_listen_range(0, 0); | |
| 270 | |
| 271 if (sock < 0) { | |
| 10654 | 272 gaim_notify_error(gc, NULL, _("File Transfer Failed"), |
| 8351 | 273 _("Gaim could not open a listening port.")); |
| 274 gaim_xfer_cancel_local(xfer); | |
| 275 return; | |
| 276 } | |
| 277 | |
| 278 xd->fd = sock; | |
| 279 | |
| 280 port = gaim_network_get_port_from_fd(sock); | |
| 281 gaim_debug_misc("irc", "port is %hu\n", port); | |
| 282 /* Monitor the listening socket */ | |
| 283 xfer->watcher = gaim_input_add(sock, GAIM_INPUT_READ, | |
| 284 irc_dccsend_send_connected, xfer); | |
| 285 | |
| 286 /* Send the intended recipient the DCC request */ | |
| 287 arg[0] = xfer->who; | |
| 8838 | 288 inet_aton(gaim_network_get_my_ip(irc->fd), &addr); |
|
11656
f9c5480ad0ce
[gaim-migrate @ 13940]
Richard Laager <rlaager@wiktel.com>
parents:
11318
diff
changeset
|
289 arg[1] = tmp = g_strdup_printf("\001DCC SEND \"%s\" %u %hu %" G_GSIZE_FORMAT "\001", |
| 8351 | 290 xfer->filename, ntohl(addr.s_addr), |
| 291 port, xfer->size); | |
| 292 | |
| 10555 | 293 irc_cmd_privmsg(gc->proto_data, "msg", NULL, arg); |
| 8351 | 294 g_free(tmp); |
| 295 } | |
| 296 | |
|
12143
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
297 GaimXfer *irc_dccsend_new_xfer(GaimConnection *gc, const char *who) { |
| 8351 | 298 GaimXfer *xfer; |
| 299 struct irc_xfer_send_data *xd; | |
| 300 | |
| 301 /* Build the file transfer handle */ | |
| 9466 | 302 xfer = gaim_xfer_new(gaim_connection_get_account(gc), GAIM_XFER_SEND, who); |
| 8351 | 303 |
| 304 xd = g_new0(struct irc_xfer_send_data, 1); | |
| 305 xd->fd = -1; | |
| 306 xfer->data = xd; | |
| 307 | |
|
12143
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
308 /* Setup our I/O op functions */ |
| 8351 | 309 gaim_xfer_set_init_fnc(xfer, irc_dccsend_send_init); |
| 310 gaim_xfer_set_write_fnc(xfer, irc_dccsend_send_write); | |
| 311 gaim_xfer_set_end_fnc(xfer, irc_dccsend_send_destroy); | |
| 312 gaim_xfer_set_request_denied_fnc(xfer, irc_dccsend_send_destroy); | |
| 313 gaim_xfer_set_cancel_send_fnc(xfer, irc_dccsend_send_destroy); | |
|
12143
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
314 |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
315 return xfer; |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
316 } |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
317 |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
318 /** |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
319 * Gaim calls this function when the user selects Send File from the |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
320 * buddy menu |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
321 * It sets up the GaimXfer struct and tells Gaim to go ahead |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
322 */ |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
323 void irc_dccsend_send_file(GaimConnection *gc, const char *who, const char *file) { |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
324 GaimXfer *xfer = irc_dccsend_new_xfer(gc, who); |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
325 |
|
cbebda5f019c
[gaim-migrate @ 14444]
Richard Laager <rlaager@wiktel.com>
parents:
11656
diff
changeset
|
326 /* Perform the request */ |
| 9466 | 327 if (file) |
| 328 gaim_xfer_request_accepted(xfer, file); | |
| 329 else | |
| 330 gaim_xfer_request(xfer); | |
| 8351 | 331 } |
