Mercurial > pidgin.yaz
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 /*******************************************************************/ |