]> andersk Git - libfaim.git/blob - aim_ft.c
- Wed Dec 13 00:38:56 UTC 2000
[libfaim.git] / aim_ft.c
1 #include <faim/aim.h>
2
3 #ifndef _WIN32
4 #include <netdb.h>
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <sys/utsname.h> /* for aim_directim_initiate */
8 #include <arpa/inet.h> /* for inet_ntoa */
9 #endif
10
11 /* aim_msgcookies.c is mostly new. just look at the diff and replace yours, easiest. */
12
13 /* 
14    function name       where i had it
15    aim_send_im_direct aim_im.c
16    aim_directim_initiate aim_im.c
17    aim_filetransfer_accept aim_im.c
18    aim_getlisting aim_misc.c (?!) -- prototype function. can be ignored.
19    establish aim_misc.c
20    aim_get_command_rendezvous aim_r
21    oft_getfh aim_rxqueue.c
22 */
23
24 faim_export int aim_handlerendconnect(struct aim_session_t *sess, struct aim_conn_t *cur)
25 {
26   int acceptfd = 0;
27   rxcallback_t userfunc;
28   struct sockaddr cliaddr;
29   socklen_t clilen = sizeof(cliaddr);
30   int ret = 0;
31
32   /*
33    * Listener sockets only have incoming connections. No data.
34    */
35   if( (acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1)
36     return -1;
37
38   if (cliaddr.sa_family != AF_INET) /* just in case IPv6 really is happening */
39     return -1;
40
41   switch(cur->subtype) {
42   case AIM_CONN_SUBTYPE_OFT_DIRECTIM: {
43     struct aim_directim_priv *priv;
44     
45     priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
46
47     snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
48
49     if(!cur->priv)
50       cur->priv = priv; /* what happens if there is one?! -- mid */
51
52     cur->type = AIM_CONN_TYPE_RENDEZVOUS;
53     close(cur->fd); /* should we really do this? seems like the client should decide. maybe clone the connection and keep the listener open. -- mid */
54     cur->fd = acceptfd;
55
56     if ( (userfunc = aim_callhandler(cur, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE)))
57       ret = userfunc(sess, NULL, cur);
58                                    
59     break;
60   }
61   case AIM_CONN_SUBTYPE_OFT_GETFILE: {
62     struct aim_filetransfer_priv *priv;
63
64     priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
65
66     snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
67
68     if(!cur->priv)
69       cur->priv = priv;
70
71     if ( (userfunc = aim_callhandler(cur, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE)))
72       ret = userfunc(sess, NULL, cur);
73     break;
74   } 
75   default: {
76     /* XXX */
77   }
78   }
79   return ret;
80 }
81
82
83 /*
84  * aim_send_im_direct:
85  * sess - session
86  * conn - directim connection
87  * msg  - null-terminated string to send
88  */
89
90 faim_export int aim_send_im_direct(struct aim_session_t *sess, 
91                                    struct aim_conn_t *conn,
92                                    char *msg)
93 {
94   struct command_tx_struct *newpacket , *newpacket2; 
95
96   /* newpacket contains a real header with data, newpacket2 is just a
97      null packet, with a cookie and a lot of 0x00s. newpacket is the
98      "i'm sending", newpacket2 is the "i'm typing".*/
99
100   /* uhm. the client should send those as two seperate things -- mid */
101
102   struct aim_directim_priv *priv = NULL;
103   int i;
104
105   if (!sess || !conn || !(conn->type) || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !conn->priv || !msg) {
106     printf("faim: directim: invalid arguments\n");
107     return -1;
108   };
109
110   if (strlen(msg) >= MAXMSGLEN)
111     return -1;
112
113   priv = (struct aim_directim_priv *)conn->priv;
114
115   /* NULLish Header */
116
117   if (!(newpacket2 = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0001, conn, 0))) {
118     printf("faim: directim: tx_new2 failed\n");
119     return -1;
120   }                                                                           
121
122   newpacket2->lock = 1; /* lock struct */
123
124   memcpy(newpacket2->hdr.oft.magic, "ODC2", 4);
125   newpacket2->hdr.oft.hdr2len = 0x44;
126
127   if (!(newpacket2->hdr.oft.hdr2 = calloc(1,newpacket2->hdr.oft.hdr2len))) {
128     free(newpacket2);
129     return -1;
130   }
131
132   i = 0;
133   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0006);
134   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
135
136   i += aimutil_putstr(newpacket2->hdr.oft.hdr2+i, (char *)priv->cookie, 8);
137
138   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
139   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
140   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
141   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
142
143   i += aimutil_put32(newpacket2->hdr.oft.hdr2+i, 0x00000000);
144
145   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
146   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
147   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
148
149   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x000e);
150
151   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
152   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
153
154   i += aimutil_putstr(newpacket2->hdr.oft.hdr2+i, sess->sn, strlen(sess->sn));
155   
156   i = 52; /* 0x34 */
157   i += aimutil_put8(newpacket2->hdr.oft.hdr2+i, 0x00); /* 53 */
158   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000); /* 55 */
159   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
160   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
161   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);/* 61 */
162   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);
163   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);/* 65 */
164   i += aimutil_put16(newpacket2->hdr.oft.hdr2+i, 0x0000);/* end of hdr2 */
165
166   newpacket2->lock = 0;
167   newpacket2->data = NULL;
168   
169   aim_tx_enqueue(sess, newpacket2);
170
171   /* Header packet */
172
173   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0001, conn, strlen(msg)))) {
174     printf("faim: directim: tx_new failed\n");
175     return -1;
176   }
177
178   newpacket->lock = 1; /* lock struct */
179
180   memcpy(newpacket->hdr.oft.magic, "ODC2", 4);
181   newpacket->hdr.oft.hdr2len = 0x54;
182
183   if (!(newpacket->hdr.oft.hdr2 = calloc(1,newpacket->hdr.oft.hdr2len))) {
184     free(newpacket);
185     return -1;
186   }
187
188   i = 0;
189   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0006);
190   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
191
192   i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, (char *)priv->cookie, 8);
193
194   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
195   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
196   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
197   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
198
199   i += aimutil_put32(newpacket->hdr.oft.hdr2+i, strlen(msg));
200
201   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
202   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
203   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
204   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
205   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
206   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
207
208   i += aimutil_putstr(newpacket->hdr.oft.hdr2+i, sess->sn, strlen(sess->sn));
209   
210   i = 52; /* 0x34 */
211   i += aimutil_put8(newpacket->hdr.oft.hdr2+i, 0x00); /* 53 */
212   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); /* 55 */
213   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
214   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
215   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* 61 */
216   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
217   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* 65 */
218   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* end of hdr2 */
219
220   /* values grabbed from a dump */
221   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008); /* 69 */
222   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c);
223   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);/* 71 */
224   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466);/* 73 */ 
225   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001);/* 73 */ 
226   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f);
227   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e);
228   i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8);
229
230   memcpy(newpacket->data, msg, strlen(msg));
231
232   newpacket->lock = 0;
233
234   aim_tx_enqueue(sess, newpacket);
235
236   return 0;
237 }
238
239 /*
240  * aim_directim_intitiate:
241  * For those times when we want to open up the directim channel ourselves.
242  * sess is your session,
243  * conn is the BOS conn,
244  * priv is a dummy priv value (we'll let it get filled in later) (if
245  * you pass a NULL, we alloc one) 
246  * destsn is the SN to connect to.  
247  */
248
249
250 faim_export struct aim_conn_t *aim_directim_initiate(struct aim_session_t *sess,
251                                                      struct aim_conn_t *conn,
252                                                      struct aim_directim_priv *priv,
253                                                      char *destsn)
254 {
255   struct command_tx_struct *newpacket;
256   struct aim_conn_t *newconn;
257
258   struct aim_msgcookie_t *cookie;
259
260   int curbyte, i, listenfd;
261   short port = 4443;
262
263   struct hostent *hptr;
264   char localhost[129];
265
266   unsigned char cap[16];
267   char d[4]; /* XXX: IPv6. *cough* */
268
269   /*
270    * Open our socket
271    */
272
273   if( (listenfd = aim_listenestablish(port)) == -1)
274     return NULL;
275
276   /*
277    * get our local IP
278    */
279
280   if(gethostname(localhost, 128) < 0)
281     return NULL;
282
283   if( (hptr = gethostbyname(localhost)) == NULL)
284     return NULL;
285
286   memcpy(&d, hptr->h_addr_list[0], 4); /* XXX: this probably isn't quite kosher, but it works */
287
288   aim_putcap(cap, 16, AIM_CAPS_IMIMAGE);
289
290   /*
291    * create the OSCAR packet
292    */
293
294   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(destsn)+4+4+0x32)))
295     return NULL;
296
297   newpacket->lock = 1; /* lock struct */
298
299   curbyte  = 0;
300   curbyte += aim_putsnac(newpacket->data+curbyte, 
301                          0x0004, 0x0006, 0x0000, sess->snac_nextid);
302
303   /* 
304    * Generate a random message cookie 
305    * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible.
306    */
307   for (i=0;i<7;i++)
308     curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) rand() % 20));
309   curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
310
311   /*
312    * grab all the data for cookie caching.
313    */
314   cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t));
315
316   memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
317   cookie->type = AIM_COOKIETYPE_OFTIM;
318   
319   if(!priv)
320     priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
321
322   memcpy(priv->cookie, cookie, 8);
323   memcpy(priv->sn, destsn, sizeof(priv->sn));
324  
325   cookie->data = priv;
326
327   aim_cachecookie(sess, cookie);  /* cache da cookie */
328
329   /*
330    * Channel ID
331    */
332   curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
333
334   /* 
335    * Destination SN (prepended with byte length)
336    */
337   curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
338   curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
339
340   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
341   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
342
343   /* 
344    * enTLV start
345    */
346   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
347   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0032);
348
349   /*
350    * Flag data / ICBM Parameters?
351    */
352   curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
353   curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
354
355   /*
356    * Cookie 
357    */
358   curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
359
360   /*
361    * Capability String
362    */
363   curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
364
365   /*
366    * 000a/0002 : 0001
367    */
368   curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
369   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
370   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
371
372   /*
373    * 0003/0004: IP address
374    */
375
376   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
377   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
378
379   for(i = 0; i < 4; i++)
380     curbyte += aimutil_put8(newpacket->data+curbyte, d[i]); /* already in network byte order */
381
382   /*
383    * 0005/0002: Port
384    */
385
386   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
387   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
388   curbyte += aimutil_put16(newpacket->data+curbyte, port);
389
390   /*
391    * 000f/0000: umm.. dunno. Zigamorph[1]?
392    * [1]: see esr's TNHD.
393    */
394
395   curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
396   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
397
398   printf("curbyte: 0x%x\n",curbyte);
399
400   newpacket->commandlen = curbyte;
401   newpacket->lock = 0;
402
403   aim_tx_enqueue(sess, newpacket);
404
405   /*
406    * allocate and set up our connection
407    */
408
409   newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
410   if (!newconn) { 
411     perror("aim_newconn");
412     aim_conn_kill(sess, &newconn);
413     return NULL;
414   } 
415
416   newconn->fd = listenfd;
417   newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
418   newconn->priv = priv;
419   printf("faim: listening (fd = %d, unconnected)\n", newconn->fd);
420
421   /*
422    * XXX We need some way of closing the listener socket after
423    * n seconds of no connection. -- mid
424    */
425
426 #ifdef USE_SNAC_FOR_IMS
427  {
428     struct aim_snac_t snac;
429
430     snac.id = sess->snac_nextid;
431     snac.family = 0x0004;
432     snac.type = 0x0006;
433     snac.flags = 0x0000;
434
435     snac.data = malloc(strlen(destsn)+1);
436     memcpy(snac.data, destsn, strlen(destsn)+1);
437
438     aim_newsnac(sess, &snac);
439
440     aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */
441   }
442 #endif
443   
444   return (newconn);
445
446
447 /*
448  * struct aim_conn_t *aim_directim_connect(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_directim_priv *priv)
449  * sess is the session to append the conn to,
450  * conn is the BOS connection,
451  * priv is the filled-in priv data structure for the connection
452  * returns conn if connected, NULL on error
453  */
454
455
456 faim_export struct aim_conn_t *aim_directim_connect(struct aim_session_t *sess,
457                                                     struct aim_conn_t *conn,
458                                                     struct aim_directim_priv *priv )
459 {
460   struct aim_conn_t *newconn = NULL;;
461
462   newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, priv->ip);
463   if (!newconn || (newconn->fd == -1)) { 
464     printf("could not connect to %s\n", priv->ip);
465     perror("aim_newconn");
466     aim_conn_kill(sess, &newconn);
467     return NULL;
468   } else {    
469     newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
470     newconn->priv = priv;
471     printf("faim: connected to peer (fd = %d)\n", newconn->fd);
472     return newconn;
473   }
474   return newconn;
475 }
476
477 /*
478  * struct aim_conn_t *aim_directim_getconn(struct aim_session_t *sess, const char *name)
479  * sess is your session, 
480  * name is the name to get,
481  * returns conn for directim with name, NULL if none found.
482  */
483
484 faim_export struct aim_conn_t *aim_directim_getconn(struct aim_session_t *sess, const char *name)
485 {
486   struct aim_conn_t *cur;
487   struct aim_directim_priv *priv;
488   
489   faim_mutex_lock(&sess->connlistlock);
490   for (cur = sess->connlist; cur; cur = cur->next) {
491     if (cur->type != AIM_CONN_TYPE_RENDEZVOUS || cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)
492       continue;
493     
494     priv = cur->priv;
495     if (aim_sncmp(priv->sn, name) == 0)
496       break;
497   }
498   faim_mutex_unlock(&sess->connlistlock);
499
500   return cur;
501 }
502
503 /*
504  * aim_accepttransfer:
505  * accept a file transfer request.
506  * sess is the session,
507  * conn is the BOS conn for the CAP reply
508  * sn is the screenname to send it to,
509  * cookie is the cookie used
510  * ip is the ip to connect to
511  * file is the listing file to use
512  * rendid is CAP type
513  *
514  * returns connection
515  */
516
517 faim_export struct aim_conn_t *aim_accepttransfer(struct aim_session_t *sess,
518                           struct aim_conn_t *conn, 
519                           char *sn,
520                           char *cookie,
521                           char *ip,
522                           FILE *file,
523                           unsigned short rendid)
524 {
525   struct command_tx_struct *newpacket, *newoft;
526
527   struct aim_conn_t *newconn;
528   struct aim_fileheader_t *listingfh;
529   struct aim_filetransfer_priv *priv;
530   struct aim_msgcookie_t *cachedcook;
531
532   int curbyte, i;
533
534   if(rendid == AIM_CAPS_GETFILE) {
535
536     newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, ip);
537     
538     newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
539     
540     if (!newconn || (newconn->fd == -1)) {
541       perror("aim_newconn");
542       faimdprintf(2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0);
543       aim_conn_kill(sess, &newconn);
544       return NULL;
545     } else {
546       priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
547       memcpy(priv->cookie, cookie, 8);
548       strncpy(priv->sn, sn, MAXSNLEN);
549       strncpy(priv->ip, ip, sizeof(priv->ip));
550       newconn->priv = (void *)priv;
551       faimdprintf(2, "faim: connected to peer (fd = %d)\n", newconn->fd);
552     }
553  
554     /* cache da cookie. COOOOOKIES! */
555
556     if(!(cachedcook = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) {
557       faimdprintf(1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n");
558       /* XXX die here, or go on? search for cachedcook for future references */
559     }
560
561     if(cachedcook)
562       memcpy(cachedcook->cookie, cookie, 8);
563   
564     /*  strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name)); */
565
566     if(cachedcook) { /* see above calloc of cachedcook */
567       cachedcook->type = AIM_COOKIETYPE_OFTGET;
568       cachedcook->data = (void *)priv;
569     
570       if (aim_cachecookie(sess, cachedcook) != 0)
571         faimdprintf(1, "faim: ERROR caching message cookie\n");
572     }
573   
574     if(rendid == AIM_CAPS_GETFILE) {
575       faimdprintf(2, "faim: getfile request accept\n");
576       if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x1108, newconn, 0))) {
577         faimdprintf(2, "faim: aim_accepttransfer: tx_new OFT failed\n");
578         /* XXX: what else do we need to clean up here? */
579         return NULL;
580       }
581     
582       newoft->lock = 1;
583     
584       memcpy(newoft->hdr.oft.magic, "OFT2", 4);
585       newoft->hdr.oft.hdr2len = 0xf8; /* 0x100 - 8 */
586     
587       if(!(listingfh = aim_getlisting(file))) {
588         return NULL;
589       }      
590
591       if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
592         free(newoft);
593         return NULL;
594       }  
595
596       memcpy(listingfh->bcookie, cookie, 8);
597
598       aim_oft_buildheader((void *)newoft->hdr.oft.hdr2, listingfh);
599
600       free(listingfh);
601
602       newoft->lock = 0;
603       aim_tx_enqueue(sess, newoft);
604       printf("faim: getfile: OFT listing enqueued.\n");
605     
606     }
607
608     /* OSCAR CAP accept packet */
609
610     if(!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(sn)+4+2+8+16)))
611       return NULL;
612   
613     newpacket->lock = 1;
614   
615     curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
616     for (i = 0; i < 8; i++)
617       curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
618     curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
619     curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn));
620     curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));
621     curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
622     curbyte += aimutil_put16(newpacket->data+curbyte, 0x001a);
623     curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002 /* accept */);
624     for (i = 0; i < 8; i++)
625       curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
626     curbyte += aim_putcap(newpacket->data+curbyte, 0x10, rendid);
627
628     newpacket->lock = 0;
629     aim_tx_enqueue(sess, newpacket);
630
631     return newconn;
632   }
633   return NULL;
634 }
635
636 /*
637  * aim_getlisting(FILE *file)
638  * 
639  * file is an opened listing file
640  * returns a pointer to the filled-in fileheader_t
641  *
642  * currently omits checksum. we'll fix this when AOL breaks us, i
643  * guess.
644  *
645  */
646
647 faim_internal struct aim_fileheader_t *aim_getlisting(FILE *file) 
648 {
649   struct aim_fileheader_t *fh;
650   u_long totsize = 0, size = 0, checksum = 0xffff0000;
651   short totfiles = 0;
652   char *linebuf, sizebuf[9];
653   
654   int linelength = 1024;
655
656   /* XXX: if we have a line longer than 1024chars, God help us. */
657   if( (linebuf = (char *)calloc(1, linelength)) == NULL ) {
658     faimdprintf(2, "linebuf calloc failed\n");
659     return NULL;
660   }  
661
662   if(fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */
663     perror("getlisting END1 fseek:");
664     faimdprintf(2, "getlising fseek END1 error\n");
665   }
666
667   if(fgetpos(file, &size) == -1) {
668     perror("getlisting END1 getpos:");
669     faimdprintf(2, "getlising getpos END1 error\n");
670   }
671
672   if(fseek(file, 0, SEEK_SET) != 0) {
673     perror("getlesting fseek(SET):");
674     faimdprintf(2, "faim: getlisting: couldn't seek to beginning of listing file\n");
675   }
676
677   bzero(linebuf, linelength);
678
679   size = 0;
680
681   while(fgets(linebuf,  linelength, file)) {
682     totfiles++;
683     bzero(sizebuf, 9);
684
685     size += strlen(linebuf);
686     
687     if(strlen(linebuf) < 23) {
688       faimdprintf(2, "line \"%s\" too short. skipping\n", linebuf);
689       continue;
690     }
691     if(linebuf[strlen(linebuf)-1] != '\n') {
692       faimdprintf(2, "faim: OFT: getlisting -- hit EOF or line too long!\n");
693     }
694
695     memcpy(sizebuf, linebuf+17, 8);
696
697     totsize += strtol(sizebuf, NULL, 10);
698     bzero(linebuf, linelength);
699   }   
700
701   /*  if(size != 0) {
702     faimdprintf(2, "faim: getlisting: size != 0 after while.. %i\n", size);
703     }*/
704
705   if(fseek(file, 0, SEEK_SET) == -1) {
706     perror("getlisting END2 fseek:");
707     faimdprintf(2, "getlising fseek END2 error\n");
708   }  
709
710   free(linebuf);
711
712   /* we're going to ignore checksumming the data for now -- that
713    * requires walking the whole listing.txt. it should probably be
714    * done at register time and cached, but, eh. */  
715
716   if(!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t))))
717     return NULL;
718
719   printf( "faim: OFT: getlisting: totfiles: %u, totsize: %lu, size: %lu\n", totfiles, totsize, size);
720
721   fh->encrypt     = 0x0000;
722   fh->compress    = 0x0000; 
723   fh->totfiles    = totfiles;
724   fh->filesleft   = totfiles; /* is this right ?*/
725   fh->totparts    = 0x0001;
726   fh->partsleft   = 0x0001;
727   fh->totsize     = totsize;
728   fh->size        = size; /* ls -l listing.txt */
729   fh->modtime     = (int)time(NULL); /* we'll go with current time for now */
730   fh->checksum    = checksum; /* XXX: checksum ! */
731   fh->rfcsum      = 0x00000000;
732   fh->rfsize      = 0x00000000;
733   fh->cretime     = 0x00000000;
734   fh->rfcsum      = 0x00000000;
735   fh->nrecvd      = 0x00000000;
736   fh->recvcsum    = 0x00000000;
737
738   /*  memset(fh->idstring, 0, sizeof(fh->idstring)); */
739   memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
740   memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring));
741
742   fh->flags       = 0x02;
743   fh->lnameoffset = 0x1a;
744   fh->lsizeoffset = 0x10;
745
746   /*  memset(fh->dummy, 0, sizeof(fh->dummy)); */
747   memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
748
749   fh->nencode     = 0x0000; /* we need to figure out these encodings for filenames */
750   fh->nlanguage   = 0x0000;
751
752   /*  memset(fh->name, 0, sizeof(fh->name)); */
753   memcpy(fh->name, "listing.txt", sizeof(fh->name));
754   memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name));
755
756   faimdprintf(2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name))));
757   return fh;
758 }
759
760 /*
761  * establish: create a listening socket on a port. you need to call
762  * accept() when it's connected.
763  * portnum is the port number to bind to.
764  * returns your fd
765  */
766
767 faim_internal int aim_listenestablish(u_short portnum)
768 {
769 #if defined(__linux__) /* XXX what other OS's support getaddrinfo? */
770   int listenfd;
771   const int on = 1;
772   struct addrinfo hints, *res, *ressave;
773   char serv[5];
774   sprintf(serv, "%d", portnum);
775   memset(&hints, 0, sizeof(struct addrinfo));
776   hints.ai_flags = AI_PASSIVE;
777   hints.ai_family = AF_UNSPEC;
778   hints.ai_socktype = SOCK_STREAM;
779   if (getaddrinfo(NULL/*any IP*/, serv, &hints, &res) != 0) {
780     perror("getaddrinfo");
781     return -1;
782   }
783   ressave = res;
784   do {
785     listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
786     if (listenfd < 0)
787       continue;
788     setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
789     if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
790       break; /* success */
791     close(listenfd);
792   } while ( (res = res->ai_next) );
793   if (!res)
794     return -1;
795   if (listen(listenfd, 1024)!=0) {
796     perror("listen");
797     return -1;
798   }
799   freeaddrinfo(ressave);
800   return listenfd;
801 #else 
802   int listenfd;
803   const int on = 1;
804   struct sockaddr_in sockin;
805   
806   if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
807     perror("socket(listenfd)");
808     return -1;
809   } 
810   if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on) != 0)) {
811     perror("setsockopt(listenfd)");
812     close(listenfd);
813     return -1;
814   }
815   memset(&sockin, 0, sizeof(struct sockaddr_in));
816   sockin.sin_family = AF_INET;
817   sockin.sin_port = htons(portnum);
818   if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
819     perror("bind(listenfd)");
820     close(listenfd);
821     return -1;
822   }
823   if (listen(listenfd, 4) != 0) {
824     perror("listen(listenfd)");
825     close(listenfd);
826     return -1;
827   }
828
829   return listenfd;
830 #endif
831 }
832
833 faim_internal int aim_get_command_rendezvous(struct aim_session_t *sess, struct aim_conn_t *conn)
834 {
835   unsigned char hdrbuf1[6];
836   unsigned char *hdr = NULL;
837   int hdrlen, hdrtype;
838   int flags = 0;
839   rxcallback_t userfunc = NULL;
840
841
842   memset(hdrbuf1, 0, sizeof(hdrbuf1));
843
844   faim_mutex_lock(&conn->active); /* gets locked down for the entirety */
845
846   if ( (hdrlen = aim_recv(conn->fd, hdrbuf1, 6)) < 6) {    
847  
848     faimdprintf(2, "faim: rend: read error (fd: %i) %02x%02x%02x%02x%02x%02x (%i)\n", conn->fd, hdrbuf1[0],hdrbuf1[1],hdrbuf1[0],hdrbuf1[0],hdrbuf1[0],hdrbuf1[0],hdrlen);
849     faim_mutex_unlock(&conn->active);
850     
851     if(hdrlen < 0)
852       perror("read");
853     else { /* disconnected */
854       switch(conn->subtype) {
855       case AIM_CONN_SUBTYPE_OFT_DIRECTIM: { /* XXX: clean up cookies here ? */
856         struct aim_directim_priv *priv = NULL;
857         if(!(priv = (struct aim_directim_priv *)conn->priv) ) 
858           return -1; /* not much we can do */
859         aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTIM);
860
861         
862         if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT)) ) {
863           aim_conn_close(conn);
864           return  userfunc(sess, NULL, conn, priv->sn);
865         }
866
867         break;
868       }
869
870       case AIM_CONN_SUBTYPE_OFT_GETFILE: {
871         struct aim_filetransfer_priv *priv;
872         if(!(priv = (struct aim_filetransfer_priv *)conn->priv))
873           return -1;
874
875         aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTGET);
876
877         if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT)) ) {
878           aim_conn_close(conn);
879           return userfunc(sess, NULL, conn, priv->sn);
880         }
881
882         break;
883       }
884
885       case AIM_CONN_SUBTYPE_OFT_SENDFILE: {
886         struct aim_filetransfer_priv *priv;
887         if(!(priv = (struct aim_filetransfer_priv *)conn->priv))
888           return -1;
889
890         aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTSEND);
891
892         if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEDISCONNECT)) ) {
893           aim_conn_close(conn);
894           return userfunc(sess, NULL, conn, priv->sn);
895         }
896
897         break;
898       }
899       }
900
901       aim_conn_close(conn);
902       aim_conn_kill(sess, &conn);
903       
904       return -1;
905     }
906   }
907
908   hdrlen = aimutil_get16(hdrbuf1+4);
909
910   hdrlen -= 6;
911   if (!(hdr = malloc(hdrlen))) {
912     faim_mutex_unlock(&conn->active);
913     return -1;
914   }
915
916   if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) {
917     perror("read");
918     faimdprintf(2,"faim: rend: read2 error\n");
919     free(hdr);
920     faim_mutex_unlock(&conn->active);
921     aim_conn_close(conn);
922     return -1;
923   }
924
925   hdrtype = aimutil_get16(hdr);  
926
927   switch (hdrtype) {
928   case 0x0001: { /* directim */
929     int payloadlength = 0;
930     char *snptr = NULL;
931     struct aim_directim_priv *priv;
932     int i;
933
934     priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
935
936     payloadlength = aimutil_get32(hdr+22);
937     flags = aimutil_get16(hdr+32);
938     snptr = (char *)hdr+38;
939
940     strncpy(priv->sn, snptr, MAXSNLEN);
941
942     faimdprintf(2, "faim: OFT frame: %04x / %04x / %04x / %s\n", hdrtype, payloadlength, flags, snptr); 
943
944     if (flags == 0x000e) {
945       faim_mutex_unlock(&conn->active);
946       if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)) )
947         return userfunc(sess, NULL, snptr);
948     } else if ((flags == 0x0000) && payloadlength) {
949       unsigned char *msg;
950
951       if(! (msg = calloc(1, payloadlength+1)) ) {
952         faim_mutex_unlock(&conn->active);
953         return -1;
954       }
955       
956       if (aim_recv(conn->fd, msg, payloadlength) < payloadlength) {
957         perror("read");
958         printf("faim: rend: read3 error\n");
959         free(msg);
960         faim_mutex_unlock(&conn->active);
961         aim_conn_close(conn);
962         return -1;
963       }
964       faim_mutex_unlock(&conn->active);
965       msg[payloadlength] = '\0';
966       faimdprintf(2, "faim: directim: %s/%04x/%04x/%s\n", snptr, payloadlength, flags, msg);
967
968
969       if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) )
970         i = userfunc(sess, NULL, conn, snptr, msg);
971       
972       free(msg);
973       return i;
974     }
975     break;
976   }
977 #if 0
978   /* currently experimental to a non-compiling degree */
979   case 0x1108: { /* getfile listing.txt incoming tx->rx */
980     struct aim_filetransfer_priv *ft;
981     struct aim_fileheader_t *fh;
982     struct aim_msgcookie_t *cook;
983     struct aim_conn_type *newoft;
984
985     int commandlen;
986     char *data;
987
988     faimdprintf(2,"faim: rend: fileget 0x1108\n");
989     
990     if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) {
991       faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n");
992       faim_mutex_unlock(&conn->active);
993       return -1;
994     }
995
996     ft->state = 1; /* we're waaaaiiiting.. */
997
998     fh = aim_oft_getfh(hdr);
999
1000     memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
1001     
1002     if(!(cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET))) {
1003       faim_mutex_unlock(&conn->active);
1004       return -1;
1005     }
1006
1007     if(cook->data)
1008       free(cook->data);
1009
1010     cook->data = ft;
1011
1012     aim_cachecookie(sess, cook);
1013
1014     if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x1209, conn, 0))) {
1015       /* XXX: what else do we need to clean up here? */
1016       faim_mutex_unlock(&conn->active);
1017       return -1;
1018     }
1019
1020
1021     aim_oft_buildheader((void *)newoft->hdr.oft.hdr2, ft->fh); /* no change */
1022     newoft->lock = 0;
1023     aim_tx_enqueue(sess, newoft);
1024     break;
1025   }
1026 #endif 
1027   case 0x1209: { /* get file listing ack rx->tx */
1028     struct aim_filetransfer_priv *ft;
1029     struct aim_fileheader_t *fh;
1030     struct aim_msgcookie_t *cook;
1031
1032     int commandlen;
1033     char *data;
1034
1035     faimdprintf(2,"faim: rend: fileget 0x1209\n");
1036
1037     if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) {
1038       faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n");
1039       faim_mutex_unlock(&conn->active);
1040       return -1;
1041     }
1042
1043     fh = aim_oft_getfh(hdr);
1044
1045     memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
1046     
1047     cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET);
1048
1049     /* we currently trust the other client to be giving us Valid
1050      *  Enough input, else this gets to be a messy function (and we
1051      *  won't break like winaim does when it gets bad input =) */
1052
1053     if(cook->data)
1054       free(cook->data); /* XXX */
1055   
1056     cook->data = ft;
1057     
1058     aim_cachecookie(sess, cook);
1059
1060     /* XXX: have this send chunks of the file instead of the whole
1061      * file. requires rethinking some code. */
1062
1063     if(fseek(sess->oft.listing, 0, SEEK_SET) != 0) {
1064       perror("get_command_rendezvous 1209 fseek(SET):");
1065       faimdprintf(2, "faim: getlisting: couldn't seek to beginning of listing file\n");
1066     }
1067     commandlen = ft->fh.size;
1068
1069     if((data = (char *)calloc(1, commandlen)) == NULL) {
1070       faimdprintf(2, "faim: get_command_rendezvous 1209: couldn't malloc data.\n");
1071       faim_mutex_unlock(&conn->active);
1072       return -1;
1073
1074     }
1075
1076     for(commandlen = 0; commandlen < ft->fh.size; commandlen++)
1077       if( (data[commandlen] = (unsigned char)fgetc(sess->oft.listing)) == EOF)
1078         faimdprintf(2, "faim: get_command_rendezvous 1209: got early EOF (error?)\n");
1079
1080     commandlen = ft->fh.size;
1081
1082     if (write(conn->fd, data, commandlen) != commandlen) {
1083       perror("listing write error");
1084     }
1085     faim_mutex_unlock(&conn->active);
1086
1087     faimdprintf(2, "faim: get_command_rendezvous: hit end of 1209\n");
1088
1089     free(data);
1090
1091     break;
1092   }
1093   case 0x120b: { /* get file second */
1094     struct aim_filetransfer_priv *ft;
1095     struct aim_msgcookie_t *cook;
1096
1097     struct aim_fileheader_t *fh;
1098
1099     faimdprintf(2, "faim: rend: fileget 120b\n");
1100
1101     if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) {
1102       faimdprintf(2, "faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n");
1103       faim_mutex_unlock(&conn->active);
1104       return -1;
1105     }
1106
1107     fh = aim_oft_getfh(hdr);
1108
1109     memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
1110  
1111     if(!(cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET))) {
1112       faim_mutex_unlock(&conn->active);
1113       return -1;
1114     }
1115   
1116     if(cook->data)
1117       free(cook->data); /* XXX: integrate cookie caching */
1118
1119     cook->data = ft;
1120     
1121     aim_cachecookie(sess, cook);
1122
1123     faim_mutex_unlock(&conn->active);
1124
1125     /* call listing.txt rx confirm */    
1126
1127     break;
1128   }
1129   case 0x120c: { /* yet more get file */
1130     struct aim_filetransfer_priv *ft;
1131     struct aim_msgcookie_t *cook;
1132     struct aim_fileheader_t *listingfh;
1133     struct command_tx_struct *newoft;
1134
1135     int i;
1136
1137     rxcallback_t userfunc;
1138     
1139     printf("faim: rend: fileget 120c\n");
1140
1141     if(!(ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) {
1142       printf("faim: couldn't malloc ft. um. bad. bad bad. file transfer will likely fail, sorry.\n");
1143       faim_mutex_unlock(&conn->active);
1144       return -1;
1145     }
1146
1147     if(hdrlen != 0x100)
1148       printf("faim: fileget_command(120c): um. hdrlen != 0x100..\n");
1149
1150     listingfh = aim_oft_getfh((char *)hdr);
1151
1152     memcpy(&(ft->fh), listingfh, sizeof(struct aim_fileheader_t));
1153     
1154     if(!(cook = aim_checkcookie(sess, ft->fh.bcookie, AIM_COOKIETYPE_OFTGET))) {
1155       faim_mutex_unlock(&conn->active);
1156       return -1;
1157     }
1158   
1159     if(cook->data)
1160       free(cook->data); /* XXX */
1161
1162     cook->data = ft;
1163     
1164     aim_cachecookie(sess, cook);
1165
1166     faim_mutex_unlock(&conn->active);
1167
1168     faimdprintf(2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name);
1169
1170     if( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) )
1171       i = userfunc(sess, NULL, conn, &ft->fh, cook->cookie);
1172     
1173     if(i < 0)
1174       return -1;
1175       
1176     if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0101, conn, 0))) {
1177       faimdprintf(2, "faim: send_final_transfer: tx_new OFT failed\n");
1178       return -1;
1179     }
1180     
1181     newoft->lock = 1;
1182     
1183     memcpy(newoft->hdr.oft.magic, "OFT2", 4);
1184     newoft->hdr.oft.hdr2len = 0xf8; /* 0x100 - 8 */
1185     
1186     if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
1187       newoft->lock = 0;
1188       aim_tx_destroy(newoft);
1189       return -1;
1190     }  
1191     
1192     /*  memcpy(listingfh->bcookie, ft->fh.bcookie, 8); */
1193     
1194     listingfh->nrecvd = 0; /* these need reset for protocol-correctness */
1195     listingfh->recvcsum = 0;
1196
1197     aim_oft_buildheader((void *)newoft->hdr.oft.hdr2, listingfh);
1198     
1199     newoft->lock = 0;
1200     aim_tx_enqueue(sess, newoft);
1201     faimdprintf(2, "faim: OFT: OFT file enqueued.\n");
1202     
1203     if( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL)
1204       return 1;
1205     
1206     i = userfunc(sess, NULL, conn, listingfh, listingfh->bcookie);
1207
1208     free(listingfh);
1209
1210     return i;
1211
1212     break;
1213   }
1214   case 0x0202: { /* get file: ready to recieve data */
1215     struct aim_fileheader_t *fh;    
1216     fh = aim_oft_getfh((char *)hdr);
1217
1218     faimdprintf(2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n");
1219
1220     faim_mutex_unlock(&conn->active);
1221     
1222     if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) == NULL)
1223       return 1;
1224
1225     return userfunc(sess, NULL, conn, fh);       
1226
1227     free(fh);
1228
1229     break;
1230   }
1231   case 0x0204: { /* get file: finished. close it up */
1232     int i;
1233
1234     struct aim_fileheader_t *fh;    
1235     fh = aim_oft_getfh((char *)hdr);
1236
1237     faimdprintf(2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n");
1238
1239     faim_mutex_unlock(&conn->active);
1240     
1241     if ( (userfunc = aim_callhandler(conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) )
1242       i = userfunc(sess, NULL, conn, fh);
1243     else
1244       i = 1;
1245
1246     /*
1247           free(fh); */
1248     /* not sure where to do this yet, as we need to keep it to allow multiple file sends... bleh */
1249
1250     return i;
1251     break;
1252   }
1253   default: {
1254     printf("OFT frame: type %04x\n", hdrtype);  
1255     /* data connection may be unreliable here */
1256     faim_mutex_unlock(&conn->active);
1257     break;
1258   }
1259   } /* switch */
1260
1261   free(hdr);
1262   
1263   return 0;
1264 }
1265
1266 /*
1267  * aim_oft_registerlisting()
1268  * sess: aim session
1269  * file: opened FILE *
1270  * listingdir: the path to listing.txt
1271  * returns -1 on error, 0 on success.
1272  *
1273  * it's not my problem if the listing fd is already set.
1274  */
1275
1276 faim_export int aim_oft_registerlisting(struct aim_session_t *sess, FILE *file, char* listingdir) 
1277 {
1278   if(!sess)
1279     return -1;
1280
1281   /* XXX: checksum each file in the listing */
1282
1283 #if 0
1284    if(sess->oft.listing) {
1285      faimdprintf(1, "We already have a file pointer. Closing and overwriting.\n");
1286     fclose(sess->oft.listing);
1287   }
1288 #endif
1289   sess->oft.listing = file;
1290 #if 0
1291   if(sess->oft.listingdir) {
1292     faimdprintf(1, "We already have a directory string. Freeing and overwriting\n");
1293     free(sess->oft.listingdir);
1294   }
1295 #endif
1296   
1297   if( (sess->oft.listingdir = (char *)calloc(1, strlen(listingdir)+1)) )
1298     memcpy(sess->oft.listingdir, listingdir, strlen(listingdir));
1299   else
1300     return -1;
1301   return 0;
1302 }
1303
1304 faim_internal struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr) 
1305 {
1306   struct aim_fileheader_t *fh;
1307   int i, j;
1308
1309   if(!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
1310     return NULL;
1311
1312   /* [0] and [1] are the type. we can ignore those here. */
1313
1314   i = 2;
1315
1316   for(j = 0; j < 8; j++, i++)
1317     fh->bcookie[j] = hdr[i];
1318   fh->encrypt = aimutil_get16(hdr+i);
1319   i += 2;
1320   fh->compress = aimutil_get16(hdr+i);
1321   i += 2;
1322   fh->totfiles = aimutil_get16(hdr+i);
1323   i += 2;
1324   fh->filesleft = aimutil_get16(hdr+i);
1325   i += 2;
1326   fh->totparts = aimutil_get16(hdr+i);
1327   i += 2;
1328   fh->partsleft = aimutil_get16(hdr+i);
1329   i += 2;
1330   fh->totsize = aimutil_get32(hdr+i);
1331   i += 4;
1332   fh->size = aimutil_get32(hdr+i);
1333   i += 4;
1334   fh->modtime = aimutil_get32(hdr+i);
1335   i += 4;
1336   fh->checksum = aimutil_get32(hdr+i);
1337   i += 4;
1338   fh->rfrcsum = aimutil_get32(hdr+i);
1339   i += 4;
1340   fh->rfsize = aimutil_get32(hdr+i);
1341   i += 4;
1342   fh->cretime = aimutil_get32(hdr+i);
1343   i += 4;
1344   fh->rfcsum = aimutil_get32(hdr+i);
1345   i += 4;
1346   fh->nrecvd = aimutil_get32(hdr+i);
1347   i += 4;
1348   fh->recvcsum = aimutil_get32(hdr+i);
1349   i += 4;
1350
1351   memcpy(fh->idstring, hdr+i, 32);
1352   i += 32;
1353
1354   fh->flags = aimutil_get8(hdr+i);
1355   i += 1;
1356   fh->lnameoffset = aimutil_get8(hdr+i);
1357   i += 1;
1358   fh->lsizeoffset = aimutil_get8(hdr+i);
1359   i += 1;
1360
1361   memcpy(fh->dummy, hdr+i, 69);
1362   i += 69;
1363
1364   memcpy(fh->macfileinfo, hdr+i, 16);
1365   i += 16;
1366
1367   fh->nencode = aimutil_get16(hdr+i);
1368   i += 2;
1369   fh->nlanguage = aimutil_get16(hdr+i);
1370   i += 2;
1371
1372   memcpy(fh->name, hdr+i, 64);
1373   i += 64;
1374
1375   return fh;
1376 }
1377
1378 faim_export int aim_oft_checksum(char *buffer, int bufsize, int *checksum)
1379 {    
1380   short check0, check1;
1381   int i;
1382   
1383   check0 = ((*checksum & 0xFF000000) >> 16);
1384   check1 = ((*checksum & 0x00ff0000) >> 16);
1385
1386   for(i = 0; i < bufsize; i++) {
1387
1388     if(i % 2)  { /* use check1 -- second byte */
1389       if ( (short)buffer[i] > check1 ) { /* wrapping */
1390         
1391         check1 += 0x100; /* this is a cheap way to wrap */
1392
1393         /* if we're wrapping, decrement the other one */
1394         if(check0 == 0) /* XXX: check this corner case */
1395           check0 = 0x00ff;
1396         else
1397           check0--;                     
1398       }
1399
1400       check1 -= buffer[i];
1401     } else { /* use check0 -- first byte */
1402       if ( (short)buffer[i] > check0 ) { /* wrapping */
1403         
1404         check0 += 0x100; /* this is a cheap way to wrap */
1405
1406         /* if we're wrapping, decrement the other one */
1407         if(check1 == 0) /* XXX: check this corner case */
1408           check1 = 0x00ff;
1409         else
1410           check1--;                     
1411       }
1412
1413       check0 -= buffer[i];
1414     } 
1415   }
1416
1417   if(check0 > 0xff || check1 > 0xff) { /* they shouldn't be able to do this. error! */
1418     faimdprintf(2, "check0 or check1 is too high: 0x%04x, 0x%04x\n", check0, check1);
1419     return -1;
1420   }
1421   
1422   check0 &= 0xff; /* grab just the lowest byte */
1423   check1 &= 0xff; /* this should be clean, but just in case */
1424
1425   *checksum = ((check0 * 0x1000000) + (check1 * 0x10000));
1426
1427   return *checksum;
1428 }
1429
1430
1431 /*
1432  * aim_oft_buildheader:
1433  * fills a buffer with network-order fh data.
1434  * returns length written; -1 on error.
1435  * dest: buffer to fill -- pre-alloced
1436  * listingfh: fh to get data from
1437  *
1438  * DOES NOT DO BOUNDS CHECKING!
1439  */
1440
1441
1442 faim_internal int aim_oft_buildheader(char *dest,struct aim_fileheader_t *listingfh) 
1443 {
1444   int i, curbyte;
1445
1446   if(!dest || !listingfh)
1447     return -1;
1448   
1449   curbyte = 0;    
1450   for(i = 0; i < 8; i++)
1451     curbyte += aimutil_put8(dest+curbyte, listingfh->bcookie[i]);
1452   curbyte += aimutil_put16(dest+curbyte, listingfh->encrypt);
1453   curbyte += aimutil_put16(dest+curbyte, listingfh->compress);
1454   curbyte += aimutil_put16(dest+curbyte, listingfh->totfiles);
1455   curbyte += aimutil_put16(dest+curbyte, listingfh->filesleft);
1456   curbyte += aimutil_put16(dest+curbyte, listingfh->totparts);
1457   curbyte += aimutil_put16(dest+curbyte, listingfh->partsleft);
1458   curbyte += aimutil_put32(dest+curbyte, listingfh->totsize);
1459   curbyte += aimutil_put32(dest+curbyte, listingfh->size);
1460   curbyte += aimutil_put32(dest+curbyte, listingfh->modtime);
1461   curbyte += aimutil_put32(dest+curbyte, listingfh->checksum);
1462   curbyte += aimutil_put32(dest+curbyte, listingfh->rfrcsum);
1463   curbyte += aimutil_put32(dest+curbyte, listingfh->rfsize);
1464   curbyte += aimutil_put32(dest+curbyte, listingfh->cretime);
1465   curbyte += aimutil_put32(dest+curbyte, listingfh->rfcsum);
1466   curbyte += aimutil_put32(dest+curbyte, listingfh->nrecvd);
1467   curbyte += aimutil_put32(dest+curbyte, listingfh->recvcsum);
1468
1469   memcpy(dest+curbyte, listingfh->idstring, 32);
1470   curbyte += 32;
1471
1472   curbyte += aimutil_put8(dest+curbyte, listingfh->flags);
1473   curbyte += aimutil_put8(dest+curbyte, listingfh->lnameoffset);
1474   curbyte += aimutil_put8(dest+curbyte, listingfh->lsizeoffset);
1475
1476   memcpy(dest+curbyte, listingfh->dummy, 69);
1477   curbyte += 69;
1478     
1479   memcpy(dest+curbyte, listingfh->macfileinfo, 16);
1480   curbyte += 16;
1481
1482   curbyte += aimutil_put16(dest+curbyte, listingfh->nencode);
1483   curbyte += aimutil_put16(dest+curbyte, listingfh->nlanguage);
1484
1485   memcpy(dest+curbyte, listingfh->name, 64); /* XXX: Filenames longer than 64B */
1486   curbyte += 64;
1487
1488   return curbyte;
1489 }
1490
1491 /*
1492  * int aim_getfile_send(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh)
1493  * conn is the OFT connection to shove the data down,
1494  * tosend is the FILE * for the file to send
1495  * fh is the filled-in fh value
1496  * returns -1 on error, 1 on success.
1497  */
1498
1499 faim_export int aim_getfile_send(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh)
1500 {
1501   int  pos, bufpos, i; 
1502   const int bufsize = 4096;
1503   char *buf;
1504
1505   /* sanity checks */
1506   if(conn->type != AIM_CONN_TYPE_RENDEZVOUS || conn->subtype != AIM_CONN_SUBTYPE_OFT_GETFILE) {
1507     faimdprintf(1, "getfile_send: conn->type(0x%04x) != RENDEZVOUS or conn->subtype(0x%04x) != GETFILE\n", conn->type, conn->subtype);
1508     return -1;
1509   }
1510   
1511   if(!tosend) {
1512     faimdprintf(1, "getfile_send: file pointer isn't valid\n");
1513     return -1;
1514   }
1515
1516   if(!fh) {
1517     faimdprintf(1, "getfile_send: fh isn't valid\n");
1518     return -1;
1519   }
1520
1521   /* real code */
1522
1523   if(!(buf = (char *)calloc(1, bufsize))) {
1524     perror("faim: getfile_send: calloc:");
1525     faimdprintf(2, "getfile_send calloc error\n");
1526     return -1;
1527   }
1528
1529   pos = 0;
1530
1531   if( fseek(tosend, 0, SEEK_SET) == -1) {
1532     perror("faim: getfile_send:  fseek:");
1533     faimdprintf(2, "getfile_send fseek error\n");
1534   }  
1535
1536   faimdprintf(2, "faim: getfile_send: using %i byte blocks\n", bufsize);
1537
1538   for(pos = 0; pos < fh->size; pos++) {
1539     bufpos = pos % bufsize;
1540
1541     if(bufpos == 0 && pos > 0) { /* filled our buffer. spit it across the wire */
1542       if ( (i = write(conn->fd, buf, bufsize)) != bufsize ) {
1543         perror("faim: getfile_send: write1: ");
1544         faimdprintf(1, "faim: getfile_send: whoopsy, didn't write it all...\n");
1545         free(buf);   
1546         return -1;
1547       }
1548     }
1549     if( (buf[bufpos] = fgetc(tosend)) == EOF) {
1550       if(pos != fh->size) {
1551         printf("faim: getfile_send: hrm... apparent early EOF at pos 0x%x of 0x%lx\n", pos, fh->size);
1552         faimdprintf(1, "faim: getfile_send: hrm... apparent early EOF at pos 0x%lx of 0x%lx\n", pos, fh->size);
1553         free(buf);   
1554         return -1;
1555       }
1556     }
1557       
1558       
1559   }
1560
1561   if( (i = write(conn->fd, buf, bufpos+1)) != (bufpos+1)) {
1562     perror("faim: getfile_send: write2: ");
1563     faimdprintf(1, "faim: getfile_send cleanup: whoopsy, didn't write it all...\n");
1564     free(buf);   
1565     return -1;
1566   }
1567
1568   free(buf);   
1569   
1570   fclose(tosend);
1571   return 1; 
1572 }
1573
1574 /*
1575  * int aim_getfile_send_chunk(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh, int pos, int bufsize)
1576  * conn is the OFT connection to shove the data down,
1577  * tosend is the FILE * for the file to send
1578  * fh is the filled-in fh value
1579  * pos is the position to start at (at beginning should be 0, after 5
1580  *  bytes are sent should be 5); -1 for "don't seek"
1581  * bufsize is the size of the chunk to send
1582  *
1583  * returns -1 on error, new pos on success.
1584  *
1585  * 
1586  * Notes on usage: 
1587  * You don't really have to keep track of pos if you don't want
1588  *  to. just always call with -1 for pos, and it'll use the one
1589  *  contained within the FILE *.
1590  *
1591  * if (pos + chunksize > fh->size), we only send as much data as we
1592  *  can get (ie: up to fh->size.  
1593  */
1594 faim_export int aim_getfile_send_chunk(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh, int pos, int bufsize)
1595 {
1596   int bufpos; 
1597   char *buf;
1598
1599   /* sanity checks */
1600   if(conn->type != AIM_CONN_TYPE_RENDEZVOUS || conn->type != AIM_CONN_SUBTYPE_OFT_GETFILE) {
1601     faimdprintf(1, "faim: getfile_send_chunk: conn->type(0x%04x) != RENDEZVOUS or conn->subtype(0x%04x) != GETFILE\n", conn->type, conn->subtype);
1602     return -1;
1603   }
1604   
1605   if(!tosend) {
1606     faimdprintf(1, "faim: getfile_send_chunk: file pointer isn't valid\n");
1607     return -1;
1608   }
1609
1610   if(!fh) {
1611     faimdprintf(1, "faim: getfile_send_chunk: fh isn't valid\n");
1612     return -1;
1613   }
1614
1615   /* real code */
1616
1617   if(!(buf = (char *)calloc(1, bufsize))) {
1618     perror("faim: getfile_send_chunk: calloc:");
1619     faimdprintf(2, "faim: getfile_send_chunk calloc error\n");
1620     return -1;
1621   }
1622   
1623   if(pos != -1) {
1624     if( fseek(tosend, pos, SEEK_SET) == -1) {
1625       perror("faim: getfile_send_chunk:  fseek:");
1626       faimdprintf(2, "faim: getfile_send_chunk fseek error\n");
1627     }  
1628   }
1629
1630   faimdprintf(2, "faim: getfile_send_chunk: using %i byte blocks\n", bufsize);
1631
1632   for(bufpos = 0; pos < fh->size; bufpos++, pos++) {
1633     if( (buf[bufpos] = fgetc(tosend)) == EOF) {
1634       if(pos != fh->size) {
1635         faimdprintf(1, "faim: getfile_send_chunk: hrm... apparent early EOF at pos 0x%x of 0x%x\n", pos, fh->size);
1636         free(buf);   
1637         return -1;
1638       }
1639     }      
1640   }
1641
1642   if( write(conn->fd, buf, bufpos+1) != (bufpos+1)) {
1643     faimdprintf(1, "faim: getfile_send_chunk cleanup: whoopsy, didn't write it all...\n");
1644     free(buf);   
1645     return -1;
1646   }
1647
1648   free(buf);   
1649   
1650   return (pos + bufpos);
1651 }
1652
1653 /*
1654  * aim_tx_destroy:
1655  * free's tx_command_t's. if command is locked, doesn't free.
1656  * returns -1 on error (locked struct); 0 on success.
1657  * command is the command to free 
1658  */
1659
1660 faim_internal int aim_tx_destroy(struct command_tx_struct *command)
1661 {
1662   if(command->lock)
1663     return -1;
1664   if(command->data)
1665     free(command->data);
1666   free(command);
1667
1668   return 0;
1669 }
1670
1671 #if 0
1672 /*
1673  * aim_getfile_intitiate:
1674  * For those times when we want to open up the directim channel ourselves.
1675  * sess is your session,
1676  * conn is the BOS conn,
1677  * priv is a dummy priv value (we'll let it get filled in later) (if
1678  * you pass a NULL, we alloc one) 
1679  * destsn is the SN to connect to.  
1680  */
1681
1682
1683 faim_export struct aim_conn_t *aim_getfile_initiate(struct aim_session_t *sess,
1684                           struct aim_conn_t *conn,
1685                           struct aim_getfile_priv *priv,
1686                           char *destsn)
1687 {
1688   struct command_tx_struct *newpacket;
1689   struct aim_conn_t *newconn;
1690
1691   struct aim_msgcookie_t *cookie;
1692
1693   int curbyte, i, listenfd;
1694   short port = 4443;
1695
1696   struct hostent *hptr;
1697   struct utsname myname;
1698
1699   char cap[16];
1700   char d[4]; /* XXX: IPv6. *cough* */
1701
1702   /*
1703    * Open our socket
1704    */
1705
1706   if( (listenfd = aim_listenestablish(port)) == -1)
1707     return NULL;
1708
1709   /*
1710    * get our local IP
1711    */
1712
1713   if(uname(&myname) < 0)
1714     return NULL;
1715
1716   if( (hptr = gethostbyname(myname.nodename)) == NULL)
1717     return NULL;
1718
1719   memcpy(&d, hptr->h_addr_list[0], 4); /* XXX: this probably isn't quite kosher, but it works */
1720
1721   aim_putcap(cap, 16, AIM_CAPS_IMIMAGE);
1722
1723   /*
1724    * create the OSCAR packet
1725    */
1726
1727   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(destsn)+4+4+0x32)))
1728     return NULL;
1729
1730   newpacket->lock = 1; /* lock struct */
1731
1732   curbyte  = 0;
1733   curbyte += aim_putsnac(newpacket->data+curbyte, 
1734                          0x0004, 0x0006, 0x0000, sess->snac_nextid);
1735
1736   /* 
1737    * Generate a random message cookie 
1738    * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible.
1739    */
1740   for (i=0;i<7;i++)
1741     curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) random() % 20));
1742   curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
1743
1744   /*
1745    * grab all the data for cookie caching.
1746    */
1747   cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t));
1748
1749   memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
1750   cookie->type = AIM_COOKIETYPE_OFTIM;
1751   
1752   if(!priv)
1753     priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
1754
1755   memcpy(priv->cookie, cookie, 8);
1756   memcpy(priv->sn, destsn, sizeof(priv->sn));
1757  
1758   cookie->data = priv;
1759
1760   aim_cachecookie(sess, cookie);  /* cache da cookie */
1761
1762   /*
1763    * Channel ID
1764    */
1765   curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
1766
1767   /* 
1768    * Destination SN (prepended with byte length)
1769    */
1770   curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
1771   curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
1772
1773   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
1774   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
1775
1776   /* 
1777    * enTLV start
1778    */
1779   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
1780   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0032);
1781
1782   /*
1783    * Flag data / ICBM Parameters?
1784    */
1785   curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
1786   curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
1787
1788   /*
1789    * Cookie 
1790    */
1791   curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
1792
1793   /*
1794    * Capability String
1795    */
1796   curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
1797
1798   /*
1799    * 000a/0002 : 0001
1800    */
1801   curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
1802   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
1803   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
1804
1805   /*
1806    * 0003/0004: IP address
1807    */
1808
1809   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
1810   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
1811
1812   for(i = 0; i < 4; i++)
1813     curbyte += aimutil_put8(newpacket->data+curbyte, d[i]); /* already in network byte order */
1814
1815   /*
1816    * 0005/0002: Port
1817    */
1818
1819   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
1820   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
1821   curbyte += aimutil_put16(newpacket->data+curbyte, port);
1822
1823   /*
1824    * 000f/0000: umm.. dunno. Zigamorph[1]?
1825    * [1]: see esr's TNHD.
1826    */
1827
1828   curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
1829   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
1830
1831   printf("curbyte: 0x%x\n",curbyte);
1832
1833   newpacket->commandlen = curbyte;
1834   newpacket->lock = 0;
1835
1836   aim_tx_enqueue(sess, newpacket);
1837
1838   /*
1839    * allocate and set up our connection
1840    */
1841
1842   i = fcntl(listenfd, F_GETFL, 0);
1843   fcntl(listenfd, F_SETFL, i | O_NONBLOCK);
1844
1845   newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
1846   if (!newconn) { 
1847     perror("aim_newconn");
1848     aim_conn_kill(sess, &newconn);
1849     return NULL;
1850   } 
1851
1852   newconn->fd = listenfd;
1853   newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
1854   newconn->priv = priv;
1855   printf("faim: listening (fd = %d, unconnected)\n", newconn->fd);
1856
1857   /*
1858    * XXX We need some way of closing the listener socket after
1859    * n seconds of no connection. -- mid
1860    */
1861
1862 #ifdef USE_SNAC_FOR_IMS
1863  {
1864     struct aim_snac_t snac;
1865
1866     snac.id = sess->snac_nextid;
1867     snac.family = 0x0004;
1868     snac.type = 0x0006;
1869     snac.flags = 0x0000;
1870
1871     snac.data = malloc(strlen(destsn)+1);
1872     memcpy(snac.data, destsn, strlen(destsn)+1);
1873
1874     aim_newsnac(sess, &snac);
1875
1876     aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */
1877   }
1878 #endif
1879   
1880   return newconn;
1881 }
1882
1883 #endif
This page took 0.191686 seconds and 5 git commands to generate.