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