summaryrefslogblamecommitdiffstats
path: root/src/fbgui/fbgui.cpp
blob: 9cb906da2affa69c8c28c4083a18fa82331fecce (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
                  
                    
                            
                                     







                                                      
                   
                  
                   
 
                                                                                             









                                                                      
                
                        
 

                        
 
 




                      
                                                           
                                
 
                       

                                                          
                                          

                                                                     

                                                                        
                                                





                                                            

                                                                                    
 







                                                                                 

                                                                                               



                                                                                            
 


                                                  
 


                                                                                              

                  
                         
 

                                                                                             
                                                       
                                                                                             
   
                                                
  

                                                            
   
                               









                                                                                       

                                                                                             
   
                                   







                                                                                         
                       







                                                                                 


                                                                                                                                         



                                                                     
 
                                                                                    
         
                                       
 

                                                                           
                                                





                                                                                 








                                                                                        
         
 

                                                                                             
                                                               






                                                         
                              
                                                               

                                                                          
                                     

                  




                                                                                             

















                                                                                                                                                      
 




                                                                                             
                            
                                                                                                                         





























                                                                                                                                             
                                                                                                                         
 
 
                                                                                             



                                                                  
                                                             


                                                                           

                                                            



                                     
                                      






























                                                                                                
                       

                                                                         


                                        






                                                                    
 
                                                                                             
                                                                           
                                                                                             
                          




                                                                                             

                                                                                             
   



                                                                                        
  






                                                                              


                            


















































                                                                                                   
 



                                                                                             
                                         

   
                        







                                                                         
 
#include "fbgui.h"
#include "sysinfo.h"
#include "downloadmanager.h"
#include "javascriptinterfacefbgui.h"

#include <log4cxx/logger.h>
#include "qlog4cxx.h"

using namespace log4cxx;
using namespace log4cxx::helpers;
LoggerPtr coreLogger(Logger::getLogger("fbgui.core"));

#include <iostream>
#include <QThread>
#include <QtWebKit>

//-------------------------------------------------------------------------------------------
/**
 * A constructor.
 *
 * The constructor of the fbgui class. It initializes the main objects
 * which are needed while the program is running.
 * The appearance of the webView is here also defined.
 *
 * @see JavascriptInterface
 * @see DownloadManager
 */
fbgui::fbgui() :
		agui() {
}
fbgui::~fbgui() {
	dmThread.quit();
}

/**
 * init function.
 */
void fbgui::init() {
	// start fbgui
	LOG4CXX_DEBUG(coreLogger, "Initializing fbgui...");
	setWindowTitle("fbgui");

	if (sslSupport)
		LOG4CXX_DEBUG(coreLogger, "SSL enabled.");

	// initialize javascript interface
	JavascriptInterfaceFBGUI* jsi = new JavascriptInterfaceFBGUI(
			this->_webView->page()->mainFrame());
	QObject::connect(jsi, SIGNAL(quitFbgui()), this, SLOT(close()));
	QObject::connect(jsi, SIGNAL(shutDownClient()), this,
			SLOT(shutdownSystem()));
	QObject::connect(jsi, SIGNAL(deleteCookies()), this,
			SLOT(clearAllCookies()));
	QObject::connect(jsi, SIGNAL(showCookies()), this,
			SLOT(printCookies()));
	QObject::connect(jsi, SIGNAL(showHistory()), this,
			SLOT(printHistory()));
	QObject::connect(_webView->page()->mainFrame(), SIGNAL(
			javaScriptWindowObjectCleared()), jsi, SLOT(attachToDOM()));

	// initialize download manager
	DownloadManager* dm = new DownloadManager();
	QObject::connect(dm, SIGNAL(downloadInfo(const QString&, const double&)),
			jsi, SLOT(downloadInfo(const QString&, const double&)));
	QObject::connect(dm, SIGNAL(notify(const QString&)), jsi,
			SLOT(notify(const QString&)));
	QObject::connect(jsi, SIGNAL(requestFile(const QString&)), dm,
			SLOT(downloadFile(const QString&)));
	QObject::connect(dm,
			SIGNAL(updateProgress(const int&, const double&, const QString&)), jsi,
			SLOT(updateProgressBar(const int&, const double&, const QString&)));
	QObject::connect(dm, SIGNAL(downloadQueueEmpty()), jsi, SLOT(
			callbackOnFinished()));
	QObject::connect(dm, SIGNAL(downloadQueueEmpty()), this, SLOT(loadSystem()));

	// move download manager to its own thread
	dm->moveToThread(&dmThread);
	dmThread.start();

	// connect to handler for loaded URLs
	QObject::connect(_webView, SIGNAL(loadFinished(bool)), this, SLOT(loadURLDone(bool)));

	loadURL();

	showFullScreen();

}
//-------------------------------------------------------------------------------------------
//                            Preparations for URL load
//-------------------------------------------------------------------------------------------
/**
 * This method checks the existance of the host.
 *
 * This method checks if the host exists / can be found.
 * The host is from the URL given through the configuration.
 */
bool fbgui::checkHost() const {
	QHostInfo hostInfo = QHostInfo::fromName(baseURL.host());
	if (hostInfo.error() != QHostInfo::NoError) {
		LOG4CXX_DEBUG(coreLogger, "Lookup of " << baseURL.host() << "failed.");
		LOG4CXX_DEBUG(coreLogger, "Host can not be reached.");
		return false;
	} else {
		LOG4CXX_DEBUG(coreLogger,
				"Lookup of " << baseURL.host() << " succeeded.");
		return true;
	}
}
//-------------------------------------------------------------------------------------------
/**
 * This method tries loads the URL.
 *
 * This method loads the main screen via an POST request. If also disconnects the watcher
 * of the file, (Watcher is set in the fbgui::watchForTrigger() method).
 * and generates the POST data body.
 *
 * @see fbgui::watchForTrigger()
 * @see fbgui::generatePOSTData()
 */
void fbgui::loadURL() {
	if (checkHost()) {
		LOG4CXX_DEBUG(coreLogger,
				"Loading URL: " << baseURL.toString() << " ...");

		// Generate POST identification data needed by PBS.
		QByteArray postData = generatePOSTData();
		// Generate a Network Request Object
		QNetworkRequest req(baseURL);
		 QObject::connect(_webView->page()->networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> & )),
				 	 	 	 	 this, SLOT(sslErrorHandler(QNetworkReply*, QList<QSslError> )));


		// show cursor again since user is about to interact.
		req.setHeader(QNetworkRequest::ContentTypeHeader,
				"application/x-www-form-urlencoded");

		_webView->load(req, QNetworkAccessManager::PostOperation, postData);
	}
	// TODO: error page if no host.
}
void fbgui::sslErrorHandler(QNetworkReply* rep, QList<QSslError> errList) {
		LOG4CXX_DEBUG(coreLogger, "Received SSL errors:");
		//rep->ignoreSslErrors(errList);
		QList<QSslError>::iterator n;
		for (n = errList.begin(); n != errList.end(); ++n) {
			LOG4CXX_DEBUG(coreLogger, "Error :" << n->errorString());
		}
		LOG4CXX_DEBUG(coreLogger, "End SSL errors:");
}
void fbgui::loadURLDone(bool success) {
	// done contains the success of the loading: false / true
	if (!success) {
		LOG4CXX_DEBUG(coreLogger,
				"Loading failed. URL: " << _webView->url().toString());
		LOG4CXX_DEBUG(coreLogger, "You can quit with CTRL + X ...");
		// TODO handle failure properly...
	} else {
		LOG4CXX_DEBUG(coreLogger, "Loaded URL: " << _webView->url().toString());
	}
}
//-------------------------------------------------------------------------------------------
/**
 * This method clears all cookies and starts a new pbs-session.
 *
 */

