comparison src/protocols/oscar/peer.c @ 13592:6519aeb66b31

[gaim-migrate @ 15978] Holy cow this is crazy. 34 files changed, 5760 insertions(+), 8517 deletions(-) * Non-blocking I/O for all of oscar. That includes normal FLAP connections as well as file transfers and direct IM. * Kick-ass file transfer and direct IM. Either party can request the connection. Gaim will try both the "public" IP and the "client" IP. It'll fall back to transferring through a proxy if that fails. Should be relatively few memleaks (I didn't have a lot of confidence in the non-memleakiness of the old code). And the code is reasonably generic, so it shouldn't be too much work to add voice chat. This might still be a LITTLE buggy, but it shouldn't be too bad. If anything, file transfer will be more buggy than direct IM. And sending a file will be more buggy than receiving a file. Bug reports with a series of steps to reproduce are welcome. * I merged OscarData and aim_session_t * Somewhere between 50 and 100 hours of work. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Fri, 07 Apr 2006 05:10:56 +0000
parents e9802db22b06
children 6c34fbb75bbd
comparison
equal deleted inserted replaced
13591:dcfda39ad547 13592:6519aeb66b31
17 * License along with this library; if not, write to the Free Software 17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */ 19 */
20 20
21 /* 21 /*
22 * Oscar File transfer (OFT) and Oscar Direct Connect (ODC). 22 * Functions dealing with peer connections. This includes the code
23 * (ODC is also referred to as DirectIM and IM Image.) 23 * used to establish a peer connection for both Oscar File transfer
24 * 24 * (OFT) and Oscar Direct Connect (ODC). (ODC is also referred to
25 * There are a few static helper functions at the top, then 25 * as DirectIM and IM Image.)
26 * ODC stuff, then ft stuff.
27 *
28 * I feel like this is a good place to explain OFT, so I'm going to
29 * do just that. Each OFT packet has a header type. I guess this
30 * is pretty similar to the subtype of a SNAC packet. The type
31 * basically tells the other client the meaning of the OFT packet.
32 * There are two distinct types of file transfer, which I usually
33 * call "sendfile" and "getfile." Sendfile is when you send a file
34 * to another AIM user. Getfile is when you share a group of files,
35 * and other users request that you send them the files.
36 *
37 * A typical sendfile file transfer goes like this:
38 * 1) Sender sends a channel 2 ICBM telling the other user that
39 * we want to send them a file. At the same time, we open a
40 * listener socket (this should be done before sending the
41 * ICBM) on some port, and wait for them to connect to us.
42 * The ICBM we sent should contain our IP address and the port
43 * number that we're listening on.
44 * 2) The receiver connects to the sender on the given IP address
45 * and port. After the connection is established, the receiver
46 * sends an ICBM signifying that we are ready and waiting.
47 * 3) The sender sends an OFT PROMPT message over the OFT
48 * connection.
49 * 4) The receiver of the file sends back an exact copy of this
50 * OFT packet, except the cookie is filled in with the cookie
51 * from the ICBM. I think this might be an attempt to verify
52 * that the user that is connected is actually the guy that
53 * we sent the ICBM to. Oh, I've been calling this the ACK.
54 * 5) The sender starts sending raw data across the connection
55 * until the entire file has been sent.
56 * 6) The receiver knows the file is finished because the sender
57 * sent the file size in an earlier OFT packet. So then the
58 * receiver sends the DONE thingy (after filling in the
59 * "received" checksum and size) and closes the connection.
60 */ 26 */
61 27
62 #ifdef HAVE_CONFIG_H 28 #ifdef HAVE_CONFIG_H
63 #include <config.h> 29 #include <config.h>
64 #endif 30 #endif
65 31
32 /* From the oscar PRPL */
66 #include "oscar.h" 33 #include "oscar.h"
67 #include "peer.h" 34 #include "peer.h"
35
36 /* From Gaim */
37 #include "conversation.h"
38 #include "ft.h"
39 #include "network.h"
40 #include "notify.h"
41 #include "request.h"
42 #include "util.h"
68 43
69 #ifndef _WIN32 44 #ifndef _WIN32
70 #include <stdio.h> 45 #include <stdio.h>
71 #include <netdb.h> 46 #include <netdb.h>
72 #include <sys/socket.h> 47 #include <sys/socket.h>
73 #include <netinet/in.h> 48 #include <netinet/in.h>
74 #include <sys/utsname.h> /* for aim_odc_initiate */
75 #include <arpa/inet.h> /* for inet_ntoa */ 49 #include <arpa/inet.h> /* for inet_ntoa */
76 #include <limits.h> /* for UINT_MAX */ 50 #include <limits.h> /* for UINT_MAX */
77 #endif 51 #endif
78 52
79 #ifdef _WIN32 53 #ifdef _WIN32
92 */ 66 */
93 #ifndef PF_INET6 67 #ifndef PF_INET6
94 #define PF_INET6 PF_INET 68 #define PF_INET6 PF_INET
95 #endif 69 #endif
96 70
97 struct aim_odc_intdata { 71 PeerConnection *
98 guint8 cookie[8]; 72 peer_connection_find_by_type(OscarData *od, const char *sn, OscarCapability type)
99 char sn[MAXSNLEN+1]; 73 {
100 char ip[22]; 74 GList *cur;
101 }; 75 PeerConnection *conn;
102 76
103 /** 77 for (cur = od->peer_connections; cur != NULL; cur = cur->next)
104 * Convert the directory separator from / (0x2f) to ^A (0x01) 78 {
79 conn = cur->data;
80 if ((conn->type == type) && !aim_sncmp(conn->sn, sn))
81 return conn;
82 }
83
84 return NULL;
85 }
86
87 /**
88 * @param cookie This must be exactly 8 characters.
89 */
90 PeerConnection *
91 peer_connection_find_by_cookie(OscarData *od, const char *sn, const guchar *cookie)
92 {
93 GList *cur;
94 PeerConnection *conn;
95
96 for (cur = od->peer_connections; cur != NULL; cur = cur->next)
97 {
98 conn = cur->data;
99 if (!memcmp(conn->cookie, cookie, 8) && !aim_sncmp(conn->sn, sn))
100 return conn;
101 }
102
103 return NULL;
104 }
105
106 PeerConnection *
107 peer_connection_new(OscarData *od, OscarCapability type, const char *sn)
108 {
109 PeerConnection *conn;
110 GaimAccount *account;
111
112 account = gaim_connection_get_account(od->gc);
113
114 conn = g_new0(PeerConnection, 1);
115 conn->od = od;
116 conn->type = type;
117 conn->sn = g_strdup(sn);
118 conn->buffer_outgoing = gaim_circ_buffer_new(0);
119 conn->listenerfd = -1;
120 conn->fd = -1;
121 conn->lastactivity = time(NULL);
122 conn->use_proxy |= gaim_account_get_bool(account, "use_rv_proxy", FALSE);
123
124 if (type == OSCAR_CAPABILITY_DIRECTIM)
125 memcpy(conn->magic, "ODC2", 4);
126 else if (type == OSCAR_CAPABILITY_SENDFILE)
127 memcpy(conn->magic, "OFT2", 4);
128
129 od->peer_connections = g_list_prepend(od->peer_connections, conn);
130
131 return conn;
132 }
133
134 static void
135 peer_connection_close(PeerConnection *conn)
136 {
137 if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
138 peer_odc_close(conn);
139 else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
140 peer_oft_close(conn);
141
142 if (conn->watcher_incoming != 0)
143 {
144 gaim_input_remove(conn->watcher_incoming);
145 conn->watcher_incoming = 0;
146 }
147 if (conn->watcher_outgoing != 0)
148 {
149 gaim_input_remove(conn->watcher_outgoing);
150 conn->watcher_outgoing = 0;
151 }
152 if (conn->listenerfd != -1)
153 {
154 close(conn->listenerfd);
155 conn->listenerfd = -1;
156 }
157 if (conn->fd != -1)
158 {
159 close(conn->fd);
160 conn->fd = -1;
161 }
162
163 g_free(conn->buffer_incoming.data);
164 conn->buffer_incoming.data = NULL;
165 conn->buffer_incoming.len = 0;
166 conn->buffer_incoming.offset = 0;
167
168 gaim_circ_buffer_destroy(conn->buffer_outgoing);
169 conn->buffer_outgoing = gaim_circ_buffer_new(0);
170
171 conn->flags &= ~PEER_CONNECTION_FLAG_IS_INCOMING;
172 }
173
174 static gboolean
175 peer_connection_destroy_cb(gpointer data)
176 {
177 PeerConnection *conn;
178
179 conn = data;
180
181 gaim_request_close_with_handle(conn);
182
183 peer_connection_close(conn);
184
185 if (conn->xfer != NULL)
186 {
187 GaimXferStatusType status;
188 conn->xfer->data = NULL;
189 status = gaim_xfer_get_status(conn->xfer);
190 if ((status != GAIM_XFER_STATUS_DONE) &&
191 (status != GAIM_XFER_STATUS_CANCEL_LOCAL) &&
192 (status != GAIM_XFER_STATUS_CANCEL_REMOTE))
193 {
194 if ((conn->disconnect_reason == PEER_DISCONNECT_REMOTE_CLOSED) ||
195 (conn->disconnect_reason == PEER_DISCONNECT_REMOTE_REFUSED))
196 gaim_xfer_cancel_remote(conn->xfer);
197 else
198 gaim_xfer_cancel_local(conn->xfer);
199 }
200 gaim_xfer_unref(conn->xfer);
201 conn->xfer = NULL;
202 }
203
204 g_free(conn->proxyip);
205 g_free(conn->clientip);
206 g_free(conn->verifiedip);
207 gaim_circ_buffer_destroy(conn->buffer_outgoing);
208
209 conn->od->peer_connections = g_list_remove(conn->od->peer_connections, conn);
210
211 g_free(conn);
212
213 return FALSE;
214 }
215
216 void
217 peer_connection_destroy(PeerConnection *conn, PeerDisconnectReason reason)
218 {
219 conn->disconnect_reason = reason;
220 if (conn->destroy_timeout != 0)
221 gaim_timeout_remove(conn->destroy_timeout);
222 peer_connection_destroy_cb(conn);
223 }
224
225 void
226 peer_connection_schedule_destroy(PeerConnection *conn, PeerDisconnectReason reason)
227 {
228 if (conn->destroy_timeout != 0)
229 /* Already taken care of */
230 return;
231
232 gaim_debug_info("oscar", "Scheduling destruction of peer connection\n");
233 conn->disconnect_reason = reason;
234 conn->destroy_timeout = gaim_timeout_add(0, peer_connection_destroy_cb, conn);
235 }
236
237 /*******************************************************************/
238 /* Begin code for receiving data on a peer connection */
239 /*******************************************************************/
240
241 /**
242 * This should be used to read ODC and OFT framing info. It should
243 * NOT be used to read the payload sent across the connection (IMs,
244 * file data, etc), and it should NOT be used to read proxy negotiation
245 * headers.
105 * 246 *
106 * @param name The filename to convert. 247 * Unlike flap_connection_recv_cb(), this only reads one frame at a
107 */ 248 * time. This is done so that the watcher can be changed during the
249 * handling of the frame. If the watcher is changed then this
250 * function will not read in any more data. This happens when
251 * reading the payload of a direct IM frame, or when we're
252 * receiving a file from the remote user. Once the data has been
253 * read, the watcher will be switched back to this function to
254 * continue reading the next frame.
255 */
256 void
257 peer_connection_recv_cb(gpointer data, gint source, GaimInputCondition cond)
258 {
259 PeerConnection *conn;
260 ssize_t read;
261 guint8 header[6];
262
263 conn = data;
264
265 /* Start reading a new ODC/OFT frame */
266 if (conn->buffer_incoming.data == NULL)
267 {
268 /* Peek at the first 6 bytes to get the length */
269 read = recv(conn->fd, &header, 6, MSG_PEEK);
270
271 /* Check if the remote user closed the connection */
272 if (read == 0)
273 {
274 peer_connection_destroy(conn, PEER_DISCONNECT_REMOTE_CLOSED);
275 return;
276 }
277
278 /* If there was an error then close the connection */
279 if (read == -1)
280 {
281 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
282 /* No worries */
283 return;
284
285 peer_connection_destroy(conn, PEER_DISCONNECT_LOST_CONNECTION);
286 return;
287 }
288
289 conn->lastactivity = time(NULL);
290
291 /* If we don't even have the first 6 bytes then do nothing */
292 if (read < 6)
293 return;
294
295 /* Read the first 6 bytes (magic string and frame length) */
296 read = recv(conn->fd, &header, 6, 0);
297
298 /* All ODC/OFT frames must start with a magic string */
299 if (memcmp(conn->magic, header, 4))
300 {
301 gaim_debug_warning("oscar", "Expecting magic string to "
302 "be %c%c%c%c but received magic string %c%c%c%c. "
303 "Closing connection.\n",
304 conn->magic[0], conn->magic[1], conn->magic[2],
305 conn->magic[3], header[0], header[1], header[2], header[3]);
306 peer_connection_destroy(conn, PEER_DISCONNECT_INVALID_DATA);
307 return;
308 }
309
310 /* Initialize a new temporary ByteStream for incoming data */
311 conn->buffer_incoming.len = aimutil_get16(&header[4]) - 6;
312 conn->buffer_incoming.data = g_new(guint8, conn->buffer_incoming.len);
313 conn->buffer_incoming.offset = 0;
314 }
315
316 /* Read data into the temporary buffer until it is complete */
317 read = recv(conn->fd,
318 &conn->buffer_incoming.data[conn->buffer_incoming.offset],
319 conn->buffer_incoming.len - conn->buffer_incoming.offset,
320 0);
321
322 /* Check if the remote user closed the connection */
323 if (read == 0)
324 {
325 peer_connection_destroy(conn, PEER_DISCONNECT_REMOTE_CLOSED);
326 return;
327 }
328
329 if (read == -1)
330 {
331 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
332 /* No worries */
333 return;
334
335 peer_connection_destroy(conn, PEER_DISCONNECT_LOST_CONNECTION);
336 return;
337 }
338
339 conn->lastactivity = time(NULL);
340 conn->buffer_incoming.offset += read;
341 if (conn->buffer_incoming.offset < conn->buffer_incoming.len)
342 /* Waiting for more data to arrive */
343 return;
344
345 /* We have a complete ODC/OFT frame! Handle it and continue reading */
346 byte_stream_rewind(&conn->buffer_incoming);
347 if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
348 {
349 peer_odc_recv_frame(conn, &conn->buffer_incoming);
350 }
351 else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
352 {
353 peer_oft_recv_frame(conn, &conn->buffer_incoming);
354 }
355 g_free(conn->buffer_incoming.data);
356 conn->buffer_incoming.data = NULL;
357 }
358
359 /*******************************************************************/
360 /* End code for receiving data on a peer connection */
361 /*******************************************************************/
362
363 /*******************************************************************/
364 /* Begin code for sending data on a peer connection */
365 /*******************************************************************/
366
108 static void 367 static void
109 aim_oft_dirconvert_tostupid(char *name) 368 send_cb(gpointer data, gint source, GaimInputCondition cond)
110 { 369 {
111 while (name[0]) { 370 PeerConnection *conn;
112 if (name[0] == 0x01) 371 gsize writelen;
113 name[0] = G_DIR_SEPARATOR; 372 ssize_t wrotelen;
114 name++; 373
115 } 374 conn = data;
116 } 375 writelen = gaim_circ_buffer_get_max_read(conn->buffer_outgoing);
117 376
118 /** 377 if (writelen == 0)
119 * Convert the directory separator from ^A (0x01) to / (0x2f) 378 {
379 gaim_input_remove(conn->watcher_outgoing);
380 conn->watcher_outgoing = 0;
381 return;
382 }
383
384 wrotelen = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
385 if (wrotelen <= 0)
386 {
387 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
388 /* No worries */
389 return;
390
391 if (conn->ready)
392 peer_connection_schedule_destroy(conn, PEER_DISCONNECT_LOST_CONNECTION);
393 else
394 {
395 /*
396 * This could happen when unable to send a negotiation
397 * frame to a peer proxy server.
398 */
399 peer_connection_trynext(conn);
400 }
401 return;
402 }
403
404 gaim_circ_buffer_mark_read(conn->buffer_outgoing, wrotelen);
405 conn->lastactivity = time(NULL);
406 }
407
408 /**
409 * This should be called by OFT/ODC code to send a standard OFT or ODC
410 * frame across the peer connection along with some payload data. Or
411 * maybe a file. Anything, really.
412 */
413 void
414 peer_connection_send(PeerConnection *conn, ByteStream *bs)
415 {
416 /* Add everything to our outgoing buffer */
417 gaim_circ_buffer_append(conn->buffer_outgoing, bs->data, bs->len);
418
419 /* If we haven't already started writing stuff, then start the cycle */
420 if (conn->watcher_outgoing == 0)
421 {
422 conn->watcher_outgoing = gaim_input_add(conn->fd,
423 GAIM_INPUT_WRITE, send_cb, conn);
424 send_cb(conn, conn->fd, 0);
425 }
426 }
427
428 /*******************************************************************/
429 /* End code for sending data on a peer connection */
430 /*******************************************************************/
431
432 /*******************************************************************/
433 /* Begin code for establishing a peer connection */
434 /*******************************************************************/
435
436 void
437 peer_connection_finalize_connection(PeerConnection *conn)
438 {
439 conn->watcher_incoming = gaim_input_add(conn->fd,
440 GAIM_INPUT_READ, peer_connection_recv_cb, conn);
441
442 if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
443 {
444 /*
445 * If we are connecting to them then send our cookie so they
446 * can verify who we are. Note: This doesn't seem to be
447 * necessary, but it also doesn't seem to hurt.
448 */
449 if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING))
450 peer_odc_send_cookie(conn);
451 }
452 else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
453 {
454 if (gaim_xfer_get_type(conn->xfer) == GAIM_XFER_SEND)
455 {
456 peer_oft_send_prompt(conn);
457 }
458 }
459
460 /*
461 * Tell the remote user that we're connected (which may also imply
462 * that we've accepted their request).
463 */
464 if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING))
465 aim_im_sendch2_connected(conn);
466 }
467
468 /**
469 * We tried to make an outgoing connection to a remote user. It
470 * either connected or failed to connect.
471 */
472 static void
473 peer_connection_established_cb(gpointer data, gint source, GaimInputCondition cond)
474 {
475 NewPeerConnectionData *new_conn_data;
476 GaimConnection *gc;
477 PeerConnection *conn;
478
479 new_conn_data = data;
480 gc = new_conn_data->gc;
481 conn = new_conn_data->conn;
482 g_free(new_conn_data);
483
484 if (!g_list_find(gaim_connections_get_all(), gc))
485 {
486 if (source >= 0)
487 close(source);
488 return;
489 }
490
491 if (source < 0)
492 {
493 peer_connection_trynext(conn);
494 return;
495 }
496
497 conn->fd = source;
498
499 peer_connection_finalize_connection(conn);
500 }
501
502 /**
503 * This is the watcher callback for any listening socket that is
504 * waiting for a peer to connect. When a peer connects we set the
505 * input watcher to start reading data from the peer.
120 * 506 *
121 * @param name The filename to convert. 507 * To make sure that the connection is with the intended person and
122 */ 508 * not with a malicious middle man, we don't send anything until we've
123 static void 509 * received a peer frame from the remote user and have verified that
124 aim_oft_dirconvert_fromstupid(char *name) 510 * the cookie in the peer frame matches the cookie that was exchanged
125 { 511 * in the channel 2 ICBM.
126 while (name[0]) { 512 */
127 if (name[0] == G_DIR_SEPARATOR) 513 void
128 name[0] = 0x01; 514 peer_connection_listen_cb(gpointer data, gint source, GaimInputCondition cond)
129 name++; 515 {
130 } 516 PeerConnection *conn;
131 } 517 OscarData *od;
132 518 GaimConnection *gc;
133 /** 519 GaimAccount *account;
134 * Calculate oft checksum of buffer
135 *
136 * Prevcheck should be 0xFFFF0000 when starting a checksum of a file. The
137 * checksum is kind of a rolling checksum thing, so each time you get bytes
138 * of a file you just call this puppy and it updates the checksum. You can
139 * calculate the checksum of an entire file by calling this in a while or a
140 * for loop, or something.
141 *
142 * Thanks to Graham Booker for providing this improved checksum routine,
143 * which is simpler and should be more accurate than Josh Myer's original
144 * code. -- wtm
145 *
146 * This algorithm works every time I have tried it. The other fails
147 * sometimes. So, AOL who thought this up? It has got to be the weirdest
148 * checksum I have ever seen.
149 *
150 * @param buffer Buffer of data to checksum. Man I'd like to buff her...
151 * @param bufsize Size of buffer.
152 * @param prevcheck Previous checksum.
153 */
154 guint32
155 aim_oft_checksum_chunk(const guint8 *buffer, int bufferlen, guint32 prevcheck)
156 {
157 guint32 check = (prevcheck >> 16) & 0xffff, oldcheck;
158 int i;
159 unsigned short val;
160
161 for (i=0; i<bufferlen; i++) {
162 oldcheck = check;
163 if (i&1)
164 val = buffer[i];
165 else
166 val = buffer[i] << 8;
167 check -= val;
168 /*
169 * The following appears to be necessary.... It happens
170 * every once in a while and the checksum doesn't fail.
171 */
172 if (check > oldcheck)
173 check--;
174 }
175 check = ((check & 0x0000ffff) + (check >> 16));
176 check = ((check & 0x0000ffff) + (check >> 16));
177 return check << 16;
178 }
179
180 guint32
181 aim_oft_checksum_file(char *filename)
182 {
183 FILE *fd;
184 guint32 checksum = 0xffff0000;
185
186 if ((fd = fopen(filename, "rb"))) {
187 int bytes;
188 guint8 buffer[1024];
189
190 while ((bytes = fread(buffer, 1, 1024, fd)))
191 checksum = aim_oft_checksum_chunk(buffer, bytes, checksum);
192 fclose(fd);
193 }
194
195 return checksum;
196 }
197
198 /**
199 * After establishing a listening socket, this is called to accept a connection. It
200 * clones the conn used by the listener, and passes both of these to a signal handler.
201 * The signal handler should close the listener conn and keep track of the new conn,
202 * since this is what is used for file transfers and what not.
203 *
204 * @param sess The session.
205 * @param cur The conn the incoming connection is on.
206 * @return Return 0 if no errors, otherwise return the error number.
207 */
208 int
209 aim_handlerendconnect(OscarSession *sess, OscarConnection *cur)
210 {
211 int acceptfd = 0;
212 struct sockaddr addr; 520 struct sockaddr addr;
213 socklen_t addrlen = sizeof(addr); 521 socklen_t addrlen = sizeof(addr);
214 int ret = 0; 522
215 OscarConnection *newconn; 523 conn = data;
216 char ip[20]; 524 od = conn->od;
217 unsigned short port; 525 gc = od->gc;
218 526 account = gaim_connection_get_account(gc);
219 if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1) 527
220 return 0; /* not an error */ 528 gaim_debug_info("oscar", "Accepting connection on listener socket.\n");
221 529
222 if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6)) { 530 conn->fd = accept(conn->listenerfd, &addr, &addrlen);
223 close(acceptfd); 531 if (conn->fd == -1)
224 aim_conn_close(sess, cur); 532 {
225 return -1; 533 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
226 } 534 /* No connection yet--no worries */
227 535 /* TODO: Hmm, but they SHOULD be connected if we're here, right? */
228 strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip)); 536 return;
229 port = ntohs(((struct sockaddr_in *)&addr)->sin_port); 537
230 538 peer_connection_trynext(conn);
231 if (!(newconn = aim_cloneconn(sess, cur))) { 539 return;
232 close(acceptfd); 540 }
233 aim_conn_close(sess, cur); 541
234 return -ENOMEM; 542 if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6))
235 } 543 {
236 544 /* Invalid connection type?! Continue waiting. */
237 newconn->type = AIM_CONN_TYPE_RENDEZVOUS; 545 close(conn->fd);
238 newconn->fd = acceptfd; 546 return;
239 547 }
240 if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { 548
241 aim_rxcallback_t userfunc; 549 fcntl(conn->fd, F_SETFL, O_NONBLOCK);
242 struct aim_odc_intdata *priv; 550 gaim_input_remove(conn->watcher_incoming);
243 551
244 priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal); 552 peer_connection_finalize_connection(conn);
245 cur->internal = NULL; 553 }
246 snprintf(priv->ip, sizeof(priv->ip), "%s:%hu", ip, port); 554
247 555 /**
248 if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, PEER_TYPE_DIRECTIM_ESTABLISHED))) 556 * We've just opened a listener socket, so we send the remote
249 ret = userfunc(sess, NULL, newconn, cur); 557 * user an ICBM and ask them to connect to us.
250 558 */
251 } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) { 559 static void
252 } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) { 560 peer_connection_establish_listener_cb(int listenerfd, gpointer data)
253 aim_rxcallback_t userfunc; 561 {
254 562 NewPeerConnectionData *new_conn_data;
255 if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, PEER_TYPE_ESTABLISHED))) 563 PeerConnection *conn;
256 ret = userfunc(sess, NULL, newconn, cur); 564 OscarData *od;
257 565 GaimConnection *gc;
258 } else { 566 GaimAccount *account;
259 gaim_debug_warning("oscar", "Got a connection on a listener that's not rendezvous. Closing connection.\n"); 567 GaimConversation *conv;
260 aim_conn_close(sess, newconn); 568 char *tmp;
261 ret = -1; 569 FlapConnection *bos_conn;
262 } 570 const char *listener_ip;
263 571 unsigned short listener_port;
264 return ret; 572
265 } 573 new_conn_data = data;
266 574 gc = new_conn_data->gc;
267 /** 575 conn = new_conn_data->conn;
268 * Send client-to-client typing notification over an established direct connection. 576 g_free(new_conn_data);
577
578 if (!g_list_find(gaim_connections_get_all(), gc))
579 {
580 if (listenerfd != -1)
581 close(listenerfd);
582 return;
583 }
584
585 if (listenerfd == -1)
586 {
587 /* Could not open listener socket */
588 peer_connection_trynext(conn);
589 return;
590 }
591
592 od = conn->od;
593 account = gaim_connection_get_account(gc);
594 conn->listenerfd = listenerfd;
595
596 /* Send the "please connect to me!" ICBM */
597 bos_conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
598 listener_ip = gaim_network_get_my_ip(bos_conn->fd);
599 listener_port = gaim_network_get_port_from_fd(conn->listenerfd);
600 if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
601 {
602 aim_im_sendch2_odc_requestdirect(od,
603 conn->cookie, conn->sn, gaim_network_ip_atoi(listener_ip),
604 listener_port, ++conn->lastrequestnumber);
605
606 /* Print a message to a local conversation window */
607 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, conn->sn);
608 tmp = g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
609 "Direct IM."), conn->sn, listener_ip, listener_port);
610 gaim_conversation_write(conv, NULL, tmp, GAIM_MESSAGE_SYSTEM, time(NULL));
611 g_free(tmp);
612 }
613 else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
614 {
615 aim_im_sendch2_sendfile_requestdirect(od,
616 conn->cookie, conn->sn,
617 gaim_network_ip_atoi(listener_ip),
618 listener_port, ++conn->lastrequestnumber,
619 (const gchar *)conn->xferdata.name,
620 conn->xferdata.size, conn->xferdata.totfiles);
621 }
622 }
623
624 /**
625 * Try to establish the given PeerConnection using a defined
626 * sequence of steps.
627 */
628 void
629 peer_connection_trynext(PeerConnection *conn)
630 {
631 NewPeerConnectionData *new_conn_data;
632 GaimAccount *account;
633
634 new_conn_data = g_new(NewPeerConnectionData, 1);
635 new_conn_data->gc = conn->od->gc;
636 new_conn_data->conn = conn;
637
638 account = gaim_connection_get_account(new_conn_data->gc);
639
640 /*
641 * Close any remnants of a previous failed connection attempt.
642 */
643 peer_connection_close(conn);
644
645 /*
646 * 1. Attempt to connect to the remote user using their verifiedip.
647 */
648 if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_VERIFIEDIP) &&
649 (conn->verifiedip != NULL) && (conn->port != 0) && (!conn->use_proxy))
650 {
651 conn->flags |= PEER_CONNECTION_FLAG_TRIED_VERIFIEDIP;
652
653 if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
654 {
655 gchar *tmp;
656 GaimConversation *conv;
657 tmp = g_strdup_printf(_("Attempting to connect to %s:%hu."),
658 conn->verifiedip, conn->port);
659 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, conn->sn);
660 gaim_conversation_write(conv, NULL, tmp,
661 GAIM_MESSAGE_SYSTEM, time(NULL));
662 g_free(tmp);
663 }
664
665 if (gaim_proxy_connect(account, conn->verifiedip, conn->port,
666 peer_connection_established_cb, new_conn_data) == 0)
667 {
668 /* Connecting... */
669 return;
670 }
671 }
672
673 /*
674 * 2. Attempt to connect to the remote user using their clientip.
675 */
676 if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_CLIENTIP) &&
677 (conn->clientip != NULL) && (conn->port != 0) && (!conn->use_proxy))
678 {
679 conn->flags |= PEER_CONNECTION_FLAG_TRIED_CLIENTIP;
680
681 if (strcmp(conn->verifiedip, conn->clientip))
682 {
683 if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
684 {
685 gchar *tmp;
686 GaimConversation *conv;
687 tmp = g_strdup_printf(_("Attempting to connect to %s:%hu."),
688 conn->clientip, conn->port);
689 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, conn->sn);
690 gaim_conversation_write(conv, NULL, tmp,
691 GAIM_MESSAGE_SYSTEM, time(NULL));
692 g_free(tmp);
693 }
694
695 if (gaim_proxy_connect(account, conn->clientip, conn->port,
696 peer_connection_established_cb, new_conn_data) == 0)
697 {
698 /* Connecting... */
699 return;
700 }
701 }
702 }
703
704 /*
705 * 3. Attempt to have the remote user connect to us (using both
706 * our verifiedip and our clientip).
707 */
708 if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_INCOMING) &&
709 (!conn->use_proxy))
710 {
711 conn->flags |= PEER_CONNECTION_FLAG_TRIED_INCOMING;
712
713 /*
714 * Remote user is connecting to us, so we'll need to verify
715 * that the user who connected is our friend.
716 */
717 conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
718
719 if (gaim_network_listen_range(5190, 5290, SOCK_STREAM,
720 peer_connection_establish_listener_cb, new_conn_data))
721 {
722 /* Opening listener socket... */
723 return;
724 }
725 }
726
727 /*
728 * 4. Attempt to have both users connect to an intermediate proxy
729 * server.
730 */
731 if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_PROXY))
732 {
733 conn->flags |= PEER_CONNECTION_FLAG_TRIED_PROXY;
734
735 /*
736 * If we initiate the proxy connection, then the remote user
737 * could be anyone, so we need to verify that the user who
738 * connected is our friend.
739 */
740 if (!conn->use_proxy)
741 conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
742
743 if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
744 {
745 gchar *tmp;
746 GaimConversation *conv;
747 tmp = g_strdup_printf(_("Attempting to connect via proxy server."));
748 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, conn->sn);
749 gaim_conversation_write(conv, NULL, tmp,
750 GAIM_MESSAGE_SYSTEM, time(NULL));
751 g_free(tmp);
752 }
753
754 if (gaim_proxy_connect(account,
755 (conn->proxyip != NULL) ? conn->proxyip : PEER_PROXY_SERVER,
756 PEER_PROXY_PORT,
757 peer_proxy_connection_established_cb, new_conn_data) == 0)
758 {
759 /* Connecting... */
760 return;
761 }
762 }
763
764 g_free(new_conn_data);
765
766 /* Give up! */
767 peer_connection_destroy(conn, PEER_DISCONNECT_COULD_NOT_CONNECT);
768 }
769
770 /**
771 * Initiate a peer connection with someone.
772 */
773 void
774 peer_connection_propose(OscarData *od, OscarCapability type, const char *sn)
775 {
776 PeerConnection *conn;
777 GaimAccount *account;
778
779 account = gaim_connection_get_account(od->gc);
780
781 if (type == OSCAR_CAPABILITY_DIRECTIM)
782 {
783 conn = peer_connection_find_by_type(od, sn, type);
784 if (conn != NULL)
785 {
786 if (conn->ready)
787 {
788 GaimConversation *conv;
789
790 gaim_debug_info("oscar", "Already have a direct IM "
791 "session with %s.\n", sn);
792 account = gaim_connection_get_account(od->gc);
793 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM,
794 sn, account);
795 if (conv != NULL)
796 gaim_conversation_present(conv);
797 return;
798 }
799
800 /* Cancel the old connection and try again */
801 peer_connection_destroy(conn, PEER_DISCONNECT_RETRYING);
802 }
803 }
804
805 conn = peer_connection_new(od, type, sn);
806 conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
807 conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
808 aim_icbm_makecookie(conn->cookie);
809
810 peer_connection_trynext(conn);
811 }
812
813 /**
814 * Someone else wants to establish a peer connection with us,
815 * and we said yes.
816 */
817 static void
818 peer_connection_got_proposition_yes_cb(gpointer data, gint id)
819 {
820 PeerConnection *conn;
821
822 conn = data;
823
824 conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
825 peer_connection_trynext(conn);
826 }
827
828 /**
829 * Someone else wants to establish a peer connection with us,
830 * and we said no.
269 * 831 *
270 * @param sess The session. 832 * "Well, one time my friend asked me if I wanted to play the
271 * @param conn The already-connected ODC connection. 833 * piccolo. But I said no."
272 * @param typing If 0x0002, sends a "typing" message, 0x0001 sends "typed," and 834 */
273 * 0x0000 sends "stopped." 835 static void
274 * @return Return 0 if no errors, otherwise return the error number. 836 peer_connection_got_proposition_no_cb(gpointer data, gint id)
275 */ 837 {
276 int 838 PeerConnection *conn;
277 aim_odc_send_typing(OscarSession *sess, OscarConnection *conn, int typing) 839
278 { 840 conn = data;
279 struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal; 841
280 FlapFrame *fr; 842 aim_im_denytransfer(conn->od, conn->sn, conn->cookie,
281 ByteStream *hdrbs; 843 AIM_TRANSFER_DENY_DECLINE);
282 guint8 *hdr; 844 peer_connection_destroy(conn, PEER_DISCONNECT_LOCAL_CLOSED);
283 int hdrlen = 0x44; 845 }
284 846
285 if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) 847 /**
286 return -EINVAL; 848 * Someone else wants to establish a peer connection with us.
287 849 */
288 if (!(fr = flap_frame_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, 0))) 850 void
289 return -ENOMEM; 851 peer_connection_got_proposition(OscarData *od, const gchar *sn, const gchar *message, IcbmArgsCh2 *args)
290 memcpy(fr->hdr.rend.magic, "ODC2", 4); 852 {
291 fr->hdr.rend.hdrlen = hdrlen + 8; 853 GaimConnection *gc;
292 854 GaimAccount *account;
293 if (!(hdr = calloc(1, hdrlen))) { 855 PeerConnection *conn;
294 aim_frame_destroy(fr); 856 gchar *buf;
295 return -ENOMEM; 857
296 } 858 gc = od->gc;
297 859 account = gaim_connection_get_account(gc);
298 hdrbs = &(fr->data); 860
299 aim_bstream_init(hdrbs, hdr, hdrlen);
300
301 aimbs_put16(hdrbs, 0x0006);
302 aimbs_put16(hdrbs, 0x0000);
303 aimbs_putraw(hdrbs, intdata->cookie, 8);
304 aimbs_put16(hdrbs, 0x0000);
305 aimbs_put16(hdrbs, 0x0000);
306 aimbs_put16(hdrbs, 0x0000);
307 aimbs_put16(hdrbs, 0x0000);
308 aimbs_put32(hdrbs, 0x00000000);
309 aimbs_put16(hdrbs, 0x0000);
310 aimbs_put16(hdrbs, 0x0000);
311 aimbs_put16(hdrbs, 0x0000);
312
313 if (typing == 0x0002)
314 aimbs_put16(hdrbs, 0x0002 | 0x0008);
315 else if (typing == 0x0001)
316 aimbs_put16(hdrbs, 0x0002 | 0x0004);
317 else
318 aimbs_put16(hdrbs, 0x0002);
319
320 aimbs_put16(hdrbs, 0x0000);
321 aimbs_put16(hdrbs, 0x0000);
322 aimbs_putstr(hdrbs, sess->sn);
323
324 aim_bstream_setpos(hdrbs, 52); /* bleeehh */
325
326 aimbs_put8(hdrbs, 0x00);
327 aimbs_put16(hdrbs, 0x0000);
328 aimbs_put16(hdrbs, 0x0000);
329 aimbs_put16(hdrbs, 0x0000);
330 aimbs_put16(hdrbs, 0x0000);
331 aimbs_put16(hdrbs, 0x0000);
332 aimbs_put16(hdrbs, 0x0000);
333 aimbs_put16(hdrbs, 0x0000);
334 aimbs_put8(hdrbs, 0x00);
335
336 /* end of hdr */
337
338 aim_tx_enqueue(sess, fr);
339
340 return 0;
341 }
342
343 /**
344 * Send client-to-client IM over an established direct connection.
345 * Call this just like you would aim_send_im, to send a directim.
346 *
347 * @param sess The session.
348 * @param conn The already-connected ODC connection.
349 * @param msg Null-terminated string to send.
350 * @param len The length of the message to send, including binary data.
351 * @param encoding See the AIM_CHARSET_* defines in oscar.h
352 * @param isawaymsg 0 if this is not an auto-response, 1 if it is.
353 * @return Return 0 if no errors, otherwise return the error number.
354 */
355 int
356 aim_odc_send_im(OscarSession *sess, OscarConnection *conn, const char *msg, int len, int encoding, int isawaymsg)
357 {
358 FlapFrame *fr;
359 ByteStream *hdrbs;
360 struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
361 int hdrlen = 0x44;
362 guint8 *hdr;
363
364 if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg)
365 return -EINVAL;
366
367 if (!(fr = flap_frame_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0)))
368 return -ENOMEM;
369
370 memcpy(fr->hdr.rend.magic, "ODC2", 4);
371 fr->hdr.rend.hdrlen = hdrlen + 8;
372
373 if (!(hdr = calloc(1, hdrlen + len))) {
374 aim_frame_destroy(fr);
375 return -ENOMEM;
376 }
377
378 hdrbs = &(fr->data);
379 aim_bstream_init(hdrbs, hdr, hdrlen + len);
380
381 aimbs_put16(hdrbs, 0x0006);
382 aimbs_put16(hdrbs, 0x0000);
383 aimbs_putraw(hdrbs, intdata->cookie, 8);
384 aimbs_put16(hdrbs, 0x0000);
385 aimbs_put16(hdrbs, 0x0000);
386 aimbs_put16(hdrbs, 0x0000);
387 aimbs_put16(hdrbs, 0x0000);
388 aimbs_put32(hdrbs, len);
389 aimbs_put16(hdrbs, encoding);
390 aimbs_put16(hdrbs, 0x0000);
391 aimbs_put16(hdrbs, 0x0000);
392
393 /* flags - used for typing notification and to mark if this is an away message */
394 aimbs_put16(hdrbs, 0x0000 | isawaymsg);
395
396 aimbs_put16(hdrbs, 0x0000);
397 aimbs_put16(hdrbs, 0x0000);
398 aimbs_putstr(hdrbs, sess->sn);
399
400 aim_bstream_setpos(hdrbs, 52); /* bleeehh */
401
402 aimbs_put8(hdrbs, 0x00);
403 aimbs_put16(hdrbs, 0x0000);
404 aimbs_put16(hdrbs, 0x0000);
405 aimbs_put16(hdrbs, 0x0000);
406 aimbs_put16(hdrbs, 0x0000);
407 aimbs_put16(hdrbs, 0x0000);
408 aimbs_put16(hdrbs, 0x0000);
409 aimbs_put16(hdrbs, 0x0000);
410 aimbs_put8(hdrbs, 0x00);
411
412 /* end of hdr2 */
413
414 #if 0 /* XXX - this is how you send buddy icon info... */
415 aimbs_put16(hdrbs, 0x0008);
416 aimbs_put16(hdrbs, 0x000c);
417 aimbs_put16(hdrbs, 0x0000);
418 aimbs_put16(hdrbs, 0x1466);
419 aimbs_put16(hdrbs, 0x0001);
420 aimbs_put16(hdrbs, 0x2e0f);
421 aimbs_put16(hdrbs, 0x393e);
422 aimbs_put16(hdrbs, 0xcac8);
423 #endif
424 aimbs_putraw(hdrbs, (guchar *)msg, len);
425
426 aim_tx_enqueue(sess, fr);
427
428 return 0;
429 }
430
431 /**
432 * Get the screen name of the peer of a direct connection.
433 *
434 * @param conn The ODC connection.
435 * @return The screen name of the dude, or NULL if there was an anomaly.
436 */
437 const char *
438 aim_odc_getsn(OscarConnection *conn)
439 {
440 struct aim_odc_intdata *intdata;
441
442 if (!conn || !conn->internal)
443 return NULL;
444
445 if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) ||
446 (conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
447 return NULL;
448
449 intdata = (struct aim_odc_intdata *)conn->internal;
450
451 return intdata->sn;
452 }
453
454 /**
455 * Get the cookie of a direct connection.
456 *
457 * @param conn The ODC connection.
458 * @return The cookie, an 8 byte unterminated string, or NULL if there was an anomaly.
459 */
460 const guchar *
461 aim_odc_getcookie(OscarConnection *conn)
462 {
463 struct aim_odc_intdata *intdata;
464
465 if (!conn || !conn->internal)
466 return NULL;
467
468 intdata = (struct aim_odc_intdata *)conn->internal;
469
470 return intdata->cookie;
471 }
472
473 /**
474 * Find the conn of a direct connection with the given buddy.
475 *
476 * @param sess The session.
477 * @param sn The screen name of the buddy whose direct connection you want to find.
478 * @return The conn for the direct connection with the given buddy, or NULL if no
479 * connection was found.
480 */
481 OscarConnection *
482 aim_odc_getconn(OscarSession *sess, const char *sn)
483 {
484 GList *cur;
485 struct aim_odc_intdata *intdata;
486
487 if (!sess || !sn || !strlen(sn))
488 return NULL;
489
490 for (cur = sess->oscar_connections; cur; cur = cur->next)
491 {
492 OscarConnection *conn;
493 conn = cur->data;
494 if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) && (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
495 intdata = conn->internal;
496 if (!aim_sncmp(intdata->sn, sn))
497 return conn;
498 }
499 }
500
501 return NULL;
502 }
503
504 /**
505 * For those times when we want to open up the direct connection channel ourselves.
506 *
507 * You'll want to set up some kind of watcher on this socket.
508 * When the state changes, call aim_handlerendconnection with
509 * the connection returned by this. aim_handlerendconnection
510 * will accept the pending connection and stop listening.
511 *
512 * @param sess The session
513 * @param sn The screen name to connect to.
514 * @return The new connection.
515 */
516 OscarConnection *
517 aim_odc_initiate(OscarSession *sess, const char *sn, int listenfd,
518 const guint8 *localip, guint16 port, const guint8 *mycookie)
519 {
520 OscarConnection *newconn;
521 IcbmCookie *cookie;
522 struct aim_odc_intdata *priv;
523 guint8 ck[8];
524
525 if (!localip)
526 return NULL;
527
528 if (mycookie) {
529 memcpy(ck, mycookie, 8);
530 aim_im_sendch2_odcrequest(sess, ck, TRUE, sn, localip, port);
531 } else
532 aim_im_sendch2_odcrequest(sess, ck, FALSE, sn, localip, port);
533
534 cookie = (IcbmCookie *)calloc(1, sizeof(IcbmCookie));
535 memcpy(cookie->cookie, ck, 8);
536 cookie->type = AIM_COOKIETYPE_OFTIM;
537
538 /* this one is for the cookie */
539 priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
540
541 memcpy(priv->cookie, ck, 8);
542 strncpy(priv->sn, sn, sizeof(priv->sn));
543 cookie->data = priv;
544 aim_cachecookie(sess, cookie);
545
546 /* XXX - switch to aim_cloneconn()? */
547 if (!(newconn = oscar_connection_new(sess, AIM_CONN_TYPE_LISTENER))) {
548 close(listenfd);
549 return NULL;
550 }
551
552 /* this one is for the conn */
553 priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
554
555 memcpy(priv->cookie, ck, 8);
556 strncpy(priv->sn, sn, sizeof(priv->sn));
557
558 newconn->fd = listenfd;
559 newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
560 newconn->internal = priv;
561 newconn->lastactivity = time(NULL);
562
563 return newconn;
564 }
565
566 /**
567 * Connect directly to the given buddy for directim.
568 *
569 * This is a wrapper for oscar_connection_new.
570 *
571 * If addr is NULL, the socket is not created, but the connection is
572 * allocated and setup to connect.
573 *
574 * @param sess The Godly session.
575 * @param sn The screen name we're connecting to. I hope it's a girl...
576 * @param addr Address to connect to.
577 * @return The new connection.
578 */
579 OscarConnection *
580 aim_odc_connect(OscarSession *sess, const char *sn, const char *addr, const guint8 *cookie)
581 {
582 OscarConnection *newconn;
583 struct aim_odc_intdata *intdata;
584
585 if (!sess || !sn)
586 return NULL;
587
588 if (!(intdata = calloc(1, sizeof(struct aim_odc_intdata))))
589 return NULL;
590 memcpy(intdata->cookie, cookie, 8);
591 strncpy(intdata->sn, sn, sizeof(intdata->sn));
592 if (addr)
593 strncpy(intdata->ip, addr, sizeof(intdata->ip));
594
595 /* XXX - verify that non-blocking connects actually work */
596 if (!(newconn = oscar_connection_new(sess, AIM_CONN_TYPE_RENDEZVOUS))) {
597 free(intdata);
598 return NULL;
599 }
600
601 newconn->internal = intdata;
602 newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
603
604 return newconn;
605 }
606
607 /**
608 * Sometimes you just don't know with these kinds of people.
609 *
610 * @param sess The session.
611 * @param conn The ODC connection of the incoming data.
612 * @param frr The frame allocated for the incoming data.
613 * @param bs It stands for "bologna sandwich."
614 * @return Return 0 if no errors, otherwise return the error number.
615 */
616 static int
617 handlehdr_odc(OscarSession *sess, OscarConnection *conn, FlapFrame *frr, ByteStream *bs)
618 {
619 FlapFrame fr;
620 int ret = 0;
621 aim_rxcallback_t userfunc;
622 guint32 payloadlength;
623 guint16 flags, encoding;
624 char *snptr = NULL;
625
626 fr.conn = conn;
627
628 /* AAA - ugly */
629 aim_bstream_setpos(bs, 20);
630 payloadlength = aimbs_get32(bs);
631
632 aim_bstream_setpos(bs, 24);
633 encoding = aimbs_get16(bs);
634
635 aim_bstream_setpos(bs, 30);
636 flags = aimbs_get16(bs);
637
638 aim_bstream_setpos(bs, 36);
639 /* XXX - create an aimbs_getnullstr function? */
640 snptr = aimbs_getstr(bs, 32); /* Next 32 bytes contain the sn, padded with null chars */
641
642 gaim_debug_misc("oscar", "faim: OFT frame: handlehdr_odc: %04x / %04x / %s\n", payloadlength, flags, snptr);
643
644 if (flags & 0x0008) {
645 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, PEER_TYPE_DIRECTIMTYPING)))
646 ret = userfunc(sess, &fr, snptr, 2);
647 } else if (flags & 0x0004) {
648 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, PEER_TYPE_DIRECTIMTYPING)))
649 ret = userfunc(sess, &fr, snptr, 1);
650 } else {
651 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, PEER_TYPE_DIRECTIMTYPING)))
652 ret = userfunc(sess, &fr, snptr, 0);
653 }
654
655 if ((payloadlength != 0) && (payloadlength != UINT_MAX)) {
656 char *msg;
657 int recvd = 0;
658 int i, isawaymsg;
659
660 isawaymsg = flags & 0x0001;
661
662 if (!(msg = calloc(1, payloadlength+1))) {
663 free(snptr);
664 return -ENOMEM;
665 }
666
667 while (payloadlength - recvd) {
668 if (payloadlength - recvd >= 1024)
669 i = aim_recv(conn->fd, &msg[recvd], 1024);
670 else
671 i = aim_recv(conn->fd, &msg[recvd], payloadlength - recvd);
672 if (i <= 0) {
673 free(msg);
674 free(snptr);
675 return -1;
676 }
677 recvd = recvd + i;
678 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
679 ret = userfunc(sess, &fr, snptr, (double)recvd / payloadlength);
680 }
681
682 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, PEER_TYPE_DIRECTIMINCOMING)))
683 ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding, isawaymsg);
684
685 free(msg);
686 }
687
688 free(snptr);
689
690 return ret;
691 }
692
693 PeerConnection *
694 aim_oft_createinfo(OscarSession *sess, const guint8 *cookie, const char *sn, const char *ip, guint16 port, guint32 size, guint32 modtime, char *filename, int send_or_recv, int method, int stage)
695 {
696 PeerConnection *new;
697
698 if (!sess)
699 return NULL;
700
701 if (!(new = (PeerConnection *)calloc(1, sizeof(PeerConnection))))
702 return NULL;
703
704 new->sess = sess;
705 if (cookie)
706 memcpy(new->cookie, cookie, 8);
707 else
708 aim_icbm_makecookie(new->cookie);
709 if (ip)
710 new->clientip = strdup(ip);
711 else
712 new->clientip = NULL;
713 if (sn)
714 new->sn = strdup(sn);
715 else
716 new->sn = NULL;
717 new->method = method;
718 new->send_or_recv = send_or_recv;
719 new->stage = stage;
720 new->port = port;
721 new->xfer_reffed = FALSE;
722 new->success = FALSE;
723 new->fh.totfiles = 1;
724 new->fh.filesleft = 1;
725 new->fh.totparts = 1;
726 new->fh.partsleft = 1;
727 new->fh.totsize = size;
728 new->fh.size = size;
729 new->fh.modtime = modtime;
730 new->fh.checksum = 0xffff0000;
731 new->fh.rfrcsum = 0xffff0000;
732 new->fh.rfcsum = 0xffff0000;
733 new->fh.recvcsum = 0xffff0000;
734 strncpy(new->fh.idstring, "OFT_Windows ICBMFT V1.1 32", 31);
735 if (filename) {
736 strncpy(new->fh.name, filename, 63);
737 new->fh.name[63] = '\0';
738 }
739
740 sess->peer_connections = g_list_prepend(sess->peer_connections, new);
741
742 return new;
743 }
744
745 PeerProxyInfo *aim_rv_proxy_createinfo(OscarSession *sess, const guint8 *cookie,
746 guint16 port)
747 {
748 PeerProxyInfo *proxy_info;
749
750 if (!(proxy_info = (PeerProxyInfo*)calloc(1, sizeof(PeerProxyInfo))))
751 return NULL;
752
753 proxy_info->sess = sess;
754 proxy_info->port = port;
755 proxy_info->packet_ver = AIM_RV_PROXY_PACKETVER_DFLT;
756 proxy_info->unknownA = AIM_RV_PROXY_UNKNOWNA_DFLT;
757
758 if (cookie)
759 memcpy(proxy_info->cookie, cookie, 8);
760
761 return proxy_info;
762 }
763
764 /**
765 * Remove the given PeerConnection from the PeerConnection linked list, and
766 * then free its memory.
767 *
768 * @param sess The session.
769 * @param peer_connection The PeerConnection that we're destroying.
770 * @return Return 0 if no errors, otherwise return the error number.
771 */
772 int
773 aim_oft_destroyinfo(PeerConnection *peer_connection)
774 {
775 OscarSession *sess;
776
777 if (!peer_connection || !(sess = peer_connection->sess))
778 return -EINVAL;
779
780 sess->peer_connections = g_list_remove(sess->peer_connections, peer_connection);
781
782 free(peer_connection->sn);
783 free(peer_connection->proxyip);
784 free(peer_connection->clientip);
785 free(peer_connection->verifiedip);
786 free(peer_connection);
787
788 return 0;
789 }
790
791 /**
792 * Creates a listener socket so the other dude can connect to us.
793 *
794 * You'll want to set up some kind of watcher on this socket.
795 * When the state changes, call aim_handlerendconnection with
796 * the connection returned by this. aim_handlerendconnection
797 * will accept the pending connection and stop listening.
798 *
799 * @param sess The session.
800 * @param peer_connection File transfer information associated with this
801 * connection.
802 * @return Return 0 if no errors, otherwise return the error number.
803 */
804 int
805 aim_sendfile_listen(OscarSession *sess, PeerConnection *peer_connection, int listenfd)
806 {
807 if (!peer_connection)
808 return -EINVAL;
809
810 if (!(peer_connection->conn = oscar_connection_new(sess, AIM_CONN_TYPE_LISTENER))) {
811 close(listenfd);
812 return -ENOMEM;
813 }
814
815 peer_connection->conn->fd = listenfd;
816 peer_connection->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
817 peer_connection->conn->lastactivity = time(NULL);
818
819 return 0;
820 }
821
822 /**
823 * Extract an &aim_fileheader_t from the given buffer.
824 *
825 * @param bs The should be from an incoming rendezvous packet.
826 * @return A pointer to new struct on success, or NULL on error.
827 */
828 static PeerFrame *
829 aim_oft_getheader(ByteStream *bs)
830 {
831 PeerFrame *fh;
832
833 if (!(fh = calloc(1, sizeof(PeerFrame))))
834 return NULL;
835
836 /* The bstream should be positioned after the hdrtype. */
837 aimbs_getrawbuf(bs, fh->bcookie, 8);
838 fh->encrypt = aimbs_get16(bs);
839 fh->compress = aimbs_get16(bs);
840 fh->totfiles = aimbs_get16(bs);
841 fh->filesleft = aimbs_get16(bs);
842 fh->totparts = aimbs_get16(bs);
843 fh->partsleft = aimbs_get16(bs);
844 fh->totsize = aimbs_get32(bs);
845 fh->size = aimbs_get32(bs);
846 fh->modtime = aimbs_get32(bs);
847 fh->checksum = aimbs_get32(bs);
848 fh->rfrcsum = aimbs_get32(bs);
849 fh->rfsize = aimbs_get32(bs);
850 fh->cretime = aimbs_get32(bs);
851 fh->rfcsum = aimbs_get32(bs);
852 fh->nrecvd = aimbs_get32(bs);
853 fh->recvcsum = aimbs_get32(bs);
854 aimbs_getrawbuf(bs, (guchar *)fh->idstring, 32);
855 fh->flags = aimbs_get8(bs);
856 fh->lnameoffset = aimbs_get8(bs);
857 fh->lsizeoffset = aimbs_get8(bs);
858 aimbs_getrawbuf(bs, (guchar *)fh->dummy, 69);
859 aimbs_getrawbuf(bs, (guchar *)fh->macfileinfo, 16);
860 fh->nencode = aimbs_get16(bs);
861 fh->nlanguage = aimbs_get16(bs);
862 aimbs_getrawbuf(bs, (guchar *)fh->name, 64); /* XXX - filenames longer than 64B */
863 fh->name[63] = '\0';
864
865 return fh;
866 }
867
868 /**
869 * Fills a buffer with network-order fh data
870 *
871 * @param bs A bstream to fill -- automatically initialized
872 * @param fh A PeerFrame to get data from.
873 * @return Return non-zero on error.
874 */
875 static int
876 aim_oft_buildheader(ByteStream *bs, PeerFrame *fh)
877 {
878 guint8 *hdr;
879
880 if (!bs || !fh)
881 return -EINVAL;
882
883 if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8)))
884 return -ENOMEM;
885
886 aim_bstream_init(bs, hdr, 0x100 - 8);
887 aimbs_putraw(bs, fh->bcookie, 8);
888 aimbs_put16(bs, fh->encrypt);
889 aimbs_put16(bs, fh->compress);
890 aimbs_put16(bs, fh->totfiles);
891 aimbs_put16(bs, fh->filesleft);
892 aimbs_put16(bs, fh->totparts);
893 aimbs_put16(bs, fh->partsleft);
894 aimbs_put32(bs, fh->totsize);
895 aimbs_put32(bs, fh->size);
896 aimbs_put32(bs, fh->modtime);
897 aimbs_put32(bs, fh->checksum);
898 aimbs_put32(bs, fh->rfrcsum);
899 aimbs_put32(bs, fh->rfsize);
900 aimbs_put32(bs, fh->cretime);
901 aimbs_put32(bs, fh->rfcsum);
902 aimbs_put32(bs, fh->nrecvd);
903 aimbs_put32(bs, fh->recvcsum);
904 aimbs_putraw(bs, (guchar *)fh->idstring, 32);
905 aimbs_put8(bs, fh->flags);
906 aimbs_put8(bs, fh->lnameoffset);
907 aimbs_put8(bs, fh->lsizeoffset);
908 aimbs_putraw(bs, (guchar *)fh->dummy, 69);
909 aimbs_putraw(bs, (guchar *)fh->macfileinfo, 16);
910 aimbs_put16(bs, fh->nencode);
911 aimbs_put16(bs, fh->nlanguage);
912 aimbs_putraw(bs, (guchar *)fh->name, 64); /* XXX - filenames longer than 64B */
913
914 return 0;
915 }
916
917 /**
918 * Create an OFT packet based on the given information, and send it on its merry way.
919 *
920 * @param sess The session.
921 * @param type The subtype of the OFT packet we're sending.
922 * @param peer_connection The PeerConnection with the connection and OFT
923 * info we're sending.
924 * @return Return 0 if no errors, otherwise return the error number.
925 */
926 int aim_oft_sendheader(OscarSession *sess, guint16 type, PeerConnection *peer_connection)
927 {
928 FlapFrame *fr;
929
930 if (!sess || !peer_connection || !peer_connection->conn || (peer_connection->conn->type != AIM_CONN_TYPE_RENDEZVOUS))
931 return -EINVAL;
932
933 #if 0
934 /* 861 /*
935 * If you are receiving a file, the cookie should be null, if you are sending a 862 * If we have a connection with this same cookie then they are
936 * file, the cookie should be the same as the one used in the ICBM negotiation 863 * probably just telling us they weren't able to connect to us
937 * SNACs. 864 * and we should try connecting to them, instead. Or they want
865 * to go through a proxy.
938 */ 866 */
939 fh->lnameoffset = 0x1a; 867 conn = peer_connection_find_by_cookie(od, sn, args->cookie);
940 fh->lsizeoffset = 0x10; 868 if ((conn != NULL) && (conn->type == args->type))
941 869 {
942 /* These should be the same as charset and charsubset in ICBMs */ 870 gaim_debug_info("oscar", "Remote user wants to try a "
943 fh->nencode = 0x0000; 871 "different connection method\n");
944 fh->nlanguage = 0x0000; 872 g_free(conn->proxyip);
945 #endif 873 g_free(conn->clientip);
946 874 g_free(conn->verifiedip);
947 aim_oft_dirconvert_tostupid(peer_connection->fh.name); 875 if (args->use_proxy)
948 876 conn->proxyip = g_strdup(args->proxyip);
949 if (!(fr = flap_frame_new(sess, peer_connection->conn, AIM_FRAMETYPE_OFT, type, 0)))
950 return -ENOMEM;
951
952 if (aim_oft_buildheader(&fr->data, &peer_connection->fh) == -1) {
953 aim_frame_destroy(fr);
954 return -ENOMEM;
955 }
956
957 memcpy(fr->hdr.rend.magic, "OFT2", 4);
958 fr->hdr.rend.hdrlen = aim_bstream_curpos(&fr->data) + 8;
959
960 aim_tx_enqueue(sess, fr);
961
962 return 0;
963 }
964
965 /**
966 * Create a rendezvous "init recv" packet and send it on its merry way.
967 * This is the first packet sent to the proxy server by the second client
968 * involved in this rendezvous proxy session.
969 *
970 * @param sess The session.
971 * @param proxy_info Changable pieces of data for this packet
972 * @return Return 0 if no errors, otherwise return the error number.
973 */
974 int
975 aim_rv_proxy_init_recv(PeerProxyInfo *proxy_info)
976 {
977 #if 0
978 aim_tlvlist_t *tlvlist_sendfile;
979 #endif
980 ByteStream bs;
981 guint8 *bs_raw;
982 guint16 packet_len;
983 guint8 sn_len;
984 int err;
985
986 err = 0;
987
988 if (!proxy_info)
989 return -EINVAL;
990
991 sn_len = strlen(proxy_info->sess->sn);
992 packet_len = 2 + 2 /* packet_len, packet_ver */
993 + 2 + 4 /* cmd_type, unknownA */
994 + 2 /* flags */
995 + 1 + sn_len /* Length/value pair for screenname */
996 + 8 /* ICBM Cookie */
997 + 2 /* port */
998 + 2 + 2 + 16; /* TLV for Filesend capability block */
999
1000 if (!(bs_raw = malloc(packet_len)))
1001 return -ENOMEM;
1002
1003 aim_bstream_init(&bs, bs_raw, packet_len);
1004 aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */
1005 aimbs_put16(&bs, proxy_info->packet_ver);
1006 aimbs_put16(&bs, AIM_RV_PROXY_INIT_RECV);
1007 aimbs_put32(&bs, proxy_info->unknownA);
1008 aimbs_put16(&bs, proxy_info->flags);
1009 aimbs_put8(&bs, sn_len);
1010 aimbs_putraw(&bs, (const guchar *)proxy_info->sess->sn, sn_len);
1011 aimbs_put16(&bs, proxy_info->port);
1012 aimbs_putraw(&bs, proxy_info->cookie, 8);
1013
1014 aimbs_put16(&bs, 0x0001); /* Type */
1015 aimbs_put16(&bs, 16); /* Length */
1016 aimbs_putcaps(&bs, AIM_CAPS_SENDFILE); /* Value */
1017
1018
1019 #if 0
1020 /* TODO: Use built-in TLV */
1021 aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE);
1022 aim_tlvlist_write(&bs, &tlvlist_sendfile);
1023 #endif
1024
1025 aim_bstream_rewind(&bs);
1026 if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len)
1027 err = errno;
1028 proxy_info->conn->lastactivity = time(NULL);
1029
1030 #if 0
1031 aim_tlvlist_free(tlvlist_sendfile);
1032 #endif
1033 free(bs_raw);
1034
1035 return err;
1036 }
1037
1038
1039 /**
1040 * Create a rendezvous "init send" packet and send it on its merry way.
1041 * This is the first packet sent to the proxy server by the client
1042 * first indicating that this will be a proxied connection
1043 *
1044 * @param sess The session.
1045 * @param proxy_info Changable pieces of data for this packet
1046 * @return Return 0 if no errors, otherwise return the error number.
1047 */
1048 int
1049 aim_rv_proxy_init_send(PeerProxyInfo *proxy_info)
1050 {
1051 #if 0
1052 aim_tlvlist_t *tlvlist_sendfile;
1053 #endif
1054 ByteStream bs;
1055 guint8 *bs_raw;
1056 guint16 packet_len;
1057 guint8 sn_len;
1058 int err;
1059
1060 err = 0;
1061
1062 if (!proxy_info)
1063 return -EINVAL;
1064
1065 sn_len = strlen(proxy_info->sess->sn);
1066 packet_len = 2 + 2 /* packet_len, packet_ver */
1067 + 2 + 4 /* cmd_type, unknownA */
1068 + 2 /* flags */
1069 + 1 + sn_len /* Length/value pair for screenname */
1070 + 8 /* ICBM Cookie */
1071 + 2 + 2 + 16; /* TLV for Filesend capability block */
1072
1073 if (!(bs_raw = malloc(packet_len)))
1074 return -ENOMEM;
1075
1076 aim_bstream_init(&bs, bs_raw, packet_len);
1077 aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */
1078 aimbs_put16(&bs, proxy_info->packet_ver);
1079 aimbs_put16(&bs, AIM_RV_PROXY_INIT_SEND);
1080 aimbs_put32(&bs, proxy_info->unknownA);
1081 aimbs_put16(&bs, proxy_info->flags);
1082 aimbs_put8(&bs, sn_len);
1083 aimbs_putraw(&bs, (const guchar *)proxy_info->sess->sn, sn_len);
1084 aimbs_putraw(&bs, proxy_info->cookie, 8);
1085
1086 aimbs_put16(&bs, 0x0001); /* Type */
1087 aimbs_put16(&bs, 16); /* Length */
1088 aimbs_putcaps(&bs, AIM_CAPS_SENDFILE); /* Value */
1089
1090 /* TODO: Use built-in TLV */
1091 #if 0
1092 aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE);
1093 aim_tlvlist_write(&bs, &tlvlist_sendfile);
1094 #endif
1095
1096 aim_bstream_rewind(&bs);
1097 if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len)
1098 err = errno;
1099 proxy_info->conn->lastactivity = time(NULL);
1100
1101 #if 0
1102 aim_tlvlist_free(tlvlist_sendfile);
1103 #endif
1104 free(bs_raw);
1105
1106 return err;
1107 }
1108
1109 /**
1110 * Handle incoming data on a rendezvous connection. This is analogous to the
1111 * consumesnac function in rxhandlers.c, and I really think this should probably
1112 * be in rxhandlers.c as well, but I haven't finished cleaning everything up yet.
1113 *
1114 * @param sess The session.
1115 * @param fr The frame allocated for the incoming data.
1116 * @return Return 0 if the packet was handled correctly, otherwise return the
1117 * error number.
1118 */
1119 int
1120 aim_rxdispatch_rendezvous(OscarSession *sess, FlapFrame *fr)
1121 {
1122 OscarConnection *conn = fr->conn;
1123 int ret = 1;
1124
1125 if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
1126 if (fr->hdr.rend.type == 0x0001)
1127 ret = handlehdr_odc(sess, conn, fr, &fr->data);
1128 else 877 else
1129 gaim_debug_info("oscar", "ODC directim frame unknown, type is %04x\n", fr->hdr.rend.type); 878 conn->proxyip = NULL;
1130 879 conn->verifiedip = g_strdup(args->verifiedip);
1131 } else { 880 conn->clientip = g_strdup(args->clientip);
1132 aim_rxcallback_t userfunc; 881 conn->port = args->port;
1133 PeerFrame *header = aim_oft_getheader(&fr->data); 882 conn->use_proxy |= args->use_proxy;
1134 aim_oft_dirconvert_fromstupid(header->name); /* XXX - This should be client-side */ 883 conn->lastrequestnumber++;
1135 884 peer_connection_trynext(conn);
1136 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type))) 885 return;
1137 ret = userfunc(sess, fr, conn, header->bcookie, header); 886 }
1138 887
1139 free(header); 888 /* If this is a direct IM, then close any existing session */
1140 } 889 if (args->type == OSCAR_CAPABILITY_DIRECTIM)
1141 890 {
1142 if (ret == -1) 891 conn = peer_connection_find_by_type(od, sn, args->type);
1143 aim_conn_close(sess, conn); 892 if (conn != NULL)
1144 893 {
1145 return ret; 894 /* Close the old direct IM and start a new one */
1146 } 895 gaim_debug_info("oscar", "Received new direct IM request "
1147 896 "from %s. Destroying old connection.\n", sn);
1148 /** 897 peer_connection_destroy(conn, PEER_DISCONNECT_REMOTE_CLOSED);
1149 * Handle incoming data on a rendezvous proxy connection. This is similar to 898 }
1150 * aim_rxdispatch_rendezvous above and should probably be kept with that function. 899 }
1151 * 900
1152 * @param sess The session. 901 /* Check for proper arguments */
1153 * @param fr The frame allocated for the incoming data. 902 if (args->type == OSCAR_CAPABILITY_SENDFILE)
1154 * @return Return 0 if the packet was handled correctly, otherwise return the 903 {
1155 * error number. 904 if ((args->info.sendfile.filename == NULL) ||
1156 */ 905 (args->info.sendfile.totsize == 0) ||
1157 PeerProxyInfo * 906 (args->info.sendfile.totfiles == 0))
1158 aim_rv_proxy_read(OscarSession *sess, OscarConnection *conn) 907 {
1159 { 908 gaim_debug_warning("oscar",
1160 ByteStream bs_hdr; 909 "%s tried to send you a file with incomplete "
1161 guint8 hdr_buf[AIM_RV_PROXY_HDR_LEN]; 910 "information.\n", sn);
1162 ByteStream bs_body; /* The body (everything but the header) of the packet */ 911 return;
1163 guint8 *body_buf = NULL; 912 }
1164 guint8 body_len; 913 }
1165 914
1166 char str_ip[30] = {""}; 915 conn = peer_connection_new(od, args->type, sn);
1167 guint8 ip_temp[4]; 916 memcpy(conn->cookie, args->cookie, 8);
1168 917 if (args->use_proxy)
1169 guint16 len; 918 conn->proxyip = g_strdup(args->proxyip);
1170 PeerProxyInfo *proxy_info; 919 conn->clientip = g_strdup(args->clientip);
1171 920 conn->verifiedip = g_strdup(args->verifiedip);
1172 if(!(proxy_info = malloc(sizeof(PeerProxyInfo)))) 921 conn->port = args->port;
1173 return NULL; 922 conn->use_proxy |= args->use_proxy;
1174 923 conn->lastrequestnumber++;
1175 aim_bstream_init(&bs_hdr, hdr_buf, AIM_RV_PROXY_HDR_LEN); 924
1176 if (aim_bstream_recv(&bs_hdr, conn->fd, AIM_RV_PROXY_HDR_LEN) == AIM_RV_PROXY_HDR_LEN) { 925 if (args->type == OSCAR_CAPABILITY_DIRECTIM)
1177 aim_bstream_rewind(&bs_hdr); 926 {
1178 len = aimbs_get16(&bs_hdr); 927 buf = g_strdup_printf(_("%s has just asked to directly connect to %s"),
1179 proxy_info->packet_ver = aimbs_get16(&bs_hdr); 928 sn, gaim_account_get_username(account));
1180 proxy_info->cmd_type = aimbs_get16(&bs_hdr); 929
1181 proxy_info->unknownA = aimbs_get32(&bs_hdr); 930 gaim_request_action(conn, NULL, buf,
1182 proxy_info->flags = aimbs_get16(&bs_hdr); 931 _("This requires a direct connection between "
1183 if(proxy_info->cmd_type == AIM_RV_PROXY_READY) { 932 "the two computers and is necessary for IM "
1184 /* Do a little victory dance 933 "Images. Because your IP address will be "
1185 * A ready packet contains no additional information */ 934 "revealed, this may be considered a privacy "
1186 } else if(proxy_info->cmd_type == AIM_RV_PROXY_ERROR) { 935 "risk."),
1187 if(len == AIM_RV_PROXY_ERROR_LEN - 2) { 936 GAIM_DEFAULT_ACTION_NONE, conn, 2,
1188 body_len = AIM_RV_PROXY_ERROR_LEN - AIM_RV_PROXY_HDR_LEN; 937 _("_Connect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
1189 body_buf = malloc(body_len); 938 _("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb));
1190 aim_bstream_init(&bs_body, body_buf, body_len); 939 }
1191 if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) { 940 else if (args->type == OSCAR_CAPABILITY_SENDFILE)
1192 aim_bstream_rewind(&bs_body); 941 {
1193 proxy_info->err_code = aimbs_get16(&bs_body); 942 gchar *filename;
1194 } else { 943
1195 gaim_debug_warning("oscar","error reading rv proxy error packet\n"); 944 conn->xfer = gaim_xfer_new(account, GAIM_XFER_RECEIVE, sn);
1196 aim_conn_close(sess, conn); 945 conn->xfer->data = conn;
1197 free(proxy_info); 946 gaim_xfer_ref(conn->xfer);
1198 proxy_info = NULL; 947 gaim_xfer_set_size(conn->xfer, args->info.sendfile.totsize);
1199 } 948
1200 } else { 949 /* Set the file name */
1201 gaim_debug_warning("oscar","invalid length for proxy error packet\n"); 950 if (g_utf8_validate(args->info.sendfile.filename, -1, NULL))
1202 free(proxy_info); 951 filename = g_strdup(args->info.sendfile.filename);
1203 proxy_info = NULL; 952 else
1204 } 953 filename = gaim_utf8_salvage(args->info.sendfile.filename);
1205 } else if(proxy_info->cmd_type == AIM_RV_PROXY_ACK) { 954
1206 if(len == AIM_RV_PROXY_ACK_LEN - 2) { 955 if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR)
1207 body_len = AIM_RV_PROXY_ACK_LEN - AIM_RV_PROXY_HDR_LEN; 956 {
1208 body_buf = malloc(body_len); 957 /*
1209 aim_bstream_init(&bs_body, body_buf, body_len); 958 * If they are sending us a directory then the last character
1210 if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) { 959 * of the file name will be an asterisk. We don't want to
1211 int i; 960 * save stuff to a directory named "*" so we remove the
1212 aim_bstream_rewind(&bs_body); 961 * asterisk from the file name.
1213 proxy_info->port = aimbs_get16(&bs_body); 962 */
1214 for(i=0; i<4; i++) 963 char *tmp = strrchr(filename, '\\');
1215 ip_temp[i] = aimbs_get8(&bs_body); 964 if ((tmp != NULL) && (tmp[1] == '*'))
1216 snprintf(str_ip, sizeof(str_ip), "%hhu.%hhu.%hhu.%hhu", 965 tmp[0] = '\0';
1217 ip_temp[0], ip_temp[1], 966 }
1218 ip_temp[2], ip_temp[3]); 967 gaim_xfer_set_filename(conn->xfer, filename);
1219 proxy_info->ip = strdup(str_ip); 968 g_free(filename);
1220 } else { 969
1221 gaim_debug_warning("oscar","error reading rv proxy error packet\n"); 970 /*
1222 aim_conn_close(sess, conn); 971 * Set the message (unless this is the dummy message from an
1223 free(proxy_info); 972 * ICQ client or an empty message from an AIM client.
1224 proxy_info = NULL; 973 * TODO: Maybe we should strip HTML and then see if strlen>0?
1225 } 974 */
1226 } else { 975 if ((message != NULL) &&
1227 gaim_debug_warning("oscar","invalid length for proxy error packet\n"); 976 (g_ascii_strncasecmp(message, "<ICQ_COOL_FT>", 13) != 0) &&
1228 free(proxy_info); 977 (g_ascii_strcasecmp(message, "<HTML>") != 0))
1229 proxy_info = NULL; 978 {
1230 } 979 gaim_xfer_set_message(conn->xfer, message);
1231 } else { 980 }
1232 gaim_debug_warning("oscar","unknown type for aim rendezvous proxy packet\n"); 981
1233 } 982 /* Setup our I/O op functions */
1234 } else { 983 gaim_xfer_set_init_fnc(conn->xfer, peer_oft_recvcb_init);
1235 gaim_debug_warning("oscar","error reading header of rv proxy packet\n"); 984 gaim_xfer_set_end_fnc(conn->xfer, peer_oft_recvcb_end);
1236 aim_conn_close(sess, conn); 985 gaim_xfer_set_request_denied_fnc(conn->xfer, peer_oft_cb_generic_cancel);
1237 free(proxy_info); 986 gaim_xfer_set_cancel_recv_fnc(conn->xfer, peer_oft_cb_generic_cancel);
1238 proxy_info = NULL; 987 gaim_xfer_set_ack_fnc(conn->xfer, peer_oft_recvcb_ack_recv);
1239 } 988
1240 if(body_buf) { 989 /* Now perform the request */
1241 free(body_buf); 990 gaim_xfer_request(conn->xfer);
1242 body_buf = NULL; 991 }
1243 } 992 }
1244 return proxy_info; 993
1245 } 994 /*******************************************************************/
995 /* End code for establishing a peer connection */
996 /*******************************************************************/