/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "DialogOpenFiles.h"
#include "DebuggerCoreInterface.h"
#include "Debugger.h"

#include <QHeaderView>
#include <QMessageBox>
#include <QTextStream>
#include <QDir>
#include <QHostAddress>
#include <arpa/inet.h>

// this file is generally very linux specific
#ifndef Q_OS_LINUX
#error "Unsupported Platform"
#endif

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
DialogOpenFiles::DialogOpenFiles(QWidget *parent) : QDialog(parent) {
	ui.setupUi(this);
	ui.tableWidget->verticalHeader()->hide();
	ui.tableWidget->horizontalHeader()->setStretchLastSection(true);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpenFiles::showEvent(QShowEvent *) {
	on_btnRefresh_clicked();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QString DialogOpenFiles::fileType(const QString &filename) {
	QFileInfo info(filename);
	QString basename(info.completeBaseName());
	
	if(basename.startsWith("socket:")) {
		return "Socket";
	}

	if(basename.startsWith("pipe:")) {
		return "Pipe";
	}
	
	return "File";
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool DialogOpenFiles::TCPSocketProcessor(QString &symlink, int socketNumber, const QStringList &lst) {
	if(lst.size() >= 13) {

		bool ok;
		const quint32 local_address = ntohl(lst[1].toUInt(&ok, 16));
		if(ok) {
			const quint16 local_port = lst[2].toUInt(&ok, 16);
			if(ok) {
				const quint32 remote_address = ntohl(lst[3].toUInt(&ok, 16));
				if(ok) {
					const quint16 remote_port = lst[4].toUInt(&ok, 16);
					if(ok) {
						const quint8 state = lst[5].toUInt(&ok, 16);
						Q_UNUSED(state);
						if(ok) {
							const int inode = lst[13].toUInt(&ok, 10);
							if(ok) {
								if(inode == socketNumber) {
									symlink.sprintf("TCP: %s:%d -> %s:%d",
										qPrintable(QHostAddress(local_address).toString()),
										local_port,
										qPrintable(QHostAddress(remote_address).toString()),
										remote_port);
										return true;
								}
							}
						}
					}
				}
			}
		}
	}
	return false;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool DialogOpenFiles::UDPSocketProcessor(QString &symlink, int socketNumber, const QStringList &lst) {
	if(lst.size() >= 13) {

		bool ok;
		const quint32 local_address = ntohl(lst[1].toUInt(&ok, 16));
		if(ok) {
			const quint16 local_port = lst[2].toUInt(&ok, 16);
			if(ok) {
				const quint32 remote_address = ntohl(lst[3].toUInt(&ok, 16));
				if(ok) {
					const quint16 remote_port = lst[4].toUInt(&ok, 16);
					if(ok) {
						const quint8 state = lst[5].toUInt(&ok, 16);
						Q_UNUSED(state);
						if(ok) {
							const int inode = lst[13].toUInt(&ok, 10);
							if(ok) {
								if(inode == socketNumber) {
									symlink.sprintf("UDP: %s:%d -> %s:%d",
										qPrintable(QHostAddress(local_address).toString()),
										local_port,
										qPrintable(QHostAddress(remote_address).toString()),
										remote_port);
										return true;
								}
							}
						}
					}
				}
			}
		}
	}
	return false;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool DialogOpenFiles::UNIXSocketProcessor(QString &symlink, int socketNumber, const QStringList &lst) {
	if(lst.size() >= 6) {
		bool ok;
		const int inode = lst[6].toUInt(&ok, 10);
		if(ok) {
			if(inode == socketNumber) {
				symlink.sprintf("UNIX [%s]", qPrintable(lst[0]));
				return true;
			}
		}
	}
	return false;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QString DialogOpenFiles::processSocketTCP(QString &symlink) {
	const QString sockinfo(symlink.mid(symlink.indexOf("socket:[")));
	const int sockNum = sockinfo.mid(8).remove("]").toUInt();
	
	return processSocketFile<TCPSocketProcessor>("/proc/net/tcp", symlink, sockNum);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QString DialogOpenFiles::processSocketUNIX(QString &symlink) {
	const QString sockinfo(symlink.mid(symlink.indexOf("socket:[")));
	const int sockNum = sockinfo.mid(8).remove("]").toUInt();

	return processSocketFile<UNIXSocketProcessor>("/proc/net/unix", symlink, sockNum);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QString DialogOpenFiles::processSocketUDP(QString &symlink) {
	const QString sockinfo(symlink.mid(symlink.indexOf("socket:[")));
	const int sockNum = sockinfo.mid(8).remove("]").toUInt();

	return processSocketFile<UDPSocketProcessor>("/proc/net/udp", symlink, sockNum);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpenFiles::on_btnRefresh_clicked() {
	ui.tableWidget->setSortingEnabled(false);
	ui.tableWidget->setRowCount(0);
	if(Debugger::debuggerBase != 0) {
		const edb::pid_t pid = Debugger::debuggerBase->pid();
		if(pid != -1) {
			QDir dir(QString().sprintf("/proc/%d/fd/", pid));
			QFileInfoList entries = dir.entryInfoList(QStringList() << "[0-9]*");
			foreach(QFileInfo info, entries) {
				if (info.isSymLink()) {
					QString symlink(Debugger::symlinkTarget(info));
					const QString type(fileType(symlink));
					
					if(type == "Socket") {
						symlink = processSocketTCP(symlink);
						symlink = processSocketUDP(symlink);
						symlink = processSocketUNIX(symlink);
					}
					
					if(type == "Pipe") {
						symlink = "FIFO";
					}
					
					const int row = ui.tableWidget->rowCount();
					ui.tableWidget->insertRow(row);
					
					
					QTableWidgetItem *const itemFD = new QTableWidgetItem;
					itemFD->setData(Qt::DisplayRole, info.fileName().toUInt());
									
					ui.tableWidget->setItem(row, 0, itemFD);
	        		ui.tableWidget->setItem(row, 1, new QTableWidgetItem(type));
					ui.tableWidget->setItem(row, 2, new QTableWidgetItem(symlink));
				}
			}
		}
	}
	ui.tableWidget->setSortingEnabled(true);
}
