/*
Copyright (C) 2006 - 2009 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 <QtDebug>
#include <QHeaderView>
#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);
	ui.tableWidget->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
}

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

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
template <class F>
QString DialogOpenFiles::processSocketFile(const QString &filename, QString &symlink, int socketNumber, F fp) {
	QFile net(filename);
	net.open(QIODevice::ReadOnly | QIODevice::Text);
	if(net.isOpen()) {
		QTextStream in(&net);
		QString line;

		// ditch first line, it is just table headings
		line = in.readLine();

		// read in the first line we care about
		line = in.readLine();

		// a null string means end of file (but not an empty string!)
		while(!line.isNull()) {

			QString lline(line);
			const QStringList lst = lline.replace(":", " ").split(" ", QString::SkipEmptyParts);

			if(fp(symlink, socketNumber, lst)) {
				break;
			}

			line = in.readLine();
		}
	}
	return symlink;
}

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

	if(basename.startsWith("pipe:")) {
		return tr("Pipe");
	}
	
	return tr("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 = QString("TCP: %1:%2 -> %3:%4")
										.arg(QHostAddress(local_address).toString())
										.arg(local_port)
										.arg(QHostAddress(remote_address).toString())
										.arg(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 = QString("UDP: %1:%2 -> %3:%4")
										.arg(QHostAddress(local_address).toString())
										.arg(local_port)
										.arg(QHostAddress(remote_address).toString())
										.arg(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 = QString("UNIX [%1]").arg(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("/proc/net/tcp", symlink, sockNum, TCPSocketProcessor);
}

//------------------------------------------------------------------------------
// 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("/proc/net/unix", symlink, sockNum, UNIXSocketProcessor);
}

//------------------------------------------------------------------------------
// 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("/proc/net/udp", symlink, sockNum, UDPSocketProcessor);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpenFiles::doFind() {
	ui.tableWidget->setSortingEnabled(false);
	ui.tableWidget->setRowCount(0);

	const edb::pid_t pid = edb::v1::debuggerBase->pid();
	if(pid != -1) {
		QDir dir(QString("/proc/%1/fd/").arg(pid));
		QFileInfoList entries = dir.entryInfoList(QStringList() << "[0-9]*");
		Q_FOREACH(const QFileInfo &info, entries) {
			if (info.isSymLink()) {
				QString symlink(info.symLinkTarget());
				const QString type(fileType(symlink));

				if(type == tr("Socket")) {
					symlink = processSocketTCP(symlink);
					symlink = processSocketUDP(symlink);
					symlink = processSocketUNIX(symlink);
				}

				if(type == tr("Pipe")) {
					symlink = tr("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);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpenFiles::on_btnRefresh_clicked() {
	ui.btnRefresh->setEnabled(false);
	doFind();
	ui.btnRefresh->setEnabled(true);
}