void fbgui::clearAllCookies() {

	LOG4CXX_DEBUG(coreLogger, "Clearing cookies...");

	// delete all cookies.
	QNetworkCookieJar* cookieJar = new QNetworkCookieJar();
	_webView->page()->networkAccessManager()->setCookieJar(cookieJar);

	// start new session with pbs
	loadURL();
}
//-------------------------------------------------------------------------------------------
/**
 * This method prints cookies [DEBUGGING]
 *
 */
void fbgui::printCookies() {

	QList<QNetworkCookie> cookieList = _webView->page()->networkAccessManager()->cookieJar()->cookiesForUrl(_webView->page()->mainFrame()->url());

	if (cookieList.isEmpty()) {
		LOG4CXX_DEBUG(coreLogger, "No cookies saved!");
	}
	else {
		QList<QNetworkCookie>::iterator i;
		for (i = cookieList.begin(); i != cookieList.end(); ++i) {
			if (i->domain() == baseURL.host() || i->domain() == "." + baseURL.host()) {
				LOG4CXX_DEBUG(coreLogger, "Keeping cookie from domain '" << i->domain() << "': " << i->toRawForm());
			}
			else {
				LOG4CXX_DEBUG(coreLogger, "Deleting cookie from domain '" << i->domain() << "': " << i->toRawForm());
			}
		}
	}
}
//-------------------------------------------------------------------------------------------
/**
 * This method prints site history [DEBUGGING]
 *
 */
