| |
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| |
2 |
| |
3 /* |
| |
4 * $Id: tcp.c 2096 2001-07-31 01:00:39Z warmenhoven $ |
| |
5 * |
| |
6 * Copyright (C) 1998-2001, Denis V. Dmitrienko <denis@null.net> and |
| |
7 * Bill Soudan <soudan@kde.org> |
| |
8 * |
| |
9 * This program is free software; you can redistribute it and/or modify |
| |
10 * it under the terms of the GNU General Public License as published by |
| |
11 * the Free Software Foundation; either version 2 of the License, or |
| |
12 * (at your option) any later version. |
| |
13 * |
| |
14 * This program is distributed in the hope that it will be useful, |
| |
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
17 * GNU General Public License for more details. |
| |
18 * |
| |
19 * You should have received a copy of the GNU General Public License |
| |
20 * along with this program; if not, write to the Free Software |
| |
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| |
22 * |
| |
23 */ |
| |
24 |
| |
25 /* |
| |
26 * Peer-to-peer ICQ protocol implementation |
| |
27 * |
| |
28 * Uses version 2 of the ICQ protocol |
| |
29 * |
| |
30 * Thanks to Douglas F. McLaughlin and many others for |
| |
31 * packet details (see tcp02.txt) |
| |
32 * |
| |
33 */ |
| |
34 |
| |
35 #include <stdlib.h> |
| |
36 |
| |
37 #include <fcntl.h> |
| |
38 #include <errno.h> |
| |
39 |
| |
40 #ifdef _WIN32 |
| |
41 #include <winsock.h> |
| |
42 #endif |
| |
43 |
| |
44 #include <sys/stat.h> |
| |
45 |
| |
46 #include "icqlib.h" |
| |
47 |
| |
48 #include "tcp.h" |
| |
49 #include "stdpackets.h" |
| |
50 #include "chatsession.h" |
| |
51 #include "filesession.h" |
| |
52 |
| |
53 /** |
| |
54 Initializes structures necessary for TCP use. Not required by user |
| |
55 programs. |
| |
56 |
| |
57 \return true on error |
| |
58 */ |
| |
59 |
| |
60 int icq_TCPInit(icq_Link *icqlink) |
| |
61 { |
| |
62 icq_TCPLink *plink; |
| |
63 |
| |
64 /* allocate lists */ |
| |
65 icqlink->d->icq_TCPLinks=icq_ListNew(); |
| |
66 icqlink->d->icq_ChatSessions=icq_ListNew(); |
| |
67 icqlink->d->icq_FileSessions=icq_ListNew(); |
| |
68 |
| |
69 /* only the main listening socket gets created upon initialization - |
| |
70 * the other two are created when necessary */ |
| |
71 plink=icq_TCPLinkNew(icqlink); |
| |
72 icq_TCPLinkListen(plink); |
| |
73 icqlink->icq_TCPSrvPort=ntohs(plink->socket_address.sin_port); |
| |
74 |
| |
75 /* reset tcp sequence number */ |
| |
76 icqlink->d->icq_TCPSequence=0xfffffffe; |
| |
77 |
| |
78 return 0; |
| |
79 } |
| |
80 |
| |
81 void icq_TCPDone(icq_Link *icqlink) |
| |
82 { |
| |
83 /* close and deallocate all tcp links, this will also close any attached |
| |
84 * file or chat sessions */ |
| |
85 if (icqlink->d->icq_TCPLinks) { |
| |
86 icq_ListDelete(icqlink->d->icq_TCPLinks, icq_TCPLinkDelete); |
| |
87 icqlink->d->icq_TCPLinks = 0; |
| |
88 } |
| |
89 if (icqlink->d->icq_ChatSessions) { |
| |
90 icq_ListDelete(icqlink->d->icq_ChatSessions, icq_ChatSessionDelete); |
| |
91 icqlink->d->icq_ChatSessions = 0; |
| |
92 } |
| |
93 if (icqlink->d->icq_FileSessions) { |
| |
94 icq_ListDelete(icqlink->d->icq_FileSessions, icq_FileSessionDelete); |
| |
95 icqlink->d->icq_FileSessions = 0; |
| |
96 } |
| |
97 } |
| |
98 |
| |
99 icq_TCPLink *icq_TCPCheckLink(icq_Link *icqlink, DWORD uin, int type) |
| |
100 { |
| |
101 icq_TCPLink *plink=icq_FindTCPLink(icqlink, uin, type); |
| |
102 |
| |
103 if(!plink) |
| |
104 { |
| |
105 plink=icq_TCPLinkNew(icqlink); |
| |
106 if(type==TCP_LINK_MESSAGE) |
| |
107 icq_TCPLinkConnect(plink, uin, 0); |
| |
108 } |
| |
109 |
| |
110 return plink; |
| |
111 } |
| |
112 |
| |
113 DWORD icq_TCPSendMessage(icq_Link *icqlink, DWORD uin, const char *message) |
| |
114 { |
| |
115 icq_TCPLink *plink; |
| |
116 icq_Packet *p; |
| |
117 DWORD sequence; |
| |
118 char data[ICQ_MAX_MESSAGE_SIZE] ; |
| |
119 |
| |
120 strncpy(data,message,sizeof(data)) ; |
| |
121 data[sizeof(data)-1]='\0'; |
| |
122 icq_RusConv("kw", data) ; |
| |
123 |
| |
124 plink=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
125 |
| |
126 /* create and send the message packet */ |
| |
127 p=icq_TCPCreateMessagePacket(plink, data); |
| |
128 sequence=icq_TCPLinkSendSeq(plink, p, 0); |
| |
129 |
| |
130 #ifdef TCP_PACKET_TRACE |
| |
131 printf("message packet sent to uin %lu { sequence=%lx }\n", uin, p->id); |
| |
132 #endif |
| |
133 |
| |
134 return sequence; |
| |
135 } |
| |
136 |
| |
137 DWORD icq_TCPSendURL(icq_Link *icqlink, DWORD uin, const char *message, const char *url) |
| |
138 { |
| |
139 icq_TCPLink *plink; |
| |
140 icq_Packet *p; |
| |
141 DWORD sequence; |
| |
142 char data[ICQ_MAX_MESSAGE_SIZE]; |
| |
143 |
| |
144 plink=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
145 |
| |
146 strncpy(data, message, sizeof(data)); |
| |
147 data[sizeof(data)-1] = '\0'; |
| |
148 icq_RusConv("kw", data); |
| |
149 |
| |
150 /* create and send the url packet */ |
| |
151 p=icq_TCPCreateURLPacket(plink, data, url); |
| |
152 sequence=icq_TCPLinkSendSeq(plink, p, 0); |
| |
153 |
| |
154 #ifdef TCP_PACKET_TRACE |
| |
155 printf("url packet queued for uin %lu { sequence=%lx }\n", uin, p->id); |
| |
156 #endif |
| |
157 |
| |
158 return sequence; |
| |
159 } |
| |
160 |
| |
161 DWORD icq_TCPSendAwayMessageReq(icq_Link *icqlink, DWORD uin, int status) |
| |
162 { |
| |
163 icq_TCPLink *plink; |
| |
164 icq_Packet *p; |
| |
165 DWORD sequence; |
| |
166 WORD type; |
| |
167 |
| |
168 plink=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
169 |
| |
170 /* create and send the message packet */ |
| |
171 switch(status) |
| |
172 { |
| |
173 case STATUS_AWAY: |
| |
174 type=ICQ_TCP_MSG_READAWAY; |
| |
175 break; |
| |
176 case STATUS_DND: |
| |
177 type=ICQ_TCP_MSG_READDND; |
| |
178 break; |
| |
179 case STATUS_NA: |
| |
180 type=ICQ_TCP_MSG_READNA; |
| |
181 break; |
| |
182 case STATUS_OCCUPIED: |
| |
183 type=ICQ_TCP_MSG_READOCCUPIED; |
| |
184 break; |
| |
185 case STATUS_FREE_CHAT: |
| |
186 type=ICQ_TCP_MSG_READFFC; |
| |
187 break; |
| |
188 default: |
| |
189 type=ICQ_TCP_MSG_READAWAY; |
| |
190 break; |
| |
191 } |
| |
192 p=icq_TCPCreateAwayReqPacket(plink, type); |
| |
193 sequence=icq_TCPLinkSendSeq(plink, p, 0); |
| |
194 |
| |
195 #ifdef TCP_PACKET_TRACE |
| |
196 printf("away msg request packet sent to uin %lu { sequence=%lx }\n", uin, p->id); |
| |
197 #endif |
| |
198 |
| |
199 return sequence; |
| |
200 } |
| |
201 |
| |
202 DWORD icq_SendChatRequest(icq_Link *icqlink, DWORD uin, const char *message) |
| |
203 { |
| |
204 icq_TCPLink *plink; |
| |
205 icq_Packet *p; |
| |
206 DWORD sequence; |
| |
207 char data[ICQ_MAX_MESSAGE_SIZE]; |
| |
208 |
| |
209 plink=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
210 |
| |
211 strncpy(data, message, sizeof(data)); |
| |
212 data[sizeof(data)-1] = '\0'; |
| |
213 icq_RusConv("kw", data); |
| |
214 |
| |
215 /* create and send the url packet */ |
| |
216 p=icq_TCPCreateChatReqPacket(plink, data); |
| |
217 sequence=icq_TCPLinkSendSeq(plink, p, 0); |
| |
218 |
| |
219 #ifdef TCP_PACKET_TRACE |
| |
220 printf("chat req packet sent to uin %lu { sequence=%lx }\n", uin, p->id); |
| |
221 #endif |
| |
222 |
| |
223 return sequence; |
| |
224 } |
| |
225 |
| |
226 unsigned long icq_SendFileRequest(icq_Link *icqlink, unsigned long uin, |
| |
227 const char *message, char **files) |
| |
228 { |
| |
229 icq_TCPLink *plink; |
| |
230 icq_FileSession *pfile; |
| |
231 icq_Packet *p; |
| |
232 unsigned long sequence; |
| |
233 char filename[64]; |
| |
234 char data[ICQ_MAX_MESSAGE_SIZE]; |
| |
235 char **filesiterator; |
| |
236 char **pfilesiterator; |
| |
237 |
| |
238 plink=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
239 |
| |
240 /* create the file session, this will be linked to the incoming icq_TCPLink |
| |
241 * in icq_HandleFileAck */ |
| |
242 pfile=icq_FileSessionNew(icqlink); |
| |
243 pfile->remote_uin=uin; |
| |
244 pfile->files=files; |
| |
245 pfile->direction=FILE_STATUS_SENDING; |
| |
246 |
| |
247 /* count the number and size of the files */ |
| |
248 pfile->total_files=0; |
| |
249 filesiterator = files; |
| |
250 while(*filesiterator) { |
| |
251 #ifdef _WIN32 |
| |
252 struct _stat file_status; |
| |
253 if(_stat(*filesiterator, &file_status)==0) { |
| |
254 #else |
| |
255 struct stat file_status; |
| |
256 if(stat(*filesiterator, &file_status)==0) { |
| |
257 #endif |
| |
258 pfile->total_files++; |
| |
259 pfile->total_bytes+=file_status.st_size; |
| |
260 } |
| |
261 filesiterator++; |
| |
262 } |
| |
263 |
| |
264 pfile->files=(char **)malloc(sizeof(char *)*(pfile->total_files +1)); |
| |
265 filesiterator = files; |
| |
266 pfilesiterator = pfile->files; |
| |
267 while (*filesiterator) { |
| |
268 *pfilesiterator=(char *)malloc(strlen(*filesiterator)+1); |
| |
269 strcpy(*pfilesiterator,*filesiterator); |
| |
270 filesiterator++; |
| |
271 pfilesiterator++; |
| |
272 } |
| |
273 *pfilesiterator = NULL; |
| |
274 |
| |
275 strncpy(filename, *(pfile->files), 64); |
| |
276 |
| |
277 strncpy(data, message, sizeof(data)); |
| |
278 data[sizeof(data)-1]='\0'; |
| |
279 icq_RusConv("kw", data); |
| |
280 |
| |
281 /* create and send the file req packet */ |
| |
282 p=icq_TCPCreateFileReqPacket(plink, (char *)data, filename, |
| |
283 pfile->total_bytes); |
| |
284 sequence=icq_TCPLinkSendSeq(plink, p, 0); |
| |
285 pfile->id=sequence; |
| |
286 |
| |
287 #ifdef TCP_PACKET_TRACE |
| |
288 printf("file req packet sent to uin %lu { sequence=%lx }\n", uin, p->id); |
| |
289 #endif |
| |
290 |
| |
291 return sequence; |
| |
292 } |
| |
293 |
| |
294 void icq_AcceptChatRequest(icq_Link *icqlink, DWORD uin, unsigned long sequence) |
| |
295 { |
| |
296 icq_TCPLink *pmessage, *plisten; |
| |
297 icq_ChatSession *pchat; |
| |
298 icq_Packet *p; |
| |
299 |
| |
300 pmessage=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
301 |
| |
302 /* create the chat listening socket if necessary */ |
| |
303 if(!(plisten=icq_FindTCPLink(icqlink, 0, TCP_LINK_CHAT))) |
| |
304 { |
| |
305 plisten=icq_TCPLinkNew(icqlink); |
| |
306 plisten->type=TCP_LINK_CHAT; |
| |
307 icq_TCPLinkListen(plisten); |
| |
308 } |
| |
309 |
| |
310 /* create the chat session, this will be linked to the incoming icq_TCPLink |
| |
311 * in TCPProcessHello */ |
| |
312 pchat=icq_ChatSessionNew(icqlink); |
| |
313 pchat->id=sequence; |
| |
314 pchat->remote_uin=uin; |
| |
315 |
| |
316 /* create and send the ack packet */ |
| |
317 p=icq_TCPCreateChatReqAck(pmessage, |
| |
318 ntohs(plisten->socket_address.sin_port)); |
| |
319 (void)icq_TCPLinkSendSeq(pmessage, p, sequence); |
| |
320 |
| |
321 #ifdef TCP_PACKET_TRACE |
| |
322 printf("chat req ack sent to uin %lu { sequence=%lx }\n", uin, sequence); |
| |
323 #endif |
| |
324 } |
| |
325 |
| |
326 void icq_TCPSendChatData(icq_Link *icqlink, DWORD uin, const char *data) |
| |
327 { |
| |
328 icq_TCPLink *plink=icq_FindTCPLink(icqlink, uin, TCP_LINK_CHAT); |
| |
329 char data1[ICQ_MAX_MESSAGE_SIZE]; |
| |
330 int data1_len; |
| |
331 |
| |
332 if(!plink) |
| |
333 return; |
| |
334 |
| |
335 strncpy(data1,data,sizeof(data1)) ; |
| |
336 data1[sizeof(data1)-1] = '\0'; |
| |
337 data1_len = strlen(data); |
| |
338 icq_ChatRusConv_n("kw", data1, data1_len); |
| |
339 |
| |
340 send(plink->socket, data1, data1_len, 0); |
| |
341 } |
| |
342 |
| |
343 void icq_TCPSendChatData_n(icq_Link *icqlink, DWORD uin, const char *data, int len) |
| |
344 { |
| |
345 icq_TCPLink *plink=icq_FindTCPLink(icqlink, uin, TCP_LINK_CHAT); |
| |
346 char *data1; |
| |
347 |
| |
348 if(!plink) |
| |
349 return; |
| |
350 |
| |
351 data1 = (char *)malloc(len); |
| |
352 memcpy(data1, data, len); |
| |
353 icq_ChatRusConv_n("kw", data1, len); |
| |
354 |
| |
355 send(plink->socket, data1, len, 0); |
| |
356 } |
| |
357 |
| |
358 icq_FileSession *icq_AcceptFileRequest(icq_Link *icqlink, DWORD uin, |
| |
359 unsigned long sequence) |
| |
360 { |
| |
361 icq_TCPLink *pmessage, *plisten; |
| |
362 icq_FileSession *pfile; |
| |
363 icq_Packet *p; |
| |
364 |
| |
365 pmessage=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
366 |
| |
367 /* create the file listening socket if necessary */ |
| |
368 if(!(plisten=icq_FindTCPLink(icqlink, 0, TCP_LINK_FILE))) |
| |
369 { |
| |
370 plisten=icq_TCPLinkNew(icqlink); |
| |
371 plisten->type=TCP_LINK_FILE; |
| |
372 icq_TCPLinkListen(plisten); |
| |
373 } |
| |
374 |
| |
375 /* create the file session, this will be linked to the incoming icq_TCPLink |
| |
376 * in TCPProcessHello */ |
| |
377 pfile=icq_FileSessionNew(icqlink); |
| |
378 pfile->id=sequence; |
| |
379 pfile->remote_uin=uin; |
| |
380 pfile->direction=FILE_STATUS_RECEIVING; |
| |
381 pfile->tcplink=plisten; |
| |
382 icq_FileSessionSetStatus(pfile, FILE_STATUS_LISTENING); |
| |
383 |
| |
384 /* create and send the ack packet */ |
| |
385 p=icq_TCPCreateFileReqAck(pmessage, |
| |
386 ntohs(plisten->socket_address.sin_port)); |
| |
387 (void)icq_TCPLinkSendSeq(pmessage, p, sequence); |
| |
388 |
| |
389 #ifdef TCP_PACKET_TRACE |
| |
390 printf("file req ack sent to uin %lu { sequence=%lx }\n", uin, sequence); |
| |
391 #endif |
| |
392 |
| |
393 return pfile; |
| |
394 } |
| |
395 |
| |
396 void icq_RefuseFileRequest(icq_Link *icqlink, DWORD uin, |
| |
397 unsigned long sequence, const char *reason) |
| |
398 { |
| |
399 icq_TCPLink *pmessage=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
400 icq_Packet *p; |
| |
401 |
| |
402 /* create and send the refuse packet */ |
| |
403 p=icq_TCPCreateFileReqRefuse(pmessage, |
| |
404 ntohs(pmessage->socket_address.sin_port), reason); |
| |
405 (void)icq_TCPLinkSendSeq(pmessage, p, sequence); |
| |
406 |
| |
407 #ifdef TCP_PACKET_TRACE |
| |
408 printf("file req refuse sent to uin %lu { sequence=%lx, reason=\"%s\" }\n", |
| |
409 uin, sequence, reason); |
| |
410 #endif |
| |
411 } |
| |
412 |
| |
413 void icq_CancelFileRequest(icq_Link *icqlink, DWORD uin, unsigned long sequence) |
| |
414 { |
| |
415 icq_TCPLink *pmessage=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
416 icq_FileSession *psession=icq_FindFileSession(icqlink, uin, sequence); |
| |
417 icq_Packet *p; |
| |
418 |
| |
419 if (psession) |
| |
420 icq_FileSessionClose(psession); |
| |
421 |
| |
422 /* create and send the cancel packet */ |
| |
423 p=icq_TCPCreateFileReqCancel(pmessage, |
| |
424 ntohs(pmessage->socket_address.sin_port)); |
| |
425 (void)icq_TCPLinkSendSeq(pmessage, p, sequence); |
| |
426 #ifdef TCP_PACKET_TRACE |
| |
427 printf("file req cancel sent to uin %lu { sequence=%lx }\n", uin, sequence); |
| |
428 #endif |
| |
429 } |
| |
430 |
| |
431 void icq_RefuseChatRequest(icq_Link *icqlink, DWORD uin, |
| |
432 unsigned long sequence, const char *reason) |
| |
433 { |
| |
434 icq_TCPLink *pmessage=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
435 icq_Packet *p; |
| |
436 |
| |
437 /* create and send the refuse packet */ |
| |
438 p=icq_TCPCreateChatReqRefuse(pmessage, |
| |
439 ntohs(pmessage->socket_address.sin_port), reason); |
| |
440 (void)icq_TCPLinkSendSeq(pmessage, p, sequence); |
| |
441 |
| |
442 #ifdef TCP_PACKET_TRACE |
| |
443 printf("chat req refuse sent to uin %lu { sequence=%lx, reason=\"%s\" }\n", |
| |
444 uin, sequence, reason); |
| |
445 #endif |
| |
446 } |
| |
447 |
| |
448 void icq_CancelChatRequest(icq_Link *icqlink, DWORD uin, unsigned long sequence) |
| |
449 { |
| |
450 icq_TCPLink *pmessage=icq_TCPCheckLink(icqlink, uin, TCP_LINK_MESSAGE); |
| |
451 icq_ChatSession *psession=icq_FindChatSession(icqlink, uin); |
| |
452 icq_Packet *p; |
| |
453 |
| |
454 if (psession) |
| |
455 icq_ChatSessionClose(psession); |
| |
456 |
| |
457 /* create and send the cancel packet */ |
| |
458 p=icq_TCPCreateChatReqCancel(pmessage, |
| |
459 ntohs(pmessage->socket_address.sin_port)); |
| |
460 (void)icq_TCPLinkSendSeq(pmessage, p, sequence); |
| |
461 |
| |
462 #ifdef TCP_PACKET_TRACE |
| |
463 printf("chat req cancel sent to uin %lu { sequence=%lx }\n", uin, sequence); |
| |
464 #endif |
| |
465 } |