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