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

                                
 







                                                      
                   
                  
                   
 
                 
                             
                    
                 

                         


                             
                
                   
                          
 
                                                                                             









                                                                      

                
 
                
                   
 
 




                      
                                                           
 


                                                          




                                                                        
                                                

                                                                                    
 















                                                                                            
 


                                                  
 



                                
                         

                                                                                             
                                                       
                                                                                             
   
                                                
  

                                                            
   
                               

                                                            

                                                                             

                   
                                                                                 

                  

                                                                                             
   
                                   







                                                                                         
                       
                     
                                                                                 


                                                         
                                          
                                          
 
                                                   
                                                                                                                                   


                                                                                 

                                                                               











                                                                                                                                     
 



                                                                     
                                                      
 





                                                                                
                                                           
                                                                                   
                                                                                               
                                                                                            
                                                                            
    
                                  
 
 


                                                            

                                                                                        
                                        
           
                                                                              

    

 
                                  
                                                                                     

                                  
                                                                
 




                                                                       

 
                              
                                                    



                                                                                  





                                                                    
                                                                          



                                          
                                                                           


                                                                              

                                                                                                            








                                                                    

 

                                                             
                                                                      

                                                                                                    
 
                                                               
                                        
                                         
 
 





                                                                   
 
 
 
                                                                                             



                                                                  
                                                             


                                                                           

                                                            



                                     
                                      
                                                        







                                                                             
                                                                                                
    
                                                         

                                                                             
                                                                  
 



                                         
                                                                            




                              
                                                                    
 




                                                    
                                                               
                   
 
                                                                                             
                                                                           
                                                                                             







                                                                                             
   



                                                                                        
  






                                                                              


                            
                                                    


                                               
                                                                     



                                
                                                      
 


                                                    
                                                                     



                                     
                                                        
 


                        
                                                      
 


                                                                              

                                                                          
                                                           


                                         

                                                                  
                                                    
           
                                                               



                                                                 

                                                                                              


                 
 



                                                                                             
                                         

   
                        


                                          

                                                               
                                                    


                                        
#include "fbgui.h"
#include "sysinfo.h"
#include "downloadmanager.h"
#include "javascriptinterface.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>

QThread dmThread;
QString ipConfigFilePath("");
QString binPath("");
QUrl baseURL("");
QString downloadPath("");
int updateInterval = -1;
QString fileToTriggerURL("");
QString serialLocation("");
QString sessionID("");
bool sslSupport;
//int debugMode=-1;
//QString logFilePath("");

