Add an additional "protocol index" argument to "{old_}dissector_add()",
[obnox/wireshark/wip.git] / packet-icq.c
1 /* packet-icq.c
2  * Routines for ICQ packet disassembly
3  *
4  * $Id: packet-icq.c,v 1.26 2001/01/09 06:31:36 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Johan Feyaerts
8  * Copyright 1999 Johan Feyaerts
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 /*
25  * This file: by Kojak <kojak@bigwig.net>
26  *
27  * Decoding code ripped, reference to the original author at the
28  * appropriate place with the code itself.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #ifdef HAVE_SYS_TYPES_H
36 # include <sys/types.h>
37 #endif
38
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42
43 #ifdef HAVE_STDDEF_H
44 #include <stddef.h>
45 #endif
46
47 #ifdef HAVE_ARPA_INET_H
48 #include <arpa/inet.h>
49 #endif
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <assert.h>
54 #include <ctype.h>
55 #include <time.h>
56 #include <string.h>
57 #include <glib.h>
58
59 #ifdef NEED_SNPRINTF_H
60 # include "snprintf.h"
61 #endif
62
63 #include "packet.h"
64 #include "resolv.h"
65
66 static int proto_icq = -1;
67 static int hf_icq_uin =-1;
68 static int hf_icq_cmd =-1;
69 static int hf_icq_sessionid =-1;
70 static int hf_icq_checkcode =-1;
71 static int hf_icq_decode = -1;
72 static int hf_icq_type = -1;
73
74 static gint ett_icq = -1;
75 static gint ett_icq_header = -1;
76 static gint ett_icq_decode = -1;
77 static gint ett_icq_body = -1;
78 static gint ett_icq_body_parts = -1;
79
80 #define UDP_PORT_ICQ    4000
81
82 enum { ICQ5_client, ICQ5_server};
83
84 static void dissect_icqv5(const u_char *pd,
85                    int offset,
86                    frame_data *fd,
87                    proto_tree *tree);
88
89 static void
90 dissect_icqv5Server(const u_char *pd,
91                     int offset,
92                     frame_data *fd,
93                     proto_tree *tree,
94                     guint32 pktsize);
95
96 /* Offsets of fields in the ICQ headers */
97 /* Can be 0x0002 or 0x0005 */
98 #define ICQ_VERSION             0x00
99 /* Is either one (server) or four (client) bytes long */
100 /* Client header offsets */
101 #define ICQ5_UNKNOWN            0x02
102 #define ICQ5_CL_UIN             0x06
103 #define ICQ5_CL_SESSIONID       0x0a
104 #define ICQ5_CL_CMD             0x0e
105 #define ICQ5_CL_SEQNUM1         0x10
106 #define ICQ5_CL_SEQNUM2         0x12
107 #define ICQ5_CL_CHECKCODE       0x14
108 #define ICQ5_CL_PARAM           0x18
109 #define ICQ5_CL_HDRSIZE         0x18
110
111 /* Server header offsets */
112 #define ICQ5_SRV_SESSIONID      0x03
113 #define ICQ5_SRV_CMD            0x07
114 #define ICQ5_SRV_SEQNUM1        0x09
115 #define ICQ5_SRV_SEQNUM2        0x0b
116 #define ICQ5_SRV_UIN            0x0d
117 #define ICQ5_SRV_CHECKCODE      0x11
118 #define ICQ5_SRV_PARAM          0x15
119 #define ICQ5_SRV_HDRSIZE        0x15
120
121 typedef struct _cmdcode {
122     char* descr;
123     int code;
124 } cmdcode;
125
126 #define SRV_ACK                 0x000a
127
128 #define SRV_SILENT_TOO_LONG     0x001e
129
130 #define SRV_GO_AWAY             0x0028
131
132 #define SRV_NEW_UIN             0x0046
133
134 /* LOGIN_REPLY is very scary. It has a lot of fields that are undocumented
135  * Only the IP field makes sense */
136 #define SRV_LOGIN_REPLY         0x005a
137 #define SRV_LOGIN_REPLY_IP      0x000c
138
139 #define SRV_BAD_PASS            0x0064
140
141 #define SRV_USER_ONLINE         0x006e
142 #define SRV_USER_ONL_UIN        0x0000
143 #define SRV_USER_ONL_IP         0x0004
144 #define SRV_USER_ONL_PORT       0x0008
145 #define SRV_USER_ONL_REALIP     0x000c
146 #define SRV_USER_ONL_X1         0x0010
147 #define SRV_USER_ONL_STATUS     0x0013
148 #define SRV_USER_ONL_X2         0x0015
149
150 #define SRV_USER_OFFLINE        0x0078
151 #define SRV_USER_OFFLINE_UIN    0x0000
152
153 #define SRV_MULTI               0x0212
154 #define SRV_MULTI_NUM           0x0000
155
156 #define SRV_META_USER           0x03de
157 #define SRV_META_USER_SUBCMD    0x0000
158 #define SRV_META_USER_RESULT    0x0002
159 #define SRV_META_USER_DATA      0x0003
160
161 #define SRV_UPDATE_SUCCESS      0x01e0
162
163 #define SRV_UPDATE_FAIL         0x01ea
164
165 /*
166  * ICQv5 SRV_META_USER subcommands
167  */
168 #define META_EX_USER_FOUND      0x0190
169 #define META_USER_FOUND         0x019a
170 #define META_ABOUT              0x00e6
171 #define META_USER_INFO          0x00c8
172
173 #define SRV_RECV_MESSAGE        0x00dc
174 #define SRV_RECV_MSG_UIN        0x0000
175 #define SRV_RECV_MSG_YEAR       0x0004
176 #define SRV_RECV_MSG_MONTH      0x0006
177 #define SRV_RECV_MSG_DAY        0x0007
178 #define SRV_RECV_MSG_HOUR       0x0008
179 #define SRV_RECV_MSG_MINUTE     0x0009
180 #define SRV_RECV_MSG_MSG_TYPE   0x000a
181
182 #define SRV_RAND_USER           0x024e
183 #define SRV_RAND_USER_UIN       0x0000
184 #define SRV_RAND_USER_IP        0x0004
185 #define SRV_RAND_USER_PORT      0x0008
186 #define SRV_RAND_USER_REAL_IP   0x000c
187 #define SRV_RAND_USER_CLASS     0x0010
188 #define SRV_RAND_USER_X1        0x0011
189 #define SRV_RAND_USER_STATUS    0x0015
190 #define SRV_RAND_USER_TCP_VER   0x0019
191
192 /* This message has the same structure as cmd_send_msg */
193 #define SRV_SYS_DELIVERED_MESS  0x0104
194
195 cmdcode serverMetaSubCmdCode[] = {
196     { "META_USER_FOUND", META_USER_FOUND },
197     { "META_EX_USER_FOUND", META_EX_USER_FOUND },
198     { "META_ABOUT", META_ABOUT },
199     { "META_USER_INFO", META_USER_INFO },
200     { NULL, -1 }
201 };
202
203 cmdcode serverCmdCode[] = {
204     { "SRV_ACK", SRV_ACK },
205     { "SRV_SILENT_TOO_LONG", SRV_SILENT_TOO_LONG },
206     { "SRV_GO_AWAY", SRV_GO_AWAY },
207     { "SRV_NEW_UIN", SRV_NEW_UIN },
208     { "SRV_LOGIN_REPLY", SRV_LOGIN_REPLY },
209     { "SRV_BAD_PASS", SRV_BAD_PASS },
210     { "SRV_USER_ONLINE", SRV_USER_ONLINE },
211     { "SRV_USER_OFFLINE", SRV_USER_OFFLINE },
212     { "SRV_QUERY", 130 },
213     { "SRV_USER_FOUND", 140 },
214     { "SRV_END_OF_SEARCH", 160 },
215     { "SRV_NEW_USER", 180 },
216     { "SRV_UPDATE_EXT", 200 },
217     { "SRV_RECV_MESSAGE", SRV_RECV_MESSAGE },
218     { "SRV_END_OFFLINE_MESSAGES", 230 },
219     { "SRV_NOT_CONNECTED", 240 },
220     { "SRV_TRY_AGAIN", 250 },
221     { "SRV_SYS_DELIVERED_MESS", SRV_SYS_DELIVERED_MESS },
222     { "SRV_INFO_REPLY", 280 },
223     { "SRV_EXT_INFO_REPLY", 290 },
224     { "SRV_STATUS_UPDATE", 420 },
225     { "SRV_SYSTEM_MESSAGE", 450 },
226     { "SRV_UPDATE_SUCCESS", SRV_UPDATE_SUCCESS },
227     { "SRV_UPDATE_FAIL", SRV_UPDATE_FAIL },
228     { "SRV_AUTH_UPDATE", 500 },
229     { "SRV_MULTI_PACKET", SRV_MULTI },
230     { "SRV_END_CONTACTLIST_STATUS", 540 },
231     { "SRV_RAND_USER", SRV_RAND_USER },
232     { "SRV_META_USER", SRV_META_USER },
233     { NULL, -1 }
234 };
235
236 #define MSG_TEXT                0x0001
237 #define MSG_URL                 0x0004
238 #define MSG_AUTH_REQ            0x0006
239 #define MSG_AUTH                0x0008
240 #define MSG_USER_ADDED          0x000c
241 #define MSG_EMAIL               0x000e
242 #define MSG_CONTACTS            0x0013
243
244 #define STATUS_ONLINE           0x00000000
245 #define STATUS_AWAY             0x00000001
246 #define STATUS_DND              0x00000013
247 #define STATUS_INVISIBLE        0x00000100
248 #define STATUS_OCCUPIED         0x00000010
249 #define STATUS_NA               0x00000004
250 #define STATUS_CHAT             0x00000020
251
252 /* Offsets for all packets measured from the start of the payload; i.e.
253  * with the ICQ header removed
254  */
255 #define CMD_ACK                 0x000a
256 #define CMD_ACK_RANDOM          0x0000
257
258 #define CMD_SEND_MSG            0x010E
259 #define CMD_SEND_MSG_RECV_UIN   0x0000
260 #define CMD_SEND_MSG_MSG_TYPE   0x0004
261 #define CMD_SEND_MSG_MSG_LEN    0x0006
262 #define CMD_SEND_MSG_MSG_TEXT   0x0008
263 /* The rest of the packet should be a null-term string */
264
265 #define CMD_LOGIN               0x03E8
266 #define CMD_LOGIN_TIME          0x0000
267 #define CMD_LOGIN_PORT          0x0004
268 #define CMD_LOGIN_PASSLEN       0x0008
269 #define CMD_LOGIN_PASSWD        0x000A
270 /* The password is variable length; so when we've decoded the passwd,
271  * the structure starts counting at 0 again.
272  */
273 #define CMD_LOGIN_IP            0x0004
274 #define CMD_LOGIN_STATUS        0x0009
275
276 #define CMD_CONTACT_LIST        0x0406
277 #define CMD_CONTACT_LIST_NUM    0x0000
278
279 #define CMD_USER_META           0x064a
280
281 #define CMD_REG_NEW_USER        0x03fc
282
283 #define CMD_ACK_MESSAGES        0x0442
284 #define CMD_ACK_MESSAGES_RANDOM 0x0000
285
286 #define CMD_KEEP_ALIVE          0x042e
287 #define CMD_KEEP_ALIVE_RANDOM   0x0000
288
289 #define CMD_SEND_TEXT_CODE      0x0438
290 #define CMD_SEND_TEXT_CODE_LEN  0x0000
291 #define CMD_SEND_TEXT_CODE_TEXT 0x0002
292
293 #define CMD_MSG_TO_NEW_USER     0x0456
294
295 #define CMD_QUERY_SERVERS       0x04ba
296
297 #define CMD_QUERY_ADDONS        0x04c4
298
299 #define CMD_STATUS_CHANGE       0x04d8
300 #define CMD_STATUS_CHANGE_STATUS        0x0000
301
302 #define CMD_ADD_TO_LIST         0x053c
303 #define CMD_ADD_TO_LIST_UIN     0x0000
304
305 #define CMD_RAND_SEARCH         0x056e
306 #define CMD_RAND_SEARCH_GROUP   0x0000
307
308 #define CMD_META_USER           0x064a
309
310 cmdcode msgTypeCode[] = {
311     { "MSG_TEXT", MSG_TEXT },
312     { "MSG_URL", MSG_URL },
313     { "MSG_AUTH_REQ", MSG_AUTH_REQ },
314     { "MSG_AUTH", MSG_AUTH },
315     { "MSG_USER_ADDED", MSG_USER_ADDED},
316     { "MSG_EMAIL", MSG_EMAIL},
317     { "MSG_CONTACTS", MSG_CONTACTS},
318     { NULL, 0}
319 };
320
321 cmdcode statusCode[] = {
322     { "ONLINE", STATUS_ONLINE },
323     { "AWAY", STATUS_AWAY },
324     { "DND", STATUS_DND },
325     { "INVISIBLE", STATUS_INVISIBLE },
326     { "OCCUPIED", STATUS_OCCUPIED },
327     { "NA", STATUS_NA },
328     { "Free for Chat", STATUS_CHAT },
329     { NULL, 0}
330 };
331
332 cmdcode clientCmdCode[] = {
333     { "CMD_ACK", CMD_ACK },
334     { "CMD_SEND_MESSAGE", CMD_SEND_MSG },
335     { "CMD_LOGIN", CMD_LOGIN },
336     { "CMD_REG_NEW_USER", CMD_REG_NEW_USER },
337     { "CMD_CONTACT_LIST", 1030 },
338     { "CMD_SEARCH_UIN", 1050 },
339     { "CMD_SEARCH_USER", 1060 },
340     { "CMD_KEEP_ALIVE", 1070 },
341     { "CMD_SEND_TEXT_CODE", CMD_SEND_TEXT_CODE },
342     { "CMD_ACK_MESSAGES", CMD_ACK_MESSAGES },
343     { "CMD_LOGIN_1", 1100 },
344     { "CMD_MSG_TO_NEW_USER", CMD_MSG_TO_NEW_USER },
345     { "CMD_INFO_REQ", 1120 },
346     { "CMD_EXT_INFO_REQ", 1130 },
347     { "CMD_CHANGE_PW", 1180 },
348     { "CMD_NEW_USER_INFO", 1190 },
349     { "CMD_UPDATE_EXT_INFO", 1200 },
350     { "CMD_QUERY_SERVERS", CMD_QUERY_SERVERS },
351     { "CMD_QUERY_ADDONS", CMD_QUERY_ADDONS },
352     { "CMD_STATUS_CHANGE", CMD_STATUS_CHANGE },
353     { "CMD_NEW_USER_1", 1260 },
354     { "CMD_UPDATE_INFO", 1290 },
355     { "CMD_AUTH_UPDATE", 1300 },
356     { "CMD_KEEP_ALIVE2", 1310 },
357     { "CMD_LOGIN_2", 1320 },
358     { "CMD_ADD_TO_LIST", CMD_ADD_TO_LIST },
359     { "CMD_RAND_SET", 1380 },
360     { "CMD_RAND_SEARCH", CMD_RAND_SEARCH },
361     { "CMD_META_USER", CMD_META_USER },
362     { "CMD_INVIS_LIST", 1700 },
363     { "CMD_VIS_LIST", 1710 },
364     { "CMD_UPDATE_LIST", 1720 },
365     { NULL, 0 }
366 };
367
368 /*
369  * All ICQv5 decryption code thanx to Sebastien Dault (daus01@gel.usherb.ca)
370  */
371 const u_char
372 table_v5 [] = {
373  0x59, 0x60, 0x37, 0x6B, 0x65, 0x62, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x60, 0x57, 0x5B, 0x3D,
374  0x5E, 0x34, 0x6D, 0x36, 0x50, 0x3F, 0x6F, 0x67, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x47, 0x63, 0x39,
375  0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x43, 0x69, 0x48, 0x33, 0x31, 0x64, 0x35, 0x5A, 0x4A, 0x42,
376  0x56, 0x40, 0x67, 0x53, 0x41, 0x07, 0x6C, 0x49, 0x58, 0x3B, 0x4D, 0x46, 0x68, 0x43, 0x69, 0x48,
377  0x33, 0x31, 0x44, 0x65, 0x62, 0x46, 0x48, 0x53, 0x41, 0x07, 0x6C, 0x69, 0x48, 0x33, 0x51, 0x54,
378  0x5D, 0x4E, 0x6C, 0x49, 0x38, 0x4B, 0x55, 0x4A, 0x62, 0x46, 0x48, 0x33, 0x51, 0x34, 0x6D, 0x36,
379  0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x63, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64, 0x35, 0x5A,
380  0x6A, 0x52, 0x6E, 0x3C, 0x51, 0x34, 0x6D, 0x36, 0x50, 0x5F, 0x5F, 0x3F, 0x4F, 0x37, 0x4B, 0x35,
381  0x5A, 0x4A, 0x62, 0x66, 0x58, 0x3B, 0x4D, 0x66, 0x58, 0x5B, 0x5D, 0x4E, 0x6C, 0x49, 0x58, 0x3B,
382  0x4D, 0x66, 0x58, 0x3B, 0x4D, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64,
383  0x55, 0x6A, 0x32, 0x3E, 0x44, 0x45, 0x52, 0x6E, 0x3C, 0x31, 0x64, 0x55, 0x6A, 0x52, 0x4E, 0x6C,
384  0x69, 0x48, 0x53, 0x61, 0x4C, 0x39, 0x30, 0x6F, 0x47, 0x63, 0x59, 0x60, 0x57, 0x5B, 0x3D, 0x3E,
385  0x64, 0x35, 0x3A, 0x3A, 0x5A, 0x6A, 0x52, 0x4E, 0x6C, 0x69, 0x48, 0x53, 0x61, 0x6C, 0x49, 0x58,
386  0x3B, 0x4D, 0x46, 0x68, 0x63, 0x39, 0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x67, 0x53, 0x41, 0x25, 0x41,
387  0x3C, 0x51, 0x54, 0x3D, 0x5E, 0x54, 0x5D, 0x4E, 0x4C, 0x39, 0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F,
388  0x47, 0x43, 0x69, 0x48, 0x33, 0x51, 0x54, 0x5D, 0x6E, 0x3C, 0x31, 0x64, 0x35, 0x5A, 0x00, 0x00 };
389  
390 static char*
391 findcmd(cmdcode* c, int num)
392 {
393     static char buf[16];
394     cmdcode* p = c;
395     while (p->descr != NULL) {
396         if (p->code == num) {
397             return p->descr;
398         }
399         p++;
400     }
401     snprintf(buf, sizeof(buf), "(%x)", num);
402     return buf;
403 }
404
405 static char*
406 findMsgType(int num)
407 {
408     return findcmd(msgTypeCode, num);
409 }
410
411 static char*
412 findSubCmd(int num)
413 {
414     return findcmd(serverMetaSubCmdCode, num);
415 }
416
417 static char*
418 findClientCmd(int num)
419 {
420     return findcmd(clientCmdCode, num);
421 }
422
423 static char*
424 findServerCmd(int num)
425 {
426     return findcmd(serverCmdCode, num);
427 }
428
429 static char*
430 findStatus(int num)
431 {
432     return findcmd(statusCode, num);
433 }
434
435 static void
436 proto_tree_add_hexdump(proto_tree* t,
437                        guint32 offset,
438                        const u_char *data,
439                        int size)
440 {
441     int i;
442     char buf[96];
443     int n;
444     int done = 0, line = 0;
445     int added = 0;
446
447     if (size==0)
448         return;
449     
450     line = size / 16;
451     
452     for (i=0;i<line;i++) {
453         added = 0;
454         done = 0;
455         for (n = i * 16; n < (i+1)*16; n++) {
456             added = sprintf(buf+done, "%02x", data[n]);
457             if ((n%8)==7)
458                 added += sprintf(buf + done + added, "  ");
459             else
460                 added += sprintf(buf + done + added, " ");
461             done += added;
462         }
463         for (n = i * 16; n < (i+1)*16; n++) {
464             if (isprint(data[n]))
465                 added = sprintf(buf + done, "%c", data[n]);
466             else
467                 added = sprintf(buf + done, ".");
468             done += added;
469         }
470         proto_tree_add_text(t, NullTVB,
471                             offset + i*16,
472                             16,
473                             buf);
474     }
475     if ((size%16)!=0) {
476         done = 0;
477         for (n = line * 16 ; n < size ; n++) {
478             added = sprintf(buf+done, "%02x", data[n]);
479             if ((n%8)==7)
480                 added += sprintf(buf + done + added, "  ");
481             else
482                 added += sprintf(buf + done + added, " ");
483             done += added;
484         }
485         for (n = size ; (n%16)!=0;n++) {
486             added = 0;
487             if ((n%8)==7)
488                 added += sprintf(buf + done + added, "    ");
489             else
490                 added += sprintf(buf + done + added, "   ");
491             done += added;
492         }
493         for (n = line * 16; n < (line+1)*16; n++) {
494             added = 0;
495             if (n<size) {
496                 if (isprint(data[n]))
497                     added = sprintf(buf + done, "%c", data[n]);
498                 else
499                     added = sprintf(buf + done, ".");
500             } else {
501                 added = sprintf(buf + done, " ");
502             }
503             done += added;
504         }
505         proto_tree_add_text(t, NullTVB,
506                             offset + line*16,
507                             size % 16,
508                             buf);
509     }
510 }
511
512 static guint32
513 get_v5key(const u_char* pd, int len)
514 {
515     guint32 a1, a2, a3, a4, a5;
516     guint32 code, check, key;
517
518     code = pletohl(&pd[ICQ5_CL_CHECKCODE]);
519
520     a1 = code & 0x0001f000;
521     a2 = code & 0x07c007c0;
522     a3 = code & 0x003e0001;
523     a4 = code & 0xf8000000;
524     a5 = code & 0x0000083e;
525
526     a1 = a1 >> 0x0c;
527     a2 = a2 >> 0x01;
528     a3 = a3 << 0x0a;
529     a4 = a4 >> 0x10;
530     a5 = a5 << 0x0f;
531
532     check = a5 + a1 + a2 + a3 + a4;
533     key = len * 0x68656C6C;
534     key += check;
535     return key;
536 }
537
538 static void
539 decrypt_v5(u_char *bfr, guint32 size,guint32 key)
540 {
541     guint32 i;
542     guint32 k;
543     for (i=0x0a; i < size+3; i+=4 ) {
544         k = key+table_v5[i&0xff];
545         if ( i != 0x16 ) {
546             bfr[i] ^= (u_char)(k & 0xff);
547             bfr[i+1] ^= (u_char)((k & 0xff00)>>8);
548         }
549         if ( i != 0x12 ) {
550             bfr[i+2] ^= (u_char)((k & 0xff0000)>>16);
551             bfr[i+3] ^= (u_char)((k & 0xff000000)>>24);
552         }
553     }
554 }
555
556 static void
557 dissect_icqv4(const u_char *pd,
558               int offset,
559               frame_data *fd, 
560               proto_tree *tree)
561 {
562     /* Not really implemented yet */
563     if (check_col(fd, COL_PROTOCOL)) {
564         col_set_str(fd, COL_PROTOCOL, "ICQv4 (UDP)");
565     }
566     if (check_col(fd, COL_INFO)) {
567         col_set_str(fd, COL_INFO, "ICQ Version 4 protocol");
568     }
569 }
570
571 static void
572 dissect_icqv3(const u_char *pd,
573               int offset,
574               frame_data *fd, 
575               proto_tree *tree)
576 {
577     /* Not really implemented yet */
578     if (check_col(fd, COL_PROTOCOL)) {
579         col_set_str(fd, COL_PROTOCOL, "ICQv3 (UDP)");
580     }
581     if (check_col(fd, COL_INFO)) {
582         col_set_str(fd, COL_INFO, "ICQ Version 3 protocol");
583     }
584 }
585
586 static void
587 dissect_icqv2(const u_char *pd,
588               int offset,
589               frame_data *fd, 
590               proto_tree *tree)
591 {
592     /* Not really implemented yet */
593     if (check_col(fd, COL_PROTOCOL)) {
594         col_set_str(fd, COL_PROTOCOL, "ICQv2 (UDP)");
595     }
596     if (check_col(fd, COL_INFO)) {
597         col_set_str(fd, COL_INFO, "ICQ Version 2 protocol");
598     }
599 }
600
601 /*
602  * Find first occurrence of ch in buf
603  * Buf is max size big.
604  */
605 static char*
606 strnchr(const u_char* buf, u_char ch, int size)
607 {
608     int i;
609     u_char* p = (u_char*) buf;
610     for (i=0;(*p) && (*p!=ch) && (i<size); p++, i++)
611         ;
612     if ((*p == '\0') || (i>=size))
613         return NULL;
614     return p;
615 }
616
617 /*
618  * The packet at pd has a (len, string) pair.
619  * Copy the string to a buffer, and display it in the tree.
620  * Observe any limits you might cross.
621  *
622  * If anything is wrong, return -1, since -1 is not a valid string
623  * length. Else, return the number of chars processed.
624  */
625 static guint16
626 proto_add_icq_attr(proto_tree* tree, /* The tree to add to */
627                    const char* pd, /* Pointer to the field */
628                    const int offset, /* Offset from the start of packet */
629                    const int size, /* The number of bytes left in pd */
630                    char* descr) /* The description to use in the tree */
631 {
632     guint16 len;
633     char* data;
634     int left = size;
635     
636     if (size<sizeof(guint16))
637         return -1;
638     len = pletohs(pd);
639     left -= sizeof(guint16);
640     if (left<len) {
641         proto_tree_add_text(tree, NullTVB,
642                             offset,
643                             sizeof(guint16),
644                             "Length: %u", len);
645         return -1;
646     }
647                             
648     data = g_malloc(len);
649
650     strncpy(data, pd + sizeof(guint16), len);
651     data[len - 1] = '\0';
652
653     proto_tree_add_text(tree, NullTVB,
654                         offset,
655                         sizeof(guint16) + len,
656                         "%s[%u]: %s", descr, len, data);
657     g_free(data);
658
659     return len + sizeof(guint16);
660 }
661
662 static void
663 icqv5_decode_msgType(proto_tree* tree,
664                      const unsigned char* pd, /* From start of messageType */
665                      int offset,
666                      int size)
667 {
668     proto_item* ti = NULL;
669     proto_tree* subtree = NULL;
670     int left = size;
671     char *msgText = NULL;
672     guint16 msgType = -1;
673     guint16 msgLen = -1;
674     int i,j,n;
675     static char* auth_req_field_descr[] = {
676         "Nickname",
677         "First name",
678         "Last name",
679         "Email address",
680         "Unknown",
681         "Reason"};
682     static char* emain_field_descr[] = {
683         "Nickname",
684         "First name",
685         "Last name",
686         "Email address",
687         "Unknown",
688         "Text\n"
689     };
690     
691     enum {OFF_MSG_TYPE=0,
692           OFF_MSG_LEN=2,
693           OFF_MSG_TEXT=4};
694
695     
696     if (left >= sizeof(guint16)) {
697         msgType = pletohs(pd + OFF_MSG_TYPE);
698         left -= sizeof(guint16);
699     }
700     if (left >= sizeof(guint16)) {
701         msgLen = pletohs(pd + OFF_MSG_LEN);
702         left -= sizeof(guint16);
703     }
704
705     ti = proto_tree_add_text(tree, NullTVB,
706                              offset ,
707                              2,
708                              "Type: %u (%s)", msgType, findMsgType(msgType));
709     /* Create a new subtree */
710     subtree = proto_item_add_subtree(ti, ett_icq_body_parts);
711
712     switch(msgType) {
713     case 0xffff:           /* Field unknown */
714         break;
715     default:
716         fprintf(stderr, "Unknown msgType: %u (%04x)\n", msgType, msgType);
717         break;
718     case MSG_TEXT:
719         msgText = g_malloc(left + 1);
720         strncpy(msgText, pd + OFF_MSG_TEXT, left);
721         msgText[left] = '\0';
722         proto_tree_add_text(subtree, NullTVB,
723                             offset + OFF_MSG_TEXT,
724                             left,
725                             "Msg: %s", msgText);
726         g_free(msgText);
727         break;
728     case MSG_URL:
729         /* Two parts, a description and the URL. Separeted by FE */
730         for (i=0;i<left;i++) {
731             if (pd[OFF_MSG_TEXT + i] == 0xfe)
732                 break;
733         }
734         msgText = g_malloc(i + 1);
735         strncpy(msgText, pd + OFF_MSG_TEXT, i);
736         if (i==left)
737             msgText[i] = '\0';
738         else
739             msgText[i-1] = '\0';
740         proto_tree_add_text(subtree, NullTVB,
741                             offset + OFF_MSG_TEXT,
742                             i,
743                             "Description: %s", msgText);
744         if (i==left)
745             break;
746         msgText = g_realloc(msgText, left - i);
747         strncpy(msgText, pd + OFF_MSG_TEXT + i + 1, left - i - 1);
748         msgText[left - i] = '\0';
749         proto_tree_add_text(subtree, NullTVB,
750                             offset + OFF_MSG_TEXT,
751                             i,
752                             "URL: %s", msgText);
753         g_free(msgText);
754         break;
755     case MSG_EMAIL:
756         i = 0;
757         j = 0;
758         msgText = NULL;
759         for (n = 0; n < 6; n++) {
760             for (;
761                  (i<left) && (pd[OFF_MSG_TEXT+i]!=0xfe);
762                  i++)
763                 ;
764             if (i>j) {
765                 msgText = g_realloc(msgText, i-j);
766                 strncpy(msgText, pd + OFF_MSG_TEXT + j, i - j - 1);
767                 msgText[i-j-1] = '\0';
768                 proto_tree_add_text(subtree, NullTVB,
769                                     offset + OFF_MSG_TEXT + j,
770                                     i - j - 1,
771                                     "%s: %s", emain_field_descr[n], msgText);
772             } else {
773                 proto_tree_add_text(subtree, NullTVB,
774                                     offset + OFF_MSG_TEXT + j,
775                                     0,
776                                     "%s: %s", emain_field_descr[n], "(empty)");
777             }
778             j = ++i;
779         }
780         if (msgText != NULL)
781             g_free(msgText);
782         break;
783         
784     case MSG_AUTH:
785     {
786         /* Three bytes, first is a char signifying success */
787         unsigned char auth_suc = pd[OFF_MSG_LEN];
788         guint16 x1 = pd[OFF_MSG_LEN+1];
789         proto_tree_add_text(subtree, NullTVB,
790                             offset + OFF_MSG_LEN,
791                             1,
792                             "Authorization: (%u) %s",auth_suc,
793                             (auth_suc==0)?"Denied":"Allowed");
794         proto_tree_add_text(subtree, NullTVB,
795                             offset + OFF_MSG_LEN + 1,
796                             sizeof(guint16),
797                             "x1: 0x%04x",x1);
798         break;
799     }
800     case MSG_AUTH_REQ:
801         /* Six parts, separated by FE */
802         i = 0;
803         j = 0;
804         msgText = g_malloc(64);
805         for (n = 0; n < 6 && i<left; n++) {
806             while (i<left && pd[OFF_MSG_TEXT+i]!=0xfe)
807                 i++;
808             if (i<=left) {
809                 /* pd[OFF_MSG_TEXT+i] == 0xfe */
810                 if (i!=j) {   
811                     /* Otherwise, it'd be a null string */
812                     msgText = g_realloc(msgText, i - j);
813                     strncpy(msgText, pd + OFF_MSG_TEXT + j, i-j);
814                     msgText[i-j] = '\0';
815                     proto_tree_add_text(subtree, NullTVB,
816                                         offset + OFF_MSG_TEXT + j,
817                                         i - j,
818                                         "%s: %s", auth_req_field_descr[n], msgText);
819                 } else {
820                     proto_tree_add_text(subtree, NullTVB,
821                                         offset + OFF_MSG_TEXT + j,
822                                         i - j,
823                                         "%s: %s", auth_req_field_descr[n], "(null)");
824                 }
825                 j = ++i;
826                 /* i and j point after the 0xfe character */
827             }
828         }    
829
830         if (msgText != NULL)
831             g_free(msgText);
832         break;
833     case MSG_USER_ADDED:
834         /* Four parts, separated by FE */
835         i = 0;
836         j = 0;
837         /* This is necessary, because g_realloc does not behave like
838              * g_malloc if the first parameter == NULL */
839         msgText = g_malloc(64);
840         for (n = 0; n < 4 && i<left; n++) {
841             while (i<left && pd[OFF_MSG_TEXT+i]!=0xfe)
842                 i++;
843             if (i<=left) {
844                 /* pd[OFF_MSG_TEXT+i] == 0xfe */
845                 if (i!=j) {   
846                     /* Otherwise, it'd be a null string */
847                     msgText = g_realloc(msgText, i - j);
848                     strncpy(msgText, pd + OFF_MSG_TEXT + j, i-j);
849                     msgText[i-j] = '\0';
850                     proto_tree_add_text(subtree, NullTVB,
851                                         offset + OFF_MSG_TEXT + j,
852                                         i - j,
853                                         "%s: %s", auth_req_field_descr[n], msgText);
854                 } else {
855                     proto_tree_add_text(subtree, NullTVB,
856                                         offset + OFF_MSG_TEXT + j,
857                                         i - j,
858                                         "%s: %s", auth_req_field_descr[n], "(null)");
859                 }
860                 j = ++i;
861                 /* i and j point after the 0xfe character */
862             }
863         }    
864         if (msgText != NULL)
865             g_free(msgText);
866         break;
867     case MSG_CONTACTS:
868     {
869         u_char* p = (u_char*) &pd[OFF_MSG_TEXT];
870         u_char* pprev = p;
871         int sz = 0;            /* Size of the current element */
872         int n = 0;             /* The nth element */
873         int done = 0;          /* Number of chars processed */
874         u_char* msgText2 = NULL;
875         msgText = NULL;
876         /* Create a new subtree */
877         subtree = proto_item_add_subtree(ti, ett_icq_body_parts);
878         while (p!=NULL) {
879             p = strnchr(pprev, 0xfe, left);
880             
881             if (p!=NULL)
882                 sz = (int)(p - pprev);
883             else
884                 sz = left;
885             msgText = g_realloc(msgText, sz+1);
886             strncpy(msgText, pprev, sz);
887             msgText[sz] = '\0';
888             
889             if (n == 0) {
890                 /* The first element is the number of Nick/UIN pairs follow */
891                 proto_tree_add_text(subtree, NullTVB,
892                                     offset + OFF_MSG_TEXT + done,
893                                     sz,
894                                     "Number of pairs: %s", msgText);
895                 n++;
896             } else if (p!=NULL) {
897                 int svsz = sz;
898                 left -= (sz+1);
899                 pprev = p + 1;
900                 p = strnchr(pprev, 0xfe, left);
901                 if (p!=NULL)
902                     sz = (int)(p - pprev);
903                 else
904                     sz = left;
905                 msgText2 = g_malloc(sz+1);
906                 strncpy(msgText2, pprev, sz);
907                 msgText2[sz] = '\0';
908                 
909                 proto_tree_add_text(subtree, NullTVB,
910                                     offset + OFF_MSG_TEXT + done,
911                                     sz + svsz + 2,
912                                     "%s:%s", msgText, msgText2);
913                 n+=2;
914                 g_free(msgText2);
915             }
916             
917             left -= (sz+1);
918             pprev = p+1;
919         }
920         if (msgText != NULL)
921             g_free(msgText);
922         break;
923     }}
924 }
925
926 /*********************************
927  * 
928  * Client commands
929  *
930  *********************************/
931 static void
932 icqv5_cmd_ack(proto_tree* tree,/* Tree to put the data in */
933                      const u_char* pd, /* Packet content */
934                      int offset, /* Offset from the start of the packet to the content */
935                      int size)  /* Number of chars left to do */
936 {
937     guint32 random = pletohl(pd + CMD_ACK_RANDOM);
938     proto_tree* subtree;
939     proto_item* ti;
940
941     if (tree){
942         ti = proto_tree_add_uint_format(tree,
943                                         hf_icq_cmd,
944                                         NullTVB,
945                                         offset,
946                                         4,
947                                         CMD_ACK,
948                                         "Body");
949         subtree = proto_item_add_subtree(ti, ett_icq_body);
950         proto_tree_add_text(subtree, NullTVB,
951                             offset + CMD_ACK_RANDOM,
952                             4,
953                             "Random: 0x%08x", random);
954     }
955 }
956
957 static void
958 icqv5_cmd_rand_search(proto_tree* tree,       /* Tree to put the data in */
959                       const u_char* pd,       /* Packet content */
960                       int offset,             /* Offset from the start of the packet to the content */
961                       int size)               /* Number of chars left to do */
962 {
963     guint16 group = pletohs(pd + CMD_RAND_SEARCH_GROUP);
964     proto_tree* subtree;
965     proto_item* ti;
966
967     static const char* groups[] = {
968         "Name",
969         "General",
970         "Romance",
971         "Games",
972         "Students",
973         "20 Something",
974         "30 Something",
975         "40 Something",
976         "50 or worse",
977         "Man want women",
978         "Women want men"
979     };
980     
981     if (tree){
982         ti = proto_tree_add_uint_format(tree,
983                                         hf_icq_cmd,
984                                         NullTVB,
985                                         offset,
986                                         4,
987                                         CMD_RAND_SEARCH,
988                                         "Body");
989         subtree = proto_item_add_subtree(ti, ett_icq_body);
990         if (group>0 && (group<=sizeof(groups)/sizeof(const char*)))
991             proto_tree_add_text(subtree, NullTVB,
992                                 offset + CMD_RAND_SEARCH_GROUP,
993                                 4,
994                                 "Group: (%u) %s", group, groups[group-1]);
995         else
996             proto_tree_add_text(subtree, NullTVB,
997                                 offset + CMD_RAND_SEARCH_GROUP,
998                                 4,
999                                 "Group: (%u)", group);
1000     }
1001 }
1002
1003 static void
1004 icqv5_cmd_ack_messages(proto_tree* tree,/* Tree to put the data in */
1005                        const u_char* pd,      /* Packet content */
1006                        int offset,              /* Offset from the start of the packet to the content */
1007                        int size)                  /* Number of chars left to do */
1008 {
1009     guint32 random = pletohl(pd + CMD_ACK_MESSAGES_RANDOM);
1010     proto_tree* subtree;
1011     proto_item* ti;
1012
1013     if (tree){
1014         ti = proto_tree_add_uint_format(tree,
1015                                         hf_icq_cmd,
1016                                         NullTVB,
1017                                         offset,
1018                                         4,
1019                                         CMD_ACK_MESSAGES,
1020                                         "Body");
1021         subtree = proto_item_add_subtree(ti, ett_icq_body);
1022         proto_tree_add_text(subtree, NullTVB,
1023                             offset + CMD_ACK_MESSAGES_RANDOM,
1024                             4,
1025                             "Random: 0x%08x", random);
1026     }
1027 }
1028
1029 static void
1030 icqv5_cmd_keep_alive(proto_tree* tree,/* Tree to put the data in */
1031                        const u_char* pd,      /* Packet content */
1032                        int offset,              /* Offset from the start of the packet to the content */
1033                        int size)                  /* Number of chars left to do */
1034 {
1035     guint32 random = pletohl(pd + CMD_KEEP_ALIVE_RANDOM);
1036     proto_tree* subtree;
1037     proto_item* ti;
1038
1039     if (tree){
1040         ti = proto_tree_add_uint_format(tree,
1041                                         hf_icq_cmd,
1042                                         NullTVB,
1043                                         offset,
1044                                         4,
1045                                         CMD_KEEP_ALIVE,
1046                                         "Body");
1047         subtree = proto_item_add_subtree(ti, ett_icq_body);
1048         proto_tree_add_text(subtree, NullTVB,
1049                             offset + CMD_KEEP_ALIVE_RANDOM,
1050                             4,
1051                             "Random: 0x%08x", random);
1052     }
1053 }
1054
1055 static void
1056 icqv5_cmd_send_text_code(proto_tree* tree,/* Tree to put the data in */
1057                          const u_char* pd,      /* Packet content */
1058                          int offset,              /* Offset from the start of the packet to the content */
1059                          int size)                  /* Number of chars left to do */
1060 {
1061     proto_tree* subtree = NULL;
1062     proto_item* ti = NULL;
1063     guint16 len = 0;
1064     guint16 x1 = -1;
1065     char* text;
1066     int left = size;            /* The amount of data left to analyse */
1067
1068     if (tree){
1069         ti = proto_tree_add_uint_format(tree,
1070                                         hf_icq_cmd,
1071                                         NullTVB,
1072                                         offset,
1073                                         left,
1074                                         CMD_KEEP_ALIVE,
1075                                         "Body");
1076     }
1077
1078     if (left<sizeof(guint16))
1079         return;
1080     len = pletohs(pd+CMD_SEND_TEXT_CODE_LEN);
1081     left -= sizeof(gint16);
1082     if (tree){
1083         subtree = proto_item_add_subtree(ti, ett_icq_body);
1084         proto_tree_add_text(subtree, NullTVB,
1085                             offset + CMD_SEND_TEXT_CODE_LEN,
1086                             2,
1087                             "Length: %d", len);
1088     }
1089
1090     if (len>0) {
1091         len = MIN(len, left);
1092         text = g_malloc(len+1);
1093         memcpy(text, pd + CMD_SEND_TEXT_CODE_TEXT, len);
1094         text[len] = '\0';
1095         left -= len;
1096         if (tree){
1097             proto_tree_add_text(subtree, NullTVB,
1098                             offset + CMD_SEND_TEXT_CODE_TEXT,
1099                             len,
1100                             "Text: %s",text);
1101         }
1102         g_free(text);
1103     }
1104
1105     if (left<sizeof(gint16))
1106         return;
1107
1108     x1 = pletohs(pd + size - left);
1109     left -= sizeof(gint16);
1110     if (tree){
1111         proto_tree_add_text(subtree, NullTVB,
1112                             offset + CMD_SEND_TEXT_CODE_TEXT + len,
1113                             2,
1114                             "X1: 0x%04x", x1);
1115     }
1116 }
1117
1118 static void
1119 icqv5_cmd_add_to_list(proto_tree* tree,/* Tree to put the data in */
1120                       const u_char* pd,      /* Packet content */
1121                       int offset,            /* Offset from the start of the packet to the content */
1122                       int size)              /* Number of chars left to do */
1123 {
1124     guint32 uin = -1;
1125     proto_tree* subtree;
1126     proto_item* ti;
1127     if (size>=4)
1128         uin = pletohl(pd + CMD_ADD_TO_LIST);
1129     if (tree){
1130         ti = proto_tree_add_uint_format(tree,
1131                                         hf_icq_cmd,
1132                                         NullTVB,
1133                                         offset,
1134                                         4,
1135                                         CMD_ADD_TO_LIST,
1136                                         "Body");
1137         subtree = proto_item_add_subtree(ti, ett_icq_body);
1138         proto_tree_add_text(subtree, NullTVB,
1139                             offset + CMD_ADD_TO_LIST_UIN,
1140                             4,
1141                             "UIN: %u", uin);
1142     }
1143 }
1144
1145 static void
1146 icqv5_cmd_status_change(proto_tree* tree,/* Tree to put the data in */
1147                         const u_char* pd,       /* Packet content */
1148                         int offset,                /* Offset from the start of the packet to the content */
1149                         int size)                     /* Number of chars left to do */
1150 {
1151     guint32 status = -1;
1152     proto_tree* subtree;
1153     proto_item* ti;
1154
1155     if (size >= CMD_STATUS_CHANGE_STATUS + 4)
1156         status = pletohl(pd + CMD_STATUS_CHANGE_STATUS);
1157     if (tree){
1158         ti = proto_tree_add_uint_format(tree,
1159                                         hf_icq_cmd,
1160                                         NullTVB,
1161                                         offset,
1162                                         4,
1163                                         CMD_STATUS_CHANGE,
1164                                         "Body");
1165         subtree = proto_item_add_subtree(ti, ett_icq_body);
1166         if (status!=-1)
1167             proto_tree_add_text(subtree, NullTVB,
1168                                 offset + CMD_STATUS_CHANGE_STATUS,
1169                                 4,
1170                                 "Status: %08x (%s)", status, findStatus(status));
1171     }
1172 }
1173
1174 static void
1175 icqv5_cmd_send_msg(proto_tree* tree,
1176                    const u_char* pd,
1177                    int offset,
1178                    int size,
1179                    int cmd)
1180 {
1181     proto_tree* subtree;
1182     proto_item* ti;
1183     guint32 receiverUIN = 0xffffffff;
1184     guint16 msgType = 0xffff;
1185     guint16 msgLen = 0xffff;
1186     int left = size;            /* left chars to do */
1187     
1188     if (left < 4)
1189         return;
1190     receiverUIN = pletohl(pd + CMD_SEND_MSG_RECV_UIN);
1191     left -= 4;
1192     if (left < 2) 
1193         return;
1194     msgType = pletohs(pd + CMD_SEND_MSG_MSG_TYPE);
1195     left -= 2;
1196     if (left < 2) 
1197         return;
1198     msgLen = pletohs(pd + CMD_SEND_MSG_MSG_LEN);
1199     left -= 2;
1200
1201     if (tree) {
1202         ti = proto_tree_add_uint_format(tree,
1203                                         hf_icq_cmd,
1204                                         NullTVB,
1205                                         offset,
1206                                         size,
1207                                         cmd,
1208                                         "Body");
1209         subtree = proto_item_add_subtree(ti, ett_icq_body);
1210         proto_tree_add_text(subtree, NullTVB,
1211                             offset + CMD_SEND_MSG_RECV_UIN,
1212                             4,
1213                             "Receiver UIN: %u", receiverUIN);
1214         proto_tree_add_text(subtree, NullTVB,
1215                             offset + CMD_SEND_MSG_MSG_LEN,
1216                             2,
1217                             "Length: %u", msgLen);
1218
1219         icqv5_decode_msgType(subtree,
1220                              pd + CMD_SEND_MSG_MSG_TYPE,
1221                              offset + CMD_SEND_MSG_MSG_TYPE,
1222                              left+4); /* There are 4 bytes more... */
1223     }
1224 }
1225
1226 static void
1227 icqv5_cmd_login(proto_tree* tree,
1228                 const u_char* pd,
1229                 int offset,
1230                 int size)
1231 {
1232     proto_item* ti;
1233     proto_tree* subtree;
1234     time_t theTime = -1;
1235     guint32 port = -1;
1236     guint32 passwdLen = -1;
1237     char* password = NULL;
1238     const u_char *ipAddrp = NULL;
1239     guint32 status = -1;
1240     guint32 left = size;
1241
1242     if (left>=4) {
1243         theTime = pletohl(pd + CMD_LOGIN_TIME);
1244     }
1245     if (left>=8) {
1246         port = pletohl(pd + CMD_LOGIN_PORT);
1247     }
1248     if (left>=10) {
1249         passwdLen = pletohs(pd + CMD_LOGIN_PASSLEN);
1250     }
1251     if (left>=10+passwdLen) {
1252         password = g_malloc(passwdLen + 1);
1253         strncpy(password, pd + CMD_LOGIN_PASSWD, passwdLen);
1254         password[passwdLen] = '\0';
1255     }
1256
1257     if (left>=10+passwdLen+CMD_LOGIN_IP+4) {
1258         ipAddrp = pd + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_IP;
1259     }
1260     if (left>=10+passwdLen+CMD_LOGIN_STATUS+4) {
1261         status = pletohs(pd + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_STATUS);
1262     }
1263     if (tree) {
1264         ti = proto_tree_add_uint_format(tree,
1265                                         hf_icq_cmd,
1266                                         NullTVB,
1267                                         offset,
1268                                         size,
1269                                         CMD_SEND_MSG,
1270                                         "Body");
1271         subtree = proto_item_add_subtree(ti, ett_icq_body);
1272         if (theTime!=-1) {
1273             char *aTime = ctime(&theTime);
1274
1275             aTime[strlen(aTime)-1] = '\0';
1276             proto_tree_add_text(subtree, NullTVB,
1277                                 offset + CMD_LOGIN_TIME,
1278                                 4,
1279                                 "Time: %ld = %s", (long)theTime, aTime);
1280         }
1281         if (port!=-1)
1282             proto_tree_add_text(subtree, NullTVB,
1283                                 offset + CMD_LOGIN_PORT,
1284                                 4,
1285                                 "Port: %u", port);
1286         if ((passwdLen!=-1) && (password!=NULL))
1287             proto_tree_add_text(subtree, NullTVB,
1288                                 offset + CMD_LOGIN_PASSLEN,
1289                                 2 + passwdLen,
1290                                 "Passwd: %s", password);
1291         if (ipAddrp!=NULL)
1292             proto_tree_add_text(subtree, NullTVB,
1293                                 offset + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_IP,
1294                                 4,
1295                                 "IP: %s", ip_to_str(ipAddrp));
1296         if (status!=-1)
1297             proto_tree_add_text(subtree, NullTVB,
1298                                 offset + CMD_LOGIN_PASSWD + passwdLen + CMD_LOGIN_STATUS,
1299                                 4,
1300                                 "Status: %s", findStatus(status));
1301     }
1302     if (password!=NULL)
1303         g_free(password);
1304 }
1305
1306 static void
1307 icqv5_cmd_contact_list(proto_tree* tree,
1308                        const u_char* pd,
1309                        int offset,
1310                        int size)
1311 {
1312     proto_tree* subtree;
1313     proto_item* ti;
1314     unsigned char num = -1;
1315     int i, left;
1316     guint32 uin;
1317     const u_char* p = NULL;
1318
1319     if (size >= CMD_CONTACT_LIST_NUM + 1) 
1320         num = pd[CMD_CONTACT_LIST_NUM];
1321
1322     if (tree) {
1323         ti = proto_tree_add_uint_format(tree,
1324                                         hf_icq_cmd,
1325                                         NullTVB,
1326                                         offset,
1327                                         size,
1328                                         CMD_CONTACT_LIST,
1329                                         "Body");
1330         subtree = proto_item_add_subtree(ti, ett_icq_body);
1331         proto_tree_add_text(subtree, NullTVB,
1332                             offset + CMD_CONTACT_LIST,
1333                             1,
1334                             "Number of uins: %u", num);
1335         /*
1336          * A sequence of num times UIN follows
1337          */
1338         offset += (CMD_CONTACT_LIST_NUM + 1);
1339         left = size;
1340         p = &pd[CMD_CONTACT_LIST_NUM + 1];
1341         for (i = 0; (i<num) && (left>0);i++) {
1342             if (left>=4) {
1343                 uin = pletohl(p);
1344                 proto_tree_add_text(subtree, NullTVB,
1345                                     offset,
1346                                     4,
1347                                     "UIN[%d]: %u",i,uin);
1348                 p += 4;
1349                 offset += 4;
1350                 left -= 4;
1351             }
1352         }
1353     }
1354 }
1355
1356 static void
1357 icqv5_cmd_no_params(proto_tree* tree,/* Tree to put the data in */
1358                     const u_char* pd,      /* Packet content */
1359                     int offset,            /* Offset from the start of the packet to the content */
1360                     int size,              /* Number of chars left to do */
1361                     int cmd)
1362 {
1363     proto_tree* subtree;
1364     proto_item* ti;
1365
1366     if (tree){
1367         ti = proto_tree_add_uint_format(tree,
1368                                         hf_icq_cmd,
1369                                         NullTVB,
1370                                         offset,
1371                                         0,
1372                                         cmd,
1373                                         "Body");
1374         subtree = proto_item_add_subtree(ti, ett_icq_body);
1375         proto_tree_add_text(subtree, NullTVB,
1376                             offset,
1377                             0,
1378                             "No parameters");
1379     }
1380 }
1381
1382 /**********************
1383  *
1384  * Server commands
1385  *
1386  **********************
1387  */
1388 static void
1389 icqv5_srv_no_params(proto_tree* tree,/* Tree to put the data in */
1390                     const u_char* pd,      /* Packet content */
1391                     int offset,            /* Offset from the start of the packet to the content */
1392                     int size,              /* Number of chars left to do */
1393                     int cmd)
1394 {
1395     proto_tree* subtree;
1396     proto_item* ti;
1397
1398     if (tree){
1399         ti = proto_tree_add_uint_format(tree,
1400                                         hf_icq_cmd,
1401                                         NullTVB,
1402                                         offset,
1403                                         0,
1404                                         cmd,
1405                                         "Body");
1406         subtree = proto_item_add_subtree(ti, ett_icq_body);
1407         proto_tree_add_text(subtree, NullTVB,
1408                             offset,
1409                             0,
1410                             "No Parameters");
1411     }
1412 }
1413
1414 static void
1415 icqv5_srv_login_reply(proto_tree* tree,/* Tree to put the data in */
1416                       const u_char* pd,       /* Packet content */
1417                       int offset,                /* Offset from the start of the packet to the content */
1418                       int size)                     /* Number of chars left to do */
1419 {
1420     proto_tree* subtree;
1421     proto_item* ti;
1422     const u_char *ipAddrp = NULL;
1423
1424     if (size >= SRV_LOGIN_REPLY_IP + 4) 
1425         ipAddrp = &pd[SRV_LOGIN_REPLY_IP];
1426
1427     if (tree) {
1428         ti = proto_tree_add_uint_format(tree,
1429                                         hf_icq_cmd,
1430                                         NullTVB,
1431                                         offset,
1432                                         SRV_LOGIN_REPLY_IP + 8,
1433                                         SRV_LOGIN_REPLY,
1434                                         "Body");
1435         subtree = proto_item_add_subtree(ti, ett_icq_body);
1436         proto_tree_add_text(subtree, NullTVB,
1437                             offset + SRV_LOGIN_REPLY_IP,
1438                             4,
1439                             "IP: %s", ip_to_str(ipAddrp));
1440     }
1441 }
1442
1443 static void
1444 icqv5_srv_user_online(proto_tree* tree,/* Tree to put the data in */
1445                       const u_char* pd,       /* Packet content */
1446                       int offset,                /* Offset from the start of the packet to the content */
1447                       int size)                     /* Number of chars left to do */
1448 {
1449     proto_tree* subtree;
1450     proto_item* ti;
1451     guint32 uin = -1;
1452     const u_char *ipAddrp = NULL;
1453     guint32 port = -1;
1454     const u_char *realipAddrp = NULL;
1455     guint32 status = -1;
1456     guint32 version = -1;
1457
1458     if (size >= SRV_USER_ONL_UIN + 4)
1459         uin = pletohl(pd + SRV_USER_ONL_UIN);
1460     
1461     if (size >= SRV_USER_ONL_IP + 4) 
1462         ipAddrp = &pd[SRV_USER_ONL_IP];
1463
1464     if (size >= SRV_USER_ONL_PORT + 4)
1465         port = pletohl(pd + SRV_USER_ONL_PORT);
1466
1467     if (size >= SRV_USER_ONL_REALIP + 4)
1468         realipAddrp = &pd[SRV_USER_ONL_REALIP];
1469
1470     if (size >= SRV_USER_ONL_STATUS + 2)
1471         status = pletohs(pd + SRV_USER_ONL_STATUS);
1472
1473     /*
1474      * Kojak: Hypothesis is that this field might be an encoding for the
1475      * version used by the UIN that changed. To test this, I included
1476      * this line to the code.
1477      */
1478     if (size >= SRV_USER_ONL_X2 + 4)
1479         version = pletohl(pd + SRV_USER_ONL_X2);
1480
1481     if (tree) {
1482         ti = proto_tree_add_uint_format(tree,
1483                                         hf_icq_cmd,
1484                                         NullTVB,
1485                                         offset,
1486                                         SRV_LOGIN_REPLY_IP + 8,
1487                                         SRV_LOGIN_REPLY,
1488                                         "Body");
1489         subtree = proto_item_add_subtree(ti, ett_icq_body);
1490         proto_tree_add_text(subtree, NullTVB,
1491                             offset + SRV_USER_ONL_UIN,
1492                             4,
1493                             "UIN: %u", uin);
1494         proto_tree_add_text(subtree, NullTVB,
1495                             offset + SRV_USER_ONL_IP,
1496                             4,
1497                             "IP: %s", ip_to_str(ipAddrp));
1498         proto_tree_add_text(subtree, NullTVB,
1499                             offset + SRV_USER_ONL_PORT,
1500                             4,
1501                             "Port: %u", port);
1502         proto_tree_add_text(subtree, NullTVB,
1503                             offset + SRV_USER_ONL_REALIP,
1504                             4,
1505                             "RealIP: %s", ip_to_str(realipAddrp));
1506         proto_tree_add_text(subtree, NullTVB,
1507                             offset + SRV_USER_ONL_STATUS,
1508                             2,
1509                             "Status: %s", findStatus(status));
1510         proto_tree_add_text(subtree, NullTVB,
1511                             offset + SRV_USER_ONL_X2,
1512                             4,
1513                             "Version: %08x", version);
1514     }
1515 }
1516
1517 static void
1518 icqv5_srv_user_offline(proto_tree* tree,/* Tree to put the data in */
1519                       const u_char* pd,       /* Packet content */
1520                       int offset,                /* Offset from the start of the packet to the content */
1521                       int size)                     /* Number of chars left to do */
1522 {
1523     proto_tree* subtree;
1524     proto_item* ti;
1525     guint32 uin = -1;
1526
1527     if (size >= SRV_USER_OFFLINE + 4) 
1528         uin = pletohl(&pd[SRV_USER_OFFLINE]);
1529
1530     if (tree) {
1531         ti = proto_tree_add_uint_format(tree,
1532                                         hf_icq_cmd,
1533                                         NullTVB,
1534                                         offset,
1535                                         SRV_USER_OFFLINE_UIN + 4,
1536                                         SRV_USER_OFFLINE,
1537                                         "Body");
1538         subtree = proto_item_add_subtree(ti, ett_icq_body);
1539         proto_tree_add_text(subtree, NullTVB,
1540                             offset + SRV_USER_OFFLINE_UIN,
1541                             4,
1542                             "UIN: %u", uin);
1543     }
1544 }
1545
1546 static void
1547 icqv5_srv_multi(proto_tree* tree,/* Tree to put the data in */
1548                 const u_char* pd,      /* Packet content */
1549                 int offset,            /* Offset from the start of the packet to the content */
1550                 int size,              /* Number of chars left to do */
1551                 frame_data* fd)
1552 {
1553     proto_tree* subtree;
1554     proto_item* ti;
1555     unsigned char num = -1;
1556     guint16 pktSz;
1557     int i, left;
1558     const u_char* p = NULL;
1559
1560     if (size >= SRV_MULTI_NUM + 1) 
1561         num = pd[SRV_MULTI_NUM];
1562
1563     if (tree) {
1564         ti = proto_tree_add_uint_format(tree,
1565                                         hf_icq_cmd,
1566                                         NullTVB,
1567                                         offset,
1568                                         size,
1569                                         SRV_MULTI,
1570                                         "Body");
1571         subtree = proto_item_add_subtree(ti, ett_icq_body);
1572         proto_tree_add_text(subtree, NullTVB,
1573                             offset + SRV_MULTI_NUM,
1574                             1,
1575                             "Number of pkts: %u", num);
1576         /*
1577          * A sequence of num times ( pktsize, packetData) follows
1578          */
1579         offset += (SRV_MULTI_NUM + 1);
1580         left = size;
1581         p = &pd[SRV_MULTI_NUM + 1];
1582         for (i = 0; (i<num) && (left>0);i++) {
1583             if (left>=2) {
1584                 pktSz = pletohs(p);
1585                 p += 2;
1586                 offset += 2;
1587                 left -= 2;
1588                 if (left>=pktSz) {
1589                     dissect_icqv5Server(p, offset, fd, subtree, pktSz);
1590                     p += pktSz;
1591                     offset += pktSz;
1592                     left -= pktSz;
1593                 }
1594             }
1595         }
1596     }
1597 }
1598
1599 static void
1600 icqv5_srv_meta_user(proto_tree* tree,      /* Tree to put the data in */
1601                     const u_char* pd,      /* Packet content */
1602                     int offset,            /* Offset from the start of the packet to the content */
1603                     int size)              /* Number of chars left to do */
1604 {
1605 #if 0
1606     proto_tree* subtree = NULL;
1607 #endif
1608     proto_tree* sstree = NULL;
1609     proto_item* ti = NULL;
1610     int left = size;
1611     const char* p = pd;
1612
1613     guint16 subcmd = -1;
1614     unsigned char result = -1;
1615
1616     if (size>=SRV_META_USER_SUBCMD + 2)
1617         subcmd = pletohs(pd+SRV_META_USER_SUBCMD);
1618     if (size>=SRV_META_USER_RESULT + 1)
1619         result = pd[SRV_META_USER_RESULT];
1620
1621     if (tree) {
1622 #if 0
1623         ti = proto_tree_add_uint_format(tree,
1624                                         hf_icq_cmd,
1625                                         NullTVB,
1626                                         offset,
1627                                         size,
1628                                         SRV_META_USER,
1629                                         "Body");
1630         subtree = proto_item_add_subtree(ti, ett_icq_body);
1631         ti = proto_tree_add_text(subtree, NullTVB,
1632                                  offset + SRV_META_USER_SUBCMD,
1633                                  2,
1634                                  "%s", findSubCmd(subcmd));
1635         proto_tree_add_text(subtree, NullTVB,
1636                             offset + SRV_META_USER_RESULT,
1637                             1,
1638                             "%s", (result==0x0a)?"Success":"Failure");
1639         sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
1640 #else
1641         ti = proto_tree_add_text(tree, NullTVB,
1642                                  offset + SRV_META_USER_SUBCMD,
1643                                  2,
1644                                  "%s", findSubCmd(subcmd));
1645         sstree = proto_item_add_subtree(ti, ett_icq_body_parts);
1646         proto_tree_add_text(sstree, NullTVB,
1647                             offset + SRV_META_USER_RESULT,
1648                             1,
1649                             "%s", (result==0x0a)?"Success":"Failure");
1650 #endif
1651
1652         /* Skip the META_USER header */
1653         left -= 3;
1654         p += 3;
1655
1656         switch(subcmd) {
1657         case META_EX_USER_FOUND:
1658         {
1659             /* This is almost the same as META_USER_FOUND,
1660              * however, there's an extra length field
1661              */
1662             guint16 pktLen = -1;
1663
1664             /* Read the lenght field */
1665             pktLen = pletohs(p);
1666             proto_tree_add_text(sstree, NullTVB,
1667                                 offset + size - left,
1668                                 sizeof(guint16),
1669                                 "Length: %u", pktLen);
1670             
1671             p += sizeof(guint16); left -= sizeof(guint16);
1672         }
1673         case META_USER_FOUND:
1674         {
1675             /* The goto mentioned in this block should be local to this
1676              * block if C'd allow it.
1677              *
1678              * They are used to "implement" a poorman's exception handling
1679              */
1680             guint32 uin = -1;
1681             int len = 0;
1682             char *descr[] = {
1683                 "Nick",
1684                 "First name",
1685                 "Last name",
1686                 "Email",
1687                 NULL};
1688             char** d = descr;
1689             guint16 x2 = -1;
1690             guint32 x3 = -1;
1691             unsigned char auth;
1692             /*
1693              * Read UIN
1694              */
1695             if (left<sizeof(guint32))
1696                 break;
1697             uin = pletohl(p);
1698             proto_tree_add_text(sstree, NullTVB,
1699                                 offset + size - left,
1700                                 sizeof(guint32),
1701                                 "UIN: %u", uin);
1702             p+=sizeof(guint32);left-=sizeof(guint32);
1703
1704             for ( ; *d!=NULL; d++) {
1705                 len = proto_add_icq_attr(sstree,
1706                                          p,
1707                                          offset + size - left,
1708                                          left,
1709                                          *d);
1710                 if (len == -1)
1711                     return;
1712                 p += len; left -= len;
1713             }
1714             /* Get the authorize setting */
1715             if (left<sizeof(unsigned char))
1716                 break;
1717             auth = *p;
1718             proto_tree_add_text(sstree, NullTVB,
1719                                 offset + size - left,
1720                                 sizeof(guint16),
1721                                 "authorization: %s", (auth==0x01)?"Neccessary":"Who needs it");
1722             p++; left--;
1723             /* Get x2 */
1724             if (left<sizeof(guint16))
1725                 break;
1726             x2 = pletohs(p);
1727             proto_tree_add_text(sstree, NullTVB,
1728                                 offset + size - left,
1729                                 sizeof(guint16),
1730                                 "x2: %04x", x2);
1731             p+=sizeof(guint16);left-=sizeof(guint16);
1732             /* Get x3 */
1733             if (left<sizeof(guint32))
1734                 break;
1735             x3 = pletohl(p);
1736             proto_tree_add_text(sstree, NullTVB,
1737                                 offset + size - left,
1738                                 sizeof(guint32),
1739                                 "x3: %08x", x3);
1740             p+=sizeof(guint32);left-=sizeof(guint32);
1741             break;
1742         }
1743         case META_ABOUT:
1744         {
1745             int len;
1746             char* about = NULL;
1747             /* Get the about information */
1748             if (left<sizeof(guint16))
1749                 break;
1750             len = pletohs(p);
1751             p+=sizeof(guint16);left-=sizeof(guint16);
1752             if ((len<=0) || (left<len))
1753                 break;
1754             about = g_malloc(len);
1755             strncpy(about, p, len);
1756             proto_tree_add_text(sstree, NullTVB,
1757                                 offset + size - left,
1758                                 sizeof(guint16)+len,
1759                                 "About(%d): %s", len, about);
1760             p+=len;left-=len;
1761             left -= 3;
1762             g_free(about);
1763             break;
1764         }
1765         case META_USER_INFO:
1766         {
1767             /* The goto mentioned in this block should be local to this
1768              * block if C'd allow it.
1769              *
1770              * They are used to "implement" a poorman's exception handling
1771              */
1772             static const char* descr[] = {
1773                 "Nick",
1774                 "First name",
1775                 "Last name",
1776                 "Primary email",
1777                 "Secundary email",
1778                 "Old email",
1779                 "City",
1780                 "State",
1781                 "Phone",
1782                 "Fax",
1783                 "Street",
1784                 "Cellphone",
1785                 "Zip",
1786                 NULL};
1787             const char** d = descr;
1788             char* item = NULL;
1789             guint16 country;
1790             unsigned char user_timezone = -1;
1791             unsigned char auth = -1;
1792             int len = 0;
1793 #if 0
1794             /* Get the uin */
1795             if (left<sizeof(guint32))
1796                 break;
1797             uin = pletohl(p);
1798             proto_tree_add_text(sstree, NullTVB,
1799                                 offset + size - left,
1800                                 sizeof(guint32),
1801                                 "UIN: %u", uin);
1802             p+=sizeof(guint32);left-=sizeof(guint32);
1803 #endif
1804             
1805             /*
1806              * Get every field from the description
1807              */
1808             while ((*d)!=NULL) {
1809                 if (left<sizeof(guint16))
1810                     break;
1811                 len = pletohs(p);
1812                 p+=sizeof(guint16);left-=sizeof(guint16);
1813                 if ((len<0) || (left<len))
1814                     break;
1815                 if (len>0) {
1816                     item = g_malloc(len);
1817                     strncpy(item, p, len);
1818                     proto_tree_add_text(sstree, NullTVB,
1819                                         offset + size - left - sizeof(guint16),
1820                                         sizeof(guint16)+len,
1821                                         "%s(%d): %s",*d, len, item);
1822                     g_free(item);
1823                     p+=len;left-=len;
1824                 }
1825                 d++;
1826             }
1827             /* Get country code */
1828             if (left<sizeof(guint16))
1829                 break;
1830             country = pletohs(p);
1831             proto_tree_add_text(sstree, NullTVB,
1832                                 offset + size - left,
1833                                 sizeof(guint16),
1834                                 "Countrycode: %u", country);
1835             p+=sizeof(guint16); left-=sizeof(guint16);
1836             /* Get the timezone setting */
1837             if (left<sizeof(unsigned char))
1838                 break;
1839             user_timezone = *p;
1840             proto_tree_add_text(sstree, NullTVB,
1841                                 offset + size - left,
1842                                 sizeof(unsigned char),
1843                                 "Timezone: %u", user_timezone);
1844             p++; left--;
1845             /* Get the authorize setting */
1846             if (left<sizeof(unsigned char))
1847                 break;
1848             auth = *p;
1849             proto_tree_add_text(sstree, NullTVB,
1850                                 offset + size - left,
1851                                 sizeof(unsigned char),
1852                                 "Authorization: (%u) %s",
1853                                 auth, (auth==0)?"No":"Yes");
1854             p++; left--;
1855             /* Get the webaware setting */
1856             if (left<sizeof(unsigned char))
1857                 break;
1858             auth = *p;
1859             proto_tree_add_text(sstree, NullTVB,
1860                                 offset + size - left,
1861                                 sizeof(unsigned char),
1862                                 "Webaware: (%u) %s",
1863                                 auth, (auth==0)?"No":"Yes");
1864             p++; left--;
1865             /* Get the authorize setting */
1866             if (left<sizeof(unsigned char))
1867                 break;
1868             auth = *p;
1869             proto_tree_add_text(sstree, NullTVB,
1870                                 offset + size - left,
1871                                 sizeof(unsigned char),
1872                                 "HideIP: (%u) %s",
1873                                 auth, (auth==0)?"No":"Yes");
1874             p++; left--;
1875             break;
1876         }
1877         default:
1878             /* This information is already printed in the tree */
1879             fprintf(stderr, "Meta subcmd: %04x\n", subcmd);
1880             break;
1881         }
1882     }
1883 }
1884
1885 static void
1886 icqv5_srv_recv_message(proto_tree* tree,      /* Tree to put the data in */
1887                        const u_char* pd,      /* Packet content */
1888                        int offset,            /* Offset from the start of the packet to the content */
1889                        int size)              /* Number of chars left to do */
1890 {
1891     proto_tree* subtree = NULL;
1892     proto_item* ti = NULL;
1893     int left = size;
1894     guint32 uin = -1;
1895     guint16 year = -1;
1896     unsigned char month = -1;
1897     unsigned char day = -1;
1898     unsigned char hour = -1;
1899     unsigned char minute = -1;
1900     
1901     if (tree) {
1902         ti = proto_tree_add_uint_format(tree,
1903                                         hf_icq_cmd,
1904                                         NullTVB,
1905                                         offset,
1906                                         4,
1907                                         SRV_RECV_MESSAGE,
1908                                         "Body");
1909         subtree = proto_item_add_subtree(ti, ett_icq_body);
1910         if (left>=sizeof(guint32)) {
1911             uin = pletohl(pd + SRV_RECV_MSG_UIN);
1912             proto_tree_add_uint_format(subtree,
1913                                        hf_icq_uin,
1914                                        NullTVB,
1915                                        offset + SRV_RECV_MSG_UIN,
1916                                        sizeof(guint32),
1917                                        uin,
1918                                        "UIN: %u", uin);
1919             left -= sizeof(guint32);
1920         } else
1921             return;
1922         if (left>=(sizeof(guint16)+4*sizeof(unsigned char))) {
1923             year = pletohs(pd + SRV_RECV_MSG_YEAR);
1924             month = pd[SRV_RECV_MSG_MONTH];
1925             day = pd[SRV_RECV_MSG_DAY];
1926             hour = pd[SRV_RECV_MSG_HOUR];
1927             minute = pd[SRV_RECV_MSG_MINUTE];
1928
1929             proto_tree_add_text(subtree, NullTVB,
1930                                 offset + SRV_RECV_MSG_YEAR,
1931                                 sizeof(guint16) + 4*sizeof(unsigned char),
1932                                 "Time: %u-%u-%u %02u:%02u",
1933                                 day, month, year, hour, minute);
1934             
1935             left -= (sizeof(guint16)+4*sizeof(unsigned char));
1936         } else
1937             return;
1938         icqv5_decode_msgType(subtree,
1939                              pd + SRV_RECV_MSG_MSG_TYPE,
1940                              offset + SRV_RECV_MSG_MSG_TYPE,
1941                              left);
1942     }
1943 }
1944
1945 static void
1946 icqv5_srv_rand_user(proto_tree* tree,      /* Tree to put the data in */
1947                        const u_char* pd,      /* Packet content */
1948                        int offset,            /* Offset from the start of the packet to the content */
1949                        int size)              /* Number of chars left to do */
1950 {
1951     proto_tree* subtree = NULL;
1952     proto_item* ti = NULL;
1953     guint32 uin = -1;
1954     const unsigned char* IP = NULL;
1955     guint32 port = -1;
1956     const unsigned char* realIP = NULL;
1957     unsigned char commClass = -1;
1958     guint32 status;
1959     guint16 tcpVer;
1960     int left = size;
1961     
1962     if (tree) {
1963         ti = proto_tree_add_uint_format(tree,
1964                                         hf_icq_cmd,
1965                                         NullTVB,
1966                                         offset,
1967                                         SRV_RAND_USER_TCP_VER + 2,
1968                                         SRV_RAND_USER,
1969                                         "Body");
1970         subtree = proto_item_add_subtree(ti, ett_icq_body);
1971         /* guint32 UIN */
1972         if (left<sizeof(guint32))
1973             return;
1974         uin = pletohl(pd + SRV_RAND_USER_UIN);
1975         proto_tree_add_text(subtree, NullTVB,
1976                             offset + SRV_RAND_USER_UIN,
1977                             sizeof(guint32),
1978                             "UIN: %u", uin);
1979         left -= sizeof(guint32);
1980         /* guint32 IP */
1981         if (left<sizeof(guint32))
1982             return;
1983         IP = pd + SRV_RAND_USER_IP;
1984         proto_tree_add_text(subtree, NullTVB,
1985                             offset + SRV_RAND_USER_IP,
1986                             sizeof(guint32),
1987                             "IP: %s", ip_to_str(IP));
1988         left -= sizeof(guint32);
1989         /* guint32 portNum */
1990         if (left<sizeof(guint32))
1991             return;
1992         port = pletohs(pd + SRV_RAND_USER_PORT);
1993         proto_tree_add_text(subtree, NullTVB,
1994                             offset + SRV_RAND_USER_UIN,
1995                             sizeof(guint32),
1996                             "Port: %u", port);
1997         left -= sizeof(guint32);
1998         /* guint32 realIP */                        
1999         if (left<sizeof(guint32))
2000             return;
2001         realIP = pd + SRV_RAND_USER_REAL_IP;
2002         proto_tree_add_text(subtree, NullTVB,
2003                             offset + SRV_RAND_USER_REAL_IP,
2004                             sizeof(guint32),
2005                             "RealIP: %s", ip_to_str(realIP));
2006         left -= sizeof(guint32);
2007         /* guit16 Communication Class */
2008         if (left<sizeof(unsigned char))
2009             return;
2010         commClass = pd[SRV_RAND_USER_CLASS];
2011         proto_tree_add_text(subtree, NullTVB,
2012                             offset + SRV_RAND_USER_CLASS,
2013                             sizeof(unsigned char),
2014                             "Class: %s", (commClass!=4)?"User to User":"Through Server");
2015         left -= sizeof(unsigned char);
2016         /* guint32 status */
2017         if (left<sizeof(guint32))
2018             return;
2019         status = pletohs(pd + SRV_RAND_USER_STATUS);
2020         proto_tree_add_text(subtree, NullTVB,
2021                             offset + SRV_RAND_USER_STATUS,
2022                             sizeof(guint32),
2023                             "Status: (%u) %s", status, findStatus(status));
2024         /* guint16 tcpVersion */
2025         if (left<sizeof(guint16))
2026             return;
2027         tcpVer = pletohs(pd + SRV_RAND_USER_TCP_VER);
2028         proto_tree_add_text(subtree, NullTVB,
2029                             offset + SRV_RAND_USER_TCP_VER,
2030                             sizeof(guint16),
2031                             "TCPVersion: %u", tcpVer);
2032         left -= sizeof(guint16);
2033     }
2034 }
2035
2036 /*
2037  * Dissect all the v5 client traffic. This is encrypted, so be careful.
2038  */
2039 static void
2040 dissect_icqv5Client(const u_char *pd,
2041                     int offset,
2042                     frame_data *fd, 
2043                     proto_tree *tree)
2044 {
2045     proto_tree *icq_tree = NULL;
2046     proto_tree *icq_header_tree = NULL;
2047     proto_tree *icq_decode_tree = NULL;
2048     proto_item *ti = NULL;
2049
2050     guint16 version = -1, cmd = -1;
2051     guint16 seqnum1 = 0 , seqnum2 = 0;
2052     guint32 uin = -1, sessionid = -1;
2053     guint32 key = -1;
2054     guint16 pktsize = -1;               /* The size of the ICQ content */
2055     static u_char *decr_pd = NULL;      /* Decrypted content */
2056     static int decr_size = 0;           /* Size of decrypted-content buffer */
2057     
2058     pktsize = END_OF_FRAME;
2059
2060     if (decr_size == 0 ) {
2061         decr_size = sizeof(u_char) * 128;
2062         decr_pd = g_malloc(decr_size);
2063     }
2064         
2065     while (decr_size < pktsize + 3) {
2066         decr_size *= 2;
2067         decr_pd = g_realloc(decr_pd, decr_size);
2068     }
2069     
2070     /* First copy the memory, we don't want to overwrite the old content */
2071     memcpy(decr_pd, &pd[offset], pktsize);
2072     if (pktsize>0x14) {
2073         key = get_v5key(decr_pd, pktsize);
2074         decrypt_v5(decr_pd, pktsize, key);
2075     
2076         /* This information only makes sense in the decrypted version */
2077         uin = pletohl(&decr_pd[ICQ5_CL_UIN]);
2078         cmd = pletohs(&decr_pd[ICQ5_CL_CMD]);
2079         sessionid = pletohl(&decr_pd[ICQ5_CL_SESSIONID]);
2080         version = pletohs(&decr_pd[ICQ_VERSION]);
2081         seqnum1 = pletohs(&decr_pd[ICQ5_CL_SEQNUM1]);
2082         seqnum2 = pletohs(&decr_pd[ICQ5_CL_SEQNUM2]);
2083
2084         if (check_col(fd, COL_INFO))
2085             col_add_fstr(fd, COL_INFO, "ICQv5 %s", findClientCmd(cmd));
2086     }
2087     
2088     if (tree) {
2089         ti = proto_tree_add_protocol_format(tree,
2090                                  proto_icq,
2091                                  NullTVB,
2092                                  offset,
2093                                  pktsize,
2094                                  "ICQv5 %s (len %u)",
2095                                  findClientCmd(cmd),
2096                                  pktsize);
2097         icq_tree = proto_item_add_subtree(ti, ett_icq);
2098         ti = proto_tree_add_uint_format(icq_tree,
2099                                         hf_icq_type,
2100                                         NullTVB,
2101                                         offset,
2102                                         ICQ5_CL_HDRSIZE,
2103                                         ICQ5_client,
2104                                         "Header");
2105         icq_header_tree = proto_item_add_subtree(ti, ett_icq_header);
2106                                         
2107         proto_tree_add_text(icq_header_tree, NullTVB,
2108                             offset + ICQ_VERSION,
2109                             2,
2110                             "Version: %u", version);
2111         proto_tree_add_uint_format(icq_header_tree,
2112                                    hf_icq_uin,
2113                                    NullTVB,
2114                                    offset+ICQ5_CL_UIN,
2115                                    4,
2116                                    uin,
2117                                    "UIN: %u (0x%08X)",
2118                                    uin, uin);
2119         proto_tree_add_uint_format(icq_header_tree,
2120                                    hf_icq_sessionid,
2121                                    NullTVB,
2122                                    offset+ICQ5_CL_SESSIONID,
2123                                    4,
2124                                    sessionid,
2125                                    "Session ID: 0x%08x",
2126                                    sessionid);
2127         proto_tree_add_text(icq_header_tree, NullTVB,
2128                             offset + ICQ5_CL_CMD,
2129                             2,
2130                             "Command: %s (%u)", findClientCmd(cmd), cmd);
2131         proto_tree_add_text(icq_header_tree, NullTVB,
2132                             offset + ICQ5_CL_SEQNUM1,
2133                             2,
2134                             "Seq Number 1: 0x%04x", seqnum1);
2135         proto_tree_add_text(icq_header_tree, NullTVB,
2136                             offset + ICQ5_CL_SEQNUM2,
2137                             2,
2138                             "Seq Number 2: 0x%04x", seqnum2);
2139         proto_tree_add_uint_format(icq_header_tree,
2140                                    hf_icq_checkcode,
2141                                    NullTVB,
2142                                    offset+ICQ5_CL_CHECKCODE,
2143                                    4,
2144                                    key,
2145                                    "Key: 0x%08x",
2146                                    key);
2147         switch(cmd) {
2148         case CMD_ACK:
2149             icqv5_cmd_ack(icq_tree,
2150                           decr_pd + ICQ5_CL_HDRSIZE,
2151                           offset + ICQ5_CL_HDRSIZE,
2152                           pktsize - ICQ5_CL_HDRSIZE);
2153             break;
2154         case CMD_SEND_MSG:
2155         case CMD_MSG_TO_NEW_USER:
2156             icqv5_cmd_send_msg(icq_tree,
2157                                decr_pd + ICQ5_CL_HDRSIZE,
2158                                offset + ICQ5_CL_HDRSIZE,
2159                                pktsize - ICQ5_CL_HDRSIZE,
2160                                cmd);
2161             break;
2162         case CMD_RAND_SEARCH:
2163             icqv5_cmd_rand_search(icq_tree,
2164                                   decr_pd + ICQ5_CL_HDRSIZE,
2165                                   offset + ICQ5_CL_HDRSIZE,
2166                                   pktsize - ICQ5_CL_HDRSIZE);
2167             break;
2168         case CMD_LOGIN:
2169             icqv5_cmd_login(icq_tree,
2170                             decr_pd + ICQ5_CL_HDRSIZE,
2171                             offset + ICQ5_CL_HDRSIZE,
2172                             pktsize - ICQ5_CL_HDRSIZE);
2173             break;
2174         case CMD_SEND_TEXT_CODE:
2175             icqv5_cmd_send_text_code(icq_tree,
2176                                      decr_pd + ICQ5_CL_HDRSIZE,
2177                                      offset + ICQ5_CL_HDRSIZE,
2178                                      pktsize - ICQ5_CL_HDRSIZE);
2179             break;
2180         case CMD_STATUS_CHANGE:
2181             icqv5_cmd_status_change(icq_tree,
2182                                     decr_pd + ICQ5_CL_HDRSIZE,
2183                                     offset + ICQ5_CL_HDRSIZE,
2184                                     pktsize - ICQ5_CL_HDRSIZE);
2185             break;
2186         case CMD_ACK_MESSAGES:
2187             icqv5_cmd_ack_messages(icq_tree,
2188                                    decr_pd + ICQ5_CL_HDRSIZE,
2189                                    offset + ICQ5_CL_HDRSIZE,
2190                                    pktsize - ICQ5_CL_HDRSIZE);
2191             break;
2192         case CMD_KEEP_ALIVE:
2193             icqv5_cmd_keep_alive(icq_tree,
2194                                  decr_pd + ICQ5_CL_HDRSIZE,
2195                                  offset + ICQ5_CL_HDRSIZE,
2196                                  pktsize - ICQ5_CL_HDRSIZE);
2197             break;
2198         case CMD_ADD_TO_LIST:
2199             icqv5_cmd_add_to_list(icq_tree,
2200                                    decr_pd + ICQ5_CL_HDRSIZE,
2201                                    offset + ICQ5_CL_HDRSIZE,
2202                                    pktsize - ICQ5_CL_HDRSIZE);
2203             break;
2204         case CMD_CONTACT_LIST:
2205             icqv5_cmd_contact_list(icq_tree,
2206                                    decr_pd + ICQ5_CL_HDRSIZE,
2207                                    offset + ICQ5_CL_HDRSIZE,
2208                                    pktsize - ICQ5_CL_HDRSIZE);
2209             break;
2210         case CMD_META_USER:
2211         case CMD_REG_NEW_USER:
2212         case CMD_QUERY_SERVERS:
2213         case CMD_QUERY_ADDONS:
2214             icqv5_cmd_no_params(icq_tree,
2215                                 decr_pd + ICQ5_CL_HDRSIZE,
2216                                 offset + ICQ5_CL_HDRSIZE,
2217                                 pktsize - ICQ5_CL_HDRSIZE,
2218                                 cmd);
2219             break;
2220         default:
2221             proto_tree_add_uint_format(icq_tree,
2222                                        hf_icq_cmd,
2223                                        NullTVB,
2224                                        offset+ICQ5_CL_CMD,
2225                                        2,
2226                                        cmd,
2227                                        "Command: %u (%s)",
2228                                        cmd, findClientCmd(cmd));
2229             fprintf(stderr,"Missing: %s\n", findClientCmd(cmd));
2230             break;
2231         }
2232         ti = proto_tree_add_text(icq_tree, NullTVB,
2233                                  offset,
2234                                  pktsize,
2235                                  "Decoded packet");
2236         icq_decode_tree = proto_item_add_subtree(ti,
2237                                                  ett_icq_decode);
2238         proto_tree_add_hexdump(icq_decode_tree, offset, decr_pd, pktsize);
2239
2240     }
2241 }
2242
2243 static void
2244 dissect_icqv5Server(const u_char *pd,
2245                     int offset,
2246                     frame_data *fd, 
2247                     proto_tree *tree,
2248                     guint32 pktsize)
2249 {
2250     /* Server traffic is easy, not encrypted */
2251     proto_tree *icq_tree = NULL;
2252     proto_tree *icq_header_tree = NULL;
2253     proto_item *ti = NULL;
2254     const u_char* decr_pd;
2255     int changeCol = (pktsize==(guint32)-1);
2256
2257     guint16 version, cmd;
2258     guint32 uin, sessionid;
2259     guint16 seqnum1, seqnum2;
2260     guint32 checkcode;
2261     
2262     uin = pletohl(&pd[ICQ5_SRV_UIN]);
2263     sessionid = pletohl(&pd[ICQ5_SRV_SESSIONID]);
2264     cmd = pletohs(&pd[ICQ5_SRV_CMD]);
2265     version = pletohs(&pd[ICQ_VERSION]);
2266     checkcode = pletohl(&pd[ICQ5_SRV_CHECKCODE]);
2267     seqnum1 = pletohs(&pd[ICQ5_SRV_SEQNUM1]);
2268     seqnum2 = pletohs(&pd[ICQ5_SRV_SEQNUM2]);
2269     if (pktsize == -1)
2270         pktsize = END_OF_FRAME;
2271     decr_pd = pd;
2272     
2273     if (changeCol && check_col(fd, COL_INFO))
2274         col_add_fstr(fd, COL_INFO, "ICQv5 %s", findServerCmd(cmd));
2275
2276     if (tree) {
2277         ti = proto_tree_add_protocol_format(tree,
2278                                         proto_icq,
2279                                         NullTVB,
2280                                         offset,
2281                                         pktsize,
2282                                         "ICQv5 %s (len %u)",
2283                                         findServerCmd(cmd),
2284                                         pktsize);
2285         
2286         icq_tree = proto_item_add_subtree(ti, ett_icq);
2287
2288         ti = proto_tree_add_uint_format(icq_tree,
2289                                         hf_icq_type,
2290                                         NullTVB,
2291                                         offset,
2292                                         ICQ5_SRV_HDRSIZE,
2293                                         ICQ5_server,
2294                                         "Header");
2295         icq_header_tree = proto_item_add_subtree(ti, ett_icq_header);
2296                                         
2297         proto_tree_add_text(icq_header_tree, NullTVB,
2298                             offset + ICQ_VERSION,
2299                             2,
2300                             "Version: %u", version);
2301         proto_tree_add_uint_format(icq_header_tree,
2302                                    hf_icq_sessionid,
2303                                    NullTVB,
2304                                    offset+ICQ5_SRV_SESSIONID,
2305                                    4,
2306                                    sessionid,
2307                                    "Session ID: 0x%08x",
2308                                    sessionid);
2309         proto_tree_add_text(icq_header_tree, NullTVB,
2310                             offset + ICQ5_SRV_CMD,
2311                             2,
2312                             "Command: %s (%u)", findServerCmd(cmd), cmd);
2313         proto_tree_add_text(icq_header_tree, NullTVB,
2314                             offset + ICQ5_SRV_SEQNUM1,
2315                             2,
2316                             "Seq Number 1: 0x%04x", seqnum1);
2317         proto_tree_add_text(icq_header_tree, NullTVB,
2318                             offset + ICQ5_SRV_SEQNUM2,
2319                             2,
2320                             "Seq Number 2: 0x%04x", seqnum2);
2321         proto_tree_add_uint_format(icq_header_tree,
2322                                    hf_icq_uin,
2323                                    NullTVB,
2324                                    offset+ICQ5_SRV_UIN,
2325                                    4,
2326                                    uin,
2327                                    "UIN: %u",
2328                                    uin);
2329         proto_tree_add_uint_format(icq_header_tree,
2330                                    hf_icq_checkcode,
2331                                    NullTVB,
2332                                    offset+ICQ5_SRV_CHECKCODE,
2333                                    4,
2334                                    checkcode,
2335                                    "Checkcode: 0x%08x",
2336                                    checkcode);
2337         switch (cmd) {
2338         case SRV_RAND_USER:
2339             icqv5_srv_rand_user(icq_tree,
2340                                decr_pd + ICQ5_SRV_HDRSIZE,
2341                                offset + ICQ5_SRV_HDRSIZE,
2342                                pktsize - ICQ5_SRV_HDRSIZE);
2343             break;
2344         case SRV_SYS_DELIVERED_MESS:
2345             /* The message structures are all the same. Why not run
2346              * the same routine? */
2347             icqv5_cmd_send_msg(icq_tree,
2348                                decr_pd + ICQ5_SRV_HDRSIZE,
2349                                offset + ICQ5_SRV_HDRSIZE,
2350                                pktsize - ICQ5_SRV_HDRSIZE,
2351                                cmd);
2352             break;
2353         case SRV_USER_ONLINE:
2354             icqv5_srv_user_online(icq_tree,
2355                                decr_pd + ICQ5_SRV_HDRSIZE,
2356                                offset + ICQ5_SRV_HDRSIZE,
2357                                pktsize - ICQ5_SRV_HDRSIZE);
2358             break;
2359         case SRV_USER_OFFLINE:
2360             icqv5_srv_user_offline(icq_tree,
2361                                decr_pd + ICQ5_SRV_HDRSIZE,
2362                                offset + ICQ5_SRV_HDRSIZE,
2363                                pktsize - ICQ5_SRV_HDRSIZE);
2364             break;
2365         case SRV_LOGIN_REPLY:
2366             icqv5_srv_login_reply(icq_tree,
2367                                decr_pd + ICQ5_SRV_HDRSIZE,
2368                                offset + ICQ5_SRV_HDRSIZE,
2369                                pktsize - ICQ5_SRV_HDRSIZE);
2370             break;
2371         case SRV_META_USER:
2372             icqv5_srv_meta_user(icq_tree,
2373                                decr_pd + ICQ5_SRV_HDRSIZE,
2374                                offset + ICQ5_SRV_HDRSIZE,
2375                                pktsize - ICQ5_SRV_HDRSIZE);
2376             break;
2377         case SRV_RECV_MESSAGE:
2378             icqv5_srv_recv_message(icq_tree,
2379                                    decr_pd + ICQ5_SRV_HDRSIZE,
2380                                    offset + ICQ5_SRV_HDRSIZE,
2381                                    pktsize - ICQ5_SRV_HDRSIZE);
2382             break;
2383         case SRV_MULTI:
2384             icqv5_srv_multi(icq_tree,
2385                             decr_pd + ICQ5_SRV_HDRSIZE,
2386                             offset + ICQ5_SRV_HDRSIZE,
2387                             pktsize - ICQ5_SRV_HDRSIZE,
2388                             fd);
2389             break;
2390         case SRV_ACK:
2391         case SRV_SILENT_TOO_LONG:
2392         case SRV_GO_AWAY:
2393         case SRV_NEW_UIN:
2394         case SRV_BAD_PASS:
2395         case SRV_UPDATE_SUCCESS:
2396             icqv5_srv_no_params(icq_tree,
2397                                 decr_pd + ICQ5_SRV_HDRSIZE,
2398                                 offset + ICQ5_SRV_HDRSIZE,
2399                                 pktsize - ICQ5_SRV_HDRSIZE,
2400                                 cmd);
2401             break;
2402         default:
2403             proto_tree_add_uint_format(icq_tree,
2404                                        hf_icq_cmd,
2405                                        NullTVB,
2406                                        offset + ICQ5_SRV_CMD,
2407                                        2,
2408                                        cmd,
2409                                        "Command: %u (%s)",
2410                                        cmd, findServerCmd(cmd));
2411             fprintf(stderr,"Missing: %s\n", findServerCmd(cmd));
2412             break;
2413         }
2414     }
2415 }
2416
2417 static void dissect_icqv5(const u_char *pd,
2418                           int offset,
2419                           frame_data *fd, 
2420                           proto_tree *tree)
2421 {
2422   guint32 unknown = pletohl(&pd[offset + ICQ5_UNKNOWN]);
2423   
2424   if (check_col(fd, COL_PROTOCOL))
2425       col_set_str(fd, COL_PROTOCOL, "ICQv5 (UDP)");
2426   if (check_col(fd, COL_INFO))
2427       col_set_str(fd, COL_INFO, "ICQv5 packet");
2428   if (unknown == 0x0L) {
2429       dissect_icqv5Client(pd, offset, fd, tree);
2430   } else {
2431       dissect_icqv5Server(pd + offset, offset, fd, tree, (guint32) -1);
2432   }
2433 }
2434
2435 static void dissect_icq(const u_char *pd,
2436                         int offset,
2437                         frame_data *fd, 
2438                         proto_tree *tree)
2439 {
2440   int version = 0;
2441
2442   OLD_CHECK_DISPLAY_AS_DATA(proto_icq, pd, offset, fd, tree);
2443
2444   version = pletohs(&pd[offset + ICQ_VERSION]);
2445   switch (version) {
2446   case 0x0005:
2447       dissect_icqv5(pd, offset, fd, tree);
2448       break;
2449   case 0x0004:
2450       dissect_icqv4(pd, offset, fd, tree);
2451       break;
2452   case 0x0003:
2453       dissect_icqv3(pd, offset, fd, tree);
2454       break;
2455   case 0x0002:
2456       dissect_icqv2(pd, offset, fd, tree);
2457       break;
2458   default:
2459       fprintf(stderr, "ICQ: Unknown version (%d)\n", version);
2460       break;
2461   }
2462 }
2463
2464 /* registration with the filtering engine */
2465 void
2466 proto_register_icq(void)
2467 {
2468     static hf_register_info hf[] = {
2469         { &hf_icq_type,
2470           {"Type", "icq.type", FT_UINT16, BASE_DEC, NULL, 0x0, ""}},
2471         { &hf_icq_uin,
2472           {"UIN", "icq.uin", FT_UINT32, BASE_DEC, NULL, 0x0, ""}},
2473         { &hf_icq_sessionid,
2474           {"SessionID", "icq.sessionid", FT_UINT32, BASE_HEX, NULL, 0x0, ""}},
2475         { &hf_icq_cmd,
2476           {"Command", "icq.cmd", FT_UINT16, BASE_DEC, NULL, 0x0, ""}},
2477         { &hf_icq_checkcode,
2478           {"Checkcode", "icq.checkcode", FT_UINT32, BASE_HEX, NULL, 0x0, ""}},
2479         { &hf_icq_decode,
2480           {"Decode", "icq.decode", FT_STRING, BASE_NONE, NULL, 0x0, ""}}
2481     };
2482     static gint *ett[] = {
2483         &ett_icq,
2484         &ett_icq_header,
2485         &ett_icq_decode,
2486         &ett_icq_body,
2487         &ett_icq_body_parts,
2488     };
2489     
2490     proto_icq = proto_register_protocol("ICQ Protocol", "ICQ", "icq");
2491     
2492     proto_register_field_array(proto_icq, hf, array_length(hf));
2493
2494     proto_register_subtree_array(ett, array_length(ett));
2495 }
2496
2497 void
2498 proto_reg_handoff_icq(void)
2499 {
2500     old_dissector_add("udp.port", UDP_PORT_ICQ, dissect_icq, proto_icq);
2501 }