source: libfaim/ft.c @ a0a5179

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