void fbgui::printHistory() {
	LOG4CXX_DEBUG(coreLogger, "-----------------------------------------------------------------------------------");
	QList<QUrl> urls;
	QList<QWebHistoryItem> history = _webView->history()->backItems(10);
	QList<QWebHistoryItem>::iterator n;
	for (n = history.begin(); n != history.end(); ++n) {
		LOG4CXX_DEBUG(coreLogger, "History item: " << n->url().toString());
		if (!(urls.contains(n->url()))) {
			urls.append(n->url());
			LOG4CXX_DEBUG(coreLogger, "Appended: " << n->url().toString());
		}
	}

	QList<QUrl>::iterator u;
	for (u = urls.begin(); u != urls.end(); ++u) {
		LOG4CXX_DEBUG(coreLogger, "Cookie from URL " << u->toString());
		QList<QNetworkCookie> cookieList = _webView->page()->networkAccessManager()->cookieJar()->cookiesForUrl(*u);
		if (cookieList.isEmpty()) {
			LOG4CXX_DEBUG(coreLogger, "No cookies saved!");
		}
		else {
			QList<QNetworkCookie>::iterator i;
			for (i = cookieList.begin(); i != cookieList.end(); ++i) {
				if (i->domain() == baseURL.host()) {
					LOG4CXX_DEBUG(coreLogger, "Keeping cookie from domain '" << i->domain() << "': " << i->toRawForm());
				}
				else {
					LOG4CXX_DEBUG(coreLogger, "Deleting cookie from domain '" << i->domain() << "': " << i->toRawForm());
				}
			}
		}
	}
	LOG4CXX_DEBUG(coreLogger, "-----------------------------------------------------------------------------------");
}

//-------------------------------------------------------------------------------------------
/**
 * This method generates the POST data body.
 *
 * This method generates the POST data body. The body contains the
 * MAC address, an hardwarehash and a specific serial number.
 * The hardwarehash is a MD5 hash over the MAC address and the
 * mainboard serial number.
 * The specific serial number is set at the creation of the usb boot stick.
 * This file has to be present on the directory specified in
 * the configuration for this to work.
 *
 * @see SysInfo::getMACAddress()
 * @see SysInfo::getMainboardSerial()
 */
QByteArray fbgui::generatePOSTData() {
	LOG4CXX_DEBUG(coreLogger, "Generating POST data...");
	// use MAC address as base data
	SysInfo si;
	QByteArray data;
	QByteArray macAdress(si.getInfo("mac").toUtf8());
	if (!macAdress.isEmpty())
		data.append(macAdress);
	else {
		LOG4CXX_DEBUG(coreLogger, "No readable MAC Address.");
	}
	// append mainboard serial to the mac address for more unique hardwarehash
	QByteArray mbserial(si.getInfo("mbserial").toUtf8());
	if (!mbserial.isEmpty())
		data.append(mbserial);
	else {
		LOG4CXX_DEBUG(coreLogger,
				"Mainboard serial was empty. Not appending to base hash data.");
	}LOG4CXX_DEBUG(coreLogger, "[post] Hashing: " << data);
	// generate MD5 hash of data
	QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Md5);
	LOG4CXX_DEBUG(coreLogger, "[post] MD5 Hash: " << hash.toHex());

	// fetch serial number from usb
	QByteArray serial;
	QFile file(serialLocation);
	if (!file.open(QIODevice::ReadOnly)) {
		LOG4CXX_DEBUG(coreLogger, "[post] No such file: " << file.fileName());
	}
	// everything ok, read data
	serial = file.readAll();
	file.close();
	serial.chop(1);
	LOG4CXX_DEBUG(coreLogger, "[post] Serial number is: " << serial);

	if (gInterfaceName.isEmpty())
		gInterfaceName = "eth0";

	// construct final byte array
	QByteArray postData("mac=");
	postData.append(si.getInfo("mac"));
	postData.append("&hardwarehash=" + hash.toHex());
	postData.append("&serialnumber=" + serial);
	LOG4CXX_DEBUG(coreLogger, "[post] POST data: " << postData);
	return postData;
}
//-------------------------------------------------------------------------------------------
//                   Preparing Kernel Switch per kexec (initiating Stage 3)
//-------------------------------------------------------------------------------------------
void fbgui::loadSystem() {
	//show loading system page.
	//_webView->disconnect(this);
	//QObject::connect(_webView, SIGNAL(loadFinished(bool)), this, SLOT(prepareKexec()));
	_webView->load(QUrl("qrc:/html/loadsystem.html"));
	QTimer::singleShot(1000, this, SLOT(prepareKexec()));
}
//-------------------------------------------------------------------------------------------
/**
 * This method prepares kexec.
 *
 * The kernel command line file that should have been downloaded from the Preboot-Server
 * and the ip config file (created by udhcpc) are merged into the final completed KCL.
 *
 * A process is then started to load the kernel, initramfs and kcl into kexec.
 * The process tries to execute kexec -l with these parameters.
 *
 * If this succeeds, runKexec() is called
 *
 * @see fbgui::runKexec()
 *
 */
