Mercurial > pidgin
comparison src/protocols/oscar/ft.c @ 4617:858979ab3867
[gaim-migrate @ 4908]
Big Changes:
-Rewrote some of the perl stuff so perl scripts can change a few of their
parameters
-Receiving a file with AIM over oscar works pretty well
Now, the "nitty gritty":
Very minor change to prefs.c: In the plugins details tab, I changed "URL"
to "Web Site." I was just going to fix the tabbing, but silvestrij
suggested changing it to "Web site," and I thought that sounded good.
I think it fits better, too. I dunno, maybe that's just me.
"Get Capabilities" has stopped working for some reason. I'm just going to
blame AOL. It's really not important anyway, and some people wanted it
taken off. It is now #ifdef 0'ed out. I'll remove it completely if it
continues to no longer function.
I took out a few plugin_event calls from oscar.c and put them in core code.
"event_error" should be, uh, "evented" when there is an error signing on.
Hopefully no one was using this. It's really pretty useless. The parameter
is now the reason for not being able to connect rather than the archaic
toc error code.
I screwed around with how perl functions are called some. There was way the
hell too much malloc'ing going on here. I think all in all it's an
improvement, though I'm still not a big fan of how changes to parameters
propagate to the actual memory.
I really think it would be nice if the perl stuff was made into a C plugin.
It's just so much cleaner. Especially if someone wanted to write, say, a
python or tcl interpreter. That's how xchat2 does it. I just think that
would be really slick. Like butter. Or ice. Very unlike Velcro.
I added a "Change Password" Protocol Action for ICQ over oscar. This was
really pretty easy. I'd like to thank my housemate Andrew for complaining
a lot that having to use Windows ICQ to change his password was a pain.
I rewrote a lot of the oscar file transfer stuff to use Christian's new
xfer interface. This involved moving a few functions from ft.c to im.c,
where they belong. I also removed all the #if 0'ed getfile functions.
I'll be rewritting them soonish. Receiving a file should work perfectly,
aside from maybe a small memleak when stuff is canceled. Sending a file is
currently disabled. No ETA on when I'll have that working.
I renamed pretty much all of the functions in im.c so they have kind of a
scheme now. They should all be aim_im_bleh, since "im" is the family
name. There comes a time when you must break the crap out of any clients
that might be using libfaim in order to make stuff cleaner. Maybe.
I got rid of the snac destructor stuff for now. I'll probably add it back
later. I wasn't entirely comfortable with how it was done.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Wed, 26 Feb 2003 05:01:37 +0000 |
parents | 9891c1458eb7 |
children | 3c7d4a060d30 |
comparison
equal
deleted
inserted
replaced
4616:767093a2ddaf | 4617:858979ab3867 |
---|---|
1 /* | 1 /* |
2 * File transfer (OFT) and DirectIM (ODC). | 2 * Oscar File transfer (OFT) and Oscar Direct Connect (ODC). |
3 * (OSCAR File Transfer, Oscar Direct Connect(ion?) | 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 another 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 and closes the connection. | |
4 */ | 39 */ |
5 | 40 |
6 #define FAIM_INTERNAL | 41 #define FAIM_INTERNAL |
7 | 42 |
8 #ifdef HAVE_CONFIG_H | 43 #ifdef HAVE_CONFIG_H |
12 | 47 |
13 #ifndef _WIN32 | 48 #ifndef _WIN32 |
14 #include <netdb.h> | 49 #include <netdb.h> |
15 #include <sys/socket.h> | 50 #include <sys/socket.h> |
16 #include <netinet/in.h> | 51 #include <netinet/in.h> |
17 #include <sys/utsname.h> /* for aim_directim_initiate */ | 52 #include <sys/utsname.h> /* for aim_odc_initiate */ |
18 #include <arpa/inet.h> /* for inet_ntoa */ | 53 #include <arpa/inet.h> /* for inet_ntoa */ |
19 #define G_DIR_SEPARATOR '/' | 54 #define G_DIR_SEPARATOR '/' |
20 #endif | 55 #endif |
21 | 56 |
22 #ifdef _WIN32 | 57 #ifdef _WIN32 |
23 #include "win32dep.h" | 58 #include "win32dep.h" |
24 #endif | 59 #endif |
25 | 60 |
26 #define AIM_OFT_PROTO_OFFER 0x0101 | 61 struct aim_odc_intdata { |
27 #define AIM_OFT_PROTO_ACCEPT 0x0202 | |
28 #define AIM_OFT_PROTO_RESUME 0x0205 | |
29 #define AIM_OFT_PROTO_RESUMEACCEPT 0x0207 | |
30 #define AIM_OFT_PROTO_ACK 0x0204 | |
31 | |
32 struct aim_filetransfer_priv { | |
33 char sn[MAXSNLEN+1]; | |
34 char cookie[8]; | |
35 char ip[30]; | |
36 int state; | |
37 struct aim_fileheader_t fh; | |
38 }; | |
39 | |
40 struct aim_directim_intdata { | |
41 fu8_t cookie[8]; | 62 fu8_t cookie[8]; |
42 char sn[MAXSNLEN+1]; | 63 char sn[MAXSNLEN+1]; |
43 char ip[22]; | 64 char ip[22]; |
44 }; | 65 }; |
45 | 66 |
46 static int listenestablish(fu16_t portnum); | 67 /** |
47 static struct aim_fileheader_t *aim_oft_getfh(aim_bstream_t *bs); | 68 * Convert the directory separator from / (0x2f) to ^A (0x01) |
48 static void oft_dirconvert(char *name); | 69 * |
49 static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh); | 70 * @param name The filename to convert. |
50 | 71 */ |
51 /** | 72 static void aim_oft_dirconvert_tostupid(char *name) |
52 * aim_handlerendconnect - call this to accept OFT connections and set up the required structures | 73 { |
53 * @sess: the session | 74 while (name[0]) { |
54 * @cur: the conn the incoming connection is on | 75 if (name[0] == 0x01) |
55 * | 76 name[0] = G_DIR_SEPARATOR; |
56 * call this when you get an outstanding read on a conn with subtype | 77 name++; |
57 * AIM_CONN_SUBTYPE_RENDEZVOUS_OUT, it will clone the current | 78 } |
58 * &aim_conn_t and tweak things as appropriate. the new conn and the | 79 } |
59 * listener conn are both returned to the client in the | 80 |
60 * %AIM_CB_FAM_OFT, %AIM_CB_OFT_<CLASS>INITIATE callback. | 81 /** |
61 */ | 82 * Convert the directory separator from ^A (0x01) to / (0x2f) |
62 faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur) | 83 * |
63 { | 84 * @param name The filename to convert. |
64 int acceptfd = 0; | 85 */ |
65 struct sockaddr cliaddr; | 86 static void aim_oft_dirconvert_fromstupid(char *name) |
66 int clilen = sizeof(cliaddr); | 87 { |
67 int ret = 0; | 88 while (name[0]) { |
68 aim_conn_t *newconn; | 89 if (name[0] == G_DIR_SEPARATOR) |
69 | 90 name[0] = 0x01; |
70 if ((acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1) | 91 name++; |
71 return 0; /* not an error */ | 92 } |
72 | 93 } |
73 if (cliaddr.sa_family != AF_INET) { /* just in case IPv6 really is happening */ | 94 |
74 close(acceptfd); | 95 /** |
75 aim_conn_close(cur); | 96 * Calculate oft checksum of buffer |
76 return -1; | 97 * |
77 } | 98 * Prevcheck should be 0xFFFF0000 when starting a checksum of a file. The |
78 | 99 * checksum is kind of a rolling checksum thing, so each time you get bytes |
79 if (!(newconn = aim_cloneconn(sess, cur))) { | 100 * of a file you just call this puppy and it updates the checksum. You can |
80 close(acceptfd); | 101 * calculate the checksum of an entire file by calling this in a while or a |
81 aim_conn_close(cur); | 102 * for loop, or something. |
82 return -1; | 103 * |
83 } | 104 * Thanks to Graham Booker for providing this improved checksum routine, |
84 | 105 * which is simpler and should be more accurate than Josh Myer's original |
85 newconn->type = AIM_CONN_TYPE_RENDEZVOUS; | 106 * code. -- wtm |
86 newconn->fd = acceptfd; | 107 * |
87 | 108 * This algorithim works every time I have tried it. The other fails |
88 if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { | 109 * sometimes. So, AOL who thought this up? It has got to be the weirdest |
89 struct aim_directim_intdata *priv; | 110 * checksum I have ever seen. |
90 aim_rxcallback_t userfunc; | 111 * |
91 | 112 * @param buffer Buffer of data to checksum. Man I'd like to buff her... |
92 priv = (struct aim_directim_intdata *)(newconn->internal = cur->internal); | 113 * @param bufsize Size of buffer. |
93 cur->internal = NULL; | 114 * @param prevcheck Previous checksum. |
94 | 115 */ |
95 snprintf(priv->ip, sizeof(priv->ip), "%s:%u", | 116 faim_export fu32_t aim_oft_checksum(const unsigned char *buffer, int bufferlen, fu32_t prevcheck) |
96 inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), | 117 { |
97 ntohs(((struct sockaddr_in *)&cliaddr)->sin_port)); | 118 fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck; |
98 | |
99 if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE))) | |
100 ret = userfunc(sess, NULL, newconn, cur); | |
101 | |
102 } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) { | |
103 #if 0 | |
104 struct aim_filetransfer_priv *priv; | |
105 aim_rxcallback_t userfunc; | |
106 | |
107 newconn->priv = cur->priv; | |
108 cur->priv = NULL; | |
109 priv = (struct aim_filetransfer_priv *)newconn->priv; | |
110 | |
111 snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port)); | |
112 | |
113 if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE))) | |
114 ret = userfunc(sess, NULL, newconn, cur); | |
115 #endif | |
116 } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) { | |
117 struct aim_filetransfer_priv *ft; | |
118 aim_rxcallback_t userfunc; | |
119 | |
120 /* The new conn automatically inherits the internal value | |
121 * of cur. */ | |
122 cur->internal = NULL; | |
123 ft = (struct aim_filetransfer_priv *)newconn->internal; | |
124 | |
125 snprintf(ft->ip, sizeof(ft->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port)); | |
126 | |
127 if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEINITIATE))) | |
128 ret = userfunc(sess, NULL, newconn, cur); | |
129 } else { | |
130 faimdprintf(sess, 1,"Got a Connection on a listener that's not Rendezvous Closing conn.\n"); | |
131 aim_conn_close(newconn); | |
132 ret = -1; | |
133 } | |
134 | |
135 return ret; | |
136 } | |
137 | |
138 /** | |
139 * aim_send_typing - send client-to-client typing notification over established connection | |
140 * @sess: session to conn | |
141 * @conn: directim connection | |
142 * @typing: If true, notify user has started typing; if false, notify user has stopped. | |
143 * | |
144 * The connection must have been previously established. | |
145 */ | |
146 faim_export int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing) | |
147 { | |
148 struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal; | |
149 aim_frame_t *fr; | |
150 aim_bstream_t *hdrbs; | |
151 fu8_t *hdr; | |
152 int hdrlen = 0x44; | |
153 | |
154 if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) | |
155 return -EINVAL; | |
156 | |
157 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0))) | |
158 return -ENOMEM; | |
159 memcpy(fr->hdr.rend.magic, "ODC2", 4); | |
160 fr->hdr.rend.hdrlen = hdrlen; | |
161 | |
162 if (!(hdr = calloc(1, hdrlen))) { | |
163 aim_frame_destroy(fr); | |
164 return -ENOMEM; | |
165 } | |
166 | |
167 hdrbs = &(fr->data); | |
168 aim_bstream_init(hdrbs, hdr, hdrlen); | |
169 | |
170 aimbs_put16(hdrbs, 0x0006); | |
171 aimbs_put16(hdrbs, 0x0000); | |
172 aimbs_putraw(hdrbs, intdata->cookie, 8); | |
173 aimbs_put16(hdrbs, 0x0000); | |
174 aimbs_put16(hdrbs, 0x0000); | |
175 aimbs_put16(hdrbs, 0x0000); | |
176 aimbs_put16(hdrbs, 0x0000); | |
177 aimbs_put32(hdrbs, 0x00000000); | |
178 aimbs_put16(hdrbs, 0x0000); | |
179 aimbs_put16(hdrbs, 0x0000); | |
180 aimbs_put16(hdrbs, 0x0000); | |
181 | |
182 /* flags -- 0x000e for "started typing", 0x0002 for "stopped typing */ | |
183 aimbs_put16(hdrbs, ( typing ? 0x000e : 0x0002)); | |
184 | |
185 aimbs_put16(hdrbs, 0x0000); | |
186 aimbs_put16(hdrbs, 0x0000); | |
187 aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn)); | |
188 | |
189 aim_bstream_setpos(hdrbs, 52); /* bleeehh */ | |
190 | |
191 aimbs_put8(hdrbs, 0x00); | |
192 aimbs_put16(hdrbs, 0x0000); | |
193 aimbs_put16(hdrbs, 0x0000); | |
194 aimbs_put16(hdrbs, 0x0000); | |
195 aimbs_put16(hdrbs, 0x0000); | |
196 aimbs_put16(hdrbs, 0x0000); | |
197 aimbs_put16(hdrbs, 0x0000); | |
198 aimbs_put16(hdrbs, 0x0000); | |
199 aimbs_put8(hdrbs, 0x00); | |
200 | |
201 /* end of hdr */ | |
202 | |
203 aim_tx_enqueue(sess, fr); | |
204 | |
205 return 0; | |
206 } | |
207 | |
208 /** | |
209 * aim_send_im_direct - send IM client-to-client over established connection | |
210 * @sess: session to conn | |
211 * @conn: directim connection | |
212 * @msg: null-terminated string to send. | |
213 * @len: The length of the message to send, including binary data. | |
214 * @encoding: 0 for ascii, 2 for Unicode, 3 for ISO 8859-1 | |
215 * | |
216 * Call this just like you would aim_send_im, to send a directim. You | |
217 * _must_ have previously established the directim connection. | |
218 */ | |
219 faim_export int aim_send_im_direct(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding) | |
220 { | |
221 struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal; | |
222 aim_frame_t *fr; | |
223 aim_bstream_t *hdrbs; | |
224 int hdrlen = 0x44; | |
225 fu8_t *hdr; | |
226 | |
227 if (!sess || !conn || !msg || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) | |
228 return -EINVAL; | |
229 | |
230 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, len))) | |
231 return -ENOMEM; | |
232 | |
233 memcpy(fr->hdr.rend.magic, "ODC2", 4); | |
234 fr->hdr.rend.hdrlen = hdrlen; | |
235 | |
236 if (!(hdr = calloc(1, hdrlen + len))) { | |
237 aim_frame_destroy(fr); | |
238 return -ENOMEM; | |
239 } | |
240 | |
241 hdrbs = &(fr->data); | |
242 aim_bstream_init(hdrbs, hdr, hdrlen + len); | |
243 | |
244 aimbs_put16(hdrbs, 0x0006); | |
245 aimbs_put16(hdrbs, 0x0000); | |
246 aimbs_putraw(hdrbs, intdata->cookie, 8); | |
247 aimbs_put16(hdrbs, 0x0000); | |
248 aimbs_put16(hdrbs, 0x0000); | |
249 aimbs_put16(hdrbs, 0x0000); | |
250 aimbs_put16(hdrbs, 0x0000); | |
251 aimbs_put32(hdrbs, len); | |
252 aimbs_put16(hdrbs, encoding); | |
253 aimbs_put16(hdrbs, 0x0000); | |
254 aimbs_put16(hdrbs, 0x0000); | |
255 | |
256 /* flags -- 0x000e for "started typing", 0x0002 for "stopped typing, 0x0000 for message */ | |
257 aimbs_put16(hdrbs, 0x0000); | |
258 | |
259 aimbs_put16(hdrbs, 0x0000); | |
260 aimbs_put16(hdrbs, 0x0000); | |
261 aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn)); | |
262 | |
263 aim_bstream_setpos(hdrbs, 52); /* bleeehh */ | |
264 | |
265 aimbs_put8(hdrbs, 0x00); | |
266 aimbs_put16(hdrbs, 0x0000); | |
267 aimbs_put16(hdrbs, 0x0000); | |
268 aimbs_put16(hdrbs, 0x0000); | |
269 aimbs_put16(hdrbs, 0x0000); | |
270 aimbs_put16(hdrbs, 0x0000); | |
271 aimbs_put16(hdrbs, 0x0000); | |
272 aimbs_put16(hdrbs, 0x0000); | |
273 aimbs_put8(hdrbs, 0x00); | |
274 | |
275 /* end of hdr2 */ | |
276 | |
277 #if 0 /* XXX this is how you send buddy icon info... */ | |
278 i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008); | |
279 i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c); | |
280 i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); | |
281 i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466); | |
282 i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001); | |
283 i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f); | |
284 i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e); | |
285 i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8); | |
286 #endif | |
287 aimbs_putraw(hdrbs, msg, len); | |
288 | |
289 aim_tx_enqueue(sess, fr); | |
290 | |
291 return 0; | |
292 } | |
293 | |
294 static int getlocalip(fu8_t *ip) | |
295 { | |
296 struct hostent *hptr; | |
297 char localhost[129]; | |
298 | |
299 /* XXX if available, use getaddrinfo() */ | |
300 /* XXX allow client to specify which IP to use for multihomed boxes */ | |
301 | |
302 if (gethostname(localhost, 128) < 0) | |
303 return -1; | |
304 | |
305 if (!(hptr = gethostbyname(localhost))) | |
306 return -1; | |
307 | |
308 memcpy(ip, hptr->h_addr_list[0], 4); | |
309 | |
310 return 0; | |
311 } | |
312 | |
313 /** | |
314 * aim_directim_intitiate - For those times when we want to open up the directim channel ourselves. | |
315 * @sess: your session, | |
316 * @conn: the BOS conn, | |
317 * @priv: a dummy priv value (we'll let it get filled in later) (if you pass a %NULL, we alloc one) | |
318 * @destsn: the SN to connect to. | |
319 * | |
320 */ | |
321 faim_export aim_conn_t *aim_directim_initiate(aim_session_t *sess, const char *destsn) | |
322 { | |
323 aim_conn_t *newconn; | |
324 aim_msgcookie_t *cookie; | |
325 struct aim_directim_intdata *priv; | |
326 int listenfd; | |
327 fu16_t port = 4443; | |
328 fu8_t localip[4]; | |
329 fu8_t ck[8]; | |
330 | |
331 if (getlocalip(localip) == -1) | |
332 return NULL; | |
333 | |
334 if ((listenfd = listenestablish(port)) == -1) | |
335 return NULL; | |
336 | |
337 aim_request_directim(sess, destsn, localip, port, ck); | |
338 | |
339 cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t)); | |
340 memcpy(cookie->cookie, ck, 8); | |
341 cookie->type = AIM_COOKIETYPE_OFTIM; | |
342 | |
343 /* this one is for the cookie */ | |
344 priv = (struct aim_directim_intdata *)calloc(1, sizeof(struct aim_directim_intdata)); | |
345 | |
346 memcpy(priv->cookie, ck, 8); | |
347 strncpy(priv->sn, destsn, sizeof(priv->sn)); | |
348 cookie->data = priv; | |
349 aim_cachecookie(sess, cookie); | |
350 | |
351 /* XXX switch to aim_cloneconn()? */ | |
352 if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) { | |
353 close(listenfd); | |
354 return NULL; | |
355 } | |
356 | |
357 /* this one is for the conn */ | |
358 priv = (struct aim_directim_intdata *)calloc(1, sizeof(struct aim_directim_intdata)); | |
359 | |
360 memcpy(priv->cookie, ck, 8); | |
361 strncpy(priv->sn, destsn, sizeof(priv->sn)); | |
362 | |
363 newconn->fd = listenfd; | |
364 newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; | |
365 newconn->internal = priv; | |
366 newconn->lastactivity = time(NULL); | |
367 | |
368 faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd); | |
369 | |
370 return newconn; | |
371 } | |
372 | |
373 /** | |
374 * aim_sendfile_intitiate - For those times when we want to send the file ourselves. | |
375 * @sess: your session, | |
376 * @conn: the BOS conn, | |
377 * @destsn: the SN to connect to. | |
378 * @filename: the name of the files you want to send | |
379 * | |
380 */ | |
381 faim_export aim_conn_t *aim_sendfile_initiate(aim_session_t *sess, const char *destsn, const char *filename, fu16_t numfiles, fu32_t totsize, char *cookret) | |
382 { | |
383 aim_conn_t *newconn; | |
384 aim_msgcookie_t *cookie; | |
385 struct aim_filetransfer_priv *ft; | |
386 int listenfd; | |
387 | |
388 /* XXX allow different ports */ | |
389 fu16_t port = 4443; | |
390 fu8_t localip[4]; | |
391 fu8_t ck[8]; | |
392 | |
393 if (getlocalip(localip) == -1) | |
394 return NULL; | |
395 | |
396 if ((listenfd = listenestablish(port)) == -1) | |
397 return NULL; | |
398 | |
399 aim_request_sendfile(sess, destsn, filename, numfiles, totsize, localip, port, ck); | |
400 | |
401 cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t)); | |
402 memcpy(cookie->cookie, ck, 8); | |
403 cookie->type = AIM_COOKIETYPE_OFTSEND; | |
404 memcpy(cookret, ck, 8); | |
405 | |
406 /* this one is for the cookie */ | |
407 ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)); | |
408 | |
409 memcpy(ft->cookie, ck, 8); | |
410 strncpy(ft->sn, destsn, sizeof(ft->sn)); | |
411 cookie->data = ft; | |
412 aim_cachecookie(sess, cookie); | |
413 | |
414 if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) { | |
415 close(listenfd); | |
416 return NULL; | |
417 } | |
418 | |
419 /* this one is for the conn */ | |
420 ft = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)); | |
421 | |
422 memcpy(ft->cookie, ck, 8); | |
423 strncpy(ft->sn, destsn, sizeof(ft->sn)); | |
424 | |
425 newconn->fd = listenfd; | |
426 newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE; | |
427 newconn->internal = ft; | |
428 newconn->lastactivity = time(NULL); | |
429 | |
430 faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd); | |
431 | |
432 return newconn; | |
433 } | |
434 | |
435 #if 0 | |
436 /** | |
437 * unsigned int aim_oft_listener_clean - close up old listeners | |
438 * @sess: session to clean up in | |
439 * @age: maximum age in seconds | |
440 * | |
441 * returns number closed, -1 on error. | |
442 */ | |
443 faim_export unsigned int aim_oft_listener_clean(aim_session_t *sess, time_t age) | |
444 { | |
445 aim_conn_t *cur; | |
446 time_t now; | |
447 unsigned int hit = 0; | |
448 | |
449 if (!sess) | |
450 return -1; | |
451 now = time(NULL); | |
452 | |
453 for(cur = sess->connlist;cur; cur = cur->next) | |
454 if (cur->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { | |
455 if (cur->lastactivity < (now - age) ) { | |
456 aim_conn_close(cur); | |
457 hit++; | |
458 } | |
459 } | |
460 return hit; | |
461 } | |
462 #endif | |
463 | |
464 faim_export const char *aim_directim_getsn(aim_conn_t *conn) | |
465 { | |
466 struct aim_directim_intdata *intdata; | |
467 | |
468 if (!conn) | |
469 return NULL; | |
470 | |
471 if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || | |
472 (conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)) | |
473 return NULL; | |
474 | |
475 if (!conn->internal) | |
476 return NULL; | |
477 | |
478 intdata = (struct aim_directim_intdata *)conn->internal; | |
479 | |
480 return intdata->sn; | |
481 } | |
482 | |
483 /** | |
484 * aim_directim_connect - connect to buddy for directim | |
485 * @sess: the session to append the conn to, | |
486 * @sn: the SN we're connecting to | |
487 * @addr: address to connect to | |
488 * | |
489 * This is a wrapper for aim_newconn. | |
490 * | |
491 * If addr is NULL, the socket is not created, but the connection is | |
492 * allocated and setup to connect. | |
493 * | |
494 */ | |
495 faim_export aim_conn_t *aim_directim_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie) | |
496 { | |
497 aim_conn_t *newconn; | |
498 struct aim_directim_intdata *intdata; | |
499 | |
500 if (!sess || !sn) | |
501 return NULL; | |
502 | |
503 if (!(intdata = malloc(sizeof(struct aim_directim_intdata)))) | |
504 return NULL; | |
505 memset(intdata, 0, sizeof(struct aim_directim_intdata)); | |
506 | |
507 memcpy(intdata->cookie, cookie, 8); | |
508 strncpy(intdata->sn, sn, sizeof(intdata->sn)); | |
509 if (addr) | |
510 strncpy(intdata->ip, addr, sizeof(intdata->ip)); | |
511 | |
512 /* XXX verify that non-blocking connects actually work */ | |
513 if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr))) { | |
514 free(intdata); | |
515 return NULL; | |
516 } | |
517 | |
518 if (!newconn) { | |
519 free(intdata); | |
520 return newconn; | |
521 } | |
522 | |
523 newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; | |
524 newconn->internal = intdata; | |
525 | |
526 return newconn; | |
527 } | |
528 | |
529 /** | |
530 * aim_directim_getconn - find a directim conn for buddy name | |
531 * @sess: your session, | |
532 * @name: the name to get, | |
533 * | |
534 * returns conn for directim with name, %NULL if none found. | |
535 * | |
536 */ | |
537 faim_export aim_conn_t *aim_directim_getconn(aim_session_t *sess, const char *name) | |
538 { | |
539 aim_conn_t *cur; | |
540 | |
541 if (!sess || !name || !strlen(name)) | |
542 return NULL; | |
543 | |
544 for (cur = sess->connlist; cur; cur = cur->next) { | |
545 struct aim_directim_intdata *intdata; | |
546 | |
547 if ((cur->type != AIM_CONN_TYPE_RENDEZVOUS) || (cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)) | |
548 continue; | |
549 | |
550 intdata = cur->internal; | |
551 | |
552 if (aim_sncmp(intdata->sn, name) == 0) | |
553 break; | |
554 } | |
555 | |
556 return cur; | |
557 } | |
558 | |
559 /** | |
560 * aim_accepttransfer - accept a file transfer request | |
561 * @sess: the session, | |
562 * @conn: the BOS conn for the CAP reply | |
563 * @sn: the screenname to send it to, | |
564 * @cookie: the cookie used | |
565 * @ip: the ip to connect to | |
566 * @port: the port to use | |
567 * @rendid: capability type (%AIM_CAPS_GETFILE or %AIM_CAPS_SENDFILE) | |
568 * | |
569 * @listingfiles: number of files to share | |
570 * @listingtotsize: total size of shared files | |
571 * @listingsize: length of the listing file(buffer) | |
572 * @listingchecksum: checksum of the listing | |
573 * | |
574 * Returns new connection or %NULL on error. | |
575 * | |
576 * XXX this should take a struct. | |
577 */ | |
578 faim_export aim_conn_t *aim_accepttransfer(aim_session_t *sess, aim_conn_t *conn, const char *sn, | |
579 const fu8_t *cookie, const fu8_t *ip, | |
580 fu16_t port, fu16_t rendid, ...) | |
581 { | |
582 aim_frame_t *newpacket; | |
583 aim_conn_t *newconn; | |
584 struct aim_filetransfer_priv *priv; | |
585 int i; | 119 int i; |
586 char addr[21]; | 120 unsigned short val; |
587 | 121 |
588 if (!sess || !conn || !sn || !cookie || !ip) { | 122 for (i=0; i<bufferlen; i++) { |
589 return NULL; | 123 oldcheck = check; |
590 } | 124 if (i&1) |
591 | 125 val = buffer[i]; |
592 /* OSCAR CAP accept packet */ | 126 else |
593 | 127 val = buffer[i] << 8; |
594 if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) { | 128 check -= val; |
595 return NULL; | 129 /* |
596 } | 130 * The following appears to be necessary.... It happens |
597 | 131 * every once in a while and the checksum doesn't fail. |
598 aim_putsnac(&newpacket->data, 0x0004, 0x0006, 0x0000, sess->snacid_next); | 132 */ |
599 | 133 if (check > oldcheck) |
600 for (i = 0; i < 8; i++) | 134 check--; |
601 aimbs_put8(&newpacket->data, cookie[i]); | 135 } |
602 | 136 check = ((check & 0x0000ffff) + (check >> 16)); |
603 aimbs_put16(&newpacket->data, 0x0002); | 137 check = ((check & 0x0000ffff) + (check >> 16)); |
604 aimbs_put8(&newpacket->data, strlen(sn)); | 138 return check << 16; |
605 aimbs_putraw(&newpacket->data, sn, strlen(sn)); | 139 } |
606 aimbs_put16(&newpacket->data, 0x0005); | 140 |
607 aimbs_put16(&newpacket->data, 0x001a); | 141 /** |
608 aimbs_put16(&newpacket->data, AIM_RENDEZVOUS_ACCEPT); | 142 * Create a listening socket on a given port. |
609 | 143 * |
610 for (i = 0; i < 8; i++) /* yes, again... */ | 144 * XXX - Give the client author the responsibility of setting up a |
611 aimbs_put8(&newpacket->data, cookie[i]); | 145 * listener, then we no longer have a libfaim problem with broken |
612 | 146 * solaris *innocent smile* -- jbm |
613 aim_putcap(&newpacket->data, rendid); | 147 * |
614 aim_tx_enqueue(sess, newpacket); | 148 * @param portnum The port number to bind to. |
615 | 149 * @return The file descriptor of the listening socket. |
616 snprintf(addr, sizeof(addr), "%s:%d", ip, port); | |
617 newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr); | |
618 | |
619 if (newconn->status & AIM_CONN_STATUS_CONNERR) { | |
620 return NULL; | |
621 } | |
622 | |
623 if (!newconn || (newconn->fd == -1)) { | |
624 perror("aim_newconn"); | |
625 faimdprintf(sess, 2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0); | |
626 return newconn; | |
627 } | |
628 | |
629 priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)); | |
630 | |
631 memcpy(priv->cookie, cookie, 8); | |
632 priv->state = 0; | |
633 strncpy(priv->sn, sn, MAXSNLEN); | |
634 strncpy(priv->ip, ip, sizeof(priv->ip)); | |
635 newconn->internal = (void *)priv; | |
636 | |
637 faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd); | |
638 | |
639 if (rendid == AIM_CAPS_GETFILE) { | |
640 return NULL; /* This should never happen for now. -- wtm */ | |
641 | |
642 #if 0 | |
643 struct aim_fileheader_t *fh; | |
644 aim_frame_t *newoft; | |
645 aim_msgcookie_t *cachedcook; | |
646 /* XXX take the following parameters fu16_t listingfiles, | |
647 fu16_t listingtotsize, | |
648 fu16_t listingsize, | |
649 fu32_t listingchecksum, */ | |
650 | |
651 newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE; | |
652 | |
653 faimdprintf(sess, 2, "faim: getfile request accept\n"); | |
654 | |
655 if (!(newoft = aim_tx_new(sess, newconn, AIM_FRAMETYPE_OFT, 0x1108, 0))) { | |
656 faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); | |
657 /* XXX: conn leak here */ | |
658 return NULL; | |
659 } | |
660 | |
661 memcpy(newoft->hdr.oft.magic, "OFT2", 4); | |
662 newoft->hdr.oft.hdr2len = 0x100 - 8; | |
663 | |
664 if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) { | |
665 /* XXX: conn leak here */ | |
666 perror("calloc"); | |
667 return NULL; | |
668 } | |
669 | |
670 fh->encrypt = 0x0000; | |
671 fh->compress = 0x0000; | |
672 fh->totfiles = listingfiles; | |
673 fh->filesleft = listingfiles; /* is this right -- total parts and parts left?*/ | |
674 fh->totparts = 0x0001; | |
675 fh->partsleft = 0x0001; | |
676 fh->totsize = listingtotsize; | |
677 fh->size = listingsize; /* ls -l listing.txt */ | |
678 fh->modtime = (int)time(NULL); /* we'll go with current time for now */ | |
679 fh->checksum = listingchecksum; | |
680 fh->rfcsum = 0x00000000; | |
681 fh->rfsize = 0x00000000; | |
682 fh->cretime = 0x00000000; | |
683 fh->rfcsum = 0x00000000; | |
684 fh->nrecvd = 0x00000000; | |
685 fh->recvcsum = 0x00000000; | |
686 memset(fh->idstring, 0, sizeof(fh->idstring)); | |
687 strncpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring)); | |
688 fh->flags = 0x02; | |
689 fh->lnameoffset = 0x1a; | |
690 fh->lsizeoffset = 0x10; | |
691 memset(fh->dummy, 0, sizeof(fh->dummy)); | |
692 memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo)); | |
693 | |
694 /* we need to figure out these encodings for filenames */ | |
695 fh->nencode = 0x0000; | |
696 fh->nlanguage = 0x0000; | |
697 memset(fh->name, 0, sizeof(fh->name)); | |
698 strncpy(fh->name, "listing.txt", sizeof(fh->name)); | |
699 | |
700 if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { | |
701 aim_frame_destroy(newoft); | |
702 /* XXX: conn leak */ | |
703 perror("calloc (1)"); | |
704 return NULL; | |
705 } | |
706 | |
707 memcpy(fh->bcookie, cookie, 8); | |
708 | |
709 if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, fh))) | |
710 faimdprintf(sess, 1, "eek, bh fail!\n"); | |
711 | |
712 aim_tx_enqueue(sess, newoft); | |
713 | |
714 if (!(cachedcook = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t)))) { | |
715 faimdprintf(sess, 1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n"); | |
716 /* XXX: more cleanup, conn leak */ | |
717 perror("calloc (2)"); | |
718 return NULL; | |
719 } | |
720 | |
721 memcpy(&(priv->fh), fh, sizeof(struct aim_fileheader_t)); | |
722 memcpy(cachedcook->cookie, cookie, 8); | |
723 | |
724 cachedcook->type = AIM_COOKIETYPE_OFTGET; | |
725 /* XXX doesn't priv need to be copied so we don't | |
726 * double free? -- wtm | |
727 */ | |
728 cachedcook->data = (void *)priv; | |
729 | |
730 if (aim_cachecookie(sess, cachedcook) == -1) | |
731 faimdprintf(sess, 1, "faim: ERROR caching message cookie\n"); | |
732 | |
733 free(fh); | |
734 #endif | |
735 | |
736 } else if (rendid == AIM_CAPS_SENDFILE) { | |
737 newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE; | |
738 priv->fh.recvcsum = 0xffff0000; | |
739 } else { | |
740 return NULL; | |
741 } | |
742 | |
743 return newconn; | |
744 } | |
745 | |
746 /* conn is a BOS connection over which to send the cancel msg */ | |
747 faim_export int aim_canceltransfer(aim_session_t *sess, aim_conn_t *conn, | |
748 const char *cookie, const char *sn, int rendid) | |
749 { | |
750 aim_frame_t *newpacket; | |
751 int i; | |
752 | |
753 if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) { | |
754 return 1; | |
755 } | |
756 | |
757 aim_putsnac(&newpacket->data, 0x0004, 0x0006, 0x0000, sess->snacid_next); | |
758 | |
759 for (i = 0; i < 8; i++) | |
760 aimbs_put8(&newpacket->data, cookie[i]); | |
761 | |
762 aimbs_put16(&newpacket->data, 0x0002); | |
763 aimbs_put8(&newpacket->data, strlen(sn)); | |
764 aimbs_putraw(&newpacket->data, sn, strlen(sn)); | |
765 aimbs_put16(&newpacket->data, 0x0005); | |
766 aimbs_put16(&newpacket->data, 0x001a); | |
767 aimbs_put16(&newpacket->data, AIM_RENDEZVOUS_CANCEL); | |
768 | |
769 for (i = 0; i < 8; i++) | |
770 aimbs_put8(&newpacket->data, cookie[i]); | |
771 | |
772 aim_putcap(&newpacket->data, rendid); | |
773 aim_tx_enqueue(sess, newpacket); | |
774 | |
775 return 0; | |
776 } | |
777 | |
778 /** | |
779 * aim_getlisting(FILE *file) -- get an aim_fileheader_t for a given FILE* | |
780 * @file is an opened listing file | |
781 * | |
782 * returns a pointer to the filled-in fileheader_t | |
783 * | |
784 * Currently omits checksum. we'll fix this when AOL breaks us, i | |
785 * guess. | |
786 * | |
787 */ | |
788 faim_export struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *file) | |
789 { | |
790 return NULL; | |
791 #if 0 | |
792 struct aim_fileheader_t *fh; | |
793 u_long totsize = 0, size = 0, checksum = 0xffff0000; | |
794 short totfiles = 0; | |
795 char *linebuf, sizebuf[9]; | |
796 int linelength = 1024; | |
797 | |
798 /* XXX: if we have a line longer than 1024chars, God help us. */ | |
799 if ((linebuf = (char *)calloc(1, linelength)) == NULL ) { | |
800 faimdprintf(sess, 2, "linebuf calloc failed\n"); | |
801 return NULL; | |
802 } | |
803 | |
804 if (fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */ | |
805 perror("getlisting END1 fseek:"); | |
806 faimdprintf(sess, 2, "getlising fseek END1 error\n"); | |
807 } | |
808 | |
809 if ((size = ftell(file)) == -1) { | |
810 perror("getlisting END1 getpos:"); | |
811 faimdprintf(sess, 2, "getlising getpos END1 error\n"); | |
812 } | |
813 | |
814 if (fseek(file, 0, SEEK_SET) != 0) { | |
815 perror("getlesting fseek(SET):"); | |
816 faimdprintf(sess, 2, "faim: getlisting: couldn't seek to beginning of listing file\n"); | |
817 } | |
818 | |
819 memset(linebuf, 0, linelength); | |
820 | |
821 size = 0; | |
822 | |
823 while(fgets(linebuf, linelength, file)) { | |
824 totfiles++; | |
825 memset(sizebuf, 0, 9); | |
826 | |
827 size += strlen(linebuf); | |
828 | |
829 if (strlen(linebuf) < 23) { | |
830 faimdprintf(sess, 2, "line \"%s\" too short. skipping\n", linebuf); | |
831 continue; | |
832 } | |
833 | |
834 if (linebuf[strlen(linebuf)-1] != '\n') { | |
835 faimdprintf(sess, 2, "faim: OFT: getlisting -- hit EOF or line too long!\n"); | |
836 } | |
837 | |
838 memcpy(sizebuf, linebuf+17, 8); | |
839 | |
840 totsize += strtol(sizebuf, NULL, 10); | |
841 memset(linebuf, 0, linelength); | |
842 } | |
843 | |
844 if (fseek(file, 0, SEEK_SET) == -1) { | |
845 perror("getlisting END2 fseek:"); | |
846 faimdprintf(sess, 2, "getlising fseek END2 error\n"); | |
847 } | |
848 | |
849 free(linebuf); | |
850 | |
851 /* we're going to ignore checksumming the data for now -- that | |
852 * requires walking the whole listing.txt. it should probably be | |
853 * done at register time and cached, but, eh. */ | |
854 | |
855 if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) | |
856 return NULL; | |
857 | |
858 fh->encrypt = 0x0000; | |
859 fh->compress = 0x0000; | |
860 fh->totfiles = totfiles; | |
861 fh->filesleft = totfiles; /* is this right? */ | |
862 fh->totparts = 0x0001; | |
863 fh->partsleft = 0x0001; | |
864 fh->totsize = totsize; | |
865 fh->size = size; /* ls -l listing.txt */ | |
866 fh->modtime = (int)time(NULL); /* we'll go with current time for now */ | |
867 fh->checksum = checksum; /* XXX: checksum ! */ | |
868 fh->rfcsum = 0x00000000; | |
869 fh->rfsize = 0x00000000; | |
870 fh->cretime = 0x00000000; | |
871 fh->rfcsum = 0x00000000; | |
872 fh->nrecvd = 0x00000000; | |
873 fh->recvcsum = 0x00000000; | |
874 | |
875 /* memset(fh->idstring, 0, sizeof(fh->idstring)); */ | |
876 memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring)); | |
877 memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring)); | |
878 | |
879 fh->flags = 0x02; | |
880 fh->lnameoffset = 0x1a; | |
881 fh->lsizeoffset = 0x10; | |
882 | |
883 /* memset(fh->dummy, 0, sizeof(fh->dummy)); */ | |
884 memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo)); | |
885 | |
886 fh->nencode = 0x0000; /* we need to figure out these encodings for filenames */ | |
887 fh->nlanguage = 0x0000; | |
888 | |
889 /* memset(fh->name, 0, sizeof(fh->name)); */ | |
890 strncpy(fh->name, "listing.txt", sizeof(fh->name)); | |
891 memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name)); | |
892 | |
893 faimdprintf(sess, 2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name)))); | |
894 return fh; | |
895 #endif | |
896 } | |
897 | |
898 /** | |
899 * aim_listenestablish - create a listening socket on a port. | |
900 * @portnum: the port number to bind to. | |
901 * | |
902 * you need to call accept() when it's connected. returns your fd | |
903 * | |
904 * XXX: give the client author the responsibility of setting up a | |
905 * listener, then we no longer have a libfaim problem with broken | |
906 * solaris *innocent smile* -jbm | |
907 */ | 150 */ |
908 static int listenestablish(fu16_t portnum) | 151 static int listenestablish(fu16_t portnum) |
909 { | 152 { |
910 #if HAVE_GETADDRINFO | 153 #if HAVE_GETADDRINFO |
911 int listenfd; | 154 int listenfd; |
916 snprintf(serv, sizeof(serv), "%d", portnum); | 159 snprintf(serv, sizeof(serv), "%d", portnum); |
917 memset(&hints, 0, sizeof(struct addrinfo)); | 160 memset(&hints, 0, sizeof(struct addrinfo)); |
918 hints.ai_flags = AI_PASSIVE; | 161 hints.ai_flags = AI_PASSIVE; |
919 hints.ai_family = AF_UNSPEC; | 162 hints.ai_family = AF_UNSPEC; |
920 hints.ai_socktype = SOCK_STREAM; | 163 hints.ai_socktype = SOCK_STREAM; |
921 if (getaddrinfo(NULL /*any IP*/, serv, &hints, &res) != 0) { | 164 if (getaddrinfo(NULL /* any IP */, serv, &hints, &res) != 0) { |
922 perror("getaddrinfo"); | 165 perror("getaddrinfo"); |
923 return -1; | 166 return -1; |
924 } | 167 } |
925 ressave = res; | 168 ressave = res; |
926 do { | 169 do { |
927 listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | 170 listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); |
928 if (listenfd < 0) | 171 if (listenfd < 0) |
929 continue; | 172 continue; |
930 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); | 173 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); |
931 if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) | 174 if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) |
932 break; | 175 break; /* success */ |
933 /* success */ | |
934 close(listenfd); | 176 close(listenfd); |
935 } while ( (res = res->ai_next) ); | 177 } while ( (res = res->ai_next) ); |
936 | 178 |
937 if (!res) | 179 if (!res) |
938 return -1; | 180 return -1; |
939 | 181 |
940 if (listen(listenfd, 1024)!=0) { | |
941 perror("listen"); | |
942 return -1; | |
943 } | |
944 | |
945 fcntl(listenfd, F_SETFL, O_NONBLOCK); | |
946 | |
947 freeaddrinfo(ressave); | 182 freeaddrinfo(ressave); |
948 return listenfd; | |
949 #else | 183 #else |
950 int listenfd; | 184 int listenfd; |
951 const int on = 1; | 185 const int on = 1; |
952 struct sockaddr_in sockin; | 186 struct sockaddr_in sockin; |
953 | 187 |
954 if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { | 188 if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { |
955 perror("socket(listenfd)"); | 189 perror("socket"); |
956 return -1; | 190 return -1; |
957 } | 191 } |
958 | 192 |
959 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) { | 193 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) { |
960 perror("setsockopt(listenfd)"); | 194 perror("setsockopt"); |
961 close(listenfd); | 195 close(listenfd); |
962 return -1; | 196 return -1; |
963 } | 197 } |
964 | 198 |
965 memset(&sockin, 0, sizeof(struct sockaddr_in)); | 199 memset(&sockin, 0, sizeof(struct sockaddr_in)); |
966 sockin.sin_family = AF_INET; | 200 sockin.sin_family = AF_INET; |
967 sockin.sin_port = htons(portnum); | 201 sockin.sin_port = htons(portnum); |
968 | 202 |
969 if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) { | 203 if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) { |
970 perror("bind(listenfd)"); | 204 perror("bind"); |
971 close(listenfd); | 205 close(listenfd); |
972 return -1; | 206 return -1; |
973 } | 207 } |
208 #endif | |
209 | |
974 if (listen(listenfd, 4) != 0) { | 210 if (listen(listenfd, 4) != 0) { |
975 perror("listen(listenfd)"); | 211 perror("listen"); |
976 close(listenfd); | 212 close(listenfd); |
977 return -1; | 213 return -1; |
978 } | 214 } |
979 fcntl(listenfd, F_SETFL, O_NONBLOCK); | 215 fcntl(listenfd, F_SETFL, O_NONBLOCK); |
216 | |
980 return listenfd; | 217 return listenfd; |
218 } | |
219 | |
220 /** | |
221 * After establishing a listening socket, this is called to accept a connection. It | |
222 * clones the conn used by the listener, and passes both of these to a signal handler. | |
223 * The signal handler should close the listener conn and keep track of the new conn, | |
224 * since this is what is used for file transfers and what not. | |
225 * | |
226 * @param sess The session. | |
227 * @param cur The conn the incoming connection is on. | |
228 * @return Return 0 if no errors, otherwise return the error number. | |
229 */ | |
230 faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur) | |
231 { | |
232 int acceptfd = 0; | |
233 struct sockaddr addr; | |
234 socklen_t addrlen = sizeof(addr); | |
235 int ret = 0; | |
236 aim_conn_t *newconn; | |
237 char ip[20]; | |
238 int port; | |
239 | |
240 debug_printf("AAA - We got a bite! Dude connected to listener\n"); | |
241 if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1) | |
242 return 0; /* not an error */ | |
243 | |
244 if (addr.sa_family != AF_INET) { /* just in case IPv6 really is happening */ | |
245 close(acceptfd); | |
246 aim_conn_close(cur); | |
247 return -1; | |
248 } | |
249 | |
250 strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip)); | |
251 port = ntohs(((struct sockaddr_in *)&addr)->sin_port); | |
252 | |
253 if (!(newconn = aim_cloneconn(sess, cur))) { | |
254 close(acceptfd); | |
255 aim_conn_close(cur); | |
256 return -ENOMEM; | |
257 } | |
258 | |
259 newconn->type = AIM_CONN_TYPE_RENDEZVOUS; | |
260 newconn->fd = acceptfd; | |
261 | |
262 if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { | |
263 aim_rxcallback_t userfunc; | |
264 struct aim_odc_intdata *priv; | |
265 | |
266 priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal); | |
267 cur->internal = NULL; | |
268 snprintf(priv->ip, sizeof(priv->ip), "%s:%u", ip, port); | |
269 | |
270 if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED))) | |
271 ret = userfunc(sess, NULL, newconn, cur); | |
272 | |
273 } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) { | |
274 } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) { | |
275 aim_rxcallback_t userfunc; | |
276 | |
277 if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED))) | |
278 ret = userfunc(sess, NULL, newconn, cur); | |
279 | |
280 } else { | |
281 faimdprintf(sess, 1,"Got a connection on a listener that's not rendezvous. Closing connection.\n"); | |
282 aim_conn_close(newconn); | |
283 ret = -1; | |
284 } | |
285 | |
286 return ret; | |
287 } | |
288 | |
289 /** | |
290 * Send client-to-client typing notification over an established direct connection. | |
291 * | |
292 * @param sess The session. | |
293 * @param conn The already-connected ODC connection. | |
294 * @param typing If true, notify user has started typing; if false, notify user has stopped. | |
295 * @return Return 0 if no errors, otherwise return the error number. | |
296 */ | |
297 faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing) | |
298 { | |
299 struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal; | |
300 aim_frame_t *fr; | |
301 aim_bstream_t *hdrbs; | |
302 fu8_t *hdr; | |
303 int hdrlen = 0x44; | |
304 | |
305 if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) | |
306 return -EINVAL; | |
307 | |
308 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0))) | |
309 return -ENOMEM; | |
310 memcpy(fr->hdr.rend.magic, "ODC2", 4); | |
311 fr->hdr.rend.hdrlen = hdrlen; | |
312 | |
313 if (!(hdr = calloc(1, hdrlen))) { | |
314 aim_frame_destroy(fr); | |
315 return -ENOMEM; | |
316 } | |
317 | |
318 hdrbs = &(fr->data); | |
319 aim_bstream_init(hdrbs, hdr, hdrlen); | |
320 | |
321 aimbs_put16(hdrbs, 0x0006); | |
322 aimbs_put16(hdrbs, 0x0000); | |
323 aimbs_putraw(hdrbs, intdata->cookie, 8); | |
324 aimbs_put16(hdrbs, 0x0000); | |
325 aimbs_put16(hdrbs, 0x0000); | |
326 aimbs_put16(hdrbs, 0x0000); | |
327 aimbs_put16(hdrbs, 0x0000); | |
328 aimbs_put32(hdrbs, 0x00000000); | |
329 aimbs_put16(hdrbs, 0x0000); | |
330 aimbs_put16(hdrbs, 0x0000); | |
331 aimbs_put16(hdrbs, 0x0000); | |
332 | |
333 /* flags -- 0x000e for "started typing", 0x0002 for "stopped typing */ | |
334 aimbs_put16(hdrbs, ( typing ? 0x000e : 0x0002)); | |
335 | |
336 aimbs_put16(hdrbs, 0x0000); | |
337 aimbs_put16(hdrbs, 0x0000); | |
338 aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn)); | |
339 | |
340 aim_bstream_setpos(hdrbs, 52); /* bleeehh */ | |
341 | |
342 aimbs_put8(hdrbs, 0x00); | |
343 aimbs_put16(hdrbs, 0x0000); | |
344 aimbs_put16(hdrbs, 0x0000); | |
345 aimbs_put16(hdrbs, 0x0000); | |
346 aimbs_put16(hdrbs, 0x0000); | |
347 aimbs_put16(hdrbs, 0x0000); | |
348 aimbs_put16(hdrbs, 0x0000); | |
349 aimbs_put16(hdrbs, 0x0000); | |
350 aimbs_put8(hdrbs, 0x00); | |
351 | |
352 /* end of hdr */ | |
353 | |
354 aim_tx_enqueue(sess, fr); | |
355 | |
356 return 0; | |
357 } | |
358 | |
359 /** | |
360 * Send client-to-client IM over an established direct connection. | |
361 * Call this just like you would aim_send_im, to send a directim. | |
362 * | |
363 * @param sess The session. | |
364 * @param conn The already-connected ODC connection. | |
365 * @param msg Null-terminated string to send. | |
366 * @param len The length of the message to send, including binary data. | |
367 * @param encoding 0 for ascii, 2 for Unicode, 3 for ISO 8859-1. | |
368 * @return Return 0 if no errors, otherwise return the error number. | |
369 */ | |
370 faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding) | |
371 { | |
372 aim_frame_t *fr; | |
373 aim_bstream_t *hdrbs; | |
374 struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal; | |
375 int hdrlen = 0x44; | |
376 fu8_t *hdr; | |
377 | |
378 if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg) | |
379 return -EINVAL; | |
380 | |
381 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, len))) | |
382 return -ENOMEM; | |
383 | |
384 memcpy(fr->hdr.rend.magic, "ODC2", 4); | |
385 fr->hdr.rend.hdrlen = hdrlen; | |
386 | |
387 if (!(hdr = calloc(1, hdrlen + len))) { | |
388 aim_frame_destroy(fr); | |
389 return -ENOMEM; | |
390 } | |
391 | |
392 hdrbs = &(fr->data); | |
393 aim_bstream_init(hdrbs, hdr, hdrlen + len); | |
394 | |
395 aimbs_put16(hdrbs, 0x0006); | |
396 aimbs_put16(hdrbs, 0x0000); | |
397 aimbs_putraw(hdrbs, intdata->cookie, 8); | |
398 aimbs_put16(hdrbs, 0x0000); | |
399 aimbs_put16(hdrbs, 0x0000); | |
400 aimbs_put16(hdrbs, 0x0000); | |
401 aimbs_put16(hdrbs, 0x0000); | |
402 aimbs_put32(hdrbs, len); | |
403 aimbs_put16(hdrbs, encoding); | |
404 aimbs_put16(hdrbs, 0x0000); | |
405 aimbs_put16(hdrbs, 0x0000); | |
406 | |
407 /* flags -- 0x000e for "started typing", 0x0002 for "stopped typing, 0x0000 for message */ | |
408 aimbs_put16(hdrbs, 0x0000); | |
409 | |
410 aimbs_put16(hdrbs, 0x0000); | |
411 aimbs_put16(hdrbs, 0x0000); | |
412 aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn)); | |
413 | |
414 aim_bstream_setpos(hdrbs, 52); /* bleeehh */ | |
415 | |
416 aimbs_put8(hdrbs, 0x00); | |
417 aimbs_put16(hdrbs, 0x0000); | |
418 aimbs_put16(hdrbs, 0x0000); | |
419 aimbs_put16(hdrbs, 0x0000); | |
420 aimbs_put16(hdrbs, 0x0000); | |
421 aimbs_put16(hdrbs, 0x0000); | |
422 aimbs_put16(hdrbs, 0x0000); | |
423 aimbs_put16(hdrbs, 0x0000); | |
424 aimbs_put8(hdrbs, 0x00); | |
425 | |
426 /* end of hdr2 */ | |
427 | |
428 #if 0 /* XXX - this is how you send buddy icon info... */ | |
429 aimbs_put16(hdrbs, 0x0008); | |
430 aimbs_put16(hdrbs, 0x000c); | |
431 aimbs_put16(hdrbs, 0x0000); | |
432 aimbs_put16(hdrbs, 0x1466); | |
433 aimbs_put16(hdrbs, 0x0001); | |
434 aimbs_put16(hdrbs, 0x2e0f); | |
435 aimbs_put16(hdrbs, 0x393e); | |
436 aimbs_put16(hdrbs, 0xcac8); | |
981 #endif | 437 #endif |
982 } | 438 aimbs_putraw(hdrbs, msg, len); |
983 | 439 |
984 static int getcommand_getfile(aim_session_t *sess, aim_conn_t *conn) | 440 aim_tx_enqueue(sess, fr); |
985 { | 441 |
986 #if 0 | 442 return 0; |
987 struct aim_filetransfer_priv *ft; | 443 } |
988 aim_rxcallback_t userfunc; | 444 |
989 | 445 /** |
990 ft = conn->priv; | 446 * Get the screen name of the peer of a direct connection. |
991 if (ft->state == 2) { | 447 * |
992 /* waiting on listing data */ | 448 * @param conn The ODC connection. |
993 int ret = 0; | 449 * @return The screen name of the dude, or NULL if there was an anomaly. |
994 char *listing; | 450 */ |
995 aim_frame_t *newoft; | 451 faim_export const char *aim_odc_getsn(aim_conn_t *conn) |
996 | 452 { |
997 if (!(listing = malloc(ft->fh.size))) | 453 struct aim_odc_intdata *intdata; |
998 return -1; | 454 |
999 | 455 if (!conn || !conn->internal) |
1000 ft->state = 0; | 456 return NULL; |
1001 if (aim_recv(conn->fd, listing, ft->fh.size) != ft->fh.size) | 457 |
1002 faimdprintf(sess, 2, "OFT get: file %s was short. (0x%lx)\n", ft->fh.name, ft->fh.size); | 458 if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || |
1003 | 459 (conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)) |
1004 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120b, 0))) { | 460 return NULL; |
1005 faimdprintf(sess, 2, "faim: aim_get_command_rendezvous: getfile listing: tx_new OFT failed\n"); | 461 |
1006 free(listing); | 462 intdata = (struct aim_odc_intdata *)conn->internal; |
1007 aim_conn_close(conn); | 463 |
1008 return -1; | 464 return intdata->sn; |
465 } | |
466 | |
467 /** | |
468 * Find the conn of a direct connection with the given buddy. | |
469 * | |
470 * @param sess The session. | |
471 * @param sn The screen name of the buddy whose direct connection you want to find. | |
472 * @return The conn for the direct connection with the given buddy, or NULL if no | |
473 * connection was found. | |
474 */ | |
475 faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn) | |
476 { | |
477 aim_conn_t *cur; | |
478 struct aim_odc_intdata *intdata; | |
479 | |
480 if (!sess || !sn || !strlen(sn)) | |
481 return NULL; | |
482 | |
483 for (cur = sess->connlist; cur; cur = cur->next) { | |
484 if ((cur->type == AIM_CONN_TYPE_RENDEZVOUS) && (cur->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) { | |
485 intdata = cur->internal; | |
486 if (!aim_sncmp(intdata->sn, sn)) | |
487 return cur; | |
1009 } | 488 } |
1010 | 489 } |
1011 memcpy(newoft->hdr.oft.magic, "OFT2", 4); | 490 |
1012 newoft->hdr.oft.hdr2len = 0x100 - 8; | 491 return NULL; |
1013 | 492 } |
1014 /* Protocol BS - set nrecvd to size of listing, recvcsum to listing checksum, flags to 0 */ | 493 |
1015 | 494 /** |
1016 ft->fh.nrecvd = ft->fh.size; | 495 * For those times when we want to open up the direct connection channel ourselves. |
1017 ft->fh.recvcsum = ft->fh.checksum; | 496 * |
1018 ft->fh.flags = 0; | 497 * You'll want to set up some kind of watcher on this socket. |
1019 | 498 * When the state changes, call aim_handlerendconnection with |
1020 if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { | 499 * the connection returned by this. aim_handlerendconnection |
1021 aim_frame_destroy(newoft); | 500 * will accept the pending connection and stop listening. |
1022 free(listing); | 501 * |
1023 return -1; | 502 * @param sess The session |
1024 } | 503 * @param conn The BOS conn. |
1025 | 504 * @param priv A dummy priv value (we'll let it get filled in later) |
1026 if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) | 505 * (if you pass a %NULL, we alloc one). |
1027 faimdprintf(sess, 2, "eek! bh fail listing\n"); | 506 * @param sn The screen name to connect to. |
1028 | 507 * @return The new connection. |
1029 /* send the 120b */ | 508 */ |
1030 aim_tx_enqueue(sess, newoft); | 509 faim_export aim_conn_t *aim_odc_initiate(aim_session_t *sess, const char *sn) |
1031 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING)) ) | 510 { |
1032 ret = userfunc(sess, NULL, conn, ft, listing); | 511 aim_conn_t *newconn; |
1033 | 512 aim_msgcookie_t *cookie; |
1034 free(listing); | 513 struct aim_odc_intdata *priv; |
1035 return ret; | 514 int listenfd; |
1036 } | 515 fu16_t port = 4443; |
1037 | 516 fu8_t localip[4]; |
1038 if (ft->state == 3) { | 517 fu8_t ck[8]; |
1039 /* waiting on file data */ | 518 |
1040 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE)) ) | 519 if (aim_util_getlocalip(localip) == -1) |
1041 return userfunc(sess, NULL, conn, ft->fh.name, | 520 return NULL; |
1042 ft->fh.size); | 521 |
1043 return 0; | 522 if ((listenfd = listenestablish(port)) == -1) |
1044 } | 523 return NULL; |
1045 | 524 |
1046 if (ft->state == 4) { | 525 aim_im_sendch2_odcrequest(sess, ck, sn, localip, port); |
1047 if( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4)) ) | 526 |
1048 return userfunc(sess, NULL, conn); | 527 cookie = (aim_msgcookie_t *)calloc(1, sizeof(aim_msgcookie_t)); |
1049 aim_conn_close(conn); | 528 memcpy(cookie->cookie, ck, 8); |
1050 return 0; | 529 cookie->type = AIM_COOKIETYPE_OFTIM; |
1051 } | 530 |
1052 | 531 /* this one is for the cookie */ |
1053 return 0; | 532 priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata)); |
1054 #else | 533 |
1055 return -1; | 534 memcpy(priv->cookie, ck, 8); |
1056 #endif | 535 strncpy(priv->sn, sn, sizeof(priv->sn)); |
1057 } | 536 cookie->data = priv; |
1058 | 537 aim_cachecookie(sess, cookie); |
1059 static void connclose_sendfile(aim_session_t *sess, aim_conn_t *conn) | 538 |
1060 { | 539 /* XXX - switch to aim_cloneconn()? */ |
1061 aim_msgcookie_t *cook; | 540 if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) { |
1062 struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->internal; | 541 close(listenfd); |
1063 | 542 return NULL; |
1064 cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTSEND); | 543 } |
1065 aim_cookie_free(sess, cook); | 544 |
1066 | 545 /* this one is for the conn */ |
1067 return; | 546 priv = (struct aim_odc_intdata *)calloc(1, sizeof(struct aim_odc_intdata)); |
1068 } | 547 |
1069 | 548 memcpy(priv->cookie, ck, 8); |
1070 static void connkill_sendfile(aim_session_t *sess, aim_conn_t *conn) | 549 strncpy(priv->sn, sn, sizeof(priv->sn)); |
1071 { | 550 |
1072 free(conn->internal); | 551 newconn->fd = listenfd; |
1073 | 552 newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; |
1074 return; | 553 newconn->internal = priv; |
1075 } | 554 newconn->lastactivity = time(NULL); |
1076 | 555 |
1077 static void connclose_getfile(aim_session_t *sess, aim_conn_t *conn) | 556 return newconn; |
1078 { | 557 } |
1079 #if 0 | 558 |
1080 aim_msgcookie_t *cook; | 559 /** |
1081 struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->priv; | 560 * Connect directly to the given buddy for directim. |
1082 | 561 * |
1083 cook = aim_uncachecookie(sess, priv->cookie, AIM_COOKIETYPE_OFTGET); | 562 * This is a wrapper for aim_newconn. |
1084 aim_cookie_free(sess, cook); | 563 * |
1085 #endif | 564 * If addr is NULL, the socket is not created, but the connection is |
1086 return; | 565 * allocated and setup to connect. |
1087 } | 566 * |
1088 | 567 * @param sess The Godly session. |
1089 static void connkill_getfile(aim_session_t *sess, aim_conn_t *conn) | 568 * @param sn The screen name we're connecting to. I hope it's a girl... |
1090 { | 569 * @param addr Address to connect to. |
1091 | 570 * @return The new connection. |
1092 free(conn->internal); | 571 */ |
1093 | 572 faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie) |
1094 return; | 573 { |
1095 } | 574 aim_conn_t *newconn; |
1096 | 575 struct aim_odc_intdata *intdata; |
1097 static void connclose_directim(aim_session_t *sess, aim_conn_t *conn) | 576 |
1098 { | 577 if (!sess || !sn) |
1099 struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal; | 578 return NULL; |
1100 aim_msgcookie_t *cook; | 579 |
1101 | 580 if (!(intdata = malloc(sizeof(struct aim_odc_intdata)))) |
1102 cook = aim_uncachecookie(sess, intdata->cookie, AIM_COOKIETYPE_OFTIM); | 581 return NULL; |
1103 aim_cookie_free(sess, cook); | 582 memset(intdata, 0, sizeof(struct aim_odc_intdata)); |
1104 | 583 memcpy(intdata->cookie, cookie, 8); |
1105 return; | 584 strncpy(intdata->sn, sn, sizeof(intdata->sn)); |
1106 } | 585 if (addr) |
1107 | 586 strncpy(intdata->ip, addr, sizeof(intdata->ip)); |
1108 static void connkill_directim(aim_session_t *sess, aim_conn_t *conn) | 587 |
1109 { | 588 /* XXX - verify that non-blocking connects actually work */ |
1110 | 589 if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr))) { |
1111 free(conn->internal); | 590 free(intdata); |
1112 | 591 return NULL; |
1113 return; | 592 } |
1114 } | 593 |
1115 | 594 newconn->internal = intdata; |
1116 faim_internal void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn) | 595 newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; |
1117 { | 596 |
1118 | 597 return newconn; |
1119 if (conn->type != AIM_CONN_TYPE_RENDEZVOUS) | 598 } |
1120 return; | 599 |
1121 | 600 /** |
1122 if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) | 601 * Creates a listener socket so the other dude can connect to us. |
1123 connclose_sendfile(sess, conn); | 602 * |
1124 else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) | 603 * You'll want to set up some kind of watcher on this socket. |
1125 connclose_getfile(sess, conn); | 604 * When the state changes, call aim_handlerendconnection with |
1126 else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) | 605 * the connection returned by this. aim_handlerendconnection |
1127 connclose_directim(sess, conn); | 606 * will accept the pending connection and stop listening. |
1128 | 607 * |
1129 return; | 608 * @param sess The session. |
1130 } | 609 * @param cookie This better be Mrs. Fields or I'm going to be pissed. |
1131 | 610 * @param ip Should be 4 bytes, each byte is 1 quartet of the IP address. |
1132 faim_internal void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn) | 611 * @param port Ye olde port number to listen on. |
1133 { | 612 * @return Return the new conn if everything went as planned. Otherwise, |
1134 | 613 * return NULL. |
1135 if (conn->type != AIM_CONN_TYPE_RENDEZVOUS) | 614 */ |
1136 return; | 615 faim_export aim_conn_t *aim_sendfile_listen(aim_session_t *sess, const fu8_t *cookie, const fu8_t *ip, fu16_t port) |
1137 | 616 { |
1138 if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) | 617 aim_conn_t *newconn; |
1139 connkill_sendfile(sess, conn); | 618 int listenfd; |
1140 else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) | 619 |
1141 connkill_getfile(sess, conn); | 620 debug_printf("AAA - listening on port %d\n", port); |
1142 else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) | 621 if ((listenfd = listenestablish(port)) == -1) |
1143 connkill_directim(sess, conn); | 622 return NULL; |
1144 | 623 |
1145 return; | 624 if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_LISTENER, NULL))) { |
1146 } | 625 close(listenfd); |
1147 | 626 return NULL; |
1148 static int handlehdr_directim(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs) | 627 } |
1149 { | 628 |
1150 aim_frame_t fr; | 629 newconn->fd = listenfd; |
1151 aim_rxcallback_t userfunc; | 630 newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE; |
1152 fu32_t payloadlength; | 631 newconn->lastactivity = time(NULL); |
1153 fu16_t flags, encoding; | 632 |
1154 char *snptr = NULL; | 633 return newconn; |
1155 | 634 } |
1156 fr.conn = conn; | 635 |
1157 | 636 /** |
1158 /* XXX ugly */ | 637 * Extract an &aim_fileheader_t from the given buffer. |
1159 aim_bstream_setpos(bs, 20); | 638 * |
1160 payloadlength = aimbs_get32(bs); | 639 * @param bs The should be from an incoming rendezvous packet. |
1161 | 640 * @return A pointer to new struct on success, or NULL on error. |
1162 aim_bstream_setpos(bs, 24); | 641 */ |
1163 encoding = aimbs_get16(bs); | 642 static struct aim_fileheader_t *aim_oft_getheader(aim_bstream_t *bs) |
1164 | |
1165 aim_bstream_setpos(bs, 30); | |
1166 flags = aimbs_get16(bs); | |
1167 | |
1168 aim_bstream_setpos(bs, 36); | |
1169 /* XXX -create an aimbs_getnullstr function? */ | |
1170 snptr = aimbs_getstr(bs, MAXSNLEN); | |
1171 | |
1172 faimdprintf(sess, 2, "faim: OFT frame: handlehdr_directim: %04x / %04x / %s\n", payloadlength, flags, snptr); | |
1173 | |
1174 if (flags & 0x0002) { | |
1175 int ret = 0; | |
1176 | |
1177 if (flags & 0x000c) { | |
1178 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) | |
1179 ret = userfunc(sess, &fr, snptr, 1); | |
1180 return ret; | |
1181 } | |
1182 | |
1183 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) | |
1184 ret = userfunc(sess, &fr, snptr, 0); | |
1185 | |
1186 return ret; | |
1187 | |
1188 } else if (((flags & 0x000f) == 0x0000) && payloadlength) { | |
1189 char *msg, *msg2; | |
1190 int ret = 0; | |
1191 int recvd = 0; | |
1192 int i; | |
1193 | |
1194 if (!(msg = calloc(1, payloadlength+1))) | |
1195 return -1; | |
1196 msg2 = msg; | |
1197 | |
1198 while (payloadlength - recvd) { | |
1199 if (payloadlength - recvd >= 1024) | |
1200 i = aim_recv(conn->fd, msg2, 1024); | |
1201 else | |
1202 i = aim_recv(conn->fd, msg2, payloadlength - recvd); | |
1203 if (i <= 0) { | |
1204 free(msg); | |
1205 return -1; | |
1206 } | |
1207 recvd = recvd + i; | |
1208 msg2 = msg2 + i; | |
1209 if ((userfunc=aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER))) | |
1210 userfunc(sess, &fr, snptr, (double)recvd / payloadlength); | |
1211 } | |
1212 | |
1213 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) ) | |
1214 ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding); | |
1215 | |
1216 free(msg); | |
1217 | |
1218 return ret; | |
1219 } | |
1220 | |
1221 return 0; | |
1222 } | |
1223 | |
1224 static int handlehdr_getfile_listing(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr) | |
1225 { | |
1226 #if 0 | |
1227 struct aim_filetransfer_priv *ft; | |
1228 struct aim_fileheader_t *fh; | |
1229 struct aim_msgcookie_t *cook; | |
1230 aim_frame_t *newoft; | |
1231 aim_rxcallback_t userfunc; | |
1232 | |
1233 faimdprintf(sess, 2,"faim: rend: fileget 0x1108\n"); | |
1234 fh = aim_oft_getfh(hdr); | |
1235 | |
1236 faim_mutex_unlock(&conn->active); | |
1237 | |
1238 if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { | |
1239 free(fh); | |
1240 return -1; | |
1241 } | |
1242 | |
1243 ft = cook->data; | |
1244 | |
1245 /* we're waaaaiiiting.. for listing.txt */ | |
1246 ft->state = 2; | |
1247 | |
1248 memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); | |
1249 free(fh); | |
1250 | |
1251 if(aim_cachecookie(sess, cook) == -1) { | |
1252 faimdprintf(sess, 1, "error caching cookie\n"); | |
1253 return -1; | |
1254 } | |
1255 | |
1256 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x1209, 0))) { | |
1257 aim_conn_close(conn); | |
1258 return -1; | |
1259 } | |
1260 | |
1261 memcpy(newoft->hdr.oft.magic, "OFT2", 4); | |
1262 newoft->hdr.oft.hdr2len = 0x100 - 8; | |
1263 | |
1264 if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { | |
1265 newoft->lock = 0; | |
1266 aim_frame_destroy(newoft); | |
1267 return -1; | |
1268 } | |
1269 | |
1270 if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) { | |
1271 newoft->lock = 0; | |
1272 aim_frame_destroy(newoft); | |
1273 return -1; | |
1274 } | |
1275 | |
1276 newoft->lock = 0; | |
1277 aim_tx_enqueue(sess, newoft); | |
1278 #endif | |
1279 return -1; | |
1280 } | |
1281 | |
1282 static int handlehdr_getfile_listing2(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr) | |
1283 { | |
1284 #if 0 | |
1285 struct aim_filetransfer_priv *ft; | |
1286 struct aim_fileheader_t *fh; | |
1287 struct aim_msgcookie_t *cook; | |
1288 int ret = 0; | |
1289 aim_rxcallback_t userfunc; | |
1290 | |
1291 fh = aim_oft_getfh(hdr); | |
1292 | |
1293 if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) | |
1294 faimdprintf(sess, 2, "shit, no cookie in 0x1209. (%i/%s)going to crash..\n", AIM_COOKIETYPE_OFTGET, fh->bcookie); | |
1295 | |
1296 ft = cook->data; | |
1297 | |
1298 if (ft->fh.size != fh->size) | |
1299 faimdprintf(sess, 2, "hrm. ft->fh.size (%ld) != fh->size (%ld). um. using ft->fh.size\n", ft->fh.size, fh->size); | |
1300 | |
1301 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ))) | |
1302 ret = userfunc(sess, NULL, conn, fh); | |
1303 | |
1304 faimdprintf(sess, 2, "faim: get_command_rendezvous: hit end of 1209\n"); | |
1305 | |
1306 free(fh); | |
1307 | |
1308 return ret; | |
1309 #else | |
1310 return -1; | |
1311 #endif | |
1312 } | |
1313 | |
1314 static int handlehdr_getfile_listing3(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr) | |
1315 { | |
1316 #if 0 | |
1317 struct aim_filetransfer_priv *ft; | |
1318 struct aim_msgcookie_t *cook; | |
1319 struct aim_fileheader_t *fh; | |
1320 aim_rxcallback_t userfunc; | |
1321 | |
1322 fh = aim_oft_getfh(hdr); | |
1323 | |
1324 if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { | |
1325 free(fh); | |
1326 return -1; | |
1327 } | |
1328 | |
1329 free(fh); | |
1330 | |
1331 ft = cook->data; | |
1332 | |
1333 if (aim_cachecookie(sess, cook) == -1) | |
1334 return -1; | |
1335 | |
1336 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGRXCONFIRM))) | |
1337 return userfunc(sess, NULL, conn); | |
1338 #endif | |
1339 return -1; | |
1340 } | |
1341 | |
1342 static int handlehdr_getfile_request(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr) | |
1343 { | |
1344 #if 0 | |
1345 struct aim_filetransfer_priv *ft; | |
1346 aim_msgcookie_t *cook; | |
1347 struct aim_fileheader_t *fh; | |
1348 aim_frame_t *newoft; | |
1349 int i = 0; | |
1350 aim_rxcallback_t userfunc; | |
1351 | |
1352 fh = aim_oft_getfh(hdr); | |
1353 | |
1354 if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { | |
1355 free(fh); | |
1356 return -1; | |
1357 } | |
1358 | |
1359 ft = cook->data; | |
1360 memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); | |
1361 free(fh); | |
1362 | |
1363 aim_cachecookie(sess, cook); | |
1364 | |
1365 faimdprintf(sess, 2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name); | |
1366 | |
1367 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) ) | |
1368 i = userfunc(sess, NULL, conn, &(ft->fh), cook->cookie); | |
1369 | |
1370 if (i < 0) | |
1371 return i; | |
1372 | |
1373 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0101, 0))) { | |
1374 faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n"); | |
1375 return -1; | |
1376 } | |
1377 | |
1378 memcpy(newoft->hdr.oft.magic, "OFT2", 4); | |
1379 newoft->hdr.oft.hdr2len = 0x100 - 8; | |
1380 | |
1381 if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) { | |
1382 aim_frame_destroy(newoft); | |
1383 return -1; | |
1384 } | |
1385 | |
1386 /* protocol BS: nrecvd, recvcsum to 0, flags to 0x20. */ | |
1387 ft->fh.nrecvd = 0; | |
1388 ft->fh.recvcsum = 0; | |
1389 ft->fh.flags = 0x20; | |
1390 | |
1391 aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)); | |
1392 | |
1393 aim_tx_enqueue(sess, newoft); | |
1394 | |
1395 faimdprintf(sess, 2, "faim: OFT: OFT file header enqueued.\n"); | |
1396 | |
1397 return i; | |
1398 #else | |
1399 return -1; | |
1400 #endif | |
1401 } | |
1402 | |
1403 static int handlehdr_getfile_sending(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr) | |
1404 { | |
1405 #if 0 | |
1406 struct aim_fileheader_t *fh; | |
1407 struct aim_filetransfer_priv *ft; | |
1408 struct aim_msgcookie_t *cook; | |
1409 struct command_tx_struct *newoft; | |
1410 aim_rxcallback_t userfunc; | |
1411 | |
1412 fh = aim_oft_getfh(hdr); | |
1413 | |
1414 if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { | |
1415 free(fh); | |
1416 return -1; | |
1417 } | |
1418 | |
1419 free(fh); | |
1420 | |
1421 ft = cook->data; | |
1422 | |
1423 ft->state = 3; | |
1424 | |
1425 if (aim_cachecookie(sess, cook) == -1) | |
1426 return -1; | |
1427 | |
1428 faimdprintf(sess, 2, "faim: fileget: %s seems to want to send %s\n", ft->sn, ft->fh.name); | |
1429 | |
1430 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) { | |
1431 faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n"); | |
1432 return -1; | |
1433 } | |
1434 | |
1435 newoft->lock = 1; | |
1436 memcpy(newoft->hdr.oft.magic, "OFT2", 4); | |
1437 | |
1438 newoft->hdr.oft.hdr2len = 0x100 - 8; | |
1439 | |
1440 if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) { | |
1441 aim_frame_destroy(newoft); | |
1442 return -1; | |
1443 } | |
1444 | |
1445 aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)); | |
1446 | |
1447 newoft->lock = 0; | |
1448 aim_tx_enqueue(sess, newoft); | |
1449 | |
1450 faimdprintf(sess, 2, "faim: OFT: OFT 0x0202 enqueued.\n"); | |
1451 | |
1452 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL) | |
1453 return 1; | |
1454 #else | |
1455 return -1; | |
1456 #endif | |
1457 } | |
1458 | |
1459 /* We are receiving a file, and the buddy sent us this header describing | |
1460 * it. We send back a similar header to confirm, then we're ready to | |
1461 * start reading the raw data. | |
1462 */ | |
1463 static int handlehdr_sendfile_sending(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs) | |
1464 { | |
1465 struct aim_filetransfer_priv *ft; | |
1466 struct aim_fileheader_t *fh; | |
1467 aim_frame_t *newoft; | |
1468 aim_rxcallback_t userfunc; | |
1469 | |
1470 fh = aim_oft_getfh(bs); | |
1471 | |
1472 /* We receive a null cookie for the first file; we must fill | |
1473 * it in to authenticate ourselves. -- wtm | |
1474 */ | |
1475 ft = conn->internal; | |
1476 memcpy(&(fh->bcookie), ft->cookie, 8); | |
1477 | |
1478 memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); | |
1479 free(fh); | |
1480 | |
1481 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, AIM_OFT_PROTO_ACCEPT, 0))) { | |
1482 faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n"); | |
1483 return -1; | |
1484 } | |
1485 | |
1486 if (aim_oft_buildheader(&(newoft->data), &(ft->fh)) == -1) { | |
1487 return -1; | |
1488 } | |
1489 memcpy(newoft->hdr.rend.magic, "OFT2", 4); | |
1490 newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data); | |
1491 | |
1492 aim_tx_enqueue(sess, newoft); | |
1493 | |
1494 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEFILEREQ)) == NULL) | |
1495 return 1; | |
1496 | |
1497 { | |
1498 char *cur; | |
1499 /* Convert the directory separator: it is sent | |
1500 * as ^A (0x01). | |
1501 */ | |
1502 while ((cur = strchr(ft->fh.name, 0x01))) { | |
1503 *cur = G_DIR_SEPARATOR; | |
1504 } | |
1505 } | |
1506 return userfunc(sess, NULL, conn, &(ft->fh)); | |
1507 } | |
1508 | |
1509 | |
1510 /* | |
1511 * These were originally described by Josh Myer: | |
1512 * http://www.geocrawler.com/archives/3/896/2000/9/0/4291064/ | |
1513 * XXX this doesn't actually work yet | |
1514 * -- wtm | |
1515 */ | |
1516 static int handlehdr_sendfile_resume(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs) { | |
1517 aim_frame_t *newoft; | |
1518 aim_msgcookie_t *cook; | |
1519 struct aim_fileheader_t *fh; | |
1520 struct aim_filetransfer_priv *ft; | |
1521 | |
1522 fh = aim_oft_getfh(bs); | |
1523 if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTSEND))) { | |
1524 free(fh); | |
1525 return -1; | |
1526 } | |
1527 ft = (struct aim_filetransfer_priv *)cook->data; | |
1528 | |
1529 ft->fh.nrecvd = fh->nrecvd; | |
1530 ft->fh.recvcsum = fh->recvcsum; | |
1531 strncpy(ft->fh.name, fh->name, sizeof(ft->fh.name)); | |
1532 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0106, 0))) { | |
1533 faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); | |
1534 free(fh); | |
1535 return -1; | |
1536 } | |
1537 | |
1538 if (aim_oft_buildheader(&(newoft->data), &(ft->fh)) == -1) { | |
1539 aim_frame_destroy(newoft); | |
1540 free(fh); | |
1541 return -1; | |
1542 } | |
1543 memcpy(newoft->hdr.rend.magic, "OFT2", 4); | |
1544 newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data); | |
1545 | |
1546 aim_tx_enqueue(sess, newoft); | |
1547 free(fh); | |
1548 | |
1549 return 0; | |
1550 } | |
1551 | |
1552 /* We are sending a file, and the buddy sent us this header indicating | |
1553 * that he or she is ready for the raw data. | |
1554 */ | |
1555 static int handlehdr_sendfile_recv(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs) { | |
1556 struct aim_fileheader_t *fh; | |
1557 aim_msgcookie_t *cook; | |
1558 int ret = 1; | |
1559 struct aim_filetransfer_priv *ft; | |
1560 aim_rxcallback_t userfunc; | |
1561 | |
1562 fh = aim_oft_getfh(bs); | |
1563 if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTSEND))) { | |
1564 free(fh); | |
1565 return -1; | |
1566 } | |
1567 ft = (struct aim_filetransfer_priv *)cook->data; | |
1568 | |
1569 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILEFILESEND)) ) | |
1570 ret = userfunc(sess, NULL, conn, &(ft->fh)); | |
1571 | |
1572 free(fh); | |
1573 | |
1574 return ret; | |
1575 } | |
1576 | |
1577 static int handlehdr_getfile_recv(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr) | |
1578 { | |
1579 #if 0 | |
1580 struct aim_fileheader_t *fh; | |
1581 struct aim_msgcookie_t *cook; | |
1582 int ret = 1; | |
1583 aim_rxcallback_t userfunc; | |
1584 struct aim_filetransfer_priv *ft; | |
1585 | |
1586 fh = aim_oft_getfh(hdr); | |
1587 | |
1588 if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { | |
1589 free(fh); | |
1590 return -1; | |
1591 } | |
1592 | |
1593 ft = cook->data; | |
1594 | |
1595 faimdprintf(sess, 2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n"); | |
1596 | |
1597 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) ) | |
1598 ret = userfunc(sess, NULL, conn, fh); | |
1599 | |
1600 free(fh); | |
1601 | |
1602 return ret; | |
1603 #else | |
1604 return -1; | |
1605 #endif | |
1606 } | |
1607 | |
1608 /* We just sent the raw data of a file, and the buddy sent us back this | |
1609 * header indicating that the transfer is complete. | |
1610 */ | |
1611 static int handlehdr_sendfile_finish(aim_session_t *sess, aim_conn_t *conn, aim_bstream_t *bs) | |
1612 { | |
1613 struct aim_fileheader_t *fh; | |
1614 aim_msgcookie_t *cook; | |
1615 aim_rxcallback_t userfunc; | |
1616 | |
1617 fh = aim_oft_getfh(bs); | |
1618 | |
1619 if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTSEND))) { | |
1620 free(fh); | |
1621 return -1; | |
1622 } | |
1623 | |
1624 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_SENDFILECOMPLETE)) ) | |
1625 userfunc(sess, NULL, conn, fh->bcookie); | |
1626 | |
1627 free(fh); | |
1628 return 0; | |
1629 } | |
1630 | |
1631 static int handlehdr_getfile_finish(aim_session_t *sess, aim_conn_t *conn, fu8_t *hdr) | |
1632 { | |
1633 #if 0 | |
1634 struct aim_fileheader_t *fh; | |
1635 aim_rxcallback_t userfunc; | |
1636 | |
1637 fh = aim_oft_getfh(hdr); | |
1638 | |
1639 faimdprintf(sess, 2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n"); | |
1640 | |
1641 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) ) | |
1642 userfunc(sess, NULL, conn, fh); | |
1643 | |
1644 free(fh); | |
1645 #endif | |
1646 | |
1647 return -1; | |
1648 } | |
1649 | |
1650 faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr) | |
1651 { | |
1652 aim_conn_t *conn = fr->conn; | |
1653 aim_bstream_t *bs = &fr->data; | |
1654 int ret = -1; | |
1655 | |
1656 if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) { | |
1657 /* This should never happen. -- wtm */ | |
1658 return getcommand_getfile(sess, conn); | |
1659 | |
1660 } else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) { | |
1661 switch(fr->hdr.rend.type) { | |
1662 case AIM_OFT_PROTO_OFFER: | |
1663 ret = handlehdr_sendfile_sending(sess, conn, bs); | |
1664 break; | |
1665 case AIM_OFT_PROTO_RESUME: | |
1666 ret = handlehdr_sendfile_resume(sess, conn, bs); | |
1667 break; | |
1668 case AIM_OFT_PROTO_RESUMEACCEPT: /* like _ACCEPT */; | |
1669 case AIM_OFT_PROTO_ACCEPT: | |
1670 ret = handlehdr_sendfile_recv(sess, conn, bs); | |
1671 break; | |
1672 case AIM_OFT_PROTO_ACK: | |
1673 ret = handlehdr_sendfile_finish(sess, conn, bs); | |
1674 break; | |
1675 default: | |
1676 faimdprintf(sess, 2, "faim: OFT frame: uknown type %04x\n", fr->hdr.rend.type); | |
1677 ret = -1; | |
1678 break; | |
1679 } | |
1680 | |
1681 } else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { | |
1682 if (fr->hdr.rend.type == 0x0001) | |
1683 ret = handlehdr_directim(sess, conn, bs); | |
1684 else | |
1685 faimdprintf(sess, 0, "faim: DIM frame: unknown type %04x\n", fr->hdr.rend.type); | |
1686 | |
1687 } else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) { | |
1688 /* This _really_ shouldn't happen. :) -- wtm */ | |
1689 char *hdr = NULL; | |
1690 int hdrtype = fr->hdr.rend.type; | |
1691 if (hdrtype == 0x1108) /* getfile listing.txt incoming tx->rx */ | |
1692 ret = handlehdr_getfile_listing(sess, conn, hdr); | |
1693 else if (hdrtype == 0x1209) /* get file listing ack rx->tx */ | |
1694 ret = handlehdr_getfile_listing2(sess, conn, hdr); | |
1695 else if (hdrtype == 0x120b) /* get file listing rx confirm */ | |
1696 ret = handlehdr_getfile_listing3(sess, conn, hdr); | |
1697 else if (hdrtype == 0x120c) /* getfile request */ | |
1698 ret = handlehdr_getfile_request(sess, conn, hdr); | |
1699 else if (hdrtype == 0x0101) /* getfile sending data */ | |
1700 ret = handlehdr_getfile_sending(sess, conn, hdr); | |
1701 else if (hdrtype == 0x0202) /* getfile recv data */ | |
1702 ret = handlehdr_getfile_recv(sess, conn, hdr); | |
1703 else if (hdrtype == 0x0204) /* getfile finished */ | |
1704 ret = handlehdr_getfile_finish(sess, conn, hdr); | |
1705 else { | |
1706 faimdprintf(sess, 2,"faim: OFT frame: uknown type %04x\n", hdrtype); | |
1707 ret = -1; | |
1708 } | |
1709 } | |
1710 | |
1711 if (ret == -1) | |
1712 aim_conn_close(conn); | |
1713 | |
1714 return ret; | |
1715 } | |
1716 | |
1717 /** | |
1718 * aim_oft_getfh - extracts an &aim_fileheader_t from buffer hdr. | |
1719 * @bs: bstream to extract header from | |
1720 * | |
1721 * returns pointer to new struct on success; %NULL on error. | |
1722 * | |
1723 */ | |
1724 static struct aim_fileheader_t *aim_oft_getfh(aim_bstream_t *bs) | |
1725 { | 643 { |
1726 struct aim_fileheader_t *fh; | 644 struct aim_fileheader_t *fh; |
1727 | 645 |
1728 if (!(fh = calloc(1, sizeof(struct aim_fileheader_t)))) | 646 if (!(fh = calloc(1, sizeof(struct aim_fileheader_t)))) |
1729 return NULL; | 647 return NULL; |
1752 fh->lsizeoffset = aimbs_get8(bs); | 670 fh->lsizeoffset = aimbs_get8(bs); |
1753 aimbs_getrawbuf(bs, fh->dummy, 69); | 671 aimbs_getrawbuf(bs, fh->dummy, 69); |
1754 aimbs_getrawbuf(bs, fh->macfileinfo, 16); | 672 aimbs_getrawbuf(bs, fh->macfileinfo, 16); |
1755 fh->nencode = aimbs_get16(bs); | 673 fh->nencode = aimbs_get16(bs); |
1756 fh->nlanguage = aimbs_get16(bs); | 674 fh->nlanguage = aimbs_get16(bs); |
1757 aimbs_getrawbuf(bs, fh->name, 64); /* XXX */ | 675 aimbs_getrawbuf(bs, fh->name, 64); /* XXX - filenames longer than 64B */ |
1758 | 676 |
1759 return fh; | 677 return fh; |
1760 } | 678 } |
1761 | 679 |
1762 /** | 680 /** |
1763 * aim_oft_checksum - calculate oft checksum of buffer | 681 * Fills a buffer with network-order fh data |
1764 * @buffer: buffer of data to checksum | 682 * |
1765 * @bufsize: size of buffer | 683 * @param bs A bstream to fill -- automatically initialized |
1766 * @prevcheck: previous checksum | 684 * @param fh A struct aim_fileheader_t to get data from. |
1767 * | 685 * @return Return non-zero on error. |
1768 * Prevcheck should be 0xFFFF0000 for each new file; you can have this | |
1769 * checksum chunks of files in series if you just call it repeatedly in a | |
1770 * for(; ; ) loop and don't reset the checksum between each call. And you | |
1771 * thought we didn't care about you and your pathetic client's meomry | |
1772 * footprint ;^) | |
1773 * | |
1774 * Thanks to Graham Booker for providing this improved checksum | |
1775 * routine, which is simpler and should be more accurate than Josh | |
1776 * Myer's original code. -- wtm | |
1777 * | |
1778 * This algorithim works every time I have tried it. The other fails | |
1779 * sometimes. So, AOL who thought this up? It has got to be the weirdest | |
1780 * checksum I have ever seen. | |
1781 */ | |
1782 faim_export fu32_t aim_oft_checksum(const unsigned char *buffer, int bufferlen, int prevcheck) { | |
1783 fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck; | |
1784 int i; | |
1785 unsigned short val; | |
1786 | |
1787 for (i=0; i<bufferlen; i++) { | |
1788 oldcheck = check; | |
1789 if (i&1) { | |
1790 val = buffer[i]; | |
1791 } else { | |
1792 val = buffer[i] << 8; | |
1793 } | |
1794 check -= val; | |
1795 /* The follownig appears to be necessary.... It happens every once in a while and the checksum doesn't fail. */ | |
1796 if (check > oldcheck) { | |
1797 check--; | |
1798 } | |
1799 } | |
1800 check = ((check & 0x0000ffff) + (check >> 16)); | |
1801 check = ((check & 0x0000ffff) + (check >> 16)); | |
1802 return check << 16; | |
1803 } | |
1804 | |
1805 faim_export fu32_t aim_update_checksum(aim_session_t *sess, aim_conn_t *conn, | |
1806 const unsigned char *buffer, int len) { | |
1807 struct aim_filetransfer_priv *ft = conn->internal; | |
1808 | |
1809 ft->fh.nrecvd += len; | |
1810 ft->fh.recvcsum = aim_oft_checksum(buffer, len, ft->fh.recvcsum); | |
1811 | |
1812 return 0; | |
1813 } | |
1814 | |
1815 /** | |
1816 * aim_oft_buildheader - fills a buffer with network-order fh data | |
1817 * @bs: bstream to fill -- automatically initialized | |
1818 * @fh: fh to get data from | |
1819 * | |
1820 * returns -1 on error. | |
1821 * | |
1822 */ | 686 */ |
1823 static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh) | 687 static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh) |
1824 { | 688 { |
1825 fu8_t *hdr; | 689 fu8_t *hdr; |
1826 | 690 |
1827 if (!bs || !fh) | 691 if (!bs || !fh) |
1828 return -1; | 692 return -EINVAL; |
1829 | 693 |
1830 | 694 if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8))) |
1831 | 695 return -ENOMEM; |
1832 | 696 |
1833 if (!(hdr = (unsigned char *)calloc(1, 0x100 - 8))) { | |
1834 return -1; | |
1835 } | |
1836 aim_bstream_init(bs, hdr, 0x100 - 8); | 697 aim_bstream_init(bs, hdr, 0x100 - 8); |
1837 | |
1838 aimbs_putraw(bs, fh->bcookie, 8); | 698 aimbs_putraw(bs, fh->bcookie, 8); |
1839 aimbs_put16(bs, fh->encrypt); | 699 aimbs_put16(bs, fh->encrypt); |
1840 aimbs_put16(bs, fh->compress); | 700 aimbs_put16(bs, fh->compress); |
1841 aimbs_put16(bs, fh->totfiles); | 701 aimbs_put16(bs, fh->totfiles); |
1842 aimbs_put16(bs, fh->filesleft); | 702 aimbs_put16(bs, fh->filesleft); |
1858 aimbs_put8(bs, fh->lsizeoffset); | 718 aimbs_put8(bs, fh->lsizeoffset); |
1859 aimbs_putraw(bs, fh->dummy, 69); | 719 aimbs_putraw(bs, fh->dummy, 69); |
1860 aimbs_putraw(bs, fh->macfileinfo, 16); | 720 aimbs_putraw(bs, fh->macfileinfo, 16); |
1861 aimbs_put16(bs, fh->nencode); | 721 aimbs_put16(bs, fh->nencode); |
1862 aimbs_put16(bs, fh->nlanguage); | 722 aimbs_put16(bs, fh->nlanguage); |
1863 aimbs_putraw(bs, fh->name, 64); | 723 aimbs_putraw(bs, fh->name, 64); /* XXX - filenames longer than 64B */ |
1864 | 724 |
1865 /* XXX: Filenames longer than 64B */ | |
1866 return 0; | 725 return 0; |
1867 } | 726 } |
1868 | 727 |
1869 /** | 728 /** |
1870 * aim_getfile_intitiate - Request an OFT getfile session | 729 * Create an OFT packet based on the given information, and send it on its merry way. |
1871 * @sess: your session, | 730 * |
1872 * @conn: the BOS conn, | 731 * @param sess The session. |
1873 * @destsn is the SN to connect to. | 732 * @param conn The already-connected OFT connection. |
1874 * | 733 * @param cookie The cookie associated with this file transfer. |
1875 * returns a new &aim_conn_t on success, %NULL on error | 734 * @param filename The filename. |
1876 */ | 735 * @param filesdone Number of files already transferred. |
1877 faim_export aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn) | 736 * @param numfiles Total number of files. |
1878 { | 737 * @param size Size in bytes of this file. |
1879 return NULL; | 738 * @param totsize Size in bytes of all files combined. |
1880 #if 0 | 739 * @param checksum Funky checksum of this file. |
1881 struct command_tx_struct *newpacket; | 740 * @param flags Any flags you want, baby. Send 0x21 when sending the |
1882 struct aim_conn_t *newconn; | 741 * "AIM_CB_OFT_DONE" message, and "0x02" for everything else. |
1883 struct aim_filetransfer_priv *priv; | 742 * @return Return 0 if no errors, otherwise return the error number. |
1884 struct aim_msgcookie_t *cookie; | 743 */ |
1885 int curbyte, i, listenfd; | 744 faim_export int aim_oft_sendheader(aim_session_t *sess, aim_conn_t *conn, fu16_t type, const fu8_t *cookie, const char *filename, fu16_t filesdone, fu16_t numfiles, fu32_t size, fu32_t totsize, fu32_t modtime, fu32_t checksum, fu8_t flags) |
1886 short port = 4443; | |
1887 struct hostent *hptr; | |
1888 struct utsname myname; | |
1889 char cap[16]; | |
1890 char d[4]; | |
1891 | |
1892 /* Open our socket */ | |
1893 | |
1894 if ( (listenfd = aim_listenestablish(port)) == -1) | |
1895 return NULL; | |
1896 | |
1897 /* get our local IP */ | |
1898 | |
1899 if (uname(&myname) < 0) | |
1900 return NULL; | |
1901 if ( (hptr = gethostbyname(myname.nodename)) == NULL) | |
1902 return NULL; | |
1903 memcpy(&d, hptr->h_addr_list[0], 4); | |
1904 | |
1905 aim_putcap(cap, 16, AIM_CAPS_GETFILE); | |
1906 | |
1907 /* create the OSCAR packet */ | |
1908 | |
1909 if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x42))) | |
1910 return NULL; | |
1911 newpacket->lock = 1; | |
1912 | |
1913 /* lock struct */ | |
1914 curbyte = 0; | |
1915 curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid); | |
1916 | |
1917 /* XXX: check the cookie before commiting to using it */ | |
1918 | |
1919 /* Generate a random message cookie | |
1920 * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. */ | |
1921 for (i=0; i<7; i++) | |
1922 curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) random() % 10)); | |
1923 | |
1924 curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); | |
1925 | |
1926 /* grab all the data for cookie caching. */ | |
1927 | |
1928 if (!(cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) | |
1929 return NULL; | |
1930 memcpy(cookie->cookie, newpacket->data+curbyte-8, 8); | |
1931 cookie->type = AIM_COOKIETYPE_OFTGET; | |
1932 | |
1933 if (!(priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) | |
1934 return NULL; | |
1935 memcpy(priv->cookie, cookie, 8); | |
1936 memcpy(priv->sn, destsn, sizeof(priv->sn)); | |
1937 memcpy(priv->fh.name, "listing.txt", strlen("listing.txt")); | |
1938 priv->state = 1; | |
1939 | |
1940 cookie->data = priv; | |
1941 | |
1942 aim_cachecookie(sess, cookie); | |
1943 | |
1944 /* Channel ID */ | |
1945 curbyte += aimutil_put16(newpacket->data+curbyte,0x0002); | |
1946 | |
1947 /* Destination SN (prepended with byte length) */ | |
1948 curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn)); | |
1949 curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); | |
1950 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); | |
1951 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
1952 | |
1953 /* enTLV start */ | |
1954 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); | |
1955 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0042); | |
1956 | |
1957 /* Flag data / ICBM Parameters? */ | |
1958 curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); | |
1959 curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); | |
1960 | |
1961 /* Cookie */ | |
1962 curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8); | |
1963 | |
1964 /* Capability String */ | |
1965 curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10); | |
1966 | |
1967 /* 000a/0002 : 0001 */ | |
1968 curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a); | |
1969 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); | |
1970 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); | |
1971 | |
1972 /* 0003/0004: IP address */ | |
1973 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); | |
1974 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004); | |
1975 for (i = 0; i < 4; i++) | |
1976 curbyte += aimutil_put8(newpacket->data+curbyte, d[i]); | |
1977 | |
1978 /* already in network byte order */ | |
1979 | |
1980 /* 0005/0002: Port */ | |
1981 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); | |
1982 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); | |
1983 curbyte += aimutil_put16(newpacket->data+curbyte, port); | |
1984 | |
1985 /* 000f/0000: ?? */ | |
1986 curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f); | |
1987 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); | |
1988 | |
1989 /* 2711/000c: ?? */ | |
1990 curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711); | |
1991 curbyte += aimutil_put16(newpacket->data+curbyte, 0x000c); | |
1992 curbyte += aimutil_put32(newpacket->data+curbyte, 0x00120001); | |
1993 | |
1994 for (i = 0; i < 0x000c - 4; i++) | |
1995 curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); | |
1996 | |
1997 newpacket->commandlen = curbyte; | |
1998 newpacket->lock = 0; | |
1999 aim_tx_enqueue(sess, newpacket); | |
2000 | |
2001 /* allocate and set up our connection */ | |
2002 | |
2003 i = fcntl(listenfd, F_GETFL, 0); | |
2004 fcntl(listenfd, F_SETFL, i | O_NONBLOCK); | |
2005 newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL); | |
2006 | |
2007 if (!newconn){ | |
2008 perror("aim_newconn"); | |
2009 return NULL; | |
2010 } | |
2011 | |
2012 newconn->fd = listenfd; | |
2013 newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE; | |
2014 newconn->internal = priv; | |
2015 faimdprintf(sess, 2,"faim: listening (fd = %d, unconnected)\n", newconn->fd); | |
2016 | |
2017 return newconn; | |
2018 #endif | |
2019 } | |
2020 | |
2021 /** | |
2022 * aim_oft_getfile_request - request a particular file over an established getfile connection | |
2023 * @sess: your session | |
2024 * @conn: the established OFT getfile connection | |
2025 * @name: filename to request | |
2026 * @size: size of the file | |
2027 * | |
2028 * | |
2029 * returns -1 on error, 0 on successful enqueuing | |
2030 */ | |
2031 #if 0 | |
2032 faim_export int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size) | |
2033 { | 745 { |
2034 aim_frame_t *newoft; | 746 aim_frame_t *newoft; |
2035 struct aim_filetransfer_priv *ft; | |
2036 if (!sess || !conn || !conn->priv || !name) | |
2037 return -1; | |
2038 | |
2039 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120c, 0))) { | |
2040 faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); | |
2041 return -1; | |
2042 } | |
2043 memcpy(newoft->hdr.oft.magic, "OFT2", 4); | |
2044 newoft->hdr.oft.hdr2len = 0x100 - 8; | |
2045 | |
2046 ft = (struct aim_filetransfer_priv *)conn->priv; | |
2047 ft->fh.filesleft = 1; | |
2048 ft->fh.totfiles = 1; | |
2049 ft->fh.totparts = 1; | |
2050 ft->fh.partsleft = 1; | |
2051 ft->fh.totsize = size; | |
2052 ft->fh.size = size; | |
2053 ft->fh.checksum = 0; | |
2054 memcpy(ft->fh.name, name, strlen(name)); | |
2055 memset(ft->fh.name+strlen(name), 0, 1); | |
2056 | |
2057 if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) { | |
2058 aim_frame_destroy(newoft); | |
2059 return -1; | |
2060 } | |
2061 | |
2062 if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, &(ft->fh)))) { | |
2063 aim_frame_destroy(newoft); | |
2064 return -1; | |
2065 } | |
2066 | |
2067 aim_tx_enqueue(sess, newoft); | |
2068 return 0; | |
2069 } | |
2070 #endif | |
2071 | |
2072 /* Identify a file that we are about to send by transmitting the | |
2073 * appropriate header. | |
2074 */ | |
2075 faim_export int aim_oft_sendfile_request(aim_session_t *sess, aim_conn_t *conn, const char *filename, int filesdone, int numfiles, int size, int totsize) | |
2076 { | |
2077 aim_frame_t *newoft; | |
2078 aim_msgcookie_t *cook; | |
2079 struct aim_filetransfer_priv *ft = (struct aim_filetransfer_priv *)conn->internal; | |
2080 struct aim_fileheader_t *fh; | 747 struct aim_fileheader_t *fh; |
2081 | 748 |
2082 if (!sess || !conn || !filename) | 749 if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !filename) |
2083 return -1; | 750 return -EINVAL; |
2084 | 751 |
2085 if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) | 752 if (!(fh = (struct aim_fileheader_t *)calloc(1, sizeof(struct aim_fileheader_t)))) |
2086 return -1; | 753 return -ENOMEM; |
2087 | 754 |
2088 #ifdef DUMB_OFT_CHECKSUM | 755 /* |
2089 /* Yes, we are supposed to checksum the whole file before sending, and | 756 * If you are receiving a file, the cookie should be null, if you are sending a |
2090 * yes, it's dumb. This is the only way to get some clients (such as | 757 * file, the cookie should be the same as the one used in the ICBM negotiation |
2091 * Mac AIM v4.5.163) to successfully complete the transfer. With | 758 * SNACs. |
2092 * the WinAIM clients, we seem to be able to get away with just | |
2093 * setting the checksum to zero. | |
2094 * -- wtm | |
2095 */ | 759 */ |
2096 { | 760 if (cookie) |
2097 int fd = open(filename, O_RDONLY); | 761 memcpy(fh->bcookie, cookie, 8); |
2098 if (fd >= 0) { | |
2099 int bytes; | |
2100 char buf[1024]; | |
2101 fh->checksum = 0xffff0000; | |
2102 while ((bytes = aim_recv(fd, buf, 1024)) > 0) { | |
2103 fh->checksum = aim_oft_checksum(buf, bytes, fh->checksum); | |
2104 } | |
2105 } | |
2106 close(fd); | |
2107 } | |
2108 #else | |
2109 fh->checksum = 0x00000000; | |
2110 #endif | |
2111 fh->encrypt = 0x0000; | |
2112 fh->compress = 0x0000; | |
2113 fh->totfiles = numfiles; | 762 fh->totfiles = numfiles; |
2114 fh->filesleft = numfiles - filesdone; | 763 fh->filesleft = numfiles - filesdone; |
2115 fh->totparts = 0x0001; /* set to 0x0002 sending Mac resource forks */ | 764 fh->totparts = 0x0001; /* set to 0x0002 sending Mac resource forks */ |
2116 fh->partsleft = 0x0001; | 765 fh->partsleft = 0x0001; |
2117 fh->totsize = totsize; | 766 fh->totsize = totsize; |
2118 fh->size = size; | 767 fh->size = size; |
2119 fh->modtime = (int)time(NULL); /* we'll go with current time for now */ | 768 fh->modtime = modtime; |
2120 /* fh->checksum set above */ | 769 fh->checksum = checksum; |
2121 fh->rfcsum = 0x00000000; | |
2122 fh->rfsize = 0x00000000; | |
2123 fh->cretime = 0x00000000; | |
2124 fh->rfcsum = 0x00000000; | |
2125 fh->nrecvd = 0x00000000; /* always zero initially */ | |
2126 fh->recvcsum= 0x00000000; /* ditto */ | |
2127 | 770 |
2128 strncpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring)); | 771 strncpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring)); |
2129 fh->flags = 0x02; | 772 fh->flags = 0x02; |
2130 fh->lnameoffset = 0x1a; | 773 fh->lnameoffset = 0x1a; |
2131 fh->lsizeoffset = 0x10; | 774 fh->lsizeoffset = 0x10; |
2135 /* apparently 0 is ASCII, 2 is UCS-2 */ | 778 /* apparently 0 is ASCII, 2 is UCS-2 */ |
2136 /* it is likely that 3 is ISO 8859-1 */ | 779 /* it is likely that 3 is ISO 8859-1 */ |
2137 fh->nencode = 0x0000; | 780 fh->nencode = 0x0000; |
2138 fh->nlanguage = 0x0000; | 781 fh->nlanguage = 0x0000; |
2139 | 782 |
2140 /* Convert the directory separator to ^A for portability. */ | |
2141 strncpy(fh->name, filename, sizeof(fh->name)); | 783 strncpy(fh->name, filename, sizeof(fh->name)); |
2142 oft_dirconvert(fh->name); | 784 aim_oft_dirconvert_tostupid(fh->name); |
2143 | 785 |
2144 /* XXX we should normally send a null cookie here, and make | 786 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, type, 0))) { |
2145 * the receiver fill it in for authentication -- wtm | |
2146 */ | |
2147 memcpy(fh->bcookie, ft->cookie, 8); | |
2148 | |
2149 if (!(cook = aim_checkcookie(sess, ft->cookie, AIM_COOKIETYPE_OFTSEND))) { | |
2150 return -1; | |
2151 } | |
2152 | |
2153 /* Update both headers to be safe. */ | |
2154 memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); | |
2155 memcpy(&(((struct aim_filetransfer_priv *)cook->data)->fh), fh, sizeof(struct aim_fileheader_t)); | |
2156 | |
2157 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, AIM_OFT_PROTO_OFFER, 0))) { | |
2158 faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); | |
2159 free(fh); | 787 free(fh); |
2160 return -1; | 788 return -ENOMEM; |
2161 } | 789 } |
2162 | 790 |
2163 if (aim_oft_buildheader(&newoft->data, fh) == -1) { | 791 if (aim_oft_buildheader(&newoft->data, fh) == -1) { |
2164 aim_frame_destroy(newoft); | 792 aim_frame_destroy(newoft); |
2165 free(fh); | 793 free(fh); |
2166 return -1; | 794 return -ENOMEM; |
2167 } | 795 } |
2168 | 796 |
2169 memcpy(newoft->hdr.rend.magic, "OFT2", 4); | 797 memcpy(newoft->hdr.rend.magic, "OFT2", 4); |
2170 newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data); | 798 newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data); |
2171 | 799 |
2172 aim_tx_enqueue(sess, newoft); | 800 aim_tx_enqueue(sess, newoft); |
801 | |
2173 free(fh); | 802 free(fh); |
803 | |
2174 return 0; | 804 return 0; |
2175 } | 805 } |
2176 | 806 |
2177 /** | 807 /** |
2178 * aim_oft_getfile_ack - acknowledge a getfile download as complete | 808 * Sometimes you just don't know with these kinds of people. |
2179 * @sess: your session | 809 * |
2180 * @conn: the getfile conn to send the ack over | 810 * @param sess The session. |
2181 * | 811 * @param conn The ODC connection of the incoming data. |
2182 * Call this function after you have read all the data in a particular | 812 * @param frr The frame allocated for the incoming data. |
2183 * filetransfer. Returns -1 on error, 0 on apparent success | 813 * @param bs It stands for "bologna sandwich." |
2184 * | 814 * @return Return 0 if no errors, otherwise return the error number. |
2185 */ | 815 */ |
2186 faim_export int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn) | 816 static int handlehdr_odc(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *frr, aim_bstream_t *bs) |
2187 { | 817 { |
2188 return -EINVAL; | 818 aim_frame_t fr; |
2189 #if 0 | 819 aim_rxcallback_t userfunc; |
2190 struct command_tx_struct *newoft; | 820 fu32_t payloadlength; |
2191 struct aim_filetransfer_priv *ft; | 821 fu16_t flags, encoding; |
2192 | 822 char *snptr = NULL; |
2193 if (!sess || !conn || !conn->priv) | 823 |
2194 return -1; | 824 fr.conn = conn; |
2195 | 825 |
2196 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) { | 826 /* AAA - ugly */ |
2197 faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); | 827 aim_bstream_setpos(bs, 20); |
2198 return -1; | 828 payloadlength = aimbs_get32(bs); |
2199 } | 829 |
2200 | 830 aim_bstream_setpos(bs, 24); |
2201 newoft->lock = 1; | 831 encoding = aimbs_get16(bs); |
2202 | 832 |
2203 memcpy(newoft->hdr.oft.magic, "OFT2", 4); | 833 aim_bstream_setpos(bs, 30); |
2204 newoft->hdr.oft.hdr2len = 0x100-8; | 834 flags = aimbs_get16(bs); |
2205 | 835 |
2206 if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { | 836 aim_bstream_setpos(bs, 36); |
2207 newoft->lock = 0; | 837 /* XXX - create an aimbs_getnullstr function? */ |
2208 aim_frame_destroy(newoft); | 838 snptr = aimbs_getstr(bs, MAXSNLEN); |
2209 return -1; | 839 |
2210 } | 840 faimdprintf(sess, 2, "faim: OFT frame: handlehdr_odc: %04x / %04x / %s\n", payloadlength, flags, snptr); |
2211 | 841 |
2212 ft = (struct aim_filetransfer_priv *)conn->priv; | 842 if (flags & 0x0002) { |
2213 | 843 int ret = 0; |
2214 if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) { | 844 |
2215 newoft->lock = 0; | 845 if (flags & 0x000c) { |
2216 aim_frame_destroy(newoft); | 846 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) |
2217 return -1; | 847 ret = userfunc(sess, &fr, snptr, 1); |
2218 } | 848 return ret; |
2219 | 849 } |
2220 newoft->lock = 0; | 850 |
2221 aim_tx_enqueue(sess, newoft); | 851 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) |
852 ret = userfunc(sess, &fr, snptr, 0); | |
853 | |
854 return ret; | |
855 | |
856 } else if (((flags & 0x000f) == 0x0000) && payloadlength) { | |
857 char *msg, *msg2; | |
858 int ret = 0; | |
859 int recvd = 0; | |
860 int i; | |
861 | |
862 if (!(msg = calloc(1, payloadlength+1))) | |
863 return -1; | |
864 msg2 = msg; | |
865 | |
866 while (payloadlength - recvd) { | |
867 if (payloadlength - recvd >= 1024) | |
868 i = aim_recv(conn->fd, msg2, 1024); | |
869 else | |
870 i = aim_recv(conn->fd, msg2, payloadlength - recvd); | |
871 if (i <= 0) { | |
872 free(msg); | |
873 return -1; | |
874 } | |
875 recvd = recvd + i; | |
876 msg2 = msg2 + i; | |
877 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER))) | |
878 userfunc(sess, &fr, snptr, (double)recvd / payloadlength); | |
879 } | |
880 | |
881 if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) ) | |
882 ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding); | |
883 | |
884 free(msg); | |
885 | |
886 return ret; | |
887 } | |
888 | |
2222 return 0; | 889 return 0; |
2223 #endif | 890 } |
2224 } | 891 |
2225 | 892 /** |
2226 /** | 893 * Handle incoming data on a rendezvous connection. This is analogous to the |
2227 * aim_oft_end - end a getfile/sendfile. | 894 * consumesnac function in rxhandlers.c, and I really think this should probably |
2228 * @sess: your session | 895 * be in rxhandlers.c as well, but I haven't finished cleaning everything up yet. |
2229 * @conn: the getfile connection | 896 * |
2230 * | 897 * @param sess The session. |
2231 * call this before you close the getfile connection if you're on the | 898 * @param fr The frame allocated for the incoming data. |
2232 * receiving/requesting end. | 899 * @return Return 0 if the packet was handled correctly, otherwise return the |
2233 */ | 900 * error number. |
2234 faim_export int aim_oft_end(aim_session_t *sess, aim_conn_t *conn) | 901 */ |
2235 { | 902 faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr) |
2236 aim_frame_t *newoft; | 903 { |
2237 struct aim_filetransfer_priv *ft; | 904 aim_conn_t *conn = fr->conn; |
2238 | 905 int ret = 1; |
2239 if (!sess || !conn || !conn->internal) | 906 |
2240 return -1; | 907 if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { |
2241 | 908 if (fr->hdr.rend.type == 0x0001) |
2242 if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, AIM_OFT_PROTO_ACK, 0))) { | 909 ret = handlehdr_odc(sess, conn, fr, &fr->data); |
2243 faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); | 910 else |
2244 return -1; | 911 faimdprintf(sess, 0, "faim: ODC directim frame unknown, type is %04x\n", fr->hdr.rend.type); |
2245 } | 912 |
2246 | 913 } else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) { |
2247 ft = (struct aim_filetransfer_priv *)conn->internal; | 914 switch (fr->hdr.rend.type) { |
2248 ft->state = 4; /* no longer wanting data */ | 915 case 0x1108: /* getfile listing.txt incoming tx->rx */ |
2249 ft->fh.flags = 0x21; | 916 break; |
2250 | 917 case 0x1209: /* get file listing ack rx->tx */ |
2251 if (aim_oft_buildheader(&(newoft->data), &(ft->fh)) == -1) { | 918 break; |
2252 aim_frame_destroy(newoft); | 919 case 0x120b: /* get file listing rx confirm */ |
2253 return -1; | 920 break; |
2254 } | 921 case 0x120c: /* getfile request */ |
2255 memcpy(newoft->hdr.rend.magic, "OFT2", 4); | 922 break; |
2256 newoft->hdr.rend.hdrlen = aim_bstream_curpos(&newoft->data); | 923 case 0x0101: /* getfile sending data */ |
2257 | 924 break; |
2258 aim_tx_enqueue(sess, newoft); | 925 case 0x0202: /* getfile recv data */ |
2259 | 926 break; |
2260 return 0; | 927 case 0x0204: /* getfile finished */ |
2261 } | 928 break; |
2262 | 929 default: |
2263 /* | 930 faimdprintf(sess, 2, "faim: OFT getfile frame uknown, type is %04x\n", fr->hdr.rend.type); |
2264 * Convert the directory separator to ^A, which seems to be AOL's attempt at portability. | 931 break; |
2265 */ | 932 } |
2266 static void oft_dirconvert(char *name) { | 933 |
2267 char *c = name; | 934 } else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) { |
2268 while ((c = strchr(c, G_DIR_SEPARATOR))) | 935 aim_rxcallback_t userfunc; |
2269 *c = 0x01; | 936 struct aim_fileheader_t *header = aim_oft_getheader(&fr->data); |
2270 } | 937 aim_oft_dirconvert_fromstupid(header->name); /* XXX - This should be client-side */ |
938 | |
939 if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, fr->hdr.rend.type))) | |
940 ret = userfunc(sess, fr, conn, header->bcookie, header); | |
941 | |
942 free(header); | |
943 } | |
944 | |
945 if (ret == -1) | |
946 aim_conn_close(conn); | |
947 | |
948 return ret; | |
949 } |