//-------------------------------------------------------------------------------------------
/**
 * 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...");

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

	// initialize javascript interface
	JavascriptInterface* jsi = new JavascriptInterface(
			_webView->page()->mainFrame());
	QObject::connect(jsi, SIGNAL(quitFbgui()), this, SLOT(close()));
	QObject::connect(jsi, SIGNAL(shutDownClient()), this,
			SLOT(shutdownSystem()));
	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();

	loadURL();

	// set properties
	setWindowTitle("fbgui");
	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
      _qnr = new QNetworkRequest(baseURL);

      //Add OpenSLX Certificate to SSLConfiguration
      QList<QSslCertificate> certList = QSslCertificate::fromPath(QLatin1String("/usr/lib/ssl/openslx/CA/certs/openslx-cert.pem"));
      setCACertificates(certList);

      //Ignore the SelfSignedCertificateInChain-error for the OpenSLX-Certificate
      QSslError error(QSslError::SelfSignedCertificateInChain, certList.at(0));
      _expectedSslErrors.append(error);

      //Add User Certificate to SSLConfiguration
      QList<QSslCertificate> userCertList = QSslCertificate::fromPath(QLatin1String("/usr/lib/ssl/openslx/CA/certs/guest-cert.pem"));
      setLocalCertificate(userCertList.at(0));

      //Add User PrivateKey to SSLConfiguration
      QFile keyFile("/usr/lib/ssl/openslx/CA/private/guest.pem");
      Q_ASSERT(keyFile.open(QIODevice::ReadOnly));
      QByteArray keyContent = keyFile.readAll();
      setPrivateKey(QSslKey(keyContent, QSsl::Rsa));



      //Connect webViews NetworkAccessManager to SSLErrorHandler SLOT
      QObject::connect(_webView->page()->networkAccessManager(),
              SIGNAL(finished(QNetworkReply*)),
              this,
              SLOT(httpErrorHandler(QNetworkReply*)));

      //Connect webViews NetworkAccessManager to ErrorHandler SLOT
      QObject::connect(_webView->page()->networkAccessManager(),
              SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> & )),
              this,
              SLOT(sslErrorHandler(QNetworkReply*, const QList<QSslError> & )));

      // show cursor again since user is about to interact.
      //QWSServer::instance()->setCursorVisible(true); //TODO: ?enabled in original
      _qnr->setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
      QObject::connect(_webView, SIGNAL(loadFinished(bool)), this, SLOT(loadURLDone(bool)));
      _webView->load(*_qnr, QNetworkAccessManager::PostOperation, postData);
   }
   // TODO: error page if no host.
}

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());
   }
}


//Handles QNetworkReply SSL Errors
void fbgui::sslErrorHandler(QNetworkReply* reply, const QList<QSslError> & errlist) {

  foreach (QSslError err, errlist)
	LOG4CXX_DEBUG(coreLogger, "SSL Error: " << err.error());

//  QSslConfiguration replySslConf = reply->sslConfiguration();
//  foreach (QSslCertificate cert, replySslConf.peerCertificateChain())
//  	  LOG4CXX_DEBUG(coreLogger,"Cert info: \n" << cert.toPem());

  reply->ignoreSslErrors(_expectedSslErrors);
}

//Handles QNetworkReply Errors
void fbgui::httpErrorHandler(QNetworkReply* reply) {
	if(reply->error() != QNetworkReply::NoError )
		LOG4CXX_DEBUG(coreLogger, "HTTP Error: " << reply->errorString());
}

//Dump all Certificates in SSLConfiguration
void fbgui::dumpCACertificates(){
	QSslConfiguration sslConfig = _qnr->sslConfiguration();
	QList<QSslCertificate> caCerts = sslConfig.caCertificates();

	foreach (QSslCertificate cert, caCerts){
		LOG4CXX_DEBUG(coreLogger,"Cert info: \n" << cert.toPem());
	}
}

//Saves CACertificates to SslConfiguration
void fbgui::setCACertificates(const QList<QSslCertificate> & certificates){

	foreach (QSslCertificate cert, certificates){
		LOG4CXX_DEBUG(coreLogger,"Is cert valid: " << cert.isValid());
		LOG4CXX_DEBUG(coreLogger,"Cert Issuer: " << cert.issuerInfo(QSslCertificate::CommonName));
		LOG4CXX_DEBUG(coreLogger,"Cert Subject: " << cert.subjectInfo(QSslCertificate::CommonName));
	}

	QSslConfiguration sslConfig = _qnr->sslConfiguration();

	QList<QSslCertificate> caCerts = sslConfig.caCertificates();
	caCerts.append(certificates);
    sslConfig.setCaCertificates(caCerts);
    _qnr->setSslConfiguration(sslConfig);

}

//Saves User Certificate to SslConfiguration
void fbgui::setLocalCertificate(const QSslCertificate& cert){
	LOG4CXX_DEBUG(coreLogger,"Is cert valid: " << cert.isValid());
	LOG4CXX_DEBUG(coreLogger,"Cert Issuer: " << cert.issuerInfo(QSslCertificate::CommonName));
	LOG4CXX_DEBUG(coreLogger,"Cert Subject: " << cert.subjectInfo(QSslCertificate::CommonName));

	QSslConfiguration sslConfig = _qnr->sslConfiguration();
    sslConfig.setLocalCertificate(cert);
    _qnr->setSslConfiguration(sslConfig);
}

//Saves PrivateKey to SslConfiguration
void fbgui::setPrivateKey(const QSslKey & key){
	LOG4CXX_DEBUG(coreLogger,"Is key valid: " << key.isNull());
	QSslConfiguration sslConfig = _qnr->sslConfiguration();
    sslConfig.setPrivateKey(key);
    _qnr->setSslConfiguration(sslConfig);
}


//-------------------------------------------------------------------------------------------
/**
 * 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(si.getInfo("mac").toUtf8());
   // 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); // chop EOF
   LOG4CXX_DEBUG(coreLogger, "[post] Serial number is: " << serial);

   // 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...
   }
}