void fbgui::prepareKexec() {

	LOG4CXX_DEBUG(coreLogger, "Preparing kexec ...");
	// try to read KCL file that was downloaded.
	QFile file(downloadPath + "/kcl");
	if (!file.open(QIODevice::ReadOnly)) {
		LOG4CXX_DEBUG(coreLogger, "No such file: " << file.fileName());
	}
	// everything ok, read data.
	QString kcl = file.readAll();
	file.close();
	LOG4CXX_DEBUG(coreLogger, "KCL from PBS: " << kcl);

	// try to read ipconfig file generated by udhcpc.
	file.setFileName("/tmp/ip_config_fbgui");
	if (!file.open(QIODevice::ReadOnly)) {
		LOG4CXX_DEBUG(coreLogger, "No such file: " << file.fileName());
	}
	// everything ok, read data.
	QString ipConfig = file.readAll();
	file.close();
	LOG4CXX_DEBUG(coreLogger, "IP config: " << ipConfig);

	// append ipConfig
	kcl.append(" ip=");
	kcl.append(ipConfig);
	LOG4CXX_DEBUG(coreLogger, "Complete KCL: " << kcl);

	// load the kernel + initramfs + append of kcl into kexec.
	QProcess *process = new QProcess(this);
	QString cmdline = "kexec -l " + downloadPath.toUtf8() + "/kernel --initrd="
			+ downloadPath.toUtf8() + "/initramfs --append=\"" + kcl.toUtf8()
			+ "\"";
	LOG4CXX_DEBUG(coreLogger, "kexec cmdline: " << cmdline);
	process->start(cmdline);
	bool ret = process->waitForFinished();
	if (!ret) {
		LOG4CXX_DEBUG(coreLogger, "Failed to execute: " << cmdline);
		LOG4CXX_DEBUG(coreLogger, "Exiting in 5 seconds...");
		QTimer::singleShot(5000, this, SLOT(close()));
	} else {
		LOG4CXX_DEBUG(coreLogger, "Kexec load was successfull.");
		if (debugMode < 0) {
			// if process successfully finished, try to run kexec -e
			runKexec();
		} else {
			LOG4CXX_DEBUG(coreLogger,
					"Skipping execution of kexec - open debug shell.");
			LOG4CXX_DEBUG(coreLogger,
					"To start the system execute \"kexec -e\" in your shell.");
			close();
		}
	}
}
//-------------------------------------------------------------------------------------------
/**
 * This method tries to execute: kexec -e
 *
 * This method tries to execute: kexec -e
 *
 */
void fbgui::runKexec() {
	QProcess *process = new QProcess(this);
	process->start("kexec -e");
	if (!process->waitForStarted()) {
		LOG4CXX_DEBUG(coreLogger, "Failed to execute: kexec -e");
		LOG4CXX_DEBUG(coreLogger, "Exiting in 5 seconds...");
		QTimer::singleShot(5000, this, SLOT(close()));
		//TODO: Handle failure properly...
	}
}