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