update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / ui / qt / models / atap_data_model.cpp
blob232c9a64cc4f2699b1a1b7f4541bf2b4db473d0b
1 /** @file
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
10 #include <epan/tap.h>
11 #include <epan/conversation.h>
12 #include <epan/conversation_table.h>
13 #include <epan/maxmind_db.h>
14 #include <epan/addr_resolv.h>
16 #include <wsutil/utf8_entities.h>
17 #include <wsutil/nstime.h>
18 #include <wsutil/str_util.h>
20 #include <ui/qt/utils/qt_ui_utils.h>
21 #include <ui/qt/utils/variant_pointer.h>
22 #include <ui/qt/main_application.h>
23 #include <ui/qt/models/atap_data_model.h>
24 #include <ui/qt/models/timeline_delegate.h>
26 #include <QSize>
27 #include <QVariant>
28 #include <QWidget>
29 #include <QDateTime>
31 static QString formatString(qlonglong value)
33 return QLocale().formattedDataSize(value, 0, QLocale::DataSizeSIFormat);
36 ATapDataModel::ATapDataModel(dataModelType type, int protoId, QString filter, QObject *parent):
37 QAbstractListModel(parent)
39 hash_.conv_array = nullptr;
40 hash_.hashtable = nullptr;
41 hash_.user_data = this;
43 storage_ = nullptr;
44 _resolveNames = false;
45 _absoluteTime = false;
46 _nanoseconds = false;
48 _protoId = protoId;
49 _filter = filter;
51 _minRelStartTime = 0;
52 _maxRelStopTime = 0;
54 _type = type;
55 _disableTap = true;
57 _tapFlags = TL_IGNORE_DISPLAY_FILTER+TL_IP_AGGREGATION_ORI;
59 QString _tap(proto_get_protocol_filter_name(protoId));
62 ATapDataModel::~ATapDataModel()
64 /* Only remove the tap if we come from a enabled model */
65 if (!_disableTap)
66 remove_tap_listener(hash());
68 if (_type == ATapDataModel::DATAMODEL_ENDPOINT)
69 reset_endpoint_table_data(&hash_);
70 else if (_type == ATapDataModel::DATAMODEL_CONVERSATION)
71 reset_conversation_table_data(&hash_);
74 int ATapDataModel::protoId() const
76 return _protoId;
79 QString ATapDataModel::tap() const
81 return proto_get_protocol_filter_name(_protoId);
84 #ifdef HAVE_MAXMINDDB
85 bool ATapDataModel::hasGeoIPData()
87 bool coordsFound = false;
88 int row = 0;
89 int count = rowCount();
90 while (!coordsFound && row < count)
92 QModelIndex idx = index(row, 0);
93 if (!data(idx, ATapDataModel::ROW_IS_FILTERED).toBool()) {
94 if (_type == ATapDataModel::DATAMODEL_ENDPOINT)
95 coordsFound = qobject_cast<EndpointDataModel *>(this)->data(idx, ATapDataModel::GEODATA_AVAILABLE).toBool();
96 else if (_type == ATapDataModel::DATAMODEL_CONVERSATION)
97 coordsFound = qobject_cast<ConversationDataModel *>(this)->data(idx, ATapDataModel::GEODATA_AVAILABLE).toBool();
99 row++;
102 return coordsFound;
104 #endif
106 bool ATapDataModel::enableTap()
108 /* We can't reenable a tap, so just return */
109 if (! _disableTap)
110 return true;
112 _disableTap = false;
114 /* The errorString is ignored. If this is not working, there is nothing really the user may do about
115 * it, so the error is only interesting to the developer.*/
116 GString * errorString = register_tap_listener(tap().toUtf8().constData(), hash(), _filter.toUtf8().constData(),
117 _tapFlags, &ATapDataModel::tapReset, conversationPacketHandler(), &ATapDataModel::tapDraw, nullptr);
118 if (errorString && errorString->len > 0) {
119 g_string_free(errorString, TRUE);
120 _disableTap = true;
121 emit tapListenerChanged(false);
122 return false;
125 if (errorString)
126 g_string_free(errorString, TRUE);
128 emit tapListenerChanged(true);
130 return true;
133 void ATapDataModel::disableTap()
135 /* Only remove the tap if we come from a enabled model */
136 if (!_disableTap)
137 remove_tap_listener(hash());
138 _disableTap = true;
139 emit tapListenerChanged(false);
142 void ATapDataModel::updateFlags(unsigned flag)
145 if(flag==0) {
146 _tapFlags |= TL_IP_AGGREGATION_ORI;
148 else {
149 _tapFlags &= ~(TL_IP_AGGREGATION_NULL|
150 TL_IP_AGGREGATION_ORI|
151 TL_IP_AGGREGATION_RESERVED);
153 set_tap_flags(&hash_, _tapFlags);
156 int ATapDataModel::rowCount(const QModelIndex &parent) const
158 return (storage_ && !parent.isValid()) ? (int) storage_->len : 0;
161 void ATapDataModel::tapReset(void *tapdata) {
162 if (! tapdata)
163 return;
165 conv_hash_t *hash = (conv_hash_t*)tapdata;
166 ATapDataModel * dataModel = qobject_cast<ATapDataModel *>((ATapDataModel *)hash->user_data);
168 dataModel->resetData();
171 void ATapDataModel::tapDraw(void *tapdata)
173 if (! tapdata)
174 return;
176 conv_hash_t *hash = (conv_hash_t*)tapdata;
177 ATapDataModel * dataModel = qobject_cast<ATapDataModel *>((ATapDataModel *)hash->user_data);
179 dataModel->updateData(hash->conv_array);
182 conv_hash_t * ATapDataModel::hash()
184 return &hash_;
187 register_ct_t * ATapDataModel::registerTable() const
189 if (_protoId > -1)
190 return get_conversation_by_proto_id(_protoId);
192 return nullptr;
195 tap_packet_cb ATapDataModel::conversationPacketHandler()
197 register_ct_t* table = registerTable();
198 if (table) {
199 if (_type == ATapDataModel::DATAMODEL_ENDPOINT)
200 return get_endpoint_packet_func(table);
201 else if (_type == ATapDataModel::DATAMODEL_CONVERSATION)
202 return get_conversation_packet_func(table);
205 return nullptr;
208 void ATapDataModel::resetData()
210 if (_disableTap)
211 return;
213 beginResetModel();
214 storage_ = nullptr;
215 if (_type == ATapDataModel::DATAMODEL_ENDPOINT)
216 reset_endpoint_table_data(&hash_);
217 else if (_type == ATapDataModel::DATAMODEL_CONVERSATION)
218 reset_conversation_table_data(&hash_);
220 _minRelStartTime = 0;
221 _maxRelStopTime = 0;
223 endResetModel();
226 void ATapDataModel::updateData(GArray * newData)
228 if (_disableTap)
229 return;
231 beginResetModel();
232 storage_ = newData;
233 endResetModel();
235 if (_type == ATapDataModel::DATAMODEL_CONVERSATION)
236 ((ConversationDataModel *)(this))->doDataUpdate();
239 bool ATapDataModel::resolveNames() const
241 return _resolveNames;
244 void ATapDataModel::setResolveNames(bool resolve)
246 if (_resolveNames == resolve)
247 return;
249 beginResetModel();
250 _resolveNames = resolve;
251 endResetModel();
254 bool ATapDataModel::allowsNameResolution() const
256 if (_protoId < 0)
257 return false;
259 QStringList mac_protos = QStringList() << "bluetooth" << "eth" << "fddi"
260 << "sll" << "tr" << "wlan";
261 QStringList net_protos = QStringList() << "dccp" << "ip" << "ipv6"
262 << "jxta" << "mptcp" << "ncp"
263 << "rsvp" << "sctp" << "sll"
264 << "tcp" << "udp";
265 QStringList transport_protos = QStringList() << "dccp" << "mptcp" << "sctp"
266 << "tcp" << "udp";
268 QString table_proto = proto_get_protocol_filter_name(_protoId);
270 if (mac_protos.contains(table_proto) && gbl_resolv_flags.mac_name)
271 return true;
272 if (net_protos.contains(table_proto) && gbl_resolv_flags.network_name)
273 return true;
274 if (transport_protos.contains(table_proto) && gbl_resolv_flags.transport_name)
275 return true;
277 return false;
280 void ATapDataModel::useAbsoluteTime(bool absolute)
282 if (absolute == _absoluteTime)
283 return;
285 beginResetModel();
286 _absoluteTime = absolute;
287 endResetModel();
290 void ATapDataModel::useNanosecondTimestamps(bool nanoseconds)
292 if (_nanoseconds == nanoseconds)
293 return;
295 beginResetModel();
296 _nanoseconds = nanoseconds;
297 endResetModel();
300 void ATapDataModel::setFilter(QString filter)
302 if (_disableTap)
303 return;
305 _filter = filter;
306 GString * errorString = set_tap_dfilter(&hash_, !_filter.isEmpty() ? _filter.toUtf8().constData() : nullptr);
307 if (errorString && errorString->len > 0) {
308 /* If this fails, chances are that the main system failed as well. Silently exiting as the
309 * user cannot react to it */
310 disableTap();
313 if (errorString)
314 g_string_free(errorString, TRUE);
317 QString ATapDataModel::filter() const
319 return _filter;
322 ATapDataModel::dataModelType ATapDataModel::modelType() const
324 return _type;
327 bool ATapDataModel::portsAreHidden() const
329 return (get_conversation_hide_ports(registerTable()));
332 bool ATapDataModel::showTotalColumn() const
334 /* Implemented to ensure future changes may be done more easily */
335 return _filter.length() > 0;
338 EndpointDataModel::EndpointDataModel(int protoId, QString filter, QObject *parent) :
339 ATapDataModel(ATapDataModel::DATAMODEL_ENDPOINT, protoId, filter, parent)
342 int EndpointDataModel::columnCount(const QModelIndex &) const
344 #ifdef HAVE_MAXMINDDB
345 int proto_ipv4 = proto_get_id_by_filter_name("ip");
346 int proto_ipv6 = proto_get_id_by_filter_name("ipv6");
347 if (protoId() == proto_ipv4 || protoId() == proto_ipv6) {
348 return ENDP_NUM_GEO_COLUMNS;
350 #endif
351 return ENDP_NUM_COLUMNS;
354 QVariant EndpointDataModel::headerData(int section, Qt::Orientation orientation, int role) const
356 if (orientation == Qt::Vertical)
357 return QVariant();
359 if (role == Qt::DisplayRole) {
360 switch (section) {
361 case ENDP_COLUMN_ADDR:
362 return tr("Address"); break;
363 case ENDP_COLUMN_PORT:
364 return tr("Port"); break;
365 case ENDP_COLUMN_PACKETS:
366 return tr("Packets"); break;
367 case ENDP_COLUMN_BYTES:
368 return tr("Bytes"); break;
369 case ENDP_COLUMN_PACKETS_TOTAL:
370 return tr("Total Packets"); break;
371 case ENDP_COLUMN_BYTES_TOTAL:
372 return tr("Percent Filtered"); break;
373 case ENDP_COLUMN_PKT_AB:
374 return tr("Tx Packets"); break;
375 case ENDP_COLUMN_BYTES_AB:
376 return tr("Tx Bytes"); break;
377 case ENDP_COLUMN_PKT_BA:
378 return tr("Rx Packets"); break;
379 case ENDP_COLUMN_BYTES_BA:
380 return tr("Rx Bytes"); break;
381 case ENDP_COLUMN_GEO_COUNTRY:
382 return tr("Country"); break;
383 case ENDP_COLUMN_GEO_CITY:
384 return tr("City"); break;
385 case ENDP_COLUMN_GEO_LATITUDE:
386 return tr("Latitude"); break;
387 case ENDP_COLUMN_GEO_LONGITUDE:
388 return tr("Longitude"); break;
389 case ENDP_COLUMN_GEO_AS_NUM:
390 return tr("AS Number"); break;
391 case ENDP_COLUMN_GEO_AS_ORG:
392 return tr("AS Organization"); break;
394 } else if (role == Qt::TextAlignmentRole) {
395 switch (section) {
396 case ENDP_COLUMN_ADDR:
397 case ENDP_COLUMN_GEO_COUNTRY:
398 case ENDP_COLUMN_GEO_CITY:
399 case ENDP_COLUMN_GEO_AS_ORG:
400 return Qt::AlignLeft;
401 default:
402 break;
404 return Qt::AlignRight;
407 return QVariant();
410 QVariant EndpointDataModel::data(const QModelIndex &idx, int role) const
412 if (! idx.isValid())
413 return QVariant();
415 // Column text cooked representation.
416 endpoint_item_t *item = &g_array_index(storage_, endpoint_item_t, idx.row());
417 const mmdb_lookup_t *mmdb_lookup = nullptr;
418 #ifdef HAVE_MAXMINDDB
419 char addr[WS_INET6_ADDRSTRLEN];
420 if (item->myaddress.type == AT_IPv4) {
421 const ws_in4_addr * ip4 = (const ws_in4_addr *) item->myaddress.data;
422 mmdb_lookup = maxmind_db_lookup_ipv4(ip4);
423 ws_inet_ntop4(ip4, addr, sizeof(addr));
424 } else if (item->myaddress.type == AT_IPv6) {
425 const ws_in6_addr * ip6 = (const ws_in6_addr *) item->myaddress.data;
426 mmdb_lookup = maxmind_db_lookup_ipv6(ip6);
427 ws_inet_ntop6(ip6, addr, sizeof(addr));
428 } else {
429 addr[0] = '\0';
431 QString ipAddress(addr);
432 #endif
434 if (role == Qt::DisplayRole || role == ATapDataModel::UNFORMATTED_DISPLAYDATA) {
435 switch (idx.column()) {
436 case ENDP_COLUMN_ADDR: {
437 char* addr_str = get_conversation_address(NULL, &item->myaddress, _resolveNames);
438 QString q_addr_str(addr_str);
439 wmem_free(NULL, addr_str);
440 return q_addr_str;
442 case ENDP_COLUMN_PORT:
443 if (_resolveNames) {
444 char* port_str = get_endpoint_port(NULL, item, _resolveNames);
445 QString q_port_str(port_str);
446 wmem_free(NULL, port_str);
447 return q_port_str;
448 } else {
449 return quint32(item->port);
451 case ENDP_COLUMN_PACKETS:
453 qlonglong packets = (qlonglong)(item->tx_frames + item->rx_frames);
454 return role == Qt::DisplayRole ? QStringLiteral("%L1").arg(packets) : (QVariant)packets;
456 case ENDP_COLUMN_BYTES:
457 return role == Qt::DisplayRole ? formatString((qlonglong)(item->tx_bytes + item->rx_bytes)) :
458 QVariant((qlonglong)(item->tx_bytes + item->rx_bytes));
459 case ENDP_COLUMN_PACKETS_TOTAL:
461 qlonglong packets = 0;
462 if (showTotalColumn())
463 packets = item->tx_frames_total + item->rx_frames_total;
464 return role == Qt::DisplayRole ? QStringLiteral("%L1").arg(packets) : (QVariant)packets;
466 case ENDP_COLUMN_BYTES_TOTAL:
468 double percent = 0;
469 if (showTotalColumn()) {
470 qlonglong totalPackets = (qlonglong)(item->tx_frames_total + item->rx_frames_total);
471 qlonglong packets = (qlonglong)(item->tx_frames + item->rx_frames);
472 percent = totalPackets == 0 ? 0 : (double) packets * 100 / (double) totalPackets;
474 QString rounded = QString::number(percent, 'f', 2);
475 /* Qt guarantees that this roundtrip conversion compares equally,
476 * so filtering with equality will work as expected.
477 * Perhaps the UNFORMATTED_DISPLAYDATA role shoud be split
478 * into one used for raw data export, and one used for comparisons.
480 return role == Qt::DisplayRole ? rounded + "%" : QVariant(rounded.toDouble());
482 case ENDP_COLUMN_PKT_AB:
483 return role == Qt::DisplayRole ? QStringLiteral("%L1").arg((qlonglong)item->tx_frames) : QVariant((qlonglong) item->tx_frames);
484 case ENDP_COLUMN_BYTES_AB:
485 return role == Qt::DisplayRole ? formatString((qlonglong)item->tx_bytes) : QVariant((qlonglong)item->tx_bytes);
486 case ENDP_COLUMN_PKT_BA:
487 return role == Qt::DisplayRole ? QStringLiteral("%L1").arg((qlonglong)item->rx_frames) : QVariant((qlonglong) item->rx_frames);
488 case ENDP_COLUMN_BYTES_BA:
489 return role == Qt::DisplayRole ? formatString((qlonglong)item->rx_bytes) : QVariant((qlonglong)item->rx_bytes);
490 case ENDP_COLUMN_GEO_COUNTRY:
491 if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->country) {
492 return QVariant(mmdb_lookup->country);
494 return QVariant();
495 case ENDP_COLUMN_GEO_CITY:
496 if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->city) {
497 return QVariant(mmdb_lookup->city);
499 return QVariant();
500 case ENDP_COLUMN_GEO_LATITUDE:
501 if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->latitude >= -90.0 && mmdb_lookup->latitude <= 90.0) {
502 return role == Qt::DisplayRole ? QStringLiteral("%L1%2").arg(mmdb_lookup->latitude).arg(UTF8_DEGREE_SIGN) : QVariant(mmdb_lookup->latitude);
504 return QVariant();
505 case ENDP_COLUMN_GEO_LONGITUDE:
506 if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->longitude >= -180.0 && mmdb_lookup->longitude <= 180.0) {
507 return role == Qt::DisplayRole ? QStringLiteral("%L1%2").arg(mmdb_lookup->longitude).arg(UTF8_DEGREE_SIGN) : QVariant(mmdb_lookup->longitude);
509 return QVariant();
510 case ENDP_COLUMN_GEO_AS_NUM:
511 if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->as_number) {
512 return QVariant(mmdb_lookup->as_number);
514 return QVariant();
515 case ENDP_COLUMN_GEO_AS_ORG:
516 if (mmdb_lookup && mmdb_lookup->found && mmdb_lookup->as_org) {
517 return QVariant(mmdb_lookup->as_org);
519 return QVariant();
520 default:
521 return QVariant();
523 } else if (role == Qt::TextAlignmentRole) {
524 switch (idx.column()) {
525 case ENDP_COLUMN_ADDR:
526 case ENDP_COLUMN_GEO_COUNTRY:
527 case ENDP_COLUMN_GEO_CITY:
528 case ENDP_COLUMN_GEO_AS_ORG:
529 return Qt::AlignLeft;
530 default:
531 break;
533 return Qt::AlignRight;
534 } else if (role == ATapDataModel::DISPLAY_FILTER) {
535 return QString(get_endpoint_filter(item));
536 } else if (role == ATapDataModel::ROW_IS_FILTERED) {
537 return (bool)item->filtered && showTotalColumn();
539 #ifdef HAVE_MAXMINDDB
540 else if (role == ATapDataModel::GEODATA_AVAILABLE) {
541 return (bool)(mmdb_lookup && maxmind_db_has_coords(mmdb_lookup));
542 } else if (role == ATapDataModel::GEODATA_LOOKUPTABLE) {
543 return VariantPointer<const mmdb_lookup_t>::asQVariant(mmdb_lookup);
544 } else if (role == ATapDataModel::GEODATA_ADDRESS) {
545 return ipAddress;
547 #endif
548 else if (role == ATapDataModel::PROTO_ID) {
549 return protoId();
550 } else if (role == ATapDataModel::DATA_ADDRESS_TYPE) {
551 if (idx.column() == EndpointDataModel::ENDP_COLUMN_ADDR)
552 return (int)item->myaddress.type;
553 return (int) AT_NONE;
554 } else if (role == ATapDataModel::DATA_IPV4_INTEGER || role == ATapDataModel::DATA_IPV6_LIST) {
555 if (idx.column() == EndpointDataModel::ENDP_COLUMN_ADDR) {
556 if (role == ATapDataModel::DATA_IPV4_INTEGER && item->myaddress.type == AT_IPv4) {
557 const ws_in4_addr * ip4 = (const ws_in4_addr *) item->myaddress.data;
558 return (quint32) GUINT32_FROM_BE(*ip4);
560 else if (role == ATapDataModel::DATA_IPV6_LIST && item->myaddress.type == AT_IPv6) {
561 const ws_in6_addr * ip6 = (const ws_in6_addr *) item->myaddress.data;
562 QList<quint8> result;
563 result.reserve(16);
564 std::copy(ip6->bytes + 0, ip6->bytes + 16, std::back_inserter(result));
565 return QVariant::fromValue(result);
570 return QVariant();
573 ConversationDataModel::ConversationDataModel(int protoId, QString filter, QObject *parent) :
574 ATapDataModel(ATapDataModel::DATAMODEL_CONVERSATION, protoId, filter, parent)
577 void ConversationDataModel::doDataUpdate()
579 _minRelStartTime = 0;
580 _maxRelStopTime = 0;
582 for (int row = 0; row < rowCount(); row ++) {
583 conv_item_t *conv_item = &g_array_index(storage_, conv_item_t, row);
585 if (row == 0) {
586 _minRelStartTime = nstime_to_sec(&(conv_item->start_time));
587 _maxRelStopTime = nstime_to_sec(&(conv_item->stop_time));
588 } else {
589 double item_rel_start = nstime_to_sec(&(conv_item->start_time));
590 if (item_rel_start < _minRelStartTime) {
591 _minRelStartTime = item_rel_start;
594 double item_rel_stop = nstime_to_sec(&(conv_item->stop_time));
595 if (item_rel_stop > _maxRelStopTime) {
596 _maxRelStopTime = item_rel_stop;
602 int ConversationDataModel::columnCount(const QModelIndex &) const
604 if(tap()=="tcp")
605 return CONV_TCP_EXT_NUM_COLUMNS;
607 return CONV_NUM_COLUMNS;
610 QVariant ConversationDataModel::headerData(int section, Qt::Orientation orientation, int role) const
612 if (orientation == Qt::Vertical)
613 return QVariant();
615 if (role == Qt::DisplayRole) {
616 switch (section) {
617 case CONV_COLUMN_SRC_ADDR:
618 return tr("Address A"); break;
619 case CONV_COLUMN_SRC_PORT:
620 return tr("Port A"); break;
621 case CONV_COLUMN_DST_ADDR:
622 return tr("Address B"); break;
623 case CONV_COLUMN_DST_PORT:
624 return tr("Port B"); break;
625 case CONV_COLUMN_PACKETS:
626 return tr("Packets"); break;
627 case CONV_COLUMN_BYTES:
628 return tr("Bytes"); break;
629 case CONV_COLUMN_CONV_ID:
630 return tr("Stream ID"); break;
631 case CONV_COLUMN_PACKETS_TOTAL:
632 return tr("Total Packets"); break;
633 case CONV_COLUMN_BYTES_TOTAL:
634 return tr("Percent Filtered"); break;
635 case CONV_COLUMN_PKT_AB:
636 return tr("Packets A " UTF8_RIGHTWARDS_ARROW " B"); break;
637 case CONV_COLUMN_BYTES_AB:
638 return tr("Bytes A " UTF8_RIGHTWARDS_ARROW " B"); break;
639 case CONV_COLUMN_PKT_BA:
640 return tr("Packets B " UTF8_RIGHTWARDS_ARROW " A"); break;
641 case CONV_COLUMN_BYTES_BA:
642 return tr("Bytes B " UTF8_RIGHTWARDS_ARROW " A"); break;
643 case CONV_COLUMN_START:
644 return _absoluteTime ? tr("Abs Start") : tr("Rel Start"); break;
645 case CONV_COLUMN_DURATION:
646 return tr("Duration"); break;
647 case CONV_COLUMN_BPS_AB:
648 return tr("Bits/s A " UTF8_RIGHTWARDS_ARROW " B"); break;
649 case CONV_COLUMN_BPS_BA:
650 return tr("Bits/s B " UTF8_RIGHTWARDS_ARROW " A"); break;
652 /* Extended conversations columns, e.g. TCP */
653 if(tap()=="tcp") {
654 switch (section) {
655 case CONV_TCP_EXT_COLUMN_A:
656 return tr("Flows"); break;
657 default :
658 ws_assert_not_reached(); break;
661 } else if (role == Qt::TextAlignmentRole) {
662 if (section == CONV_COLUMN_SRC_ADDR || section == CONV_COLUMN_DST_ADDR)
663 return Qt::AlignLeft;
664 return Qt::AlignRight;
667 return QVariant();
670 static const double min_bw_calc_duration_ = 5 / 1000.0; // seconds
672 QVariant ConversationDataModel::data(const QModelIndex &idx, int role) const
674 if (! idx.isValid())
675 return QVariant();
677 // Column text cooked representation.
678 conv_item_t *conv_item = (conv_item_t *)&g_array_index(storage_, conv_item_t,idx.row());
680 double duration = nstime_to_sec(&conv_item->stop_time) - nstime_to_sec(&conv_item->start_time);
681 double bps_ab = 0, bps_ba = 0;
682 bool bpsCalculated = false;
683 if (duration > min_bw_calc_duration_) {
684 bps_ab = conv_item->tx_bytes * 8 / duration;
685 bps_ba = conv_item->rx_bytes * 8 / duration;
686 bpsCalculated = true;
689 if (role == Qt::DisplayRole || role == ATapDataModel::UNFORMATTED_DISPLAYDATA) {
690 switch(idx.column()) {
691 case CONV_COLUMN_SRC_ADDR:
693 char* addr_str = get_conversation_address(NULL, &conv_item->src_address, _resolveNames);
694 QString q_addr_str(addr_str);
695 wmem_free(NULL, addr_str);
696 return q_addr_str;
698 case CONV_COLUMN_SRC_PORT:
699 if (_resolveNames) {
700 char* port_str = get_conversation_port(NULL, conv_item->src_port, conv_item->ctype, _resolveNames);
701 QString q_port_str(port_str);
702 wmem_free(NULL, port_str);
703 return q_port_str;
704 } else {
705 return quint32(conv_item->src_port);
707 case CONV_COLUMN_DST_ADDR:
709 char* addr_str = get_conversation_address(NULL, &conv_item->dst_address, _resolveNames);
710 QString q_addr_str(addr_str);
711 wmem_free(NULL, addr_str);
712 return q_addr_str;
714 case CONV_COLUMN_DST_PORT:
715 if (_resolveNames) {
716 char* port_str = get_conversation_port(NULL, conv_item->dst_port, conv_item->ctype, _resolveNames);
717 QString q_port_str(port_str);
718 wmem_free(NULL, port_str);
719 return q_port_str;
720 } else {
721 return quint32(conv_item->dst_port);
723 case CONV_COLUMN_PACKETS:
725 qlonglong packets = conv_item->tx_frames + conv_item->rx_frames;
726 return role == Qt::DisplayRole ? QStringLiteral("%L1").arg(packets) : (QVariant)packets;
728 case CONV_COLUMN_BYTES:
729 return role == Qt::DisplayRole ? formatString((qlonglong)conv_item->tx_bytes + conv_item->rx_bytes) :
730 QVariant((qlonglong)conv_item->tx_bytes + conv_item->rx_bytes);
731 case CONV_COLUMN_CONV_ID:
732 if(conv_item->conv_id!=CONV_ID_UNSET) {
733 return (int) conv_item->conv_id;
735 break;
736 case CONV_COLUMN_PACKETS_TOTAL:
738 qlonglong packets = 0;
739 if (showTotalColumn())
740 packets = conv_item->tx_frames_total + conv_item->rx_frames_total;
742 return role == Qt::DisplayRole ? QStringLiteral("%L1").arg(packets) : (QVariant)packets;
744 case CONV_COLUMN_BYTES_TOTAL:
746 double percent = 0;
747 if (showTotalColumn()) {
748 qlonglong totalPackets = (qlonglong)(conv_item->tx_frames_total + conv_item->rx_frames_total);
749 qlonglong packets = (qlonglong)(conv_item->tx_frames + conv_item->rx_frames);
750 percent = totalPackets == 0 ? 0 : (double) packets * 100 / (double) totalPackets;
752 QString rounded = QString::number(percent, 'f', 2);
753 /* Qt guarantees that this roundtrip conversion compares equally,
754 * so filtering with equality will work as expected.
755 * XXX: Perhaps the UNFORMATTED_DISPLAYDATA role shoud be split
756 * into one used for raw data export and comparisons with each
757 * other, and another for comparing with filters?
759 return role == Qt::DisplayRole ? rounded + "%" : QVariant(rounded.toDouble());
761 case CONV_COLUMN_PKT_AB:
763 qlonglong packets = conv_item->tx_frames;
764 return role == Qt::DisplayRole ? QStringLiteral("%L1").arg(packets) : (QVariant)packets;
766 case CONV_COLUMN_BYTES_AB:
767 return role == Qt::DisplayRole ? formatString((qlonglong)conv_item->tx_bytes) : QVariant((qlonglong)conv_item->tx_bytes);
768 case CONV_COLUMN_PKT_BA:
770 qlonglong packets = conv_item->rx_frames;
771 return role == Qt::DisplayRole ? QStringLiteral("%L1").arg(packets) : (QVariant)packets;
773 case CONV_COLUMN_BYTES_BA:
774 return role == Qt::DisplayRole ? formatString((qlonglong)conv_item->rx_bytes) : QVariant((qlonglong)conv_item->rx_bytes);
775 case CONV_COLUMN_START:
777 int width = _nanoseconds ? 9 : 6;
779 if (_absoluteTime) {
780 nstime_t *abs_time = &conv_item->start_abs_time;
781 /* XXX: QDateTime only supports millisecond resolution,
782 * and we have microseconds or nanoseconds.
783 * Should we use something else, particularly for exporting
784 * raw data? GDateTime handles microseconds.
786 QDateTime abs_dt = QDateTime::fromMSecsSinceEpoch(nstime_to_msec(abs_time));
787 if (role == Qt::DisplayRole) {
788 if (_maxRelStopTime >= 24*60*60) {
789 return abs_dt.toString(Qt::ISODateWithMs);
790 } else {
791 return abs_dt.time().toString(Qt::ISODateWithMs);
793 } else {
794 return QVariant(abs_dt);
796 } else {
797 return role == Qt::DisplayRole ?
798 QString::number(nstime_to_sec(&conv_item->start_time), 'f', width) :
799 (QVariant)((double) nstime_to_sec(&conv_item->start_time));
802 case CONV_COLUMN_DURATION:
804 int width = _nanoseconds ? 6 : 4;
805 return role == Qt::DisplayRole ? QString::number(duration, 'f', width) : (QVariant)duration;
807 case CONV_COLUMN_BPS_AB:
808 return bpsCalculated ? (role == Qt::DisplayRole ? gchar_free_to_qstring(format_size((int64_t)bps_ab, FORMAT_SIZE_UNIT_BITS_S, FORMAT_SIZE_PREFIX_SI)) : QVariant((qlonglong)bps_ab)): QVariant();
809 case CONV_COLUMN_BPS_BA:
810 return bpsCalculated ? (role == Qt::DisplayRole ? gchar_free_to_qstring(format_size((int64_t)bps_ba, FORMAT_SIZE_UNIT_BITS_S, FORMAT_SIZE_PREFIX_SI)) : QVariant((qlonglong)bps_ba)): QVariant();
812 /* Extended conversations columns, e.g. TCP */
813 if(tap()=="tcp") {
814 switch(idx.column()) {
815 case CONV_TCP_EXT_COLUMN_A:
817 qlonglong flows = (qlonglong)conv_item->ext_tcp.flows;
818 return role == Qt::DisplayRole ? QStringLiteral("%L1").arg(flows) : (QVariant)flows; break;
820 default :
821 ws_assert_not_reached(); break;
824 } else if (role == Qt::ToolTipRole) {
825 if (idx.column() == CONV_COLUMN_START || idx.column() == CONV_COLUMN_DURATION)
826 return QObject::tr("Bars show the relative timeline for each conversation.");
827 } else if (role == Qt::TextAlignmentRole) {
828 if (idx.column() == CONV_COLUMN_SRC_ADDR || idx.column() == CONV_COLUMN_DST_ADDR)
829 return Qt::AlignLeft;
830 return Qt::AlignRight;
831 } else if (role == ATapDataModel::TIMELINE_DATA) {
832 struct timeline_span span_data;
833 span_data.minRelTime = _minRelStartTime;
834 span_data.maxRelTime = _maxRelStopTime;
835 span_data.startTime = nstime_to_sec(&conv_item->start_time);
836 span_data.stopTime = nstime_to_sec(&conv_item->stop_time);
837 span_data.colStart = CONV_COLUMN_START;
838 span_data.colDuration = CONV_COLUMN_DURATION;
840 if ((_maxRelStopTime - _minRelStartTime) > 0) {
841 return QVariant::fromValue(span_data);
843 } else if (role == ATapDataModel::ENDPOINT_DATATYPE) {
844 return (int)(conv_item->ctype);
845 } else if (role == ATapDataModel::PROTO_ID) {
846 return protoId();
847 } else if (role == ATapDataModel::CONVERSATION_ID) {
848 return (int)(conv_item->conv_id);
849 } else if (role == ATapDataModel::ROW_IS_FILTERED) {
850 return (bool)conv_item->filtered && showTotalColumn();
851 } else if (role == ATapDataModel::DATA_ADDRESS_TYPE) {
852 if (idx.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR || idx.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) {
853 address tst_address = idx.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR ? conv_item->src_address : conv_item->dst_address;
854 return (int)tst_address.type;
856 return (int) AT_NONE;
857 } else if (role == ATapDataModel::DATA_IPV4_INTEGER || role == ATapDataModel::DATA_IPV6_LIST) {
858 if (idx.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR || idx.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) {
859 address tst_address = idx.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR ? conv_item->src_address : conv_item->dst_address;
860 if (role == ATapDataModel::DATA_IPV4_INTEGER && tst_address.type == AT_IPv4) {
861 const ws_in4_addr * ip4 = (const ws_in4_addr *) tst_address.data;
862 return (quint32) GUINT32_FROM_BE(*ip4);
864 else if (role == ATapDataModel::DATA_IPV6_LIST && tst_address.type == AT_IPv6) {
865 const ws_in6_addr * ip6 = (const ws_in6_addr *) tst_address.data;
866 QList<quint8> result;
867 result.reserve(16);
868 std::copy(ip6->bytes + 0, ip6->bytes + 16, std::back_inserter(result));
869 return QVariant::fromValue(result);
874 return QVariant();
877 conv_item_t * ConversationDataModel::itemForRow(int row)
879 if (row < 0 || row >= rowCount())
880 return nullptr;
881 return (conv_item_t *)&g_array_index(storage_, conv_item_t, row);
884 bool ConversationDataModel::showConversationId(int row) const
886 if (!storage_ || row < 0 || row >= (int) storage_->len)
887 return false;
889 conv_item_t *conv_item = (conv_item_t *)&g_array_index(storage_, conv_item_t, row);
890 if (conv_item && (conv_item->ctype == CONVERSATION_TCP ||
891 conv_item->ctype == CONVERSATION_UDP ||
892 conv_item->ctype == CONVERSATION_IP ||
893 conv_item->ctype == CONVERSATION_IPV6||
894 conv_item->ctype == CONVERSATION_ETH))
895 return true;
896 return false;