mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-13 09:54:27 -05:00
452 lines
13 KiB
C++
452 lines
13 KiB
C++
|
/****************************************************************************
|
||
|
***************************Author: Agnit Sarkar******************************
|
||
|
**************************CopyRight: April 2009******************************
|
||
|
********************* Email: agnitsarkar@yahoo.co.uk*************************
|
||
|
****************************************************************************/
|
||
|
#include "stegosaurus.h"
|
||
|
|
||
|
StegoSaurus::StegoSaurus(QWidget *parent, Qt::WFlags flags)
|
||
|
: QDialog(parent, flags)
|
||
|
{
|
||
|
ui.setupUi(this);
|
||
|
bitsAvailable=0;
|
||
|
sizeofData=0;
|
||
|
inputFileSize=0;
|
||
|
}
|
||
|
|
||
|
StegoSaurus::~StegoSaurus()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
void StegoSaurus::on_btnDryImg_clicked()
|
||
|
{
|
||
|
static QString srcImgFileName="/home/";
|
||
|
srcImgFileName= QFileDialog::getOpenFileName(this, "Open Bitmap", srcImgFileName, "Bitmap Files(*.bmp)");
|
||
|
|
||
|
if(srcImgFileName.isEmpty()){
|
||
|
ui.lblDryImgDisplay->setText("<h2>Drag Image Here<br><br>OR<br><br>Select it by browsing<br><br>(BMP Images only)</h2>");
|
||
|
ui.lblDryImgDisplay->setPixmap(QPixmap(":/StegoSaurus/images/stegosaurus-256x256.png"));
|
||
|
bitsAvailable=0;
|
||
|
ui.txtDryImg->setText("");
|
||
|
ui.lblDryImgInfo->setText("No image Loaded");
|
||
|
return;
|
||
|
}
|
||
|
setDryImage(srcImgFileName);
|
||
|
|
||
|
}
|
||
|
|
||
|
void StegoSaurus::on_btnFiletoHide_clicked()
|
||
|
{
|
||
|
static QString srcFileName="/home/";
|
||
|
srcFileName= QFileDialog::getOpenFileName(this, "Open file to hide", srcFileName);
|
||
|
|
||
|
if(srcFileName.isEmpty()){
|
||
|
inputFileSize=0;
|
||
|
ui.txtFiletoHide->setText("");
|
||
|
ui.lblFiletoHideinfo->setText("No file loaded");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Get File information
|
||
|
QFileInfo fi= QFileInfo(srcFileName);
|
||
|
inputFileSize= fi.size();
|
||
|
ui.txtFiletoHide->setText(srcFileName);
|
||
|
ui.lblFiletoHideinfo->setText(QString("Size=%L1 bytes").arg(inputFileSize));
|
||
|
|
||
|
}
|
||
|
|
||
|
void StegoSaurus::resizeEvent(QResizeEvent * event)
|
||
|
{
|
||
|
//Set the label image
|
||
|
if(ui.txtDryImg->text().isEmpty())
|
||
|
return;
|
||
|
QPixmap pixmap(ui.txtDryImg->text());
|
||
|
pixmap= pixmap.scaledToHeight(ui.lblDryImgDisplay->height());
|
||
|
ui.lblDryImgDisplay->setPixmap(pixmap);
|
||
|
|
||
|
QPixmap pixmap2(ui.txtWetImg->text());
|
||
|
pixmap2= pixmap2.scaledToHeight(ui.lblWetImgDisplay->height());
|
||
|
ui.lblWetImgDisplay->setPixmap(pixmap2);
|
||
|
}
|
||
|
|
||
|
void StegoSaurus::on_btnWetImg_clicked()
|
||
|
{
|
||
|
static QString srcImgFileName="/home/";
|
||
|
srcImgFileName= QFileDialog::getOpenFileName(this, "Open Bitmap", srcImgFileName, "Bitmap Files(*.bmp)");
|
||
|
|
||
|
if(srcImgFileName.isEmpty()){
|
||
|
ui.lblWetImgDisplay->setText("<h2>Drag Image Here<br><br>OR<br><br>Select it by browsing<br><br>(BMP Images only)</h2>");
|
||
|
ui.lblWetImgDisplay->setPixmap(QPixmap(":/StegoSaurus/images/stegosaurus-256x256.png"));
|
||
|
ui.lblWetImgInfo->setText("No image loaded");
|
||
|
ui.txtWetImg->setText("");
|
||
|
sizeofData=0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
setWetImage(srcImgFileName);
|
||
|
|
||
|
}
|
||
|
void StegoSaurus::setDryImage(const QString &srcImgFileName)
|
||
|
{
|
||
|
//Get file info
|
||
|
dryImage= new QImage(srcImgFileName);
|
||
|
unsigned int height= dryImage->height();
|
||
|
unsigned int width= dryImage->width();
|
||
|
int depth= dryImage->depth();
|
||
|
//Check the depth, and return if it is not 24-bit
|
||
|
if(depth<24){
|
||
|
QMessageBox::information(this, "StegoSaurus", "Must be 24-bit bitmap.");
|
||
|
ui.txtDryImg->setText("");
|
||
|
ui.lblDryImgInfo->setText("No image Loaded");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Set the label image
|
||
|
QPixmap pixmap(srcImgFileName);
|
||
|
pixmap= pixmap.scaledToHeight(ui.lblDryImgDisplay->height());
|
||
|
ui.lblDryImgDisplay->setPixmap(pixmap);
|
||
|
bitsAvailable= height*width*3;
|
||
|
|
||
|
ui.txtDryImg->setText(srcImgFileName);
|
||
|
|
||
|
if(bitsAvailable){
|
||
|
ui.lblDryImgInfo->setText(QString("Height= %L1 Width=%L2 Free Space=%L3 bytes").arg(height)
|
||
|
.arg(width).arg(bitsAvailable/8));
|
||
|
}else{
|
||
|
ui.lblDryImgInfo->setText("No room for data");
|
||
|
qApp->beep();
|
||
|
}
|
||
|
}
|
||
|
void StegoSaurus::setWetImage(const QString &srcImgFileName)
|
||
|
{
|
||
|
//Get file info
|
||
|
wetImage= new QImage(srcImgFileName);
|
||
|
unsigned int height= wetImage->height();
|
||
|
unsigned int width= wetImage->width();
|
||
|
int depth= wetImage->depth();
|
||
|
|
||
|
//Check the depth, and return if it is not 24-bit
|
||
|
if(depth<24){
|
||
|
QMessageBox::information(this, "StegoSaurus", "Must be 24-bit bitmap.");
|
||
|
ui.txtWetImg->setText("");
|
||
|
ui.lblWetImgInfo->setText("No image Loaded");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Set the label image
|
||
|
QPixmap pixmap(srcImgFileName);
|
||
|
pixmap= pixmap.scaledToHeight(ui.lblWetImgDisplay->height());
|
||
|
ui.lblWetImgDisplay->setPixmap(pixmap);
|
||
|
|
||
|
//Read the size of the embeded data
|
||
|
QFile imgFile(srcImgFileName);
|
||
|
if (!imgFile.open(QIODevice::ReadOnly)){
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to open bitmap file.");
|
||
|
return;
|
||
|
}
|
||
|
if(imgFile.seek(6)){
|
||
|
imgFile.read((char *)&sizeofData, 4);
|
||
|
}else{
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to read bitmap file.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ui.txtWetImg->setText(srcImgFileName);
|
||
|
|
||
|
if(sizeofData){
|
||
|
ui.lblWetImgInfo->setText(QString("Height= %L1 Width=%L2 Probable Size of Data=%L3 bytes").arg(height)
|
||
|
.arg(width).arg(sizeofData));
|
||
|
}else{
|
||
|
ui.lblWetImgInfo->setText("No data in image.");
|
||
|
qApp->beep();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//The drag and drop methods
|
||
|
void StegoSaurus::dragEnterEvent(QDragEnterEvent *event)
|
||
|
{
|
||
|
if(ui.tabWidget->currentIndex()==2)return;
|
||
|
|
||
|
if (event->mimeData()->hasFormat("text/uri-list")){
|
||
|
QList<QUrl> urls = event->mimeData()->urls();
|
||
|
if (urls.isEmpty())
|
||
|
return;
|
||
|
QString fileName = urls.first().toLocalFile();
|
||
|
//Check the extension
|
||
|
if (fileName.isEmpty()||!fileName.endsWith(".bmp", Qt::CaseInsensitive))
|
||
|
return;
|
||
|
event->acceptProposedAction();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void StegoSaurus::dropEvent(QDropEvent *event)
|
||
|
{
|
||
|
QList<QUrl> urls = event->mimeData()->urls();
|
||
|
if (urls.isEmpty())
|
||
|
return;
|
||
|
QString fileName = urls.first().toLocalFile();
|
||
|
if (fileName.isEmpty())
|
||
|
return;
|
||
|
|
||
|
switch(ui.tabWidget->currentIndex()){
|
||
|
case 0:
|
||
|
setDryImage(fileName);
|
||
|
break;
|
||
|
case 1:
|
||
|
setWetImage(fileName);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//The hide methods
|
||
|
void StegoSaurus::on_btnHide_clicked()
|
||
|
{
|
||
|
//Get the filenames
|
||
|
QString imgFileName= ui.txtDryImg->text();
|
||
|
QString inputFileName= ui.txtFiletoHide->text();
|
||
|
//Check if both image file and data file has been selected
|
||
|
if(imgFileName.isEmpty()||inputFileName.isEmpty()){
|
||
|
QMessageBox::information(this, "StegoSaurus", "Please select a bitmap image and a data file.");
|
||
|
return;
|
||
|
}
|
||
|
//Now check if size of the data file is less than that of the space available
|
||
|
if(inputFileSize*8>bitsAvailable){
|
||
|
QMessageBox::information(this, "StegoSaurus", "Not enough space available in image to hide the data.");
|
||
|
return;
|
||
|
}
|
||
|
static QString targetImgName="/home/";
|
||
|
targetImgName= QFileDialog::getSaveFileName(this, "Save bitmap with data to", targetImgName, "Bitmap Files(*.bmp)");
|
||
|
|
||
|
if(targetImgName.isEmpty())return;
|
||
|
|
||
|
//If an image file with the same name exists delete it and then copy, unless source and target are same
|
||
|
if(imgFileName!=inputFileName){
|
||
|
QFile::remove(targetImgName);
|
||
|
QFile::copy(imgFileName, targetImgName);
|
||
|
}
|
||
|
|
||
|
//Calculate the no. of padding bytes
|
||
|
unsigned int width= dryImage->width();
|
||
|
unsigned int padding=0;
|
||
|
if((width*3)%4==0)
|
||
|
padding=0;
|
||
|
unsigned int quotient= (width*3)/4;
|
||
|
padding= 4*(quotient+1)- width*3;
|
||
|
|
||
|
/////////////////////////////////Hide the data////////////////////////////////////////////
|
||
|
QFile inputFile(inputFileName);
|
||
|
if (!inputFile.open(QIODevice::ReadOnly)){
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to open data file for reading.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Stamp the size of the data file to the reserved bytes of the image file
|
||
|
QFile imgOutputFile(targetImgName);
|
||
|
if (!imgOutputFile.open(QIODevice::ReadWrite)){
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to open bitmap file.");
|
||
|
return;
|
||
|
}
|
||
|
if(imgOutputFile.seek(6)){
|
||
|
imgOutputFile.write((char *)&inputFileSize, 4);
|
||
|
}else{
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to write to bitmap file.");
|
||
|
return;
|
||
|
}
|
||
|
//Read the dataoffset of the bitmap
|
||
|
unsigned int dataoffset=0;
|
||
|
if(imgOutputFile.seek(10)){
|
||
|
imgOutputFile.read((char *)&dataoffset, 4);
|
||
|
}else{
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to read bitmap file.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Move by dataoffset bytes
|
||
|
if(!imgOutputFile.seek(dataoffset)){
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to read bitmap file.");
|
||
|
return;
|
||
|
}
|
||
|
QProgressDialog progress("Embedding Data", "Cancel", 0, inputFileSize, this);
|
||
|
progress.show();
|
||
|
unsigned int progresscount=0;
|
||
|
unsigned int countBytes=0;
|
||
|
while (!inputFile.atEnd()){
|
||
|
//Read a byte
|
||
|
unsigned char dataByte=0;
|
||
|
inputFile.getChar((char *)&dataByte);
|
||
|
|
||
|
//Store each bit of the data byte in 1 byte of the image pixel
|
||
|
//starting with the MSB
|
||
|
for(int i=7; i>=0; i--){
|
||
|
//Get the bit at position i in the data byte
|
||
|
unsigned char checkByte= 0x01;
|
||
|
checkByte= checkByte<<i;
|
||
|
unsigned char dataBit=(dataByte & checkByte)>>i;
|
||
|
|
||
|
unsigned char imageByte=0;
|
||
|
//To ignore the padding bytes
|
||
|
if(countBytes>=width*3){
|
||
|
countBytes= 0;
|
||
|
//Advance the read pointer
|
||
|
for(int j=0; j<padding; j++){
|
||
|
imgOutputFile.getChar((char *)&imageByte);
|
||
|
}
|
||
|
}
|
||
|
//Get a pixel byte from the bitmap
|
||
|
imgOutputFile.getChar((char *)&imageByte);
|
||
|
//Get the LSB
|
||
|
checkByte= imageByte & 0x01;
|
||
|
//Check if the databit and the LSB is different
|
||
|
if(dataBit!=checkByte){
|
||
|
//Alter the LSB to the databit
|
||
|
if(dataBit){
|
||
|
//If 1 set the LSB
|
||
|
imageByte= imageByte | 0x01;
|
||
|
}else{
|
||
|
//Reset the LSB
|
||
|
imageByte= imageByte & 0xFE;
|
||
|
}
|
||
|
//Get the current pointer position
|
||
|
int pos= imgOutputFile.pos();
|
||
|
//Put it back by 1 byte to write the current byte
|
||
|
if(!imgOutputFile.seek(pos-1)){
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to read bitmap file.");
|
||
|
return;
|
||
|
}
|
||
|
//Write the byte
|
||
|
imgOutputFile.putChar(imageByte);
|
||
|
}
|
||
|
countBytes++;
|
||
|
}
|
||
|
progresscount++;
|
||
|
progress.setValue(progresscount);
|
||
|
if (progress.wasCanceled()){
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Operation Aborted.");
|
||
|
imgOutputFile.close();
|
||
|
inputFile.close();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
progress.hide();
|
||
|
//Close the files
|
||
|
imgOutputFile.close();
|
||
|
inputFile.close();
|
||
|
|
||
|
QMessageBox::information(this, "StegoSaurus", "Data Embedded Successfully.");
|
||
|
|
||
|
}
|
||
|
|
||
|
//The recover methods
|
||
|
void StegoSaurus::on_btnRecover_clicked()
|
||
|
{
|
||
|
//Get the filename
|
||
|
QString imgFileName= ui.txtWetImg->text();
|
||
|
|
||
|
//Check image file has been selected
|
||
|
if(imgFileName.isEmpty()){
|
||
|
QMessageBox::information(this, "StegoSaurus", "Please select a bitmap image.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static QString targetFileName="/home/";
|
||
|
targetFileName= QFileDialog::getSaveFileName(this, "Save recovered data to", targetFileName, "All Files(*.*)");
|
||
|
|
||
|
if(targetFileName.isEmpty())return;
|
||
|
|
||
|
//If a file with the same name exists delete it
|
||
|
QFile::remove(targetFileName);
|
||
|
|
||
|
//Calculate the no. of padding bytes
|
||
|
unsigned int width= wetImage->width();
|
||
|
unsigned int padding=0;
|
||
|
if((width*3)%4==0)
|
||
|
padding=0;
|
||
|
unsigned int quotient= (width*3)/4;
|
||
|
padding= 4*(quotient+1)- width*3;
|
||
|
|
||
|
/////////////////////////////////Recover the data////////////////////////////////////////////
|
||
|
QFile targetFile(targetFileName);
|
||
|
if (!targetFile.open(QIODevice::WriteOnly)){
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to open data file.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
QFile imgInputFile(imgFileName);
|
||
|
if (!imgInputFile.open(QIODevice::ReadOnly)){
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to open bitmap file.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Read the dataoffset of the bitmap
|
||
|
unsigned int dataoffset=0;
|
||
|
if(imgInputFile.seek(10)){
|
||
|
imgInputFile.read((char *)&dataoffset, 4);
|
||
|
}else{
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to read bitmap file.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Move by dataoffset bytes
|
||
|
if(!imgInputFile.seek(dataoffset)){
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Unable to read bitmap file.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
QProgressDialog progress("Recovering Data", "Cancel", 0, sizeofData, this);
|
||
|
progress.show();
|
||
|
unsigned int progresscount=0;
|
||
|
unsigned int countBytes=0;
|
||
|
//Iterate to recover targetFileSize bytes
|
||
|
for(unsigned int byteNo= 0; byteNo< sizeofData; byteNo++){
|
||
|
//construct each byte
|
||
|
unsigned char dataByte= 0x00;
|
||
|
for(int bitNo=7; bitNo>=0; bitNo--){
|
||
|
unsigned char imageByte=0xFF;
|
||
|
//To ignore the padding bytes
|
||
|
if(countBytes>=width*3){
|
||
|
countBytes= 0;
|
||
|
//Advance the read pointer
|
||
|
for(int j=0; j<padding; j++){
|
||
|
imgInputFile.getChar((char *)&imageByte);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Read a byte from the bitmap file
|
||
|
imgInputFile.getChar((char *)&imageByte);
|
||
|
countBytes++;
|
||
|
//Get the LSB
|
||
|
unsigned char dataBit= imageByte & 0x01;
|
||
|
//Set the bit in data byte at bitNo position if databit is set
|
||
|
if(dataBit){
|
||
|
unsigned char tempByte= 0x01;
|
||
|
dataByte= dataByte | (tempByte<<bitNo);
|
||
|
}
|
||
|
}
|
||
|
//Write the byte
|
||
|
targetFile.putChar(dataByte);
|
||
|
|
||
|
//Update the progressbar
|
||
|
progresscount++;
|
||
|
progress.setValue(progresscount);
|
||
|
if (progress.wasCanceled()){
|
||
|
QMessageBox::warning(this, "StegoSaurus", "Operation Aborted.");
|
||
|
imgInputFile.close();
|
||
|
targetFile.close();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
progress.hide();
|
||
|
//Close the files
|
||
|
targetFile.close();
|
||
|
imgInputFile.close();
|
||
|
|
||
|
QMessageBox::information(this, "StegoSaurus", "Data Recovery Successful.");
|
||
|
|
||
|
|
||
|
}
|