Removed duplicated #include lines
[metze/wireshark/wip.git] / ui / qt / follow_stream_dialog.cpp
1 /* follow_stream_dialog.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "follow_stream_dialog.h"
23 #include "ui_follow_stream_dialog.h"
24
25 #include "main_window.h"
26 #include "wireshark_application.h"
27
28 #include "epan/follow.h"
29 #include "epan/dissectors/packet-tcp.h"
30 #include "epan/prefs.h"
31 #include "epan/addr_resolv.h"
32 #include "epan/charsets.h"
33 #include "epan/epan_dissect.h"
34 #include "epan/tap.h"
35
36 #include "ui/alert_box.h"
37 #include "ui/simple_dialog.h"
38 #include "ui/utf8_entities.h"
39
40 #include "wsutil/tempfile.h"
41 #include "wsutil/file_util.h"
42 #include "wsutil/str_util.h"
43
44 #include "ws_symbol_export.h"
45
46 #include "color_utils.h"
47
48 #include "version_info.h"
49
50 #include "ui/follow.h"
51
52 #include "qt_ui_utils.h"
53
54 #include <QKeyEvent>
55 #include <QMessageBox>
56 #include <QPrintDialog>
57 #include <QPrinter>
58 #include <QTextEdit>
59 #include <QTextStream>
60
61 // To do:
62 // - Instead of calling QMessageBox, display the error message in the text
63 //   box and disable the appropriate controls.
64 // - Add stream number for UDP.
65 // - Draw text by hand similar to ByteViewText. This would let us add
66 //   extra information, e.g. a timestamp column and get rid of the data
67 //   limit.
68
69 FollowStreamDialog::FollowStreamDialog(QWidget *parent, follow_type_t type, capture_file *cf) :
70     QDialog(parent),
71     ui(new Ui::FollowStreamDialog),
72     cap_file_(cf),
73     follow_type_(type),
74     truncated_(false),
75     save_as_(false)
76 {
77     ui->setupUi(this);
78
79     setAttribute(Qt::WA_DeleteOnClose, true);
80     memset(&follow_info_, 0, sizeof(follow_info_));
81
82     ui->teStreamContent->installEventFilter(this);
83
84     // XXX Use recent settings instead
85     if (parent) {
86         resize(parent->width() * 2 / 3, parent->height());
87     }
88
89     QComboBox *cbcs = ui->cbCharset;
90     cbcs->blockSignals(true);
91     cbcs->addItem(tr("ASCII"), SHOW_ASCII);
92     cbcs->addItem(tr("C Arrays"), SHOW_CARRAY);
93     cbcs->addItem(tr("EBCDIC"), SHOW_EBCDIC);
94     cbcs->addItem(tr("Hex Dump"), SHOW_HEXDUMP);
95     cbcs->addItem(tr("UTF-8"), SHOW_RAW);
96     cbcs->addItem(tr("YAML"), SHOW_YAML);
97     cbcs->blockSignals(false);
98
99     b_filter_out_ = ui->buttonBox->addButton(tr("Hide this stream"), QDialogButtonBox::ActionRole);
100     connect(b_filter_out_, SIGNAL(clicked()), this, SLOT(filterOut()));
101
102     b_print_ = ui->buttonBox->addButton(tr("Print"), QDialogButtonBox::ActionRole);
103     connect(b_print_, SIGNAL(clicked()), this, SLOT(printStream()));
104
105     b_save_ = ui->buttonBox->addButton(tr("Save as..."), QDialogButtonBox::ActionRole);
106     connect(b_save_, SIGNAL(clicked()), this, SLOT(saveAs()));
107
108     connect(ui->buttonBox, SIGNAL(helpRequested()), this, SLOT(helpButton()));
109     connect(ui->teStreamContent, SIGNAL(mouseMovedToTextCursorPosition(int)),
110             this, SLOT(fillHintLabel(int)));
111     connect(ui->teStreamContent, SIGNAL(mouseClickedOnTextCursorPosition(int)),
112             this, SLOT(goToPacketForTextPos(int)));
113
114     fillHintLabel(-1);
115 }
116
117 FollowStreamDialog::~FollowStreamDialog()
118 {
119     delete ui;
120     resetStream(); // Frees payload
121 }
122
123 void FollowStreamDialog::printStream()
124 {
125 #ifndef QT_NO_PRINTER
126     QPrinter printer(QPrinter::HighResolution);
127     QPrintDialog dialog(&printer, this);
128     if ( dialog.exec() == QDialog::Accepted)
129         ui->teStreamContent->print(&printer);
130 #endif
131 }
132
133 void FollowStreamDialog::fillHintLabel(int text_pos)
134 {
135     QString hint;
136     int pkt = -1;
137
138     if (text_pos >= 0) {
139         QMap<int, guint32>::iterator it = text_pos_to_packet_.upperBound(text_pos);
140         if (it != text_pos_to_packet_.end()) {
141             pkt = it.value();
142         }
143     }
144
145     if (pkt > 0) {
146         hint = QString(tr("Packet %1. ")).arg(pkt);
147     }
148
149     hint += tr("%Ln <span style=\"color: %1; background-color:%2\">client</span> pkt(s), ", "", client_packet_count_)
150             .arg(ColorUtils::fromColorT(prefs.st_client_fg).name())
151             .arg(ColorUtils::fromColorT(prefs.st_client_bg).name())
152             + tr("%Ln <span style=\"color: %1; background-color:%2\">server</span> pkt(s), ", "", server_packet_count_)
153             .arg(ColorUtils::fromColorT(prefs.st_server_fg).name())
154             .arg(ColorUtils::fromColorT(prefs.st_server_bg).name())
155             + tr("%Ln turn(s).", "", turns_);
156
157     if (pkt > 0) {
158         hint.append(QString(tr(" Click to select.")));
159     }
160
161     hint.prepend("<small><i>");
162     hint.append("</i></small>");
163     ui->hintLabel->setText(hint);
164 }
165
166 void FollowStreamDialog::goToPacketForTextPos(int text_pos)
167 {
168     int pkt = -1;
169     if (!cap_file_) {
170         return;
171     }
172
173     if (text_pos >= 0) {
174         QMap<int, guint32>::iterator it = text_pos_to_packet_.upperBound(text_pos);
175         if (it != text_pos_to_packet_.end()) {
176             pkt = it.value();
177         }
178     }
179
180     if (pkt > 0) {
181         emit goToPacket(pkt);
182     }
183 }
184
185 void FollowStreamDialog::updateWidgets(bool enable)
186 {
187     if (!cap_file_) {
188         enable = false;
189         ui->streamNumberSpinBox->setToolTip(QString());
190         ui->streamNumberLabel->setToolTip(QString());
191     }
192     ui->teStreamContent->setEnabled(enable);
193     ui->cbDirections->setEnabled(enable);
194     ui->cbCharset->setEnabled(enable);
195     ui->streamNumberSpinBox->setEnabled(enable);
196     ui->leFind->setEnabled(enable);
197     ui->bFind->setEnabled(enable);
198     b_filter_out_->setEnabled(enable);
199 }
200
201 void FollowStreamDialog::findText(bool go_back)
202 {
203     if (ui->leFind->text().isEmpty()) return;
204
205     bool found = ui->teStreamContent->find(ui->leFind->text());
206
207     if (found) {
208         ui->teStreamContent->setFocus();
209     } else if (go_back) {
210         ui->teStreamContent->moveCursor(QTextCursor::Start);
211         findText(false);
212     }
213 }
214
215 void FollowStreamDialog::saveAs()
216 {
217     QString file_name = QFileDialog::getSaveFileName(this, tr("Wireshark: Save stream content as"));
218     file_.setFileName(file_name);
219     file_.open( QIODevice::WriteOnly );
220     QTextStream out(&file_);
221
222     save_as_ = true;
223
224     readStream();
225
226     if (follow_info_.show_type != SHOW_RAW)
227     {
228         out << ui->teStreamContent->toPlainText();
229     }
230
231     save_as_ = false;
232
233     file_.close();
234 }
235
236 void FollowStreamDialog::helpButton()
237 {
238     wsApp->helpTopicAction(HELP_FOLLOW_STREAM_DIALOG);
239 }
240
241 void FollowStreamDialog::filterOut()
242 {
243
244     emit updateFilter(filter_out_filter_, TRUE);
245
246     this->close();
247 }
248
249 void FollowStreamDialog::on_cbDirections_currentIndexChanged(int index)
250 {
251     switch(index)
252     {
253     case 0 :
254         follow_info_.show_stream = BOTH_HOSTS;
255         break;
256     case 1 :
257         follow_info_.show_stream = FROM_SERVER;
258         break;
259     case 2 :
260         follow_info_.show_stream = FROM_CLIENT;
261         break;
262     default:
263         return;
264     }
265
266     readStream();
267 }
268
269 void FollowStreamDialog::on_cbCharset_currentIndexChanged(int index)
270 {
271     if (index < 0) return;
272     follow_info_.show_type = static_cast<show_type_t>(ui->cbCharset->itemData(index).toInt());
273     readStream();
274 }
275
276 void FollowStreamDialog::on_bFind_clicked()
277 {
278     findText();
279 }
280
281 void FollowStreamDialog::on_leFind_returnPressed()
282 {
283     findText();
284 }
285
286 void FollowStreamDialog::on_streamNumberSpinBox_valueChanged(int stream_num)
287 {
288     if (stream_num >= 0) {
289         updateWidgets(false);
290         follow_tcp_index(stream_num);
291         follow(QString(), true);
292         updateWidgets();
293     }
294 }
295
296 // Not sure why we have to do this manually.
297 void FollowStreamDialog::on_buttonBox_rejected()
298 {
299     reject();
300 }
301
302 void FollowStreamDialog::removeStreamControls()
303 {
304     ui->horizontalLayout->removeItem(ui->streamNumberSpacer);
305     ui->streamNumberLabel->setVisible(false);
306     ui->streamNumberSpinBox->setVisible(false);
307 }
308
309 void FollowStreamDialog::resetStream()
310 {
311     GList *cur;
312
313     filter_out_filter_.clear();
314     text_pos_to_packet_.clear();
315     if (!data_out_filename_.isEmpty()) {
316         ws_unlink(data_out_filename_.toUtf8().constData());
317     }
318     if (data_out_file) {
319         fclose(data_out_file);
320         data_out_file = NULL;
321     }
322     for (cur = follow_info_.payload; cur; cur = g_list_next(cur)) {
323         g_free(cur->data);
324     }
325     g_list_free(follow_info_.payload);
326     follow_info_.payload = NULL;
327 }
328
329 frs_return_t
330 FollowStreamDialog::readStream()
331 {
332     ui->teStreamContent->clear();
333     truncated_ = false;
334     frs_return_t ret;
335
336     client_buffer_count_ = 0;
337     server_buffer_count_ = 0;
338     client_packet_count_ = 0;
339     server_packet_count_ = 0;
340     last_packet_ = 0;
341     turns_ = 0;
342
343     switch(follow_type_) {
344
345     case FOLLOW_TCP :
346         ret = readTcpStream();
347         break;
348
349     case FOLLOW_UDP :
350         ret = readUdpStream();
351         break;
352
353     case FOLLOW_SSL :
354         ret = readSslStream();
355         break;
356
357     default :
358         g_assert_not_reached();
359         ret = (frs_return_t)0;
360         break;
361     }
362     ui->teStreamContent->moveCursor(QTextCursor::Start);
363     return ret;
364 }
365
366 //Copy from ui/gtk/follow_udp.c
367 static int
368 udp_queue_packet_data(void *tapdata, packet_info *pinfo,
369                       epan_dissect_t *edt, const void *data)
370 {
371     Q_UNUSED(edt);
372
373     follow_record_t *follow_record;
374     follow_info_t *follow_info = (follow_info_t *)tapdata;
375     tvbuff_t *next_tvb = (tvbuff_t *)data;
376
377     follow_record = g_new(follow_record_t,1);
378
379     follow_record->data = g_byte_array_sized_new(tvb_length(next_tvb));
380     follow_record->data = g_byte_array_append(follow_record->data,
381                                               tvb_get_ptr(next_tvb, 0, -1),
382                                               tvb_length(next_tvb));
383     follow_record->packet_num = pinfo->fd->num;
384
385     if (follow_info->client_port == 0) {
386         follow_info->client_port = pinfo->srcport;
387         copy_address(&follow_info->client_ip, &pinfo->src);
388     }
389
390     if (addresses_equal(&follow_info->client_ip, &pinfo->src) && follow_info->client_port == pinfo->srcport)
391         follow_record->is_server = FALSE;
392     else
393         follow_record->is_server = TRUE;
394
395     /* update stream counter */
396     follow_info->bytes_written[follow_record->is_server] += follow_record->data->len;
397
398     follow_info->payload = g_list_append(follow_info->payload, follow_record);
399     return 0;
400 }
401
402 //Copy from ui/gtk/follow_ssl.c
403 static int
404 ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *edt, const void *ssl)
405 {
406     Q_UNUSED(edt);
407
408     follow_info_t *      follow_info = (follow_info_t*) tapdata;
409     SslDecryptedRecord * rec = NULL;
410     SslDataInfo *        appl_data = NULL;
411     int                  proto_ssl = GPOINTER_TO_INT(ssl);
412     SslPacketInfo *      pi = NULL;
413     show_stream_t        from = FROM_CLIENT;
414
415     /* Skip packets without decrypted payload data. */
416     pi = (SslPacketInfo*) p_get_proto_data(wmem_file_scope(), pinfo, proto_ssl, 0);
417     if (!pi || !pi->appl_data) return 0;
418
419     /* Compute the packet's sender. */
420     if (follow_info->client_port == 0) {
421         follow_info->client_port = pinfo->srcport;
422         copy_address(&follow_info->client_ip, &pinfo->src);
423     }
424     if (ADDRESSES_EQUAL(&follow_info->client_ip, &pinfo->src) &&
425             follow_info->client_port == pinfo->srcport) {
426         from = FROM_CLIENT;
427     } else {
428         from = FROM_SERVER;
429     }
430
431     for (appl_data = pi->appl_data; appl_data != NULL; appl_data = appl_data->next) {
432
433         /* TCP segments that contain the end of two or more SSL PDUs will be
434            queued to SSL taps for each of those PDUs. Therefore a single
435            packet could be processed by this SSL tap listener multiple times.
436            The following test handles that scenario by treating the
437            follow_info->bytes_written[] values as the next expected
438            appl_data->seq. Any appl_data instances that fall below that have
439            already been processed and must be skipped. */
440         if (appl_data->seq < follow_info->bytes_written[from]) continue;
441
442         /* Allocate a SslDecryptedRecord to hold the current appl_data
443            instance's decrypted data. Even though it would be possible to
444            consolidate multiple appl_data instances into a single rec, it is
445            beneficial to use a one-to-one mapping. This affords the Follow
446            Stream dialog view modes (ASCII, EBCDIC, Hex Dump, C Arrays, Raw)
447            the opportunity to accurately reflect SSL PDU boundaries. Currently
448            the Hex Dump view does by starting a new line, and the C Arrays
449            view does by starting a new array declaration. */
450         rec = (SslDecryptedRecord*) g_malloc(sizeof(SslDecryptedRecord) + appl_data->plain_data.data_len);
451         rec->is_from_server = from == FROM_SERVER;
452         rec->packet_num = pinfo->fd->num;
453         rec->data.data = (guchar*) (rec + 1);
454         rec->data.data_len = appl_data->plain_data.data_len;
455         memcpy(rec->data.data, appl_data->plain_data.data, appl_data->plain_data.data_len);
456
457         /* Append the record to the follow_info structure. */
458         follow_info->payload = g_list_append(follow_info->payload, rec);
459         follow_info->bytes_written[from] += rec->data.data_len;
460     }
461
462     return 0;
463 }
464
465 /*
466  * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
467  * it gets handed bufferfuls.  That's fine for "follow_write_raw()"
468  * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
469  * the "print_line()" routine from "print.c", and as that routine might
470  * genuinely expect to be handed a line (if, for example, it's using
471  * some OS or desktop environment's printing API, and that API expects
472  * to be handed lines), "follow_print_text()" should probably accumulate
473  * lines in a buffer and hand them "print_line()".  (If there's a
474  * complete line in a buffer - i.e., there's nothing of the line in
475  * the previous buffer or the next buffer - it can just hand that to
476  * "print_line()" after filtering out non-printables, as an
477  * optimization.)
478  *
479  * This might or might not be the reason why C arrays display
480  * correctly but get extra blank lines very other line when printed.
481  */
482 frs_return_t
483 FollowStreamDialog::readSslStream()
484 {
485     guint32      global_client_pos = 0, global_server_pos = 0;
486     guint32 *    global_pos;
487     GList *      cur;
488     frs_return_t frs_return;
489
490     for (cur = follow_info_.payload; cur; cur = g_list_next(cur)) {
491         SslDecryptedRecord * rec = (SslDecryptedRecord*) cur->data;
492         gboolean             include_rec = FALSE;
493
494         if (rec->is_from_server) {
495             global_pos = &global_server_pos;
496             include_rec = (follow_info_.show_stream == BOTH_HOSTS) ||
497                     (follow_info_.show_stream == FROM_SERVER);
498         } else {
499             global_pos = &global_client_pos;
500             include_rec = (follow_info_.show_stream == BOTH_HOSTS) ||
501                     (follow_info_.show_stream == FROM_CLIENT);
502         }
503
504         if (include_rec) {
505             size_t nchars = rec->data.data_len;
506             gchar *buffer = (gchar *)g_memdup(rec->data.data, (guint) nchars);
507
508             frs_return = showBuffer(buffer, nchars,
509                                      rec->is_from_server, rec->packet_num, global_pos);
510             g_free(buffer);
511             if (frs_return == FRS_PRINT_ERROR)
512                 return frs_return;
513         }
514     }
515
516     return FRS_OK;
517 }
518
519 void
520 FollowStreamDialog::followStream()
521 {
522     follow_stats_t stats;
523
524     follow_info_.show_type = SHOW_ASCII;
525     follow_info_.show_stream = BOTH_HOSTS;
526
527     /* Stream to show */
528     follow_stats(&stats);
529
530     follow_info_.is_ipv6 = stats.is_ipv6;
531
532     readStream();
533 }
534
535
536
537 const int FollowStreamDialog::max_document_length_ = 2 * 1000 * 1000; // Just a guess
538 void FollowStreamDialog::addText(QString text, gboolean is_from_server, guint32 packet_num)
539 {
540     if (save_as_ == true)
541     {
542         //FILE *fh = (FILE *)arg;
543         size_t nwritten;
544         int FileDescriptor = file_.handle();
545         FILE* fh = fdopen(dup(FileDescriptor), "wb");
546         nwritten = fwrite(text.toUtf8().constData(), text.length(), 1, fh);
547         fclose(fh);
548         if ((int)nwritten != text.length()) {
549 #if 0
550             report_an_error_maybe();
551 #endif
552         }
553         return;
554     }
555
556     if (truncated_) {
557         return;
558     }
559
560     int char_count = ui->teStreamContent->document()->characterCount();
561     if (char_count + text.length() > max_document_length_) {
562         text.truncate(max_document_length_ - char_count);
563         truncated_ = true;
564     }
565
566     QColor tagserver_fg = ColorUtils::fromColorT(prefs.st_server_fg);
567     QColor tagserver_bg = ColorUtils::fromColorT(prefs.st_server_bg);
568
569     QColor tagclient_fg = ColorUtils::fromColorT(prefs.st_client_fg);
570     QColor tagclient_bg = ColorUtils::fromColorT(prefs.st_client_bg);
571
572     ui->teStreamContent->moveCursor(QTextCursor::End);
573     ui->teStreamContent->setCurrentFont(wsApp->monospaceFont());
574     if (is_from_server)
575     {
576         ui->teStreamContent->setTextColor(tagserver_fg);
577         ui->teStreamContent->setTextBackgroundColor(tagserver_bg);
578     }
579     else
580     {
581         ui->teStreamContent->setTextColor(tagclient_fg);
582         ui->teStreamContent->setTextBackgroundColor(tagclient_bg);
583     }
584     ui->teStreamContent->insertPlainText(text);
585     ui->teStreamContent->moveCursor(QTextCursor::End);
586     text_pos_to_packet_[ui->teStreamContent->textCursor().anchor()] = packet_num;
587
588     if (truncated_) {
589         ui->teStreamContent->setTextBackgroundColor(ui->teStreamContent->palette().window().color());
590         ui->teStreamContent->setTextColor(ui->teStreamContent->palette().windowText().color());
591         ui->teStreamContent->insertPlainText(tr("\n[Stream output truncated]"));
592         ui->teStreamContent->moveCursor(QTextCursor::End);
593     }
594 }
595
596 void FollowStreamDialog::setCaptureFile(capture_file *cf)
597 {
598     if (!cf) { // We only want to know when the file closes.
599         cap_file_ = NULL;
600     }
601     updateWidgets();
602 }
603
604 // The following keyboard shortcuts should work (although
605 // they may not work consistently depending on focus):
606 // / (slash), Ctrl-F - Focus and highlight the search box
607 // Ctrl-G, Ctrl-N, F3 - Find next
608 // Should we make it so that typing any text starts searching?
609 bool FollowStreamDialog::eventFilter(QObject *obj, QEvent *event)
610 {
611     Q_UNUSED(obj);
612     if (ui->teStreamContent->hasFocus() && event->type() == QEvent::KeyPress) {
613         QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
614         if (keyEvent->matches(QKeySequence::SelectAll) || keyEvent->matches(QKeySequence::Copy)
615                 || keyEvent->text().isEmpty()) {
616             return false;
617         }
618         ui->leFind->setFocus();
619         if (keyEvent->matches(QKeySequence::Find)) {
620             return true;
621         } else if (keyEvent->matches(QKeySequence::FindNext)) {
622             findText();
623             return true;
624         }
625     }
626
627     return false;
628 }
629
630 void FollowStreamDialog::keyPressEvent(QKeyEvent *event)
631 {
632     if (ui->leFind->hasFocus()) {
633         if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
634             findText();
635             return;
636         }
637     } else {
638         if (event->key() == Qt::Key_Slash || event->matches(QKeySequence::Find)) {
639             ui->leFind->setFocus();
640             ui->leFind->selectAll();
641         }
642         return;
643     }
644
645     if (event->key() == Qt::Key_F3 || (event->key() == Qt::Key_N && event->modifiers() & Qt::ControlModifier)) {
646         findText();
647         return;
648     }
649
650     QDialog::keyPressEvent(event);
651 }
652
653 static inline void sanitize_buffer(char *buffer, size_t nchars) {
654     for (size_t i = 0; i < nchars; i++) {
655         if (buffer[i] == '\n' || buffer[i] == '\r' || buffer[i] == '\t')
656             continue;
657         if (! g_ascii_isprint((guchar)buffer[i])) {
658             buffer[i] = '.';
659         }
660     }
661 }
662
663 frs_return_t
664 FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_server, guint32 packet_num,
665                                 guint32 *global_pos)
666 {
667     gchar initbuf[256];
668     guint32 current_pos;
669     static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
670
671     switch (follow_info_.show_type) {
672
673     case SHOW_EBCDIC:
674     {
675         /* If our native arch is ASCII, call: */
676         EBCDIC_to_ASCII((guint8*)buffer, (guint) nchars);
677         sanitize_buffer(buffer, nchars);
678         QByteArray ba = QByteArray(buffer, (int)nchars);
679         addText(ba, is_from_server, packet_num);
680         break;
681     }
682
683     case SHOW_ASCII:
684     {
685         /* If our native arch is EBCDIC, call:
686          * ASCII_TO_EBCDIC(buffer, nchars);
687          */
688         sanitize_buffer(buffer, nchars);
689         sanitize_buffer(buffer, nchars);
690         QByteArray ba = QByteArray(buffer, (int)nchars);
691         addText(ba, is_from_server, packet_num);
692         break;
693     }
694
695     case SHOW_RAW: // UTF-8
696     {
697         // The QString docs say that invalid characters will be replaced with
698         // replacement characters or removed. It would be nice if we could
699         // explicitly choose one or the other.
700         QString utf8 = QString::fromUtf8(buffer, (int)nchars);
701         addText(utf8, is_from_server, packet_num);
702         break;
703     }
704
705     case SHOW_HEXDUMP:
706         current_pos = 0;
707         while (current_pos < nchars) {
708             gchar hexbuf[256];
709             int i;
710             gchar *cur = hexbuf, *ascii_start;
711
712             /* is_from_server indentation : put 4 spaces at the
713              * beginning of the string */
714             /* XXX - We might want to prepend each line with "C" or "S" instead. */
715             if (is_from_server && follow_info_.show_stream == BOTH_HOSTS) {
716                 memset(cur, ' ', 4);
717                 cur += 4;
718             }
719             cur += g_snprintf(cur, 20, "%08X  ", *global_pos);
720             /* 49 is space consumed by hex chars */
721             ascii_start = cur + 49;
722             for (i = 0; i < 16 && current_pos + i < nchars; i++) {
723                 *cur++ =
724                         hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
725                 *cur++ =
726                         hexchars[buffer[current_pos + i] & 0x0f];
727                 *cur++ = ' ';
728                 if (i == 7)
729                     *cur++ = ' ';
730             }
731             /* Fill it up if column isn't complete */
732             while (cur < ascii_start)
733                 *cur++ = ' ';
734
735             /* Now dump bytes as text */
736             for (i = 0; i < 16 && current_pos + i < nchars; i++) {
737                 *cur++ =
738                         (g_ascii_isprint((guchar)buffer[current_pos + i]) ?
739                             buffer[current_pos + i] : '.' );
740                 if (i == 7) {
741                     *cur++ = ' ';
742                 }
743             }
744             current_pos += i;
745             (*global_pos) += i;
746             *cur++ = '\n';
747             *cur = 0;
748
749             addText(hexbuf, is_from_server, packet_num);
750         }
751         break;
752
753     case SHOW_CARRAY:
754         current_pos = 0;
755         g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = { /* Packet %u */\n",
756                    is_from_server ? 1 : 0,
757                    is_from_server ? server_buffer_count_++ : client_buffer_count_++,
758                    packet_num);
759         addText(initbuf, is_from_server, packet_num);
760
761         while (current_pos < nchars) {
762             gchar hexbuf[256];
763             int i, cur;
764
765             cur = 0;
766             for (i = 0; i < 8 && current_pos + i < nchars; i++) {
767                 /* Prepend entries with "0x" */
768                 hexbuf[cur++] = '0';
769                 hexbuf[cur++] = 'x';
770                 hexbuf[cur++] =
771                         hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
772                 hexbuf[cur++] =
773                         hexchars[buffer[current_pos + i] & 0x0f];
774
775                 /* Delimit array entries with a comma */
776                 if (current_pos + i + 1 < nchars)
777                     hexbuf[cur++] = ',';
778
779                 hexbuf[cur++] = ' ';
780             }
781
782             /* Terminate the array if we are at the end */
783             if (current_pos + i == nchars) {
784                 hexbuf[cur++] = '}';
785                 hexbuf[cur++] = ';';
786             }
787
788             current_pos += i;
789             (*global_pos) += i;
790             hexbuf[cur++] = '\n';
791             hexbuf[cur] = 0;
792             addText(hexbuf, is_from_server, packet_num);
793         }
794         break;
795
796     case SHOW_YAML:
797     {
798         QString yaml_text;
799
800         const int base64_raw_len = 57; // Encodes to 76 bytes, common in RFCs
801         current_pos = 0;
802
803         if (packet_num != last_packet_) {
804             yaml_text.append(QString("# Packet %1\npeer%2_%3: !!binary |\n")
805                     .arg(packet_num)
806                     .arg(is_from_server ? 1 : 0)
807                     .arg(is_from_server ? server_buffer_count_++ : client_buffer_count_++));
808         }
809         while (current_pos < nchars) {
810             int len = current_pos + base64_raw_len < nchars ? base64_raw_len : (int) nchars - current_pos;
811             QByteArray base64_data(&buffer[current_pos], len);
812
813             yaml_text += "  " + base64_data.toBase64() + "\n";
814
815             current_pos += len;
816             (*global_pos) += len;
817         }
818         addText(yaml_text, is_from_server, packet_num);
819         break;
820     }
821     }
822
823     if (last_packet_ == 0) {
824         last_from_server_ = is_from_server;
825     }
826
827     if (packet_num != last_packet_) {
828         last_packet_ = packet_num;
829         if (is_from_server) {
830             server_packet_count_++;
831         } else {
832             client_packet_count_++;
833         }
834         if (last_from_server_ != is_from_server) {
835             last_from_server_ = is_from_server;
836             turns_++;
837         }
838     }
839
840     return FRS_OK;
841 }
842
843 bool FollowStreamDialog::follow(QString previous_filter, bool use_tcp_index)
844 {
845     int                 tmp_fd;
846     QString             follow_filter;
847     const char          *hostname0 = NULL, *hostname1 = NULL;
848     char                *port0 = NULL, *port1 = NULL;
849     QString             server_to_client_string;
850     QString             client_to_server_string;
851     QString             both_directions_string;
852     follow_stats_t      stats;
853     tcp_stream_chunk    sc;
854     size_t              nchars;
855     GString *           msg;
856     gboolean is_tcp = FALSE, is_udp = FALSE;
857
858     resetStream();
859
860     if (cap_file_ == NULL)
861     {
862         QMessageBox::warning(this, tr("No capture file."), tr("Please make sure you have a capture file opened."));
863         return false;
864     }
865
866     if (cap_file_->edt == NULL)
867     {
868         QMessageBox::warning(this, tr("Error following stream."), tr("Capture file invalid."));
869         return false;
870     }
871
872     proto_get_frame_protocols(cap_file_->edt->pi.layers, NULL, &is_tcp, &is_udp, NULL, NULL);
873
874     switch (follow_type_)
875     {
876     case FOLLOW_TCP:
877         if (!is_tcp) {
878             QMessageBox::warning(this, tr("Error following stream."), tr("Please make sure you have a TCP packet selected."));
879             return false;
880         }
881         break;
882     case FOLLOW_UDP:
883         removeStreamControls();
884         if (!is_udp) {
885             QMessageBox::warning(this, tr("Error following stream."), tr("Please make sure you have a UDP packet selected."));
886             return false;
887         }
888         break;
889     case FOLLOW_SSL:
890         /* we got ssl so we can follow */
891         removeStreamControls();
892         if (!epan_dissect_packet_contains_field(cap_file_->edt, "ssl")) {
893             QMessageBox::critical(this, tr("Error following stream."),
894                                tr("Please make sure you have an SSL packet selected."));
895             return false;
896         }
897         break;
898     }
899
900     if (follow_type_ == FOLLOW_TCP || follow_type_ == FOLLOW_SSL)
901     {
902         /* Create a new filter that matches all packets in the TCP stream,
903            and set the display filter entry accordingly */
904         reset_tcp_reassembly();
905     }
906
907     if (use_tcp_index) {
908         follow_filter = gchar_free_to_qstring(build_follow_index_filter());
909     } else {
910         follow_filter = gchar_free_to_qstring(build_follow_conv_filter(&cap_file_->edt->pi));
911     }
912     if (follow_filter.isEmpty()) {
913         QMessageBox::warning(this,
914                              tr("Error creating filter for this stream."),
915                              tr("A transport or network layer header is needed."));
916         return false;
917     }
918
919     if (follow_type_ == FOLLOW_TCP || follow_type_ == FOLLOW_SSL)
920     {
921         /* Create a temporary file into which to dump the reassembled data
922            from the TCP stream, and set "data_out_file" to refer to it, so
923            that the TCP code will write to it.
924
925            XXX - it might be nicer to just have the TCP code directly
926            append stuff to the text widget for the TCP stream window,
927            if we can arrange that said window not pop up until we're
928            done. */
929         gchar *data_out_filename;
930         tmp_fd = create_tempfile(&data_out_filename, "follow");
931         data_out_filename_ = data_out_filename;
932
933         if (tmp_fd == -1) {
934             QMessageBox::warning(this, "Error",
935                                  "Could not create temporary file %1: %2",
936                                  data_out_filename_, g_strerror(errno));
937             data_out_filename_.clear();
938             return false;
939         }
940
941         data_out_file = fdopen(tmp_fd, "w+b");
942         if (data_out_file == NULL) {
943             QMessageBox::warning(this, "Error",
944                                  "Could not create temporary file %1: %2",
945                                  data_out_filename_, g_strerror(errno));
946             //ws_close(tmp_fd);
947             ws_unlink(data_out_filename_.toUtf8().constData());
948             data_out_filename_.clear();
949             return false;
950         }
951     }
952
953     /* append the negation */
954     if(!previous_filter.isEmpty()) {
955         filter_out_filter_ = QString("%1 and !(%2)")
956                 .arg(previous_filter).arg(follow_filter);
957     }
958     else
959     {
960         filter_out_filter_ = QString("!(%1)").arg(follow_filter);
961     }
962
963     switch (follow_type_)
964     {
965     case FOLLOW_TCP:
966     {
967         int stream_count = get_tcp_stream_count() - 1;
968         ui->streamNumberSpinBox->blockSignals(true);
969         ui->streamNumberSpinBox->setMaximum(stream_count);
970         ui->streamNumberSpinBox->setValue(get_follow_tcp_index());
971         ui->streamNumberSpinBox->blockSignals(false);
972         ui->streamNumberSpinBox->setToolTip(tr("%Ln total stream(s).", "", stream_count));
973         ui->streamNumberLabel->setToolTip(ui->streamNumberSpinBox->toolTip());
974
975         break;
976     }
977     case FOLLOW_UDP:
978         /* data will be passed via tap callback*/
979         msg = register_tap_listener("udp_follow", &follow_info_,
980                                     follow_filter.toUtf8().constData(),
981                                     0, NULL, udp_queue_packet_data, NULL);
982         if (msg) {
983             QMessageBox::critical(this, "Error",
984                                "Can't register udp_follow tap: %1",
985                                msg->str);
986             return false;
987         }
988         break;
989     case FOLLOW_SSL:
990         /* we got ssl so we can follow */
991         msg = register_tap_listener("ssl", &follow_info_,
992                                     follow_filter.toUtf8().constData(), 0,
993                                     NULL, ssl_queue_packet_data, NULL);
994         if (msg)
995         {
996             QMessageBox::critical(this, "Error",
997                           "Can't register ssl tap: %1", msg->str);
998             return false;
999         }
1000         break;
1001     }
1002
1003     /* Run the display filter so it goes in effect - even if it's the
1004        same as the previous display filter. */
1005     emit updateFilter(follow_filter, TRUE);
1006
1007     switch (follow_type_)
1008     {
1009     case FOLLOW_TCP:
1010
1011         break;
1012     case FOLLOW_UDP:
1013         remove_tap_listener(&follow_info_);
1014         break;
1015     case FOLLOW_SSL:
1016         remove_tap_listener(&follow_info_);
1017         break;
1018     }
1019
1020     if (follow_type_ == FOLLOW_TCP)
1021     {
1022         /* Check whether we got any data written to the file. */
1023         if (empty_tcp_stream) {
1024             QMessageBox::warning(this, "Error",
1025                                  "The packets in the capture file for that stream have no data.");
1026             //ws_close(tmp_fd);
1027             ws_unlink(data_out_filename_.toUtf8().constData());
1028             data_out_filename_.clear();
1029             return false;
1030         }
1031
1032         /* Go back to the top of the file and read the first tcp_stream_chunk
1033          * to ensure that the IP addresses and port numbers in the drop-down
1034          * list are tied to the correct lines displayed by follow_read_stream()
1035          * later on (which also reads from this file).  Close the file when
1036          * we're done.
1037          *
1038          * We read the data now, before we pop up a window, in case the
1039          * read fails.  We use the data later.
1040          */
1041
1042         rewind(data_out_file);
1043         nchars=fread(&sc, 1, sizeof(sc), data_out_file);
1044         if (nchars != sizeof(sc)) {
1045             if (ferror(data_out_file)) {
1046                 QMessageBox::warning(this, "Error",
1047                                      QString(tr("Could not read from temporary file %1: %2"))
1048                                      .arg(data_out_filename_)
1049                                      .arg(g_strerror(errno)));
1050             } else {
1051                 QMessageBox::warning(this, "Error",
1052                                      QString(tr("Short read from temporary file %1: expected %2, got %3"))
1053                                      .arg(data_out_filename_)
1054                                      .arg((unsigned long)sizeof(sc))
1055                                      .arg((unsigned long)nchars));
1056
1057             }
1058             //ws_close(tmp_fd);
1059             ws_unlink(data_out_filename_.toUtf8().constData());
1060             data_out_filename_.clear();
1061             return false;
1062         }
1063         fclose(data_out_file);
1064         data_out_file = NULL;
1065     }
1066
1067     /* Stream to show */
1068     follow_stats(&stats);
1069
1070     if (stats.is_ipv6) {
1071         struct e_in6_addr ipaddr;
1072         memcpy(&ipaddr, stats.ip_address[0], 16);
1073         hostname0 = get_hostname6(&ipaddr);
1074         memcpy(&ipaddr, stats.ip_address[1], 16);
1075         hostname1 = get_hostname6(&ipaddr);
1076     } else {
1077         guint32 ipaddr;
1078         memcpy(&ipaddr, stats.ip_address[0], 4);
1079         hostname0 = get_hostname(ipaddr);
1080         memcpy(&ipaddr, stats.ip_address[1], 4);
1081         hostname1 = get_hostname(ipaddr);
1082     }
1083
1084     switch (follow_type_)
1085     {
1086     case FOLLOW_TCP:
1087         port0 = ep_tcp_port_to_display(stats.port[0]);
1088         port1 = ep_tcp_port_to_display(stats.port[1]);
1089         break;
1090     case FOLLOW_UDP:
1091         port0 = ep_udp_port_to_display(stats.port[0]);
1092         port1 = ep_udp_port_to_display(stats.port[1]);
1093         break;
1094     case FOLLOW_SSL:
1095         port0 = ep_tcp_port_to_display(stats.port[0]);
1096         port1 = ep_tcp_port_to_display(stats.port[1]);
1097         break;
1098     }
1099
1100     follow_info_.is_ipv6 = stats.is_ipv6;
1101
1102     if (follow_type_ == FOLLOW_TCP)
1103     {
1104         /* Host 0 --> Host 1 */
1105         if ((sc.src_port == stats.port[0]) &&
1106             ((stats.is_ipv6 && (memcmp(sc.src_addr, stats.ip_address[0], 16) == 0)) ||
1107              (!stats.is_ipv6 && (memcmp(sc.src_addr, stats.ip_address[0], 4) == 0)))) {
1108             server_to_client_string =
1109                     QString("%1:%2 %3 %4:%5 (%6)")
1110                     .arg(hostname0).arg(port0)
1111                     .arg(UTF8_RIGHTWARDS_ARROW)
1112                     .arg(hostname1).arg(port1)
1113                     .arg(gchar_free_to_qstring(format_size(
1114                                                    stats.bytes_written[0],
1115                                                format_size_unit_bytes|format_size_prefix_si)));
1116         } else {
1117             server_to_client_string =
1118                     QString("%1:%2 %3 %4:%5 (%6)")
1119                     .arg(hostname1).arg(port1)
1120                     .arg(UTF8_RIGHTWARDS_ARROW)
1121                     .arg(hostname0).arg(port0)
1122                     .arg(gchar_free_to_qstring(format_size(
1123                                                    stats.bytes_written[0],
1124                                                format_size_unit_bytes|format_size_prefix_si)));
1125         }
1126
1127         /* Host 1 --> Host 0 */
1128         if ((sc.src_port == stats.port[1]) &&
1129             ((stats.is_ipv6 && (memcmp(sc.src_addr, stats.ip_address[1], 16) == 0)) ||
1130              (!stats.is_ipv6 && (memcmp(sc.src_addr, stats.ip_address[1], 4) == 0)))) {
1131             client_to_server_string =
1132                     QString("%1:%2 %3 %4:%5 (%6)")
1133                     .arg(hostname0).arg(port0)
1134                     .arg(UTF8_RIGHTWARDS_ARROW)
1135                     .arg(hostname1).arg(port1)
1136                     .arg(gchar_free_to_qstring(format_size(
1137                                                    stats.bytes_written[1],
1138                                                format_size_unit_bytes|format_size_prefix_si)));
1139         } else {
1140             client_to_server_string =
1141                     QString("%1:%2 %3 %4:%5 (%6)")
1142                     .arg(hostname1).arg(port1)
1143                     .arg(UTF8_RIGHTWARDS_ARROW)
1144                     .arg(hostname0).arg(port0)
1145                     .arg(gchar_free_to_qstring(format_size(
1146                                                    stats.bytes_written[1],
1147                                                format_size_unit_bytes|format_size_prefix_si)));
1148         }
1149
1150     }
1151     else
1152     {
1153         if ((follow_info_.client_port == stats.port[0]) &&
1154             ((stats.is_ipv6 && (memcmp(follow_info_.client_ip.data, stats.ip_address[0], 16) == 0)) ||
1155              (!stats.is_ipv6 && (memcmp(follow_info_.client_ip.data, stats.ip_address[0], 4) == 0)))) {
1156             server_to_client_string =
1157                     QString("%1:%2 %3 %4:%5 (%6)")
1158                     .arg(hostname0).arg(port0)
1159                     .arg(UTF8_RIGHTWARDS_ARROW)
1160                     .arg(hostname1).arg(port1)
1161                     .arg(gchar_free_to_qstring(format_size(
1162                                                    follow_info_.bytes_written[0],
1163                                                format_size_unit_bytes|format_size_prefix_si)));
1164
1165             client_to_server_string =
1166                     QString("%1:%2 %3 %4:%5 (%6)")
1167                     .arg(hostname1).arg(port1)
1168                     .arg(UTF8_RIGHTWARDS_ARROW)
1169                     .arg(hostname0).arg(port0)
1170                     .arg(gchar_free_to_qstring(format_size(
1171                                                    follow_info_.bytes_written[1],
1172                                                format_size_unit_bytes|format_size_prefix_si)));
1173         } else {
1174             server_to_client_string =
1175                     QString("%1:%2 %3 %4:%5 (%6)")
1176                     .arg(hostname1).arg(port1)
1177                     .arg(UTF8_RIGHTWARDS_ARROW)
1178                     .arg(hostname0).arg(port0)
1179                     .arg(gchar_free_to_qstring(format_size(
1180                                                    follow_info_.bytes_written[0],
1181                                                format_size_unit_bytes|format_size_prefix_si)));
1182
1183             client_to_server_string =
1184                     QString("%1:%2 %3 %4:%5 (%6)")
1185                     .arg(hostname0).arg(port0)
1186                     .arg(UTF8_RIGHTWARDS_ARROW)
1187                     .arg(hostname1).arg(port1)
1188                     .arg(gchar_free_to_qstring(format_size(
1189                                                    follow_info_.bytes_written[1],
1190                                                format_size_unit_bytes|format_size_prefix_si)));
1191         }
1192     }
1193
1194     /* Both Stream Directions */
1195     switch (follow_type_)
1196     {
1197     case FOLLOW_TCP:
1198         both_directions_string = QString("Entire conversation (%1)")
1199                 .arg(gchar_free_to_qstring(format_size(
1200                                                stats.bytes_written[0] + stats.bytes_written[1],
1201                      format_size_unit_bytes|format_size_prefix_si)));
1202         this->setWindowTitle(QString("Follow TCP Stream (%1)").arg(follow_filter));
1203         break;
1204     case FOLLOW_UDP:
1205         both_directions_string = QString("Entire conversation (%1)")
1206                 .arg(gchar_free_to_qstring(format_size(
1207                                                follow_info_.bytes_written[0] + follow_info_.bytes_written[1],
1208                      format_size_unit_bytes|format_size_prefix_si)));
1209         this->setWindowTitle(QString("Follow UDP Stream (%1)").arg(follow_filter));
1210         break;
1211     case FOLLOW_SSL:
1212         both_directions_string = QString("Entire conversation (%1)")
1213                 .arg(gchar_free_to_qstring(format_size(
1214                                                follow_info_.bytes_written[0] + follow_info_.bytes_written[1],
1215                      format_size_unit_bytes|format_size_prefix_si)));
1216         this->setWindowTitle(QString("Follow SSL Stream (%1)").arg(follow_filter));
1217         break;
1218     }
1219
1220     ui->cbDirections->clear();
1221     this->ui->cbDirections->addItem(both_directions_string);
1222     this->ui->cbDirections->addItem(client_to_server_string);
1223     this->ui->cbDirections->addItem(server_to_client_string);
1224
1225     followStream();
1226     fillHintLabel(-1);
1227
1228     if (data_out_file) {
1229         fclose(data_out_file);
1230         data_out_file = NULL;
1231     }
1232
1233     return true;
1234 }
1235
1236 #define FLT_BUF_SIZE 1024
1237
1238 /*
1239  * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
1240  * it gets handed bufferfuls.  That's fine for "follow_write_raw()"
1241  * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
1242  * the "print_line()" routine from "print.c", and as that routine might
1243  * genuinely expect to be handed a line (if, for example, it's using
1244  * some OS or desktop environment's printing API, and that API expects
1245  * to be handed lines), "follow_print_text()" should probably accumulate
1246  * lines in a buffer and hand them "print_line()".  (If there's a
1247  * complete line in a buffer - i.e., there's nothing of the line in
1248  * the previous buffer or the next buffer - it can just hand that to
1249  * "print_line()" after filtering out non-printables, as an
1250  * optimization.)
1251  *
1252  * This might or might not be the reason why C arrays display
1253  * correctly but get extra blank lines very other line when printed.
1254  */
1255 frs_return_t
1256 FollowStreamDialog::readTcpStream()
1257 {
1258     FILE *data_out_fp;
1259     tcp_stream_chunk    sc;
1260     size_t              bcount;
1261     size_t              bytes_read;
1262     int                 iplen;
1263     guint8              client_addr[MAX_IPADDR_LEN];
1264     guint16             client_port = 0;
1265     gboolean            is_server;
1266     guint32             global_client_pos = 0, global_server_pos = 0;
1267     guint32             *global_pos;
1268     gboolean            skip;
1269     char                buffer[FLT_BUF_SIZE+1]; /* +1 to fix ws bug 1043 */
1270     size_t              nchars;
1271     frs_return_t        frs_return;
1272
1273     iplen = (follow_info_.is_ipv6) ? 16 : 4;
1274
1275     data_out_fp = ws_fopen(data_out_filename_.toUtf8().constData(), "rb");
1276     if (data_out_fp == NULL) {
1277         QMessageBox::critical(this, "Error",
1278                       "Could not open temporary file %1: %2", data_out_filename_,
1279                       g_strerror(errno));
1280         return FRS_OPEN_ERROR;
1281     }
1282
1283     while ((nchars=fread(&sc, 1, sizeof(sc), data_out_fp))) {
1284         if (nchars != sizeof(sc)) {
1285             QMessageBox::critical(this, "Error",
1286                           QString(tr("Short read from temporary file %1: expected %2, got %3"))
1287                           .arg(data_out_filename_)
1288                           .arg(sizeof(sc))
1289                           .arg(nchars));
1290             fclose(data_out_fp);
1291             data_out_fp = NULL;
1292             return FRS_READ_ERROR;
1293         }
1294         if (client_port == 0) {
1295             memcpy(client_addr, sc.src_addr, iplen);
1296             client_port = sc.src_port;
1297         }
1298         skip = FALSE;
1299         if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
1300                 client_port == sc.src_port) {
1301             is_server = FALSE;
1302             global_pos = &global_client_pos;
1303             if (follow_info_.show_stream == FROM_SERVER) {
1304                 skip = TRUE;
1305             }
1306         } else {
1307             is_server = TRUE;
1308             global_pos = &global_server_pos;
1309             if (follow_info_.show_stream == FROM_CLIENT) {
1310                 skip = TRUE;
1311             }
1312         }
1313
1314         bytes_read = 0;
1315         while (bytes_read < sc.dlen) {
1316             bcount = ((sc.dlen-bytes_read) < FLT_BUF_SIZE) ? (sc.dlen-bytes_read) : FLT_BUF_SIZE;
1317             nchars = fread(buffer, 1, bcount, data_out_fp);
1318             if (nchars == 0)
1319                 break;
1320             /* XXX - if we don't get "bcount" bytes, is that an error? */
1321             bytes_read += nchars;
1322
1323             if (!skip)
1324             {
1325                 frs_return = showBuffer(buffer,
1326                                          nchars, is_server, sc.packet_num, global_pos);
1327                 if(frs_return == FRS_PRINT_ERROR) {
1328                     fclose(data_out_fp);
1329                     data_out_fp = NULL;
1330                     return frs_return;
1331                 }
1332
1333             }
1334         }
1335     }
1336
1337     if (ferror(data_out_fp)) {
1338         QMessageBox::critical(this, tr("Error reading temporary file"),
1339                            QString("%1: %2").arg(data_out_filename_).arg(g_strerror(errno)));
1340         fclose(data_out_fp);
1341         data_out_fp = NULL;
1342         return FRS_READ_ERROR;
1343     }
1344
1345     fclose(data_out_fp);
1346     data_out_fp = NULL;
1347     return FRS_OK;
1348 }
1349
1350 /*
1351  * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
1352  * it gets handed bufferfuls.  That's fine for "follow_write_raw()"
1353  * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
1354  * the "print_line()" routine from "print.c", and as that routine might
1355  * genuinely expect to be handed a line (if, for example, it's using
1356  * some OS or desktop environment's printing API, and that API expects
1357  * to be handed lines), "follow_print_text()" should probably accumulate
1358  * lines in a buffer and hand them "print_line()".  (If there's a
1359  * complete line in a buffer - i.e., there's nothing of the line in
1360  * the previous buffer or the next buffer - it can just hand that to
1361  * "print_line()" after filtering out non-printables, as an
1362  * optimization.)
1363  *
1364  * This might or might not be the reason why C arrays display
1365  * correctly but get extra blank lines very other line when printed.
1366  */
1367 frs_return_t
1368 FollowStreamDialog::readUdpStream()
1369 {
1370     guint32 global_client_pos = 0, global_server_pos = 0;
1371     guint32 *global_pos;
1372     gboolean skip;
1373     GList* cur;
1374     frs_return_t frs_return;
1375     follow_record_t *follow_record;
1376     char *buffer;
1377
1378     for (cur = follow_info_.payload; cur; cur = g_list_next(cur)) {
1379         follow_record = (follow_record_t *)cur->data;
1380         skip = FALSE;
1381         if (!follow_record->is_server) {
1382             global_pos = &global_client_pos;
1383             if(follow_info_.show_stream == FROM_SERVER) {
1384                 skip = TRUE;
1385             }
1386         } else {
1387             global_pos = &global_server_pos;
1388             if (follow_info_.show_stream == FROM_CLIENT) {
1389                 skip = TRUE;
1390             }
1391         }
1392
1393         if (!skip) {
1394             buffer = (char *)g_memdup(follow_record->data->data,
1395                                       follow_record->data->len);
1396
1397             frs_return = showBuffer(
1398                         buffer,
1399                         follow_record->data->len,
1400                         follow_record->is_server,
1401                         follow_record->packet_num,
1402                         global_pos);
1403             g_free(buffer);
1404             if(frs_return == FRS_PRINT_ERROR)
1405                 return frs_return;
1406         }
1407     }
1408
1409     return FRS_OK;
1410 }
1411
1412 /*
1413  * Editor modelines
1414  *
1415  * Local Variables:
1416  * c-basic-offset: 4
1417  * tab-width: 8
1418  * indent-tabs-mode: nil
1419  * End:
1420  *
1421  * ex: set shiftwidth=4 tabstop=8 expandtab:
1422  * :indentSize=4:tabSize=8:noTabs=true:
1423  */