1 /* conversations_table.c
2 * conversations_table 2003 Ronnie Sahlberg
3 * Helper routines common to all endpoint conversations tap.
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "packet_info.h"
30 #include "conversation_table.h"
31 #include "addr_resolv.h"
33 #include "stat_tap_ui.h"
35 GList *cmd_string_list_ = NULL;
38 gboolean hide_ports; /* hide TCP / UDP port columns */
39 int proto_id; /* protocol id (0-indexed) */
40 tap_packet_cb conv_func; /* function to be called for new incoming packets for conversation*/
41 tap_packet_cb host_func; /* function to be called for new incoming packets for hostlist */
42 conv_gui_init_cb conv_gui_init; /* GUI specific function to initialize conversation */
43 host_gui_init_cb host_gui_init; /* GUI specific function to initialize hostlist */
46 gboolean get_conversation_hide_ports(register_ct_t* ct)
48 return ct->hide_ports;
51 int get_conversation_proto_id(register_ct_t* ct)
59 tap_packet_cb get_conversation_packet_func(register_ct_t* ct)
64 tap_packet_cb get_hostlist_packet_func(register_ct_t* ct)
69 static GSList *registered_ct_tables = NULL;
72 dissector_conversation_init(const char *opt_arg, void* userdata)
74 register_ct_t *table = (register_ct_t*)userdata;
75 GString *cmd_str = g_string_new("conv,");
76 const char *filter=NULL;
78 g_string_append(cmd_str, proto_get_protocol_filter_name(table->proto_id));
79 if(!strncmp(opt_arg, cmd_str->str, cmd_str->len)){
80 if (opt_arg[cmd_str->len] == ',') {
81 filter = opt_arg + cmd_str->len + 1;
84 g_string_free(cmd_str, TRUE);
86 if (table->conv_gui_init)
87 table->conv_gui_init(table, filter);
91 dissector_hostlist_init(const char *opt_arg, void* userdata)
93 register_ct_t *table = (register_ct_t*)userdata;
94 GString *cmd_str = g_string_new("");
95 const char *filter=NULL;
97 g_string_printf(cmd_str, "%s,%s,", HOSTLIST_TAP_PREFIX, proto_get_protocol_filter_name(table->proto_id));
98 if(!strncmp(opt_arg, cmd_str->str, cmd_str->len)){
99 if (opt_arg[cmd_str->len] == ',') {
100 filter = opt_arg + cmd_str->len + 1;
106 g_string_free(cmd_str, TRUE);
108 if (table->host_gui_init)
109 table->host_gui_init(table, filter);
111 /** get conversation from protocol ID
113 * @param proto_id protocol ID
114 * @return tap function handler of conversation
116 register_ct_t* get_conversation_by_proto_id(int proto_id)
119 register_ct_t *table;
121 for(ct = registered_ct_tables; ct != NULL; ct = g_slist_next(ct)){
122 table = (register_ct_t*)ct->data;
123 if ((table) && (table->proto_id == proto_id))
131 insert_sorted_by_table_name(gconstpointer aparam, gconstpointer bparam)
133 const register_ct_t *a = (register_ct_t *)aparam;
134 const register_ct_t *b = (register_ct_t *)bparam;
136 return g_ascii_strcasecmp(proto_get_protocol_short_name(find_protocol_by_id(a->proto_id)), proto_get_protocol_short_name(find_protocol_by_id(b->proto_id)));
140 register_conversation_table(const int proto_id, gboolean hide_ports, tap_packet_cb conv_packet_func, tap_packet_cb hostlist_func)
142 register_ct_t *table;
144 table = g_new(register_ct_t,1);
146 table->hide_ports = hide_ports;
147 table->proto_id = proto_id;
148 table->conv_func = conv_packet_func;
149 table->host_func = hostlist_func;
150 table->conv_gui_init = NULL;
151 table->host_gui_init = NULL;
153 registered_ct_tables = g_slist_insert_sorted(registered_ct_tables, table, insert_sorted_by_table_name);
156 /* Set GUI fields for register_ct list */
158 set_conv_gui_data(gpointer data, gpointer user_data)
160 GString *conv_cmd_str = g_string_new("conv,");
162 register_ct_t *table = (register_ct_t*)data;
164 table->conv_gui_init = (conv_gui_init_cb)user_data;
166 g_string_append(conv_cmd_str, proto_get_protocol_filter_name(table->proto_id));
167 cmd_string_list_ = g_list_append(cmd_string_list_, conv_cmd_str->str);
168 ui_info.group = REGISTER_STAT_GROUP_CONVERSATION_LIST;
169 ui_info.title = NULL; /* construct this from the protocol info? */
170 ui_info.cli_string = g_string_free(conv_cmd_str, FALSE);
171 ui_info.tap_init_cb = dissector_conversation_init;
174 ui_info.params = NULL;
175 register_stat_tap_ui(&ui_info, table);
178 void conversation_table_set_gui_info(conv_gui_init_cb init_cb)
180 g_slist_foreach(registered_ct_tables, set_conv_gui_data, init_cb);
184 set_host_gui_data(gpointer data, gpointer user_data)
186 GString *host_cmd_str = g_string_new("");
188 register_ct_t *table = (register_ct_t*)data;
190 table->host_gui_init = (host_gui_init_cb)user_data;
192 g_string_printf(host_cmd_str, "%s,%s", HOSTLIST_TAP_PREFIX, proto_get_protocol_filter_name(table->proto_id));
193 ui_info.group = REGISTER_STAT_GROUP_ENDPOINT_LIST;
194 ui_info.title = NULL; /* construct this from the protocol info? */
195 ui_info.cli_string = g_string_free(host_cmd_str, FALSE);
196 ui_info.tap_init_cb = dissector_hostlist_init;
199 ui_info.params = NULL;
200 register_stat_tap_ui(&ui_info, table);
203 void hostlist_table_set_gui_info(host_gui_init_cb init_cb)
205 g_slist_foreach(registered_ct_tables, set_host_gui_data, init_cb);
208 void conversation_table_iterate_tables(GFunc func, gpointer user_data)
210 g_slist_foreach(registered_ct_tables, func, user_data);
213 guint conversation_table_get_num(void)
215 return g_slist_length(registered_ct_tables);
219 register_ct_t *get_conversation_table_by_num(guint table_num)
221 return (register_ct_t *) g_slist_nth_data(registered_ct_tables, table_num);
224 /** Compute the hash value for two given address/port pairs.
225 * (Parameter type is gconstpointer for GHashTable compatibility.)
227 * @param v Conversation Key. MUST point to a conv_key_t struct.
228 * @return Computed key hash.
231 conversation_hash(gconstpointer v)
233 const conv_key_t *key = (const conv_key_t *)v;
237 ADD_ADDRESS_TO_HASH(hash_val, &key->addr1);
238 hash_val += key->port1;
239 ADD_ADDRESS_TO_HASH(hash_val, &key->addr2);
240 hash_val += key->port2;
241 hash_val ^= key->conv_id;
246 /** Compare two conversation keys for an exact match.
247 * (Parameter types are gconstpointer for GHashTable compatibility.)
249 * @param key1 First conversation. MUST point to a conv_key_t struct.
250 * @param key2 Second conversation. MUST point to a conv_key_t struct.
251 * @return TRUE if conversations are equal, FALSE otherwise.
254 conversation_equal(gconstpointer key1, gconstpointer key2)
256 const conv_key_t *ck1 = (const conv_key_t *)key1;
257 const conv_key_t *ck2 = (const conv_key_t *)key2;
259 if (ck1->conv_id == ck2->conv_id)
261 if (ck1->port1 == ck2->port1 &&
262 ck1->port2 == ck2->port2 &&
263 ADDRESSES_EQUAL(&ck1->addr1, &ck2->addr1) &&
264 ADDRESSES_EQUAL(&ck1->addr2, &ck2->addr2)) {
268 if (ck1->port2 == ck2->port1 &&
269 ck1->port1 == ck2->port2 &&
270 ADDRESSES_EQUAL(&ck1->addr2, &ck2->addr1) &&
271 ADDRESSES_EQUAL(&ck1->addr1, &ck2->addr2)) {
277 * The addresses, ports, or conversation IDs don't match.
283 reset_conversation_table_data(conv_hash_t *ch)
289 if (ch->conv_array != NULL) {
291 for(i = 0; i < ch->conv_array->len; i++){
292 conv_item_t *conv = &g_array_index(ch->conv_array, conv_item_t, i);
293 g_free((gpointer)conv->src_address.data);
294 g_free((gpointer)conv->dst_address.data);
297 g_array_free(ch->conv_array, TRUE);
300 if (ch->hashtable != NULL) {
301 g_hash_table_destroy(ch->hashtable);
308 void reset_hostlist_table_data(conv_hash_t *ch)
314 if (ch->conv_array != NULL) {
316 for(i = 0; i < ch->conv_array->len; i++){
317 hostlist_talker_t *host = &g_array_index(ch->conv_array, hostlist_talker_t, i);
318 g_free((gpointer)host->myaddress.data);
321 g_array_free(ch->conv_array, TRUE);
324 if (ch->hashtable != NULL) {
325 g_hash_table_destroy(ch->hashtable);
332 const char *get_conversation_address(wmem_allocator_t *allocator, address *addr, gboolean resolve_names)
335 return address_to_display(allocator, addr);
337 return address_to_str(allocator, addr);
341 const char *get_conversation_port(wmem_allocator_t *allocator, guint32 port, port_type ptype, gboolean resolve_names)
344 if(!resolve_names) ptype = PT_NONE;
348 return tcp_port_to_display(allocator, port);
350 return udp_port_to_display(allocator, port);
352 return sctp_port_to_display(allocator, port);
354 return wmem_strdup_printf(allocator, "%d", port);
358 /* given an address (to distinguish between ipv4 and ipv6 for tcp/udp),
359 a port_type and a name_type (FN_...)
360 return a string for the filter name.
362 Some addresses, like AT_ETHER may actually be any of multiple types
363 of protocols, either ethernet, tokenring, fddi, wlan etc so we must be
364 more specific there; that's why we need specific_addr_type.
367 conversation_get_filter_name(conv_item_t *conv_item, conv_filter_type_e filter_type)
370 if ((conv_item == NULL) || (conv_item->dissector_info == NULL) || (conv_item->dissector_info->get_filter_type == NULL)) {
371 return CONV_FILTER_INVALID;
374 return conv_item->dissector_info->get_filter_type(conv_item, filter_type);
378 hostlist_get_filter_name(hostlist_talker_t *host, conv_filter_type_e filter_type)
381 if ((host == NULL) || (host->dissector_info == NULL) || (host->dissector_info->get_filter_type == NULL)) {
382 return CONV_FILTER_INVALID;
385 return host->dissector_info->get_filter_type(host, filter_type);
388 /* Convert a port number into a string or NULL */
390 ct_port_to_str(port_type ptype, guint32 port)
397 return g_strdup_printf("%d", port);
404 const char *get_conversation_filter(conv_item_t *conv_item, conv_direction_e direction)
406 char *sport, *dport, *src_addr, *dst_addr;
407 const char *str = "INVALID";
409 sport = ct_port_to_str(conv_item->ptype, conv_item->src_port);
410 dport = ct_port_to_str(conv_item->ptype, conv_item->dst_port);
411 src_addr = address_to_str(NULL, &conv_item->src_address);
412 dst_addr = address_to_str(NULL, &conv_item->dst_address);
414 if (conv_item->src_address.type == AT_STRINGZ || conv_item->src_address.type == AT_USB) {
417 new_addr = wmem_strdup_printf(NULL, "\"%s\"", src_addr);
418 wmem_free(NULL, src_addr);
421 if (conv_item->dst_address.type == AT_STRINGZ || conv_item->dst_address.type == AT_USB) {
424 new_addr = wmem_strdup_printf(NULL, "\"%s\"", dst_addr);
425 wmem_free(NULL, dst_addr);
430 case CONV_DIR_A_TO_FROM_B:
432 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s && %s==%s%s%s%s%s",
433 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
436 sport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
439 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
442 dport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
447 case CONV_DIR_A_TO_B:
449 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s && %s==%s%s%s%s%s",
450 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
453 sport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
456 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
459 dport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
464 case CONV_DIR_A_FROM_B:
466 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s && %s==%s%s%s%s%s",
467 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
470 sport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
473 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
476 dport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
481 case CONV_DIR_A_TO_FROM_ANY:
483 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
484 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
487 sport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
492 case CONV_DIR_A_TO_ANY:
494 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
495 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
498 sport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
503 case CONV_DIR_A_FROM_ANY:
505 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
506 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
509 sport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
514 case CONV_DIR_ANY_TO_FROM_B:
516 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
517 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
520 dport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
525 case CONV_DIR_ANY_FROM_B:
527 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
528 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
531 dport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
536 case CONV_DIR_ANY_TO_B:
538 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
539 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
542 dport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
552 wmem_free(NULL, src_addr);
553 wmem_free(NULL, dst_addr);
557 const char *get_hostlist_filter(hostlist_talker_t *host)
559 char *sport, *src_addr;
562 sport=ct_port_to_str(host->ptype, host->port);
563 src_addr = address_to_str(NULL, &host->myaddress);
564 if (host->myaddress.type == AT_STRINGZ || host->myaddress.type == AT_USB) {
567 new_addr = wmem_strdup_printf(NULL, "\"%s\"", src_addr);
568 wmem_free(NULL, src_addr);
572 str = g_strdup_printf("%s==%s%s%s%s%s",
573 hostlist_get_filter_name(host, CONV_FT_ANY_ADDRESS),
576 sport?hostlist_get_filter_name(host, CONV_FT_ANY_PORT):"",
581 wmem_free(NULL, src_addr);
586 add_conversation_table_data(conv_hash_t *ch, const address *src, const address *dst, guint32 src_port, guint32 dst_port, int num_frames, int num_bytes,
587 nstime_t *ts, nstime_t *abs_ts, ct_dissector_info_t *ct_info, port_type ptype)
589 add_conversation_table_data_with_conv_id(ch, src, dst, src_port, dst_port, CONV_ID_UNSET, num_frames, num_bytes, ts, abs_ts, ct_info, ptype);
593 add_conversation_table_data_with_conv_id(
604 ct_dissector_info_t *ct_info,
607 const address *addr1, *addr2;
608 guint32 port1, port2;
609 conv_item_t *conv_item = NULL;
610 unsigned int conversation_idx = 0;
612 if (src_port > dst_port) {
617 } else if (src_port < dst_port) {
622 } else if (CMP_ADDRESS(src, dst) < 0) {
634 /* if we don't have any entries at all yet */
635 if (ch->conv_array == NULL) {
636 ch->conv_array = g_array_sized_new(FALSE, FALSE, sizeof(conv_item_t), 10000);
638 ch->hashtable = g_hash_table_new_full(conversation_hash,
639 conversation_equal, /* key_equal_func */
640 g_free, /* key_destroy_func */
641 NULL); /* value_destroy_func */
644 /* try to find it among the existing known conversations */
645 conv_key_t existing_key;
646 gpointer conversation_idx_hash_val;
648 existing_key.addr1 = *addr1;
649 existing_key.addr2 = *addr2;
650 existing_key.port1 = port1;
651 existing_key.port2 = port2;
652 existing_key.conv_id = conv_id;
653 if (g_hash_table_lookup_extended(ch->hashtable, &existing_key, NULL, &conversation_idx_hash_val)) {
654 conv_item = &g_array_index(ch->conv_array, conv_item_t, GPOINTER_TO_UINT(conversation_idx_hash_val));
658 /* if we still don't know what conversation this is it has to be a new one
659 and we have to allocate it and append it to the end of the list */
660 if (conv_item == NULL) {
662 conv_item_t new_conv_item;
664 COPY_ADDRESS(&new_conv_item.src_address, addr1);
665 COPY_ADDRESS(&new_conv_item.dst_address, addr2);
666 new_conv_item.dissector_info = ct_info;
667 new_conv_item.ptype = ptype;
668 new_conv_item.src_port = port1;
669 new_conv_item.dst_port = port2;
670 new_conv_item.conv_id = conv_id;
671 new_conv_item.rx_frames = 0;
672 new_conv_item.tx_frames = 0;
673 new_conv_item.rx_bytes = 0;
674 new_conv_item.tx_bytes = 0;
675 new_conv_item.modified = TRUE;
678 memcpy(&new_conv_item.start_time, ts, sizeof(new_conv_item.start_time));
679 memcpy(&new_conv_item.stop_time, ts, sizeof(new_conv_item.stop_time));
680 memcpy(&new_conv_item.start_abs_time, abs_ts, sizeof(new_conv_item.start_abs_time));
682 nstime_set_unset(&new_conv_item.start_abs_time);
683 nstime_set_unset(&new_conv_item.start_time);
684 nstime_set_unset(&new_conv_item.stop_time);
686 g_array_append_val(ch->conv_array, new_conv_item);
687 conversation_idx = ch->conv_array->len - 1;
688 conv_item = &g_array_index(ch->conv_array, conv_item_t, conversation_idx);
690 /* ct->conversations address is not a constant but src/dst_address.data are */
691 new_key = g_new(conv_key_t, 1);
692 SET_ADDRESS(&new_key->addr1, conv_item->src_address.type, conv_item->src_address.len, conv_item->src_address.data);
693 SET_ADDRESS(&new_key->addr2, conv_item->dst_address.type, conv_item->dst_address.len, conv_item->dst_address.data);
694 new_key->port1 = port1;
695 new_key->port2 = port2;
696 new_key->conv_id = conv_id;
697 g_hash_table_insert(ch->hashtable, new_key, GUINT_TO_POINTER(conversation_idx));
700 /* update the conversation struct */
701 conv_item->modified = TRUE;
702 if ( (!CMP_ADDRESS(src, addr1)) && (!CMP_ADDRESS(dst, addr2)) && (src_port==port1) && (dst_port==port2) ) {
703 conv_item->tx_frames += num_frames;
704 conv_item->tx_bytes += num_bytes;
706 conv_item->rx_frames += num_frames;
707 conv_item->rx_bytes += num_bytes;
711 if (nstime_cmp(ts, &conv_item->stop_time) > 0) {
712 memcpy(&conv_item->stop_time, ts, sizeof(conv_item->stop_time));
713 } else if (nstime_cmp(ts, &conv_item->start_time) < 0) {
714 memcpy(&conv_item->start_time, ts, sizeof(conv_item->start_time));
715 memcpy(&conv_item->start_abs_time, abs_ts, sizeof(conv_item->start_abs_time));
721 * Compute the hash value for a given address/port pairs if the match
725 host_hash(gconstpointer v)
727 const host_key_t *key = (const host_key_t *)v;
731 ADD_ADDRESS_TO_HASH(hash_val, &key->myaddress);
732 hash_val += key->port;
737 * Compare two host keys for an exact match.
740 host_match(gconstpointer v, gconstpointer w)
742 const host_key_t *v1 = (const host_key_t *)v;
743 const host_key_t *v2 = (const host_key_t *)w;
745 if (v1->port == v2->port &&
746 ADDRESSES_EQUAL(&v1->myaddress, &v2->myaddress)) {
750 * The addresses or the ports don't match.
756 add_hostlist_table_data(conv_hash_t *ch, const address *addr, guint32 port, gboolean sender, int num_frames, int num_bytes, hostlist_dissector_info_t *host_info, port_type port_type_val)
758 hostlist_talker_t *talker=NULL;
761 /* XXX should be optimized to allocate n extra entries at a time
762 instead of just one */
763 /* if we don't have any entries at all yet */
764 if(ch->conv_array==NULL){
765 ch->conv_array=g_array_sized_new(FALSE, FALSE, sizeof(hostlist_talker_t), 10000);
766 ch->hashtable = g_hash_table_new_full(host_hash,
767 host_match, /* key_equal_func */
768 g_free, /* key_destroy_func */
769 NULL); /* value_destroy_func */
772 /* try to find it among the existing known conversations */
773 host_key_t existing_key;
774 gpointer talker_idx_hash_val;
776 existing_key.myaddress = *addr;
777 existing_key.port = port;
779 if (g_hash_table_lookup_extended(ch->hashtable, &existing_key, NULL, &talker_idx_hash_val)) {
780 talker = &g_array_index(ch->conv_array, hostlist_talker_t, GPOINTER_TO_UINT(talker_idx_hash_val));
784 /* if we still don't know what talker this is it has to be a new one
785 and we have to allocate it and append it to the end of the list */
788 hostlist_talker_t host;
790 COPY_ADDRESS(&host.myaddress, addr);
791 host.dissector_info = host_info;
792 host.ptype=port_type_val;
798 host.modified = TRUE;
800 g_array_append_val(ch->conv_array, host);
801 talker_idx= ch->conv_array->len - 1;
802 talker=&g_array_index(ch->conv_array, hostlist_talker_t, talker_idx);
804 /* hl->hosts address is not a constant but address.data is */
805 new_key = g_new(host_key_t,1);
806 SET_ADDRESS(&new_key->myaddress, talker->myaddress.type, talker->myaddress.len, talker->myaddress.data);
807 new_key->port = port;
808 g_hash_table_insert(ch->hashtable, new_key, GUINT_TO_POINTER(talker_idx));
811 /* if this is a new talker we need to initialize the struct */
812 talker->modified = TRUE;
814 /* update the talker struct */
816 talker->tx_frames+=num_frames;
817 talker->tx_bytes+=num_bytes;
819 talker->rx_frames+=num_frames;
820 talker->rx_bytes+=num_bytes;
830 * indent-tabs-mode: nil
833 * ex: set shiftwidth=4 tabstop=8 expandtab:
834 * :indentSize=4:tabSize=8:noTabs=true: