MSWSP: ConnectIn WIP
[wireshark-wip.git] / epan / dissectors / packet-mswsp.c
blobdd3dc7a2cb43ae6efd69b41800aeb22c3c096332
1 /* packet-mswsp.c
2 * Routines for PROTONAME dissection
3 * Copyright 2012, Gregor Beck <gregor.beck@sernet.de>
5 * $Id$
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 /* Include only as needed */
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
35 #include <glib.h>
37 #include <epan/packet.h>
38 #include <epan/prefs.h>
40 #include "packet-smb.h"
41 #include "packet-smb2.h"
43 /* IF PROTO exposes code to other dissectors, then it must be exported
44 in a header file. If not, a header file is not needed at all. */
46 * #include "packet-mswsp.h"
51 /* Forward declaration we need below (if using proto_reg_handoff...
52 as a prefs callback) */
53 void proto_reg_handoff_mswsp(void);
55 /* Initialize the protocol and registered fields */
56 static int proto_mswsp = -1;
57 static int hf_mswsp_msg = -1;
58 static int hf_mswsp_hdr = -1;
59 static int hf_mswsp_hdr_msg = -1;
60 static int hf_mswsp_hdr_status = -1;
61 static int hf_mswsp_hdr_checksum = -1;
62 static int hf_mswsp_hdr_reserved = -1;
63 static int hf_mswsp_msg_ConnectIn_ClientVersion = -1;
64 static int hf_mswsp_msg_ConnectIn_ClientIsRemote = -1;
65 static int hf_mswsp_msg_ConnectIn_Blob1 = -1;
66 static int hf_mswsp_msg_ConnectIn_Blob2 = -1;
67 static int hf_mswsp_msg_ConnectIn_MachineName = -1;
68 static int hf_mswsp_msg_ConnectIn_UserName = -1;
70 /* Global sample preference ("controls" display of numbers) */
71 static gboolean gPREF_HEX = FALSE;
72 /* Global sample port pref */
73 static guint gPORT_PREF = 1234;
75 /* Initialize the subtree pointers */
76 static gint ett_mswsp = -1;
77 static gint ett_mswsp_hdr = -1;
78 static gint ett_mswsp_msg = -1;
80 /* Code to actually dissect the packets */
82 static int dissect_CPMConnect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, gboolean in)
84 proto_item *ti = proto_tree_add_item(parent_tree, hf_mswsp_msg, tvb, 17, -1, ENC_NA);
85 proto_tree *tree = proto_item_add_subtree(ti, ett_mswsp_msg);
86 gint offset = 16;
87 guint len;
88 proto_item_set_text(ti, "CPMConnect%s", in ? "In" : "Out");
89 col_append_str(pinfo->cinfo, COL_INFO, "Connect");
90 if (in) {
91 proto_tree_add_item(tree, hf_mswsp_msg_ConnectIn_ClientVersion, tvb,
92 offset, 4, ENC_LITTLE_ENDIAN);
93 offset += 4;
95 proto_tree_add_item(tree, hf_mswsp_msg_ConnectIn_ClientIsRemote, tvb,
96 offset, 4, ENC_LITTLE_ENDIAN);
97 offset += 4;
99 proto_tree_add_item(tree, hf_mswsp_msg_ConnectIn_Blob1, tvb,
100 offset, 4, ENC_LITTLE_ENDIAN);
101 offset += 4;
103 proto_tree_add_text(tree, tvb, offset, 4, "Padding");
104 offset += 4;
106 proto_tree_add_item(tree, hf_mswsp_msg_ConnectIn_Blob2, tvb,
107 offset, 4, ENC_LITTLE_ENDIAN);
108 offset += 4;
110 proto_tree_add_text(tree, tvb, offset, 12, "Padding");
111 offset += 12;
113 len = tvb_unicode_strsize(tvb, offset);
114 ti = proto_tree_add_item(tree, hf_mswsp_msg_ConnectIn_MachineName, tvb,
115 offset, len, ENC_UTF_16);
116 /*This shouldnt be necessary, is this a bug or is there some GUI setting I've missed?*/
117 proto_item_set_text(ti, "Remote machine: %s",
118 tvb_get_unicode_string(tvb, offset, len, ENC_LITTLE_ENDIAN));
119 offset += len;
121 len = tvb_unicode_strsize(tvb, offset);
122 ti = proto_tree_add_item(tree, hf_mswsp_msg_ConnectIn_UserName, tvb,
123 offset, len, ENC_UTF_16);
124 proto_item_set_text(ti, "User: %s", tvb_get_unicode_string(tvb, offset, len, ENC_LITTLE_ENDIAN));
125 offset += len;
127 if (offset % 8) {
128 int pad = 8 - (offset % 8);
129 proto_tree_add_text(tree, tvb, offset, pad, "Padding");
130 offset += pad;
132 DISSECTOR_ASSERT((offset % 8) == 0);
133 proto_tree_add_text(tree, tvb, offset, -1, "Rest: propsets etc.");
135 return tvb_length(tvb);
138 static int dissect_CPMDisconnect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
140 col_append_str(pinfo->cinfo, COL_INFO, "Disconnect");
141 return tvb_length(tvb);
144 static int dissect_CPMCreateQuery(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
146 col_append_str(pinfo->cinfo, COL_INFO, "CreateQuery");
147 return tvb_length(tvb);
150 static int dissect_CPMFreeCursor(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
152 col_append_str(pinfo->cinfo, COL_INFO, "FreeCursor");
153 return tvb_length(tvb);
156 static int dissect_CPMGetRows(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
158 col_append_str(pinfo->cinfo, COL_INFO, "GetRows");
159 return tvb_length(tvb);
162 static int dissect_CPMRatioFinished(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
164 col_append_str(pinfo->cinfo, COL_INFO, "RatioFinished");
165 return tvb_length(tvb);
168 static int dissect_CPMCompareBmk(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
170 col_append_str(pinfo->cinfo, COL_INFO, "CompareBmk");
171 return tvb_length(tvb);
174 static int dissect_CPMGetApproximatePosition(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
176 col_append_str(pinfo->cinfo, COL_INFO, "GetApproximatePosition");
177 return tvb_length(tvb);
180 static int dissect_CPMSetBindings(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
182 col_append_str(pinfo->cinfo, COL_INFO, "SetBindings");
183 return tvb_length(tvb);
186 static int dissect_CPMGetNotify(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
188 col_append_str(pinfo->cinfo, COL_INFO, "GetNotify");
189 return tvb_length(tvb);
192 static int dissect_CPMSendNotifyOut(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
194 col_append_str(pinfo->cinfo, COL_INFO, "SendNotify");
195 return tvb_length(tvb);
198 static int dissect_CPMGetQueryStatus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
200 col_append_str(pinfo->cinfo, COL_INFO, "GetQueryStatus");
201 return tvb_length(tvb);
204 static int dissect_CPMCiState(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
206 col_append_str(pinfo->cinfo, COL_INFO, "CiState");
207 return tvb_length(tvb);
210 static int dissect_CPMFetchValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
212 col_append_str(pinfo->cinfo, COL_INFO, "FetchValue");
213 return tvb_length(tvb);
216 static int dissect_CPMGetQueryStatusEx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
218 col_append_str(pinfo->cinfo, COL_INFO, "GetQueryStatusEx");
219 return tvb_length(tvb);
222 static int dissect_CPMRestartPosition(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
224 col_append_str(pinfo->cinfo, COL_INFO, "RestartPosition");
225 return tvb_length(tvb);
228 static int dissect_CPMSetCatState(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
230 col_append_str(pinfo->cinfo, COL_INFO, "SetCatState");
231 return tvb_length(tvb);
234 static int dissect_CPMGetRowsetNotify(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
236 col_append_str(pinfo->cinfo, COL_INFO, "GetRowsetNotify");
237 return tvb_length(tvb);
240 static int dissect_CPMFindIndices(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
242 col_append_str(pinfo->cinfo, COL_INFO, "FindIndices");
243 return tvb_length(tvb);
246 static int dissect_CPMSetScopePrioritization(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
248 col_append_str(pinfo->cinfo, COL_INFO, "SetScopePrioritization");
249 return tvb_length(tvb);
252 static int dissect_CPMGetScopeStatistics(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_)
254 col_append_str(pinfo->cinfo, COL_INFO, "GetScopeStatistics");
255 return tvb_length(tvb);
260 dissect_mswsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean in)
262 proto_tree *mswsp_tree = NULL;
263 struct {
264 guint32 msg;
265 guint32 status;
266 guint32 checksum;
267 guint32 reserved;
268 } hdr;
269 int (*fn)(tvbuff_t*, packet_info*, proto_tree*, gboolean);
271 if (tvb_length(tvb) < 16) {
272 return 0;
275 hdr.msg = tvb_get_letoh24(tvb, 0);
277 switch(hdr.msg) {
278 case 0xC8:
279 fn = dissect_CPMConnect;
280 break;
281 case 0xC9:
282 fn = dissect_CPMDisconnect;
283 break;
284 case 0xCA:
285 fn = dissect_CPMCreateQuery;
286 break;
287 case 0xCB:
288 fn = dissect_CPMFreeCursor;
289 break;
290 case 0xCC:
291 fn = dissect_CPMGetRows;
292 break;
293 case 0xCD:
294 fn = dissect_CPMRatioFinished;
295 break;
296 case 0xCE:
297 fn = dissect_CPMCompareBmk;
298 break;
299 case 0xCF:
300 fn = dissect_CPMGetApproximatePosition;
301 break;
302 case 0xD0:
303 fn = dissect_CPMSetBindings;
304 break;
305 case 0xD1:
306 fn = dissect_CPMGetNotify;
307 break;
308 case 0xD2:
309 fn = dissect_CPMSendNotifyOut;
310 break;
311 case 0xD7:
312 fn = dissect_CPMGetQueryStatus;
313 break;
314 case 0xD9:
315 fn = dissect_CPMCiState;
316 break;
317 case 0xE4:
318 fn = dissect_CPMFetchValue;
319 break;
320 case 0xE7:
321 fn = dissect_CPMGetQueryStatusEx;
322 break;
323 case 0xE8:
324 fn = dissect_CPMRestartPosition;
325 break;
326 case 0xEC:
327 fn = dissect_CPMSetCatState;
328 break;
329 case 0xF1:
330 fn = dissect_CPMGetRowsetNotify;
331 break;
332 case 0xF2:
333 fn = dissect_CPMFindIndices;
334 break;
335 case 0xF3:
336 fn = dissect_CPMSetScopePrioritization;
337 break;
338 case 0xF4:
339 fn = dissect_CPMGetScopeStatistics;
340 break;
341 default:
342 return 0;
345 hdr.status = tvb_get_letoh24(tvb, 4);
346 hdr.checksum = tvb_get_letoh24(tvb, 8);
348 col_set_str(pinfo->cinfo, COL_PROTOCOL, "MS-WSP");
349 /* col_clear(pinfo->cinfo, COL_INFO); */
351 col_set_str(pinfo->cinfo, COL_INFO, "WSP ");
352 col_append_str(pinfo->cinfo, COL_INFO, in ? "Request: " : "Response: ");
354 if (tree) {
355 proto_tree *hdr_tree;
356 proto_item *ti, *hti;
358 ti = proto_tree_add_item(tree, proto_mswsp, tvb, 0, -1, ENC_NA);
359 mswsp_tree = proto_item_add_subtree(ti, ett_mswsp);
361 hti = proto_tree_add_item(mswsp_tree, hf_mswsp_hdr, tvb, 0, 16, ENC_NA);
362 hdr_tree = proto_item_add_subtree(hti, ett_mswsp_hdr);
364 proto_tree_add_item(hdr_tree, hf_mswsp_hdr_msg, tvb,
365 0, 4, ENC_LITTLE_ENDIAN);
366 proto_tree_add_item(hdr_tree, hf_mswsp_hdr_status,
367 tvb, 4, 4, ENC_LITTLE_ENDIAN);
368 proto_tree_add_item(hdr_tree, hf_mswsp_hdr_checksum,
369 tvb, 8, 4, ENC_LITTLE_ENDIAN);
370 proto_tree_add_item(hdr_tree, hf_mswsp_hdr_reserved, tvb,
371 12, 4, ENC_LITTLE_ENDIAN);
372 /* if (tvb_length(tvb) > 16) { */
373 /* bti = proto_tree_add_item(mswsp_tree, hf_mswsp_bdy, tvb, 17, -1, ENC_NA); */
375 /* mswsp_bdy_tree = proto_item_add_subtree(bti, ett_mswsp_msg); */
376 /* } */
379 fn(tvb, pinfo, mswsp_tree, in);
381 /* Return the amount of data this dissector was able to dissect */
382 return tvb_length(tvb);
386 /* Register the protocol with Wireshark */
388 /* this format is require because a script is used to build the C function
389 that calls all the protocol registration.
392 void
393 proto_register_mswsp(void)
395 module_t *mswsp_module;
397 /* Setup list of header fields See Section 1.6.1 for details*/
398 static const value_string msg_ids[] = {
399 {0x000000C8, "CPMConnect"}, /* In/Out */
400 {0x000000C9, "CPMDisconnect"},
401 {0x000000CA, "CPMCreateQuery"}, /* In/Out */
402 {0x000000CB, "CPMFreeCursor"}, /* In/Out */
403 {0x000000CC, "CPMGetRows"}, /* In/Out */
404 {0x000000CD, "CPMRatioFinished"}, /* In/Out */
405 {0x000000CE, "CPMCompareBmk"}, /* In/Out */
406 {0x000000CF, "CPMGetApproximatePosition"}, /* In/Out */
407 {0x000000D0, "CPMSetBindingsIn"},
408 {0x000000D1, "CPMGetNotify"},
409 {0x000000D2, "CPMSendNotifyOut"},
410 {0x000000D7, "CPMGetQueryStatusIn"}, /* In/Out */
411 {0x000000D9, "CPMCiStateInOut"},
412 {0x000000E4, "CPMFetchValue"}, /* In/Out */
413 {0x000000E7, "CPMGetQueryStatusEx"}, /* In/Out */
414 {0x000000E8, "CPMRestartPositionIn"},
415 {0x000000EC, "CPMSetCatStateIn"}, /* (not supported) */
416 {0x000000F1, "CPMGetRowsetNotify"}, /* In/Out */
417 {0x000000F2, "CPMFindIndices"}, /* In/Out */
418 {0x000000F3, "CPMSetScopePrioritization"}, /* In/Out */
419 {0x000000F4, "CPMGetScopeStatistics"}, /* In/Out */
422 static hf_register_info hf[] = {
423 { &hf_mswsp_hdr,
424 { "Header", "mswsp.hdr",
425 FT_NONE, BASE_NONE , NULL, 0,
426 "Message header", HFILL }
428 { &hf_mswsp_hdr_msg,
429 { "Msg id", "mswsp.hdr.id",
430 FT_UINT32, BASE_HEX , VALS(msg_ids), 0,
431 "Message id", HFILL }
433 { &hf_mswsp_hdr_status,
434 { "Status", "mswsp.hdr.status",
435 FT_UINT32, BASE_HEX , NULL, 0,
436 "Status", HFILL }
438 { &hf_mswsp_hdr_checksum,
439 { "checksum", "mswsp.hdr.checksum",
440 FT_UINT32, BASE_HEX , NULL, 0,
441 "Checksum", HFILL }
443 { &hf_mswsp_hdr_reserved,
444 { "Reserved", "mswsp.hdr.reserved",
445 FT_UINT32, BASE_HEX , NULL, 0,
446 "Reserved", HFILL }
448 { &hf_mswsp_msg,
449 { "msg", "mswsp.msg",
450 FT_NONE, BASE_NONE , NULL, 0,
451 "Message", HFILL }
453 { &hf_mswsp_msg_ConnectIn_ClientVersion,
454 { "Version", "mswsp.ConnectIn.version",
455 FT_UINT32, BASE_HEX , NULL, 0,
456 "Checksum",HFILL }
458 { &hf_mswsp_msg_ConnectIn_ClientIsRemote,
459 { "Remote", "mswsp.ConnectIn.isRemote",
460 FT_BOOLEAN, BASE_HEX , NULL, 0,
461 "Client is remote",HFILL }
463 { &hf_mswsp_msg_ConnectIn_Blob1,
464 { "Blob1 size", "mswsp.ConnectIn.blob1",
465 FT_UINT32, BASE_HEX , NULL, 0,
466 "Size of PropSet fields",HFILL }
468 { &hf_mswsp_msg_ConnectIn_Blob2,
469 { "Blob2 size", "mswsp.ConnectIn.blob2",
470 FT_UINT32, BASE_HEX , NULL, 0,
471 "Size of ExtPropSet fields",HFILL }
473 { &hf_mswsp_msg_ConnectIn_MachineName,
474 { "Remote machine", "mswsp.ConnectIn.machine",
475 FT_STRINGZ, BASE_NONE , NULL, 0,
476 "Name of remote machine",HFILL }
478 { &hf_mswsp_msg_ConnectIn_UserName,
479 { "User", "mswsp.ConnectIn.user",
480 FT_STRINGZ, BASE_NONE , NULL, 0,
481 "Name of remote user",HFILL }
485 /* Setup protocol subtree array */
486 static gint *ett[] = {
487 &ett_mswsp,
488 &ett_mswsp_hdr,
489 &ett_mswsp_msg,
492 /* Register the protocol name and description */
493 proto_mswsp = proto_register_protocol("Windows Search Protocol",
494 "MS-WSP", "mswsp");
496 /* Required function calls to register the header fields and subtrees used */
497 proto_register_field_array(proto_mswsp, hf, array_length(hf));
498 proto_register_subtree_array(ett, array_length(ett));
500 /* Register preferences module (See Section 2.6 for more on preferences) */
501 /* (Registration of a prefs callback is not required if there are no */
502 /* prefs-dependent registration functions (eg: a port pref). */
503 /* See proto_reg_handoff below. */
504 /* If a prefs callback is not needed, use NULL instead of */
505 /* proto_reg_handoff_mswsp in the following). */
506 mswsp_module = prefs_register_protocol(proto_mswsp,
507 proto_reg_handoff_mswsp);
509 /* Register preferences module under preferences subtree.
510 Use this function instead of prefs_register_protocol if you want to group
511 preferences of several protocols under one preferences subtree.
512 Argument subtree identifies grouping tree node name, several subnodes can be
513 specified using slash '/' (e.g. "OSI/X.500" - protocol preferences will be
514 accessible under Protocols->OSI->X.500-><PROTOSHORTNAME> preferences node.
516 /* mswsp_module = prefs_register_protocol_subtree(subtree, */
517 /* proto_mswsp, proto_reg_handoff_mswsp); */
519 /* Register a sample preference */
520 prefs_register_bool_preference(mswsp_module, "show_hex",
521 "Display numbers in Hex",
522 "Enable to display numerical values in hexadecimal.",
523 &gPREF_HEX);
525 /* Register a sample port preference */
526 prefs_register_uint_preference(mswsp_module, "tcp.port", "mswsp TCP Port",
527 " mswsp TCP port if other than the default",
528 10, &gPORT_PREF);
531 static int dissect_mswsp_smb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
532 smb_info_t *si = pinfo->private_data;
533 gboolean in = si->request;
535 fprintf(stderr, "dissect_mswsp_smb %d <> %d : op %02x %s %s type: %d\n",
536 pinfo->fd->num, si->tid,
537 si->cmd,
538 pinfo->dcerpc_procedure_name ? pinfo->dcerpc_procedure_name : "<NULL>",
539 in ? "Request" : "Response", si->tid);
542 if (strcmp(pinfo->dcerpc_procedure_name, "File: MsFteWds") != 0) {
543 return 0;
546 return dissect_mswsp(tvb, pinfo, tree, in);
550 static int dissect_mswsp_smb2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
551 smb2_info_t *si = pinfo->private_data;
552 gboolean in = !(si->flags & SMB2_FLAGS_RESPONSE);
554 //si->tree->share_type == SMB2_SHARE_TYPE_PIPE
555 //si->tree->connect_frame
557 fprintf(stderr, "dissect_mswsp %d <> %d : op %02x %s %s type: %d extra_file: %s\n",
558 pinfo->fd->num, si->tree ? (int)si->tree->connect_frame : -1,
559 si->opcode,
560 pinfo->dcerpc_procedure_name ? pinfo->dcerpc_procedure_name : "<NULL>",
561 in ? "Request" : "Response", si->tree ? si->tree->share_type : -1,
562 si->saved ? (si->saved->extra_info_type == SMB2_EI_FILENAME ? (char*)si->saved->extra_info : "<OTHER>") : "<NONE>"
566 if (strcmp(pinfo->dcerpc_procedure_name, "File: MsFteWds") != 0) {
567 return 0;
570 return dissect_mswsp(tvb, pinfo, tree, in);
575 /* If this dissector uses sub-dissector registration add a registration routine.
576 This exact format is required because a script is used to find these
577 routines and create the code that calls these routines.
579 If this function is registered as a prefs callback (see prefs_register_protocol
580 above) this function is also called by preferences whenever "Apply" is pressed;
581 In that case, it should accommodate being called more than once.
583 Simple form of proto_reg_handoff_mswsp which can be used if there are
584 no prefs-dependent registration function calls.
587 void
588 proto_reg_handoff_mswsp(void)
590 heur_dissector_add("smb_transact", dissect_mswsp_smb, proto_mswsp);
591 heur_dissector_add("smb2_heur_subdissectors", dissect_mswsp_smb2, proto_mswsp);
596 * Editor modelines - http://www.wireshark.org/tools/modelines.html
598 * Local variables:
599 * c-basic-offset: 4
600 * tab-width: 8
601 * indent-tabs-mode: nil
602 * End:
604 * vi: set shiftwidth=4 tabstop=8 expandtab:
605 * :indentSize=4:tabSize=8:noTabs=true: