relatorio trabalho 1 pac ufmg
DESCRIPTION
Relatório do primeiro trabalho prático da disciplina Projetos Assistidos por Computador (PAC) UFMG 2015/2.TRANSCRIPT
ELE038: Projeto Assistido por Computador
Primeiro trabalho
Esdras Vitor Silva Pinto
Professor: Renato Cardoso Mesquita
15-10-2015
I. Introdução
Neste primeiro trabalho prático é implementado uma interface gráfica para uma aplicação que
cria chaves de segurança para um arquivo utilizando os algoritmos MD5 ou SHA1. O desenvolvimento da
interface gráfica é desenvolvido na plataforma Qt Creator. Para a geração das chaves de segurança
mencionadas anteriormente, utiliza-se a aplicação File Checsum Integrity Verification (FCIV), a qual é
disponível gratuitamente no site da Microsoft.
II. Classes customizadas utilizadas na interface gráfica
A. Requisito de projeto
No paradigma da programação orientada a objetos, as classes desempenham um papel central no
desenvolvimento de uma aplicação, já que elas reúnem características e comportamentos de objetos reais
e abstratos que nos rodeiam. Desta forma, é natural pensar que cada tela de uma interface pode ser
representada por uma classe, já que cada uma delas apresentará o seu próprio comportamento e
características. Para o projeto das telas da interface gráfica, foram levadas em consideração os seguintes
requisitos de projeto:
- A interface gráfica deve permitir ao usuário as seguintes funcionalidades:
- Criar chaves de segurança para um dado arquivo usando o algoritmo MD5 ou SHA1.
- Verifica a integridade de um arquivo por meio de comparação de chaves.
- Visualizar uma lista de log contendo o nome e as chaves dos arquivos que foram criados
utilizando a interface gráfica. Adicionalmente, a lista de log deve também registrar a data
e a hora que a chave foi gerada.
Nas seções seguintes será analisado o projeto das classes desenvolvidas para a interface gráfica.
B. Classes desenvolvidas para a interface gráfica
A interface gráfica consiste em cinco telas: Menu Principal, Menu Criar Chave Segurança, Menu Log,
Menu Verificar Integridade Arquivo, e Menu de Configurações. Cada uma dessas telas está associada a
uma classe customizada, isto é. classes que foram desenvolvidas especificamente para a aplicação da
interface gráfica.
Essas classes customizadas são analisadas a seguir.
B.1. Classe MenuPrincipal
A classe MenuPrincipal contém todos o widgets que formam a tela mostrada a seguir.
Figure 1: Tela do menu principal
A tela mostrada anteriormente é a tela principal do programa, e ela contém as opções para todos
os recursos discutidos na seção II-A. A definição desta classe é mostrada abaixo.
// Definição da classe do menu principal.
class MenuPrincipal : public QWidget
{
public:
// Widgets do menu principal.
...
// Instala os filtros de eventos do menu principal.
void instalarFiltrosDeEventos(QWidget *parent);
// Verifica se um dado objeto é um CommandLinkButton membro do menu
principal.
// Retorna true se verdadeiro e false caso contrário.
bool isMenuCommandLinkButton(QObject *obj);
};
Os métodos desta classe são discutidos na seção IV (Visão geral do funcionamento do programa).
B.2. Classe MenuCriarChaveSeguranca
A classe MenuCriarChaveSeguranca contém todos o widgets que formam a tela mostrada a
seguir. Essa tela permite ao usuário escolher um arquivo para a geração da chave de segurança bem com a
escolha do algoritmo que será utilizada na criação de tal chave.
Figure 2: Tela do menu criar chave de segurança
A definição desta classe é mostrada abaixo.
// Definição da classe do menu Criar Chave de Segurança.
class MenuCriarChaveSeguranca : public QWidget
{
public:
// Widgets do menu criar chave de segurança.
...
// Contrutor da classe
MenuCriarChaveSeguranca(QWidget *parent = 0);
// Instala os filtros de eventos do menu criar chave de segurança.
void instalarFiltrosDeEventos(QWidget *parent);
// Verifica se um dado objeto é um CommandLinkButton membro do menu
principal.
// Retorna true se verdadeiro e false caso contrário.
bool isMenuCommandLinkButton(QObject *obj);
// Atualizar nome e directorio do arquivo
void atualizarArquivoInfo(QString arquivo);
};
O objetivo de cada método presente nesta classe é discutido na seção IV (Visão geral do
funcionamento do programa). Como pode ser visto na figura abaixo, a tela do menu de verificação de
chave de segurança é muito semelhante à tela de criação de chave. Sendo assim, esta tela pode ser
considerada como pertencente da classe MenuCriarChaveSegurança.
Figure 3: Tela do menu verificar integridade de arquivo
A aplicação da interface conta também com uma tela de configurações, conforme mostrada
abaixo.
Figure 4: Tela do menu de configurações.
Esta tela permite ao usuário informar à interface gráfica o diretório da aplicação fciv.exe. Como a
geração de chaves de segurança são feitas por esta aplicação, a interface gráfica só conseguirá operar
corretamente se o diretório do arquivo fciv.exe for informado corretamente. Para isto, a tela de
configuração conta com um dialogo de arquivo que permite ao usuário indicar o diretório da aplicação
fciv.exe.
Adicionalmente, a tela de configurações pode também ser considerada como pertencente da
classe MnuCriarChaveSegurança, ja que ela guarda traços da tela de criação de chave de segurança, como
a seleção de arquivos e o botão de retornar ao menu principal.
B.4. Classe FcivInterfaceGrafica
A classe FcivInterfaceGrafica está no topo da hierarquia dentre as classes mencionadas
anteriormente. Essa classe tem a função de gerenciar a mudança de telas da interface bem como realizar a
comunição com a aplicação FCVI. A definição desta classe é mostrada a seguir.
class FcivInterfaceGrafica : public QDialog
{
Q_OBJECT
public:
FcivInterfaceGrafica();
public slots:
// Realiza a leitura dos dados fornecidos pelo processo FCIV.
void readProcessData();
private:
// Filtro de eventos da interface.
bool FcivInterfaceGrafica::eventFilter(QObject *obj, QEvent *event);
// Verifica se o mouse esta dentro de um Widget.
bool isMouseDentroDoWidgest(QWidget *w, QMouseEvent *mouseEvent);
// Processa os eventos relacionados com o menu principal.
bool menuPrincipalProcessarEventos(QObject *obj, QEvent *event);
// Processa os eventos relacionados com o menu criar chave de segurança.
bool menuCriarChaveSegProcessarEventos(QObject *obj, QEvent *event);
// Processa os eventos relacionados com o menu log.
bool menuLogProcessarEventos(QObject *obj, QEvent *event);
// Processa os eventos relacionados com o menu Verificar Chave.
bool FcivInterfaceGrafica::menuVerificarChaveProcessarEventos(QObject
*obj, QEvent *event);
// Referências para os menus.
MenuCriarChaveSeguranca *menuCriarChave;
MenuPrincipal *menuPrincipal;
MenuLog *menuLog;
MenuCriarChaveSeguranca *menuVerificarChave;
MenuCriarChaveSeguranca *menuConfig;
// Referência para a lista dos menus
QStackedWidget *menus;
// Diretorio do processo responsável pela implementação dos algoritmos
MD5 e ASH1
QString FCIVprocessDiretorio;
// Processo responsável pela implementação dos algoritmos MD5 e ASH1
QProcess *processFCIV;
};
Os métodos pertencentes a esta classe são analisadas na seção IV (Visão geral do funcionamento
do programa).
IV. Visão geral do funcionamento do programa
Nesta seção é apresentada uma visão geral do programa, isto é, os pontos mais importantes da
lógica da implementação da interface gráfica são discutidos.
A. Gerenciamento das telas de interface
Conforme discutido anteriormente, algumas classes foram criadas para representar as telas da
interface. Portanto, é necessário instanciar um objeto para cada uma dessas telas. Além disso, como esses
objetos possuem diferentes telas, precisamos de um mecanismo que nos permita alterar uma tela para
outra. A opção adotada neste trabalho foi a de utilizar a classe QStackedWidget, já que ela nos permite
criar uma fila de telas sendo que apenas uma ficará ativa por vez. O fragmento de código abaixo mostra o
construtor da classe FcivInterfaceGrafica, onde as telas de menus são criadas e algumas inicializações
adicionais são feitas.
// Construtor da interface gráfica do FCIV
FcivInterfaceGrafica::FcivInterfaceGrafica()
{
// Cria instâncias para menus.
menuPrincipal = new MenuPrincipal;
menuCriarChave = new MenuCriarChaveSeguranca;
menuLog = new MenuLog;
menuVerificarChave = new MenuCriarChaveSeguranca;
menuConfig = new MenuCriarChaveSeguranca;
// Ativa o eventFilter para os menus
menuPrincipal->instalarFiltrosDeEventos(this);
menuCriarChave->instalarFiltrosDeEventos(this);
menuLog->instalarFiltrosDeEventos(this);
menuVerificarChave->instalarFiltrosDeEventos(this);
menuConfig->instalarFiltrosDeEventos(this);
// Instancia uma lista para armazenar os menus
menus = new QStackedWidget;
// Adiciona os menus na lista de menus
menus->addWidget(menuPrincipal);
menus->addWidget(menuCriarChave);
menus->addWidget(menuLog);
menus->addWidget(menuVerificarChave);
menus->addWidget(menuConfig);
// Instancia um layout do tipo grid para colocar os
// menus.
QGridLayout *gridLayout = new QGridLayout;
// Adiciona a fila de menus ao grid
gridLayout->addWidget(menus,0,0,1,1);
// Instancia o layout da janela principal
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(gridLayout);
mainLayout->setSpacing(6);
// Seta Layout da janela principal
setLayout(mainLayout);
// Conecta os slots dos objetos dos menus ao padrão do QT.
QMetaObject::connectSlotsByName(this);
// Instancia e inicia o diretorio do processo responsável pela
implementação dos algoritmos MD5 e ASH1
processFCIV = new QProcess(this);
FCIVprocessDiretorio = "./fciv.exe";
}
B. Gerenciamento dos botões da interface
Cada tela possui seus botões particulares, e cada botão executará uma função distinta. Para
permitir que possamos tratar os casos em que um dos botões da interface é pressionado, ativamos o filtro
de eventos para todos os botões das telas de interface. A ativação do filtro de eventos permite que o
programa seja direcionado para um método de tratamento desses eventos. Especificamente, qualquer
evento relacionado com os botões são direcionados para o método FcivInterfaceGrafica::eventFilter(...).
Uma vez que os eventos relacionados com todas as telas são direcionados para o mesmo lugar, é feito o
tratamento específico para cada tela. O código a seguir mostra a implementação do método
FcivInterfaceGrafica::eventFilter(...).
// Filtro de eventos dos menus.
bool FcivInterfaceGrafica::eventFilter(QObject *obj, QEvent *event)
{
// Processa os eventos associados com os menus
if(menus->currentWidget() == menuPrincipal)
FcivInterfaceGrafica::menuPrincipalProcessarEventos(obj,event);
if(menus->currentWidget() == menuCriarChave)
FcivInterfaceGrafica::menuCriarChaveSegProcessarEventos(obj,event);
if(menus->currentWidget() == menuLog)
FcivInterfaceGrafica::menuLogProcessarEventos(obj,event);
if(menus->currentWidget() == menuVerificarChave)
FcivInterfaceGrafica::menuVerificarChaveProcessarEventos(obj,event);
if(menus->currentWidget() == menuConfig)
FcivInterfaceGrafica::menuConfiguracoesProcessarEventos(obj,event);
return QWidget::eventFilter(obj,event);
}
C. Gerenciamento do Menu Principal
O gerenciamento do menu principal é feito pelo método FcivInterfaceGrafica
::menuPrincipalProcessarEventos(...). Quando o usuário clica no botão Criar Chave de Segurança, um
evento de clique do botão é disparado, e, após o tratamento deste evento, o programa irá mudar a tela do
aplicativo para a tela de criação de chave de segurança. O fragmento de código a seguir é executado
quando este botão é clicado.
// Ativa a tela de criação de chave de segurança;
menus->setCurrentWidget(menuCriarChave);
// Inicializa o status dos widgets presentes no menu
menuCriarChave->gerarChaveDeSegurancaButton->setEnabled(false);
menuCriarChave->salvarChaveTxtButton->setVisible(false);
menuCriarChave->atualizarArquivoInfo("");
menuCriarChave->md5RadioButton->setChecked(true);
menuCriarChave->chaveLineEdit->clear();
menuCriarChave->arquivoChaveSeg.clear();
A implementação dos demais botões segue a mesma filosofia, onde a diferença se encontra na tela
que será ativada bem como algumas inicializações de rotina.
D. Gerenciamento do Menu Criar Chave De Segurança
Todos os eventos relacionados com os botões são tratados pelo método FcivInterfaceGrafica::
menuCriarChaveSegProcessarEventos(...). Quando o botão "Selecionar arquivo" é pressionado, um
diálogo de arquivo é aberto para permitir que o usuário escolha o arquivo no qual será gerado a chave de
segurança. O código que executa esta função é mostrado a seguir.
// Abre a tela de dialogo para a seleção do arquivo.
QString arquivoDiretorio;
arquivoDiretorio =
QFileDialog::getOpenFileName(0,QString(),QString(),0,0);
// Se um arquivo foi selecionado...
if(arquivoDiretorio != NULL)
{
// Atualiza o nome do arquivo e seu respectivo diretorio no menu
menuCriarChave->atualizarArquivoInfo(arquivoDiretorio);
// Habilita o botao de gerar chave de segurança.
menuCriarChave->gerarChaveDeSegurancaButton->setEnabled(true);
// Limpa a chave de segurança.
//menuCriarChave->arquivoChaveSeg.clear();
menuCriarChave->arquivoChaveSeg = "";
menuCriarChave->chaveLineEdit->clear();
menuCriarChave->chaveLineEdit->setEnabled(false);
}
else
{
// Mantém o ultimo arquivo selecionado.
}
Após o usuário escolher o arquivo, ele também tem a opção de escolher o algoritmo de geração da
chave de segurança. Após a escolha do algoritmo, o usuário poderá gerar a chave de segurança para o
arquivo selecionado clicando no botão "Gerar Chave de Segurança". Quando este botão é clicado, a
interface gráfica chama a aplicação FCIV para a geração da chave de segurança. Para permitir a
comunição entre a interface gráfica e a aplicação FCIV, utiliza-se a classe QProcess. O fragmento de
código a seguir é usado para inicializar um objeto da classe Qprocess, construir os argumentos da
aplicação FCIV e por fim executar esta aplicação com os argumentos fornecidos.
// Seleciona o canal de saida de dados padrão do aplicativo
processFCIV->setReadChannel(QProcess::StandardOutput);
// Sempre que o canal de saída de dados padrão do processo FCIV estiver
pronto para
// leitura, será chamada um método para ler esses dados.
connect(processFCIV,SIGNAL(readyReadStandardOutput()),this,SLOT(readProcessDa
ta()));
// Adiciona à lista de argumentos o diretório do arquivo que se deseja obter
a chave de segurança.
QStringList argumentos;
argumentos << menuCriarChave->arquivoDiretorio;
// Adiciona o algoritimo de geração da chave de segurança à lista de
argumentos.
if(menuCriarChave->md5RadioButton->isChecked())
{
argumentos << "-md5";
}
else
{
argumentos << "-sha1";
}
// Executa o processo FCIV para obter a chave de segurança para o arquivo.
processFCIV->start(FCIVprocessDiretorio,argumentos);
menuCriarChave->chaveLineEdit->setText("Erro ao executar fciv.exe. Vá em
configurações");.
Uma vez que a aplicação FCIV é concluída, um sinal é emitido e coletado pelo slot
FcivInterfaceGrafica::readProcessData(). Internamente desse slot, é feito todo processamento para se
obter a chave de segurança do arquivo. A implementação deste slot é mostrado a seguir.
// Realiza a leitura dos dados fornecidos pelo processo FCIV.
void FcivInterfaceGrafica::readProcessData()
{
QString s;
s = processFCIV->readAllStandardOutput();
// Separa o diretorio em sub-diretorios individuais
QRegExp rx(" |\r\n");
QStringList strings = s.split(rx);
// Processa a string recebida e exibe a chave de segurança.
if(strings.count() > 10)
{
if(strings.at(9) != "")
{
QString chaveSeg = strings.at(9);
if(chaveSeg.count() == 32 || chaveSeg.count() == 40)
{
if(menus->currentWidget() == menuCriarChave)
{
if(chaveSeg.count() != menuCriarChave-
>arquivoChaveSeg.count())
{
// Atualiza a chave do objeto.
menuCriarChave->arquivoChaveSeg = chaveSeg;
// Exibe a chave para o usuário.
menuCriarChave->chaveLineEdit->setText(chaveSeg);
menuCriarChave->chaveLineEdit->setEnabled(true);
QDate date = QDate::currentDate();
QTime time = QTime::currentTime();
QFile file("log.txt");
if (!file.open(QIODevice::ReadWrite |
QIODevice::Text))
return;
QTextStream out(&file);
QString logAnterior = out.readAll();
out.seek(0);
out << "'" << menuCriarChave->arquivoNome << "'" <<
menuCriarChave->arquivoDiretorio <<"'" << menuCriarChave->arquivoChaveSeg <<
"'" << date.toString("dd.MM.yyyy") << "'"<< time.toString("hh:mm:ss") <<
logAnterior;
file.close();
}
}
// Verificação de integridade de um arquivo.
else if(menus->currentWidget() == menuVerificarChave)
{
// Verifica se a chave gerada para o arquivo coincide com
a chave
// fornecida pelo usuário.
if(chaveSeg == chaveVerificacao)
{
menuVerificarChave->gerarChaveDeSegurancaButton-
>setStyleSheet("background-color: green; color: white;");
menuVerificarChave->gerarChaveDeSegurancaButton-
>setText("Chaves iguais!");
qDebug() << "Chaves iguais";
}
else
{
menuVerificarChave->gerarChaveDeSegurancaButton-
>setStyleSheet("background-color: red; color: white;");
menuVerificarChave->gerarChaveDeSegurancaButton-
>setText("Chaves diferentes!");
qDebug() << "Chaves diferentes";
}
menuVerificarChave->chaveLineEdit-
>setText(chaveVerificacao);
}
}
else
{
if(menus->currentWidget() == menuVerificarChave)
{
menuVerificarChave->chaveLineEdit->setText("Erro ao
executar fciv.exe. Vá em configurações");
}
}
}
}
strings.clear();
}
Como pode ser visto no código acima, toda vez que é gerado uma chave para um arquivo,
salvamos esta chave juntamente com outras informações adicionais como nome do arquivo, diretório, e
dentre outros, em um arquivo de texto. Desta forma, o usuário poderá consultar todos os arquivos e suas
respectivas chaves que foram geradas anteriormente.
Finalmente, caso o usuário deseja voltar pra a tela principal, basta um clique no botão "Voltar
Menu principal".
E. Gerenciamento do Menu Verificar Integridade de um arquivo
As funcionalidades deste menu são muito semelhantes às funcionalidades do menu Criar Chave
de Segurança. A diferença neste caso é que, ao invés de gerar a chave para um arquivo e salvar no arquivo
de log, a chave gerada é comparada com uma chave fornecida pelo usuário.
F. Gerenciamento do Menu Visualizar Log
O menu de Visualizar Log, permite ao usuário consultar todos os arquivos e suas chaves
correspondentes que foram geradas anteriormente. Assim que o usuário clica no botão "Visualizar Log"
no menu principal, a tela da interface é direcionada para a tela de log. Alem disto, lemos do arquivo de
log e exibimos estas informações para o usuário. Estas tarefas são executadas pelo seguinte fragmento de
código:
// Muda para a tela de log.
menus->setCurrentWidget(menuLog);
// Carrega o arquivo de log
QFile file("log.txt");
if (!file.open(QIODevice::ReadOnly| QIODevice::Text))
return false;
QTextStream out(&file);
QString log = out.readAll();
out.seek(0);
// Calcula o numero de logs presentes no arquivo.
QRegExp rx("'");
QStringList logStrings = log.split(rx);
// Monta a string de logs para ser exibida.
QString logDisplay;
logStrings.pop_front();
while(!logStrings.isEmpty())
{
logDisplay += "*** Arquivo: " + logStrings.takeFirst() + "
*** " +"\r\n\n";
logDisplay += "-Diretorio: \n" + logStrings.takeFirst() +
"\r\n\n";
logDisplay += "-Chave Segurança: \n" +
logStrings.takeFirst() + "\r\n\n";
logDisplay += "-Data: \n" + logStrings.takeFirst() +
"\r\n\n";
logDisplay += "-Hora: \n" + logStrings.takeFirst() +
"\r\n\n";
logDisplay += "*****************************************
\r\n\n";
}
// Exibe a lista de logs
menuLog->logMenuTextBrowser->setText(logDisplay);
qDebug() << "numStrings: " << logStrings.count() << endl;
// out.seek(0);
// out << logAnterior <<" " <<menuCriarChave->arquivoNome << " "
<< menuCriarChave->arquivoChaveSeg << " " << date.toString("dd.MM.yyyy") << "
"<< time.toString("hh:mm:ss") << "\r\n";
file.close();
V. Resultados: Demonstração de funcionamento da interface gráfica.
Primeiramente, é importante informar à interface gráfica o diretório da aplicação fciv.exe. Para
isto, o usuário pode utilizar o menu de configurações, conforme mostrado abaixo.
Suponhamos que desejamos criar uma chave de segurança para um arquivo chamado myTest.txt,
cujo conteúdo é PAC-UFMG-2015-2. Inicialmente, devemos entrar no menu de criação de chave de
segurança. Uma vez neste menu, selecionamos o arquivo desejado, conforme ilustrado a seguir.
Após a escolha do arquivo, o usuário deverá selecionar o algoritmo para a geração da chave de
segurança. Uma vez escolhido o algoritmo, a chave de segurança é gerada ao clicar no botão "Gerar chave
de segurança". Logo depois, a interface gráfica mostra o valor da chave. Essa etapa é mostrada nas figuras
abaixo.
Se, por exemplo, o usuário não tivesse informado o diretório da aplicação fciv.exe, a interface
gráfica iria procurar por esta aplicação no diretório "c:/". Caso esta aplicação não esteja neste diretório,
uma mensagem de erro seria exiba no lugar da chave, como ilustrado a seguir.
Para solucionar tal erro, basta informar o diretório da aplicação fciv.exe no menu de
configurações, conforme mostrado abaixo.
A interface gráfica também disponibiliza o recurso de visualização de log, que pode ser acessado
clicando-se no botão "Visualizar Log" no menu principal. Neste exemplo, ao clicar neste botão, aparecerá
o log do arquivo myTest.txt utilizado anteriormente para a geração da chave de segurança, como indicado
abaixo.
Finalmente, a interface gráfica também conta com um recurso para comparação de chaves, isto é,
verificação da chave gerada para um arquivo contra uma chave fornecida pelo usuário. No menu de
verificação de chaves, o usuário pode escolher o arquivo que se deseja verificar bem como fornecer a
chave de comparação. Vamos escolher o arquivo myTest.txt e fornecer a chave de segurança gerada para
este arquivo anteriormente, conforme ilustrado a seguir.
A barra de status da verificação que se encontra na parte superior à chave de segurança, indica
inicialmente que a verificação não foi iniciada. Ao clicar no botão "Verificar Integridade", o status da
verificação muda para chaves iguais, indicando que a chave fornecida coincide com a chave gerada para o
arquivo, conforme pode ser visto na figura abaixo.
Se modificarmos o arquivo myTest.txt para PAC-UFMG e repetirmos o procedimento acima
mantendo a chave antiga que geramos para myTest.txt, a verificação detectará que as chaves são
diferentes, e informará ao usuário que a chave fornecida pelo usuário não coincide com a chave gerada
para o arquivo. Esta situação é ilustrada abaixo.
VI. Conclusão
Neste trabalho prático, desenvolveu-se uma interface gráfica para uma aplicação que gera chaves
de segurança por meio dos algoritmos MD5 e SHA1. A interface gráfica possuem todos os recursos que
foram levantados no inicio do projeto. Embora haja muitas melhorias que podem serem feitas no código,
a interface gráfica desenvolvida é bem intuitiva e consideravelmente robusta, o que contribui para uma
boa experiência do usuário da interface.
Referências
[1] Documentação das classes do Qt: doc.qt.io/qt-5/
[2] Duvidas e sugestões sobre Qt: http://stackoverflow.com/questions/