source: libfaim/ft.c @ cf02dd6

barnowl_perlaimdebianowlrelease-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since cf02dd6 was cf02dd6, checked in by James M. Kretchmar <kretch@mit.edu>, 17 years ago
*** empty log message ***
  • Property mode set to 100644
File size: 27.4 KB
RevLine 
[5e53c4a]1/*
[e374dee]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.
[5e53c4a]40 */
41
42#define FAIM_INTERNAL
43#ifdef HAVE_CONFIG_H
[e374dee]44#include  <config.h>
[5e53c4a]45#endif
[e374dee]46
[5e53c4a]47#include <aim.h>
48
49#ifndef _WIN32
[e374dee]50#include <stdio.h>
[5e53c4a]51#include <netdb.h>
52#include <sys/socket.h>
53#include <netinet/in.h>
[e374dee]54#include <sys/utsname.h> /* for aim_odc_initiate */
[5e53c4a]55#include <arpa/inet.h> /* for inet_ntoa */
[862371b]56#define G_DIR_SEPARATOR '/'
57#endif
[5e53c4a]58
[862371b]59#ifdef _WIN32
60#include "win32dep.h"
[5e53c4a]61#endif
62
[e374dee]63struct aim_odc_intdata {
[5e53c4a]64        fu8_t cookie[8];
65        char sn[MAXSNLEN+1];
66        char ip[22];
67};
68
69/**
[e374dee]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.
[5e53c4a]243 *
[e374dee]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.
[5e53c4a]247 */
248faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur)
[e374dee]249{
[5e53c4a]250        int acceptfd = 0;
[e374dee]251        struct sockaddr addr;
252        socklen_t addrlen = sizeof(addr);
[5e53c4a]253        int ret = 0;
254        aim_conn_t *newconn;
[e374dee]255        char ip[20];
256        int port;
[5e53c4a]257
[e374dee]258        if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1)
[5e53c4a]259                return 0; /* not an error */
260
[cf02dd6]261        if ((addr.sa_family != AF_INET) && (addr.sa_family != AF_INET6)) { /* just in case IPv6 really is happening */
[5e53c4a]262                close(acceptfd);
263                aim_conn_close(cur);
264                return -1;
[e374dee]265        }
266
267        strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip));
268        port = ntohs(((struct sockaddr_in *)&addr)->sin_port);
[5e53c4a]269
270        if (!(newconn = aim_cloneconn(sess, cur))) {
271                close(acceptfd);
272                aim_conn_close(cur);
[e374dee]273                return -ENOMEM;
[5e53c4a]274        }
275
276        newconn->type = AIM_CONN_TYPE_RENDEZVOUS;
277        newconn->fd = acceptfd;
278
[e374dee]279        if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
[5e53c4a]280                aim_rxcallback_t userfunc;
[e374dee]281                struct aim_odc_intdata *priv;
[5e53c4a]282
[e374dee]283                priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal);
[5e53c4a]284                cur->internal = NULL;
[e374dee]285                snprintf(priv->ip, sizeof(priv->ip), "%s:%u", ip, port);
[5e53c4a]286
[e374dee]287                if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED)))
[5e53c4a]288                        ret = userfunc(sess, NULL, newconn, cur);
289
290        } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
[862371b]291        } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {
292                aim_rxcallback_t userfunc;
293
[e374dee]294                if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED)))
[862371b]295                        ret = userfunc(sess, NULL, newconn, cur);
[e374dee]296
297        } else {
298                faimdprintf(sess, 1,"Got a connection on a listener that's not rendezvous.  Closing connection.\n");
[5e53c4a]299                aim_conn_close(newconn);
300                ret = -1;
301        }
302
303        return ret;
304}
305
306/**
[e374dee]307 * Send client-to-client typing notification over an established direct connection.
[5e53c4a]308 *
[e374dee]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.
[862371b]314 */
[e374dee]315faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing)
[862371b]316{
[e374dee]317        struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
[862371b]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
[e374dee]326        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, 0)))
[862371b]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
[e374dee]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);
[862371b]357
358        aimbs_put16(hdrbs, 0x0000);
359        aimbs_put16(hdrbs, 0x0000);
360        aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn));
[e374dee]361
[862371b]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;
[e374dee]379}
[862371b]380
381/**
[e374dee]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.
[862371b]384 *
[e374dee]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.
[5e53c4a]392 */
[e374dee]393faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg)
[5e53c4a]394{
395        aim_frame_t *fr;
[862371b]396        aim_bstream_t *hdrbs;
[e374dee]397        struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;
[862371b]398        int hdrlen = 0x44;
399        fu8_t *hdr;
[5e53c4a]400
[e374dee]401        if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg)
402                return -EINVAL;
[5e53c4a]403
[e374dee]404        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0)))
405                return -ENOMEM;
[5e53c4a]406
[862371b]407        memcpy(fr->hdr.rend.magic, "ODC2", 4);
408        fr->hdr.rend.hdrlen = hdrlen;
[e374dee]409
[862371b]410        if (!(hdr = calloc(1, hdrlen + len))) {
[5e53c4a]411                aim_frame_destroy(fr);
412                return -ENOMEM;
413        }
414
[862371b]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);
[e374dee]429
430        /* flags - used for typing notification and to mark if this is an away message */
431        aimbs_put16(hdrbs, 0x0000 | isawaymsg);
[862371b]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);
[e374dee]448
[5e53c4a]449        /* end of hdr2 */
[e374dee]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);
[5e53c4a]460#endif
[862371b]461        aimbs_putraw(hdrbs, msg, len);
[5e53c4a]462
[e374dee]463        aim_tx_enqueue(sess, fr);
[5e53c4a]464
465        return 0;
466}
467
468/**
[e374dee]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.
[5e53c4a]473 */
[e374dee]474faim_export const char *aim_odc_getsn(aim_conn_t *conn)
475{
476        struct aim_odc_intdata *intdata;
[5e53c4a]477
[e374dee]478        if (!conn || !conn->internal)
[5e53c4a]479                return NULL;
480
[e374dee]481        if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || 
482                        (conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
[5e53c4a]483                return NULL;
484
[e374dee]485        intdata = (struct aim_odc_intdata *)conn->internal;
[5e53c4a]486
[e374dee]487        return intdata->sn;
488}
[5e53c4a]489
[e374dee]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;
[5e53c4a]502
[e374dee]503        if (!sess || !sn || !strlen(sn))
[5e53c4a]504                return NULL;
505
[e374dee]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        }
[5e53c4a]513
[e374dee]514        return NULL;
[5e53c4a]515}
516
517/**
[e374dee]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.
[5e53c4a]524 *
[e374dee]525 * @param sess The session
526 * @param sn The screen name to connect to.
527 * @return The new connection.
[5e53c4a]528 */
[e374dee]529faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn)
530{
[5e53c4a]531        aim_conn_t *newconn;
532        aim_msgcookie_t *cookie;
[e374dee]533        struct aim_odc_intdata *priv;
[5e53c4a]534        int listenfd;
535        fu16_t port = 4443;
536        fu8_t localip[4];
537        fu8_t ck[8];
538
[e374dee]539        if (aim_util_getlocalip(localip) == -1)
[5e53c4a]540                return NULL;
541
542        if ((listenfd = listenestablish(port)) == -1)
543                return NULL;
544
[e374dee]545        aim_im_sendch2_odcrequest(sess, ck, sn, localip, port);
[5e53c4a]546
547        cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t));
548        memcpy(cookie->cookie, ck, 8);
[e374dee]549        cookie->type = AIM_COOKIETYPE_OFTIM;
[5e53c4a]550
551        /* this one is for the cookie */
[e374dee]552        priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
[5e53c4a]553
[e374dee]554        memcpy(priv->cookie, ck, 8);
555        strncpy(priv->sn, sn, sizeof(priv->sn));
556        cookie->data = priv;
[5e53c4a]557        aim_cachecookie(sess, cookie);
558
[e374dee]559        /* XXX - switch to aim_cloneconn()? */
560        if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) {
[5e53c4a]561                close(listenfd);
562                return NULL;
563        }
564
565        /* this one is for the conn */
[e374dee]566        priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata));
[5e53c4a]567
[e374dee]568        memcpy(priv->cookie, ck, 8);
569        strncpy(priv->sn, sn, sizeof(priv->sn));
[5e53c4a]570
571        newconn->fd = listenfd;
[e374dee]572        newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
573        newconn->internal = priv;
[5e53c4a]574        newconn->lastactivity = time(NULL);
575
576        return newconn;
577}
578
579/**
[e374dee]580 * Connect directly to the given buddy for directim.
[5e53c4a]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 *
[e374dee]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.
[5e53c4a]591 */
[e374dee]592faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie)
593{
[5e53c4a]594        aim_conn_t *newconn;
[e374dee]595        struct aim_odc_intdata *intdata;
[5e53c4a]596
597        if (!sess || !sn)
598                return NULL;
599
[e374dee]600        if (!(intdata = calloc(1, sizeof(struct aim_odc_intdata))))
[5e53c4a]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
[e374dee]607        /* XXX - verify that non-blocking connects actually work */
[5e53c4a]608        if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr))) {
609                free(intdata);
610                return NULL;
611        }
612
613        newconn->internal = intdata;
[e374dee]614        newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
[5e53c4a]615
616        return newconn;
617}
618
619/**
[e374dee]620 * Sometimes you just don't know with these kinds of people.
[5e53c4a]621 *
[e374dee]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.
[5e53c4a]627 */
[e374dee]628static int handlehdr_odc(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *frr, aim_bstream_t *bs)
[5e53c4a]629{
630        aim_frame_t fr;
[e374dee]631        int ret = 0;
[5e53c4a]632        aim_rxcallback_t userfunc;
633        fu32_t payloadlength;
[862371b]634        fu16_t flags, encoding;
[5e53c4a]635        char *snptr = NULL;
636
637        fr.conn = conn;
638
[e374dee]639        /* AAA - ugly */
[862371b]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);
[e374dee]650        /* XXX - create an aimbs_getnullstr function? */
651        snptr = aimbs_getstr(bs, 32); /* Next 32 bytes contain the sn, padded with null chars */
[5e53c4a]652
[e374dee]653        faimdprintf(sess, 2, "faim: OFT frame: handlehdr_odc: %04x / %04x / %s\n", payloadlength, flags, snptr);
[862371b]654
[e374dee]655        if (flags & 0x0008) {
[5e53c4a]656                if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
[e374dee]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);
[862371b]664        }
665
[e374dee]666        if (payloadlength) {
667                char *msg;
668                int recvd = 0;
669                int i, isawaymsg;
[862371b]670
[e374dee]671                isawaymsg = flags & 0x0001;
[862371b]672
[e374dee]673                if (!(msg = calloc(1, payloadlength+1))) {
674                        free(snptr);
675                        return -ENOMEM;
676                }
[862371b]677
[e374dee]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);
[862371b]695
[e374dee]696                free(msg);
697        }
[862371b]698
[e374dee]699        free(snptr);
[862371b]700
701        return ret;
702}
703
[e374dee]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)
[5e53c4a]705{
[e374dee]706        struct aim_oft_info *new;
[5e53c4a]707
[e374dee]708        if (!sess)
709                return NULL;
[5e53c4a]710
[e374dee]711        if (!(new = (struct aim_oft_info *)calloc(1, sizeof(struct aim_oft_info))))
712                return NULL;
[5e53c4a]713
[e374dee]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;
[5e53c4a]741}
742
[e374dee]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.
[862371b]750 */
[e374dee]751faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info)
[862371b]752{
[e374dee]753        aim_session_t *sess;
[862371b]754
[e374dee]755        if (!oft_info || !(sess = oft_info->sess))
756                return -EINVAL;
[862371b]757
[e374dee]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;
[862371b]765        }
766
[e374dee]767        free(oft_info->sn);
768        free(oft_info->proxyip);
769        free(oft_info->clientip);
770        free(oft_info->verifiedip);
771        free(oft_info);
[862371b]772
773        return 0;
774}
775
[e374dee]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)
[5e53c4a]790{
[e374dee]791        int listenfd;
[5e53c4a]792
[e374dee]793        if (!oft_info)
794                return -EINVAL;
[5e53c4a]795
[e374dee]796        if ((listenfd = listenestablish(oft_info->port)) == -1)
797                return 1;
[5e53c4a]798
[e374dee]799        if (!(oft_info->conn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) {
800                close(listenfd);
801                return -ENOMEM;
[5e53c4a]802        }
803
[e374dee]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;
[5e53c4a]809}
810
811/**
[e374dee]812 * Extract an &aim_fileheader_t from the given buffer.
[5e53c4a]813 *
[e374dee]814 * @param bs The should be from an incoming rendezvous packet.
815 * @return A pointer to new struct on success, or NULL on error.
[5e53c4a]816 */
[e374dee]817static struct aim_fileheader_t *aim_oft_getheader(aim_bstream_t *bs)
[5e53c4a]818{
[862371b]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);
[e374dee]850        aimbs_getrawbuf(bs, fh->name, 64); /* XXX - filenames longer than 64B */
[862371b]851
852        return fh;
[5e53c4a]853} 
854
855/**
[e374dee]856 * Fills a buffer with network-order fh data
[5e53c4a]857 *
[e374dee]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.
[5e53c4a]861 */
[862371b]862static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh)
[5e53c4a]863{ 
[862371b]864        fu8_t *hdr;
865
866        if (!bs || !fh)
[e374dee]867                return -EINVAL;
[862371b]868
[e374dee]869        if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8)))
870                return -ENOMEM;
[862371b]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);
[e374dee]898        aimbs_putraw(bs, fh->name, 64); /* XXX - filenames longer than 64B */
[862371b]899
900        return 0;
[5e53c4a]901}
902
903/**
[e374dee]904 * Create an OFT packet based on the given information, and send it on its merry way.
[5e53c4a]905 *
[e374dee]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.
[862371b]911 */
[e374dee]912faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info)
[862371b]913{
[e374dee]914        aim_frame_t *fr;
[862371b]915
[e374dee]916        if (!sess || !oft_info || !oft_info->conn || (oft_info->conn->type != AIM_CONN_TYPE_RENDEZVOUS))
917                return -EINVAL;
[862371b]918
[e374dee]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.
[862371b]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 */
[e374dee]930        /* I think "nlanguage" might be the same thing as "subenc" in im.c */
[862371b]931        fh->nencode = 0x0000;
932        fh->nlanguage = 0x0000;
[e374dee]933#endif
[862371b]934
[e374dee]935        aim_oft_dirconvert_tostupid(oft_info->fh.name);
[862371b]936
[e374dee]937        if (!(fr = aim_tx_new(sess, oft_info->conn, AIM_FRAMETYPE_OFT, type, 0)))
938                return -ENOMEM;
[862371b]939
[e374dee]940        if (aim_oft_buildheader(&fr->data, &oft_info->fh) == -1) {
941                aim_frame_destroy(fr);
942                return -ENOMEM;
[862371b]943        }
944
[e374dee]945        memcpy(fr->hdr.rend.magic, "OFT2", 4);
946        fr->hdr.rend.hdrlen = aim_bstream_curpos(&fr->data);
[862371b]947
[e374dee]948        aim_tx_enqueue(sess, fr);
[862371b]949
950        return 0;
[5e53c4a]951}
952
953/**
[e374dee]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.
[5e53c4a]957 *
[e374dee]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.
[5e53c4a]962 */
[e374dee]963faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr)
[5e53c4a]964{
[e374dee]965        aim_conn_t *conn = fr->conn;
966        int ret = 1;
[862371b]967
[e374dee]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);
[862371b]973
[e374dee]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 */
[862371b]978
[e374dee]979                if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type)))
980                        ret = userfunc(sess, fr, conn, header->bcookie, header);
[862371b]981
[e374dee]982                free(header);
[862371b]983        }
984
[e374dee]985        if (ret == -1)
986                aim_conn_close(conn);
[5e53c4a]987
[e374dee]988        return ret;
[862371b]989}
Note: See TracBrowser for help on using the repository browser.