source: libfaim/ft.c @ 44f585c

release-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 44f585c was fa8f439, checked in by Anders Kaseorg <andersk@mit.edu>, 16 years ago
Woah! I know strict aliasing! Signed-off-by: Anders Kaseorg <andersk@mit.edu>
  • Property mode set to 100644
File size: 27.4 KB
Line 
1/*
2 * Oscar File transfer (OFT) and Oscar Direct Connect (ODC).
3 * (ODC is also referred to as DirectIM and IM Image.)
4 *
5 * There are a few static helper functions at the top, then
6 * ODC stuff, then ft stuff.
7 *
8 * I feel like this is a good place to explain OFT, so I'm going to
9 * do just that.  Each OFT packet has a header type.  I guess this
10 * is pretty similar to the subtype of a SNAC packet.  The type
11 * basically tells the other client the meaning of the OFT packet. 
12 * There are two distinct types of file transfer, which I usually
13 * call "sendfile" and "getfile."  Sendfile is when you send a file
14 * to another AIM user.  Getfile is when you share a group of files,
15 * and other users request that you send them the files.
16 *
17 * A typical sendfile file transfer goes like this:
18 *   1) Sender sends a channel 2 ICBM telling the other user that
19 *      we want to send them a file.  At the same time, we open a
20 *      listener socket (this should be done before sending the
21 *      ICBM) on some port, and wait for them to connect to us. 
22 *      The ICBM we sent should contain our IP address and the port
23 *      number that we're listening on.
24 *   2) The receiver connects to the sender on the given IP address
25 *      and port.  After the connection is established, the receiver
26 *      sends an ICBM signifying that we are ready and waiting.
27 *   3) The sender sends an OFT PROMPT message over the OFT
28 *      connection.
29 *   4) The receiver of the file sends back an exact copy of this
30 *      OFT packet, except the cookie is filled in with the cookie
31 *      from the ICBM.  I think this might be an attempt to verify
32 *      that the user that is connected is actually the guy that
33 *      we sent the ICBM to.  Oh, I've been calling this the ACK.
34 *   5) The sender starts sending raw data across the connection
35 *      until the entire file has been sent.
36 *   6) The receiver knows the file is finished because the sender
37 *      sent the file size in an earlier OFT packet.  So then the
38 *      receiver sends the DONE thingy (after filling in the
39 *      "received" checksum and size) and closes the connection.
40 */
41
42#define FAIM_INTERNAL
43#ifdef HAVE_CONFIG_H
44#include  <config.h>
45#endif
46
47#include <aim.h>
48
49#ifndef _WIN32
50#include <stdio.h>
51#include <netdb.h>
52#include <sys/socket.h>
53#include <netinet/in.h>
54#include <sys/utsname.h> /* for aim_odc_initiate */
55#include <arpa/inet.h> /* for inet_ntoa */
56#define G_DIR_SEPARATOR '/'
57#endif
58
59#ifdef _WIN32
60#include "win32dep.h"
61#endif
62
63#ifndef socklen_t
64#define socklen_t int
65#endif
66
67struct aim_odc_intdata {
68        fu8_t cookie[8];
69        char sn[MAXSNLEN+1];
70        char ip[22];
71};
72
73/**
74 * Convert the directory separator from / (0x2f) to ^A (0x01)
75 *
76 * @param name The filename to convert.
77 */
78static void aim_oft_dirconvert_tostupid(char *name)
79{
80        while (name[0]) {
81                if (name[0] == 0x01)
82                        name[0] = G_DIR_SEPARATOR;
83                name++;
84        }
85}
86
87/**
88 * Convert the directory separator from ^A (0x01) to / (0x2f)
89 *
90 * @param name The filename to convert.
91 */
92static void aim_oft_dirconvert_fromstupid(char *name)
93{
94        while (name[0]) {
95                if (name[0] == G_DIR_SEPARATOR)
96                        name[0] = 0x01;
97                name++;
98        }
99}
100
101/**
102 * Calculate oft checksum of buffer
103 *
104 * Prevcheck should be 0xFFFF0000 when starting a checksum of a file.  The
105 * checksum is kind of a rolling checksum thing, so each time you get bytes
106 * of a file you just call this puppy and it updates the checksum.  You can
107 * calculate the checksum of an entire file by calling this in a while or a
108 * for loop, or something.
109 *
110 * Thanks to Graham Booker for providing this improved checksum routine,
111 * which is simpler and should be more accurate than Josh Myer's original
112 * code. -- wtm
113 *
114 * This algorithim works every time I have tried it.  The other fails
115 * sometimes.  So, AOL who thought this up?  It has got to be the weirdest
116 * checksum I have ever seen.
117 *
118 * @param buffer Buffer of data to checksum.  Man I'd like to buff her...
119 * @param bufsize Size of buffer.
120 * @param prevcheck Previous checksum.
121 */
122faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck)
123{
124        fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck;
125        int i;
126        unsigned short val;
127
128        for (i=0; i<bufferlen; i++) {
129                oldcheck = check;
130                if (i&1)
131                        val = buffer[i];
132                else
133                        val = buffer[i] << 8;
134                check -= val;
135                /*
136                 * The following appears to be necessary.... It happens
137                 * every once in a while and the checksum doesn't fail.
138                 */
139                if (check > oldcheck)
140                        check--;
141        }
142        check = ((check & 0x0000ffff) + (check >> 16));
143        check = ((check & 0x0000ffff) + (check >> 16));
144        return check << 16;
145}
146
147faim_export fu32_t aim_oft_checksum_file(char *filename) {
148        FILE *fd;
149        fu32_t checksum = 0xffff0000;
150
151        if ((fd = fopen(filename, "rb"))) {
152                int bytes;
153                fu8_t buffer[1024];
154
155                while ((bytes = fread(buffer, 1, 1024, fd)))
156                        checksum = aim_oft_checksum_chunk(buffer, bytes, checksum);
157                fclose(fd);
158        }
159
160        return checksum;
161}
162
163/**
164 * Create a listening socket on a given port.
165 *
166 * XXX - Give the client author the responsibility of setting up a
167 *       listener, then we no longer have a libfaim problem with broken
168 *       solaris *innocent smile* -- jbm
169 *
170 * @param portnum The port number to bind to.
171 * @return The file descriptor of the listening socket.
172 */
173static int listenestablish(fu16_t portnum)
174{
175#if HAVE_GETADDRINFO
176        int listenfd;
177        const int on = 1;
178        struct addrinfo hints, *res, *ressave;
179        char serv[5];
180
181        snprintf(serv, sizeof(serv), "%d", portnum);
182        memset(&hints, 0, sizeof(struct addrinfo));
183        hints.ai_flags = AI_PASSIVE;
184        hints.ai_family = AF_UNSPEC;
185        hints.ai_socktype = SOCK_STREAM;
186        if (getaddrinfo(NULL /* any IP */, serv, &hints, &res) != 0) {
187                perror("getaddrinfo");
188                return -1;
189        } 
190        ressave = res;
191        do { 
192                listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
193                if (listenfd < 0)
194                        continue;
195                setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
196                if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
197                        break; /* success */
198                close(listenfd);
199        } while ( (res = res->ai_next) );
200
201        if (!res)
202                return -1;
203
204        freeaddrinfo(ressave);
205#else
206        int listenfd;
207        const int on = 1;
208        struct sockaddr_in sockin;
209
210        if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
211                perror("socket");
212                return -1;
213        }
214
215        if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) {
216                perror("setsockopt");
217                close(listenfd);
218                return -1;
219        }
220
221        memset(&sockin, 0, sizeof(struct sockaddr_in));
222        sockin.sin_family = AF_INET;
223        sockin.sin_port = htons(portnum);
224
225        if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
226                perror("bind");
227                close(listenfd);
228                return -1;
229        }
230#endif
231
232        if (listen(listenfd, 4) != 0) {
233                perror("listen");
234                close(listenfd);
235                return -1;
236        }
237        fcntl(listenfd, F_SETFL, O_NONBLOCK);
238
239        return listenfd;
240}
241
242/**
243 * After establishing a listening socket, this is called to accept a connection.  It
244 * clones the conn used by the listener, and passes both of these to a signal handler.
245 * The signal handler should close the listener conn and keep track of the new conn,
246 * since this is what is used for file transfers and what not.
247 *
248 * @param sess The session.
249 * @param cur The conn the incoming connection is on.
250 * @return Return 0 if no errors, otherwise return the error number.
251 */
252faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur)
253{
254        int acceptfd = 0;
255        union { struct sockaddr sa; struct sockaddr_in sin; } addr;
256        socklen_t addrlen = sizeof(addr);
257        int ret = 0;
258        aim_conn_t *newconn;
259        char ip[20];
260        int port;
261
262        if ((acceptfd = accept(cur->fd, &addr.sa, &addrlen)) == -1)
263                return 0; /* not an error */
264
265        if (addr.sa.sa_family != AF_INET) { /* This code needs way more help to get IPv6 right. */
266                close(acceptfd);
267                aim_conn_close(cur);
268                return -1;
269        }
270
271        strncpy(ip, inet_ntoa(addr.sin.sin_addr), sizeof(ip));
272        port = ntohs(addr.sin.sin_port);
273
274        if (!(newconn = aim_cloneconn(sess, cur))) {
275                close(acceptfd);
276                aim_conn_close(cur);
277                return -ENOMEM;
278        }
279
280        newconn->type = AIM_CONN_TYPE_RENDEZVOUS;
281        newconn->fd = acceptfd;
282
283        if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
284                aim_rxcallback_t userfunc;
285                struct aim_odc_intdata *priv;
286
287                priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal);
288                cur->internal = NULL;
289                snprintf(priv->ip, sizeof(priv->ip), "%s:%u", ip, port);
290
291                if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED)))
292                        ret = userfunc(sess, NULL, newconn, cur);
293
294        } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
295        } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
296                aim_rxcallback_t userfunc;
297
298                if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED)))
299                        ret = userfunc(sess, NULL, newconn, cur);
300
301        } else {
302                faimdprintf(sess, 1,"Got a connection on a listener that's not rendezvous.  Closing connection.\n");
303                aim_conn_close(newconn);
304                ret = -1;
305        }
306
307        return ret;
308}
309
310/**
311 * Send client-to-client typing notification over an established direct connection.
312 *
313 * @param sess The session.
314 * @param conn The already-connected ODC connection.
315 * @param typing If 0x0002, sends a "typing" message, 0x0001 sends "typed," and
316 *        0x0000 sends "stopped."
317 * @return Return 0 if no errors, otherwise return the error number.
318 */
319faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing)
320{
321        struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
322        aim_frame_t *fr;
323        aim_bstream_t *hdrbs;
324        fu8_t *hdr;
325        int hdrlen = 0x44;
326
327        if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS))
328                return -EINVAL;
329
330        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, 0)))
331                return -ENOMEM;
332        memcpy(fr->hdr.rend.magic, "ODC2", 4);
333        fr->hdr.rend.hdrlen = hdrlen;
334
335        if (!(hdr = calloc(1, hdrlen))) {
336                aim_frame_destroy(fr);
337                return -ENOMEM;
338        }
339
340        hdrbs = &(fr->data);
341        aim_bstream_init(hdrbs, hdr, hdrlen);
342
343        aimbs_put16(hdrbs, 0x0006);
344        aimbs_put16(hdrbs, 0x0000);
345        aimbs_putraw(hdrbs, intdata->cookie, 8);
346        aimbs_put16(hdrbs, 0x0000);
347        aimbs_put16(hdrbs, 0x0000);
348        aimbs_put16(hdrbs, 0x0000);
349        aimbs_put16(hdrbs, 0x0000);
350        aimbs_put32(hdrbs, 0x00000000);
351        aimbs_put16(hdrbs, 0x0000);
352        aimbs_put16(hdrbs, 0x0000);
353        aimbs_put16(hdrbs, 0x0000);
354
355        if (typing == 0x0002)
356                aimbs_put16(hdrbs, 0x0002 | 0x0008);
357        else if (typing == 0x0001)
358                aimbs_put16(hdrbs, 0x0002 | 0x0004);
359        else
360                aimbs_put16(hdrbs, 0x0002);
361
362        aimbs_put16(hdrbs, 0x0000);
363        aimbs_put16(hdrbs, 0x0000);
364        aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn));
365
366        aim_bstream_setpos(hdrbs, 52); /* bleeehh */
367
368        aimbs_put8(hdrbs, 0x00);
369        aimbs_put16(hdrbs, 0x0000);
370        aimbs_put16(hdrbs, 0x0000);
371        aimbs_put16(hdrbs, 0x0000);
372        aimbs_put16(hdrbs, 0x0000);
373        aimbs_put16(hdrbs, 0x0000);
374        aimbs_put16(hdrbs, 0x0000);
375        aimbs_put16(hdrbs, 0x0000);
376        aimbs_put8(hdrbs, 0x00);
377
378        /* end of hdr */
379
380        aim_tx_enqueue(sess, fr);
381
382        return 0;
383}
384
385/**
386 * Send client-to-client IM over an established direct connection.
387 * Call this just like you would aim_send_im, to send a directim.
388 *
389 * @param sess The session.
390 * @param conn The already-connected ODC connection.
391 * @param msg Null-terminated string to send.
392 * @param len The length of the message to send, including binary data.
393 * @param encoding 0 for ascii, 2 for Unicode, 3 for ISO 8859-1.
394 * @param isawaymsg 0 if this is not an auto-response, 1 if it is.
395 * @return Return 0 if no errors, otherwise return the error number.
396 */
397faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg)
398{
399        aim_frame_t *fr;
400        aim_bstream_t *hdrbs;
401        struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
402        int hdrlen = 0x44;
403        fu8_t *hdr;
404
405        if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg)
406                return -EINVAL;
407
408        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0)))
409                return -ENOMEM;
410
411        memcpy(fr->hdr.rend.magic, "ODC2", 4);
412        fr->hdr.rend.hdrlen = hdrlen;
413
414        if (!(hdr = calloc(1, hdrlen + len))) {
415                aim_frame_destroy(fr);
416                return -ENOMEM;
417        }
418
419        hdrbs = &(fr->data);
420        aim_bstream_init(hdrbs, hdr, hdrlen + len);
421
422        aimbs_put16(hdrbs, 0x0006);
423        aimbs_put16(hdrbs, 0x0000);
424        aimbs_putraw(hdrbs, intdata->cookie, 8);
425        aimbs_put16(hdrbs, 0x0000);
426        aimbs_put16(hdrbs, 0x0000);
427        aimbs_put16(hdrbs, 0x0000);
428        aimbs_put16(hdrbs, 0x0000);
429        aimbs_put32(hdrbs, len);
430        aimbs_put16(hdrbs, encoding);
431        aimbs_put16(hdrbs, 0x0000);
432        aimbs_put16(hdrbs, 0x0000);
433
434        /* flags - used for typing notification and to mark if this is an away message */
435        aimbs_put16(hdrbs, 0x0000 | isawaymsg);
436
437        aimbs_put16(hdrbs, 0x0000);
438        aimbs_put16(hdrbs, 0x0000);
439        aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn));
440
441        aim_bstream_setpos(hdrbs, 52); /* bleeehh */
442
443        aimbs_put8(hdrbs, 0x00);
444        aimbs_put16(hdrbs, 0x0000);
445        aimbs_put16(hdrbs, 0x0000);
446        aimbs_put16(hdrbs, 0x0000);
447        aimbs_put16(hdrbs, 0x0000);
448        aimbs_put16(hdrbs, 0x0000);
449        aimbs_put16(hdrbs, 0x0000);
450        aimbs_put16(hdrbs, 0x0000);
451        aimbs_put8(hdrbs, 0x00);
452
453        /* end of hdr2 */
454
455#if 0 /* XXX - this is how you send buddy icon info... */       
456        aimbs_put16(hdrbs, 0x0008);
457        aimbs_put16(hdrbs, 0x000c);
458        aimbs_put16(hdrbs, 0x0000);
459        aimbs_put16(hdrbs, 0x1466);
460        aimbs_put16(hdrbs, 0x0001);
461        aimbs_put16(hdrbs, 0x2e0f);
462        aimbs_put16(hdrbs, 0x393e);
463        aimbs_put16(hdrbs, 0xcac8);
464#endif
465        aimbs_putraw(hdrbs, msg, len);
466
467        aim_tx_enqueue(sess, fr);
468
469        return 0;
470}
471
472/**
473 * Get the screen name of the peer of a direct connection.
474 *
475 * @param conn The ODC connection.
476 * @return The screen name of the dude, or NULL if there was an anomaly.
477 */
478faim_export const char *aim_odc_getsn(aim_conn_t *conn)
479{
480        struct aim_odc_intdata *intdata;
481
482        if (!conn || !conn->internal)
483                return NULL;
484
485        if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || 
486                        (conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
487                return NULL;
488
489        intdata = (struct aim_odc_intdata *)conn->internal;
490
491        return intdata->sn;
492}
493
494/**
495 * Find the conn of a direct connection with the given buddy.
496 *
497 * @param sess The session.
498 * @param sn The screen name of the buddy whose direct connection you want to find.
499 * @return The conn for the direct connection with the given buddy, or NULL if no
500 *         connection was found.
501 */
502faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn)
503{
504        aim_conn_t *cur;
505        struct aim_odc_intdata *intdata;
506
507        if (!sess || !sn || !strlen(sn))
508                return NULL;
509
510        for (cur = sess->connlist; cur; cur = cur->next) {
511                if ((cur->type == AIM_CONN_TYPE_RENDEZVOUS) && (cur->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
512                        intdata = cur->internal;
513                        if (!aim_sncmp(intdata->sn, sn))
514                                return cur;
515                }
516        }
517
518        return NULL;
519}
520
521/**
522 * For those times when we want to open up the direct connection channel ourselves.
523 *
524 * You'll want to set up some kind of watcher on this socket. 
525 * When the state changes, call aim_handlerendconnection with
526 * the connection returned by this.  aim_handlerendconnection
527 * will accept the pending connection and stop listening.
528 *
529 * @param sess The session
530 * @param sn The screen name to connect to.
531 * @return The new connection.
532 */
533faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn)
534{
535        aim_conn_t *newconn;
536        aim_msgcookie_t *cookie;
537        struct aim_odc_intdata *priv;
538        int listenfd;
539        fu16_t port = 4443;
540        fu8_t localip[4];
541        fu8_t ck[8];
542
543        if (aim_util_getlocalip(localip) == -1)
544                return NULL;
545
546        if ((listenfd = listenestablish(port)) == -1)
547                return NULL;
548
549        aim_im_sendch2_odcrequest(sess, ck, sn, localip, port);
550
551        cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t));
552        memcpy(cookie->cookie, ck, 8);
553        cookie->type = AIM_COOKIETYPE_OFTIM;
554
555        /* this one is for the cookie */
556        priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
557
558        memcpy(priv->cookie, ck, 8);
559        strncpy(priv->sn, sn, sizeof(priv->sn));
560        cookie->data = priv;
561        aim_cachecookie(sess, cookie);
562
563        /* XXX - switch to aim_cloneconn()? */
564        if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) {
565                close(listenfd);
566                return NULL;
567        }
568
569        /* this one is for the conn */
570        priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
571
572        memcpy(priv->cookie, ck, 8);
573        strncpy(priv->sn, sn, sizeof(priv->sn));
574
575        newconn->fd = listenfd;
576        newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
577        newconn->internal = priv;
578        newconn->lastactivity = time(NULL);
579
580        return newconn;
581}
582
583/**
584 * Connect directly to the given buddy for directim.
585 *
586 * This is a wrapper for aim_newconn.
587 *
588 * If addr is NULL, the socket is not created, but the connection is
589 * allocated and setup to connect.
590 *
591 * @param sess The Godly session.
592 * @param sn The screen name we're connecting to.  I hope it's a girl...
593 * @param addr Address to connect to.
594 * @return The new connection.
595 */
596faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie)
597{
598        aim_conn_t *newconn;
599        struct aim_odc_intdata *intdata;
600
601        if (!sess || !sn)
602                return NULL;
603
604        if (!(intdata = calloc(1, sizeof(struct aim_odc_intdata))))
605                return NULL;
606        memcpy(intdata->cookie, cookie, 8);
607        strncpy(intdata->sn, sn, sizeof(intdata->sn));
608        if (addr)
609                strncpy(intdata->ip, addr, sizeof(intdata->ip));
610
611        /* XXX - verify that non-blocking connects actually work */
612        if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr))) {
613                free(intdata);
614                return NULL;
615        }
616
617        newconn->internal = intdata;
618        newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
619
620        return newconn;
621}
622
623/**
624 * Sometimes you just don't know with these kinds of people.
625 *
626 * @param sess The session.
627 * @param conn The ODC connection of the incoming data.
628 * @param frr The frame allocated for the incoming data.
629 * @param bs It stands for "bologna sandwich."
630 * @return Return 0 if no errors, otherwise return the error number.
631 */
632static int handlehdr_odc(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *frr, aim_bstream_t *bs)
633{
634        aim_frame_t fr;
635        int ret = 0;
636        aim_rxcallback_t userfunc;
637        fu32_t payloadlength;
638        fu16_t flags, encoding;
639        char *snptr = NULL;
640
641        fr.conn = conn;
642
643        /* AAA - ugly */
644        aim_bstream_setpos(bs, 20);
645        payloadlength = aimbs_get32(bs);
646
647        aim_bstream_setpos(bs, 24);
648        encoding = aimbs_get16(bs);
649
650        aim_bstream_setpos(bs, 30);
651        flags = aimbs_get16(bs);
652
653        aim_bstream_setpos(bs, 36);
654        /* XXX - create an aimbs_getnullstr function? */
655        snptr = aimbs_getstr(bs, 32); /* Next 32 bytes contain the sn, padded with null chars */
656
657        faimdprintf(sess, 2, "faim: OFT frame: handlehdr_odc: %04x / %04x / %s\n", payloadlength, flags, snptr);
658
659        if (flags & 0x0008) {
660                if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
661                        ret = userfunc(sess, &fr, snptr, 2);
662        } else if (flags & 0x0004) {
663                if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
664                        ret = userfunc(sess, &fr, snptr, 1);
665        } else {
666                if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
667                        ret = userfunc(sess, &fr, snptr, 0);
668        }
669
670        if (payloadlength) {
671                char *msg;
672                int recvd = 0;
673                int i, isawaymsg;
674
675                isawaymsg = flags & 0x0001;
676
677                if (!(msg = calloc(1, payloadlength+1))) {
678                        free(snptr);
679                        return -ENOMEM;
680                }
681
682                while (payloadlength - recvd) {
683                        if (payloadlength - recvd >= 1024)
684                                i = aim_recv(conn->fd, &msg[recvd], 1024);
685                        else 
686                                i = aim_recv(conn->fd, &msg[recvd], payloadlength - recvd);
687                        if (i <= 0) {
688                                free(msg);
689                                free(snptr);
690                                return -1;
691                        }
692                        recvd = recvd + i;
693                        if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
694                                ret = userfunc(sess, &fr, snptr, (double)recvd / payloadlength);
695                }
696               
697                if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)))
698                        ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding, isawaymsg);
699
700                free(msg);
701        }
702
703        free(snptr);
704
705        return ret;
706}
707
708faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn, const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename)
709{
710        struct aim_oft_info *new;
711
712        if (!sess)
713                return NULL;
714
715        if (!(new = (struct aim_oft_info *)calloc(1, sizeof(struct aim_oft_info))))
716                return NULL;
717
718        new->sess = sess;
719        if (cookie)
720                memcpy(new->cookie, cookie, 8);
721        if (ip)
722                new->clientip = strdup(ip);
723        if (sn)
724                new->sn = strdup(sn);
725        new->port = port;
726        new->fh.totfiles = 1;
727        new->fh.filesleft = 1;
728        new->fh.totparts = 1;
729        new->fh.partsleft = 1;
730        new->fh.totsize = size;
731        new->fh.size = size;
732        new->fh.modtime = modtime;
733        new->fh.checksum = 0xffff0000;
734        new->fh.rfrcsum = 0xffff0000;
735        new->fh.rfcsum = 0xffff0000;
736        new->fh.recvcsum = 0xffff0000;
737        strncpy(new->fh.idstring, "OFT_Windows ICBMFT V1.1 32", 31);
738        if (filename)
739                strncpy(new->fh.name, filename, 63);
740
741        new->next = sess->oft_info;
742        sess->oft_info = new;
743
744        return new;
745}
746
747/**
748 * Remove the given oft_info struct from the oft_info linked list, and
749 * then free its memory.
750 *
751 * @param sess The session.
752 * @param oft_info The aim_oft_info struct that we're destroying.
753 * @return Return 0 if no errors, otherwise return the error number.
754 */
755faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info)
756{
757        aim_session_t *sess;
758
759        if (!oft_info || !(sess = oft_info->sess))
760                return -EINVAL;
761
762        if (sess->oft_info && (sess->oft_info == oft_info)) {
763                sess->oft_info = sess->oft_info->next;
764        } else {
765                struct aim_oft_info *cur;
766                for (cur=sess->oft_info; (cur->next && (cur->next!=oft_info)); cur=cur->next);
767                if (cur->next)
768                        cur->next = cur->next->next;
769        }
770
771        free(oft_info->sn);
772        free(oft_info->proxyip);
773        free(oft_info->clientip);
774        free(oft_info->verifiedip);
775        free(oft_info);
776
777        return 0;
778}
779
780/**
781 * Creates a listener socket so the other dude can connect to us.
782 *
783 * You'll want to set up some kind of watcher on this socket. 
784 * When the state changes, call aim_handlerendconnection with
785 * the connection returned by this.  aim_handlerendconnection
786 * will accept the pending connection and stop listening.
787 *
788 * @param sess The session.
789 * @param oft_info File transfer information associated with this
790 *        connection.
791 * @return Return 0 if no errors, otherwise return the error number.
792 */
793faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info)
794{
795        int listenfd;
796
797        if (!oft_info)
798                return -EINVAL;
799
800        if ((listenfd = listenestablish(oft_info->port)) == -1)
801                return 1;
802
803        if (!(oft_info->conn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) {
804                close(listenfd);
805                return -ENOMEM;
806        }
807
808        oft_info->conn->fd = listenfd;
809        oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
810        oft_info->conn->lastactivity = time(NULL);
811
812        return 0;
813}
814
815/**
816 * Extract an &aim_fileheader_t from the given buffer.
817 *
818 * @param bs The should be from an incoming rendezvous packet.
819 * @return A pointer to new struct on success, or NULL on error.
820 */
821static struct aim_fileheader_t *aim_oft_getheader(aim_bstream_t *bs)
822{
823        struct aim_fileheader_t *fh;
824
825        if (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
826                return NULL;
827
828        /* The bstream should be positioned after the hdrtype. */
829        aimbs_getrawbuf(bs, fh->bcookie, 8);
830        fh->encrypt = aimbs_get16(bs);
831        fh->compress = aimbs_get16(bs);
832        fh->totfiles = aimbs_get16(bs);
833        fh->filesleft = aimbs_get16(bs);
834        fh->totparts = aimbs_get16(bs);
835        fh->partsleft = aimbs_get16(bs);
836        fh->totsize = aimbs_get32(bs);
837        fh->size = aimbs_get32(bs);
838        fh->modtime = aimbs_get32(bs);
839        fh->checksum = aimbs_get32(bs);
840        fh->rfrcsum = aimbs_get32(bs);
841        fh->rfsize = aimbs_get32(bs);
842        fh->cretime = aimbs_get32(bs);
843        fh->rfcsum = aimbs_get32(bs);
844        fh->nrecvd = aimbs_get32(bs);
845        fh->recvcsum = aimbs_get32(bs);
846        aimbs_getrawbuf(bs, fh->idstring, 32);
847        fh->flags = aimbs_get8(bs);
848        fh->lnameoffset = aimbs_get8(bs);
849        fh->lsizeoffset = aimbs_get8(bs);
850        aimbs_getrawbuf(bs, fh->dummy, 69);
851        aimbs_getrawbuf(bs, fh->macfileinfo, 16);
852        fh->nencode = aimbs_get16(bs);
853        fh->nlanguage = aimbs_get16(bs);
854        aimbs_getrawbuf(bs, fh->name, 64); /* XXX - filenames longer than 64B */
855
856        return fh;
857} 
858
859/**
860 * Fills a buffer with network-order fh data
861 *
862 * @param bs A bstream to fill -- automatically initialized
863 * @param fh A struct aim_fileheader_t to get data from.
864 * @return Return non-zero on error.
865 */
866static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh)
867{ 
868        fu8_t *hdr;
869
870        if (!bs || !fh)
871                return -EINVAL;
872
873        if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8)))
874                return -ENOMEM;
875
876        aim_bstream_init(bs, hdr, 0x100 - 8);
877        aimbs_putraw(bs, fh->bcookie, 8);
878        aimbs_put16(bs, fh->encrypt);
879        aimbs_put16(bs, fh->compress);
880        aimbs_put16(bs, fh->totfiles);
881        aimbs_put16(bs, fh->filesleft);
882        aimbs_put16(bs, fh->totparts);
883        aimbs_put16(bs, fh->partsleft);
884        aimbs_put32(bs, fh->totsize);
885        aimbs_put32(bs, fh->size);
886        aimbs_put32(bs, fh->modtime);
887        aimbs_put32(bs, fh->checksum);
888        aimbs_put32(bs, fh->rfrcsum);
889        aimbs_put32(bs, fh->rfsize);
890        aimbs_put32(bs, fh->cretime);
891        aimbs_put32(bs, fh->rfcsum);
892        aimbs_put32(bs, fh->nrecvd);
893        aimbs_put32(bs, fh->recvcsum);
894        aimbs_putraw(bs, fh->idstring, 32);
895        aimbs_put8(bs, fh->flags);
896        aimbs_put8(bs, fh->lnameoffset);
897        aimbs_put8(bs, fh->lsizeoffset);
898        aimbs_putraw(bs, fh->dummy, 69);
899        aimbs_putraw(bs, fh->macfileinfo, 16);
900        aimbs_put16(bs, fh->nencode);
901        aimbs_put16(bs, fh->nlanguage);
902        aimbs_putraw(bs, fh->name, 64); /* XXX - filenames longer than 64B */
903
904        return 0;
905}
906
907/**
908 * Create an OFT packet based on the given information, and send it on its merry way.
909 *
910 * @param sess The session.
911 * @param type The subtype of the OFT packet we're sending.
912 * @param oft_info The aim_oft_info struct with the connection and OFT
913 *        info we're sending.
914 * @return Return 0 if no errors, otherwise return the error number.
915 */
916faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info)
917{
918        aim_frame_t *fr;
919
920        if (!sess || !oft_info || !oft_info->conn || (oft_info->conn->type != AIM_CONN_TYPE_RENDEZVOUS))
921                return -EINVAL;
922
923#if 0
924        /*
925         * If you are receiving a file, the cookie should be null, if you are sending a
926         * file, the cookie should be the same as the one used in the ICBM negotiation
927         * SNACs.
928         */
929        fh->lnameoffset = 0x1a;
930        fh->lsizeoffset = 0x10;
931
932        /* apparently 0 is ASCII, 2 is UCS-2 */
933        /* it is likely that 3 is ISO 8859-1 */
934        /* I think "nlanguage" might be the same thing as "subenc" in im.c */
935        fh->nencode = 0x0000;
936        fh->nlanguage = 0x0000;
937#endif
938
939        aim_oft_dirconvert_tostupid(oft_info->fh.name);
940
941        if (!(fr = aim_tx_new(sess, oft_info->conn, AIM_FRAMETYPE_OFT, type, 0)))
942                return -ENOMEM;
943
944        if (aim_oft_buildheader(&fr->data, &oft_info->fh) == -1) {
945                aim_frame_destroy(fr);
946                return -ENOMEM;
947        }
948
949        memcpy(fr->hdr.rend.magic, "OFT2", 4);
950        fr->hdr.rend.hdrlen = aim_bstream_curpos(&fr->data);
951
952        aim_tx_enqueue(sess, fr);
953
954        return 0;
955}
956
957/**
958 * Handle incoming data on a rendezvous connection.  This is analogous to the
959 * consumesnac function in rxhandlers.c, and I really think this should probably
960 * be in rxhandlers.c as well, but I haven't finished cleaning everything up yet.
961 *
962 * @param sess The session.
963 * @param fr The frame allocated for the incoming data.
964 * @return Return 0 if the packet was handled correctly, otherwise return the
965 *         error number.
966 */
967faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr)
968{
969        aim_conn_t *conn = fr->conn;
970        int ret = 1;
971
972        if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
973                if (fr->hdr.rend.type == 0x0001)
974                        ret = handlehdr_odc(sess, conn, fr, &fr->data);
975                else
976                        faimdprintf(sess, 0, "faim: ODC directim frame unknown, type is %04x\n", fr->hdr.rend.type);
977
978        } else {
979                aim_rxcallback_t userfunc;
980                struct aim_fileheader_t *header = aim_oft_getheader(&fr->data);
981                aim_oft_dirconvert_fromstupid(header->name); /* XXX - This should be client-side */
982
983                if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type)))
984                        ret = userfunc(sess, fr, conn, header->bcookie, header);
985
986                free(header);
987        }
988
989        if (ret == -1)
990                aim_conn_close(conn);
991
992        return ret;
993}
Note: See TracBrowser for help on using the repository browser.