2 * This file is part of Secret Notes.
3 * Copyright (C) 2010 Janusz Sobczak
5 * Secret Notes is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * Secret Notes is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with Secret Notes. If not, see <http://www.gnu.org/licenses/>.
19 #include "mainwindow.h"
20 #include "ui_mainwindow.h"
21 #include <qmessagebox.h>
24 #include <qdatastream.h>
25 #include <qinputdialog.h>
26 #include <openssl/blowfish.h>
27 #include <openssl/sha.h>
28 #include "passworddialog.h"
30 const char filename[] = "secretnotes.scn";
32 MainWindow::MainWindow(QWidget *parent) :
34 ui(new Ui::MainWindow),
36 hasPasswordChanged(false),
41 resetAction = ui->menuOptions->addAction(tr("Reset secret notes"));
42 connect(resetAction, SIGNAL(triggered()), this, SLOT(on_actionReset_secret_notes_triggered()));
44 enableTextEdit(false);
46 undoEditAction = ui->menuOptions->addAction(tr("Undo edit"));
47 connect(undoEditAction,SIGNAL(triggered()), this, SLOT(undoEdit()));
48 on_textEdit_undoAvailable(false);
50 changePasswordAction = ui->menuOptions->addAction(tr("Change password"));
51 connect(changePasswordAction, SIGNAL(triggered()), this, SLOT(on_actionChange_password_triggered()));
52 enableChangePasswordAction(false);
54 /* the secret notes file is stored in user home directory */
55 filePath = QDir::fromNativeSeparators(QDir::homePath());
57 /* or in $HOME/DOCUMENTS */
58 #if defined(DOCUMENTS)
59 #define DOC_DIR_(x) #x
60 #define DOC_DIR(x) DOC_DIR_(x)
61 QString doc(filePath + QString("/") + QString(DOC_DIR(DOCUMENTS)));
66 QTimer::singleShot(1, this, SLOT(readFile()));
69 MainWindow::~MainWindow()
74 memset(password,0,passLength);
78 void MainWindow::changeEvent(QEvent *e)
80 QMainWindow::changeEvent(e);
82 case QEvent::LanguageChange:
83 ui->retranslateUi(this);
90 void MainWindow::closeEvent(QCloseEvent *event)
92 QFile file(filePath + QString("/") + filename);
93 QString text = ui->textEdit->toPlainText();
95 /* don't save the file if content hasn't changed */
96 if (((text == plaintext) && (!hasPasswordChanged)) ||
97 (!ui->textEdit->isEnabled()) ||
98 (text.length() < 1)) {
107 /* has user provided a password */
113 encode(encoded, text);
116 if (encoded.length() < 1)
119 if (!file.open(QIODevice::WriteOnly)) {
120 QMessageBox::warning(this, tr("File open failed"),
121 tr("Can't open %1").arg(filename),QMessageBox::Ok);
123 QDataStream stream(&file);
124 stream << QString("SECRETNOTES");
125 stream << (short int)1;
127 hasPasswordChanged = false;
134 void MainWindow::readFile()
136 /* we want to call readFile only once */
141 QFile file(filePath + QString("/") + filename);
142 if (!file.open(QIODevice::ReadOnly)) {
143 ui->textEdit->setPlainText("");
144 enableTextEdit(true);
146 QMessageBox::information(this,tr("Welcome"),
147 tr("Welcome to Secret Notes.\n"
148 "Your data will be saved automatically\n"
149 "when you close Secret Notes."));
155 QDataStream stream(&file);
158 if ((head != QString("SECRETNOTES")) || (version != 1))
161 if (text.length() > 0) {
162 queryPassword(false);
163 if (!decode(decoded, text)) {
166 for (i=0; i<2; i++) {
168 ok = decode(decoded, text);
177 ui->textEdit->setPlainText(plaintext);
178 enableTextEdit(true);
179 enableChangePasswordAction(true);
185 void MainWindow::queryPassword(bool retry)
187 if (password && !retry)
190 QString pass = QInputDialog::getText(this,
192 tr("Enter passphrase:"),
193 QLineEdit::PasswordEchoOnEdit,"", &ok);
200 bool MainWindow::decode(QString &output, const QByteArray &input)
202 int size = input.length();
203 int length = size - 8 - SHA_DIGEST_LENGTH;
208 char *out = new char[size+1];
211 blowfish((unsigned char*)out, (unsigned char*)input.constData(), size, 0);
213 const char *text = out + 8 + SHA_DIGEST_LENGTH;
214 /* verify SHA checksum */
216 SHA1((unsigned char *)text, length, NULL);
217 unsigned char *digest = (unsigned char *)out + 8;
218 for (int i=0; i<SHA_DIGEST_LENGTH; i++) {
219 if (md[i] != digest[i]) {
220 memset(out, 0, size);
226 output = QString::fromUtf8(qUncompress(QByteArray(text, length)));
227 memset(out, 0, size);
232 void MainWindow::encode(QByteArray &output, const QString &input)
234 QByteArray bytes = qCompress(input.toUtf8());
235 int length = bytes.size();
236 int size = 8 + SHA_DIGEST_LENGTH + length;
238 char *out = new char[size];
242 /* add 8 psuedo random bytes */
243 qsrand((uint)&bytes + size);
245 in.append((char)qrand());
247 /* add SHA checksum */
248 unsigned char *md = SHA1((unsigned char*)bytes.constData(), length, NULL);
249 for(i=0; i<SHA_DIGEST_LENGTH; i++)
250 in.append((char)md[i]);
255 blowfish((unsigned char*)out, (unsigned char*)in.constData(), size, 1);
256 output = QByteArray(out, size);
262 void MainWindow::blowfish(unsigned char *output, const unsigned char *input,
266 unsigned char ivec[8];
269 BF_set_key(&key, passLength, password);
270 BF_cfb64_encrypt(input, output, length, &key, ivec, &num, enc);
271 memset(&key, 0, sizeof(BF_KEY));
274 void MainWindow::on_actionChange_password_triggered()
276 PasswordDialog dialog(this, (char*)password);
277 if (dialog.exec() == QDialog::Accepted) {
278 QString pass = dialog.getPassword();
284 void MainWindow::on_actionReset_secret_notes_triggered()
286 if (QMessageBox::question(this, tr("Reset secret notes"),
287 tr("Do you want to reset Secret Notes?\n"
288 "This will destroy all contents!"),
289 QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
293 memset(password, 0, passLength);
297 ui->textEdit->clear();
298 enableTextEdit(true);
302 void MainWindow::setPassword(const QString &text)
304 int length = text.length();
307 memset(password, 0, passLength);
309 password = new unsigned char[length+1];
311 for(i=0; i<length; i++)
312 password[i] = text.constData()[i].toAscii();
315 hasPasswordChanged = true;
319 void MainWindow::askNewPassword()
322 PasswordDialog dialog(this);
323 dialog.hideOldPassword(true);
325 if (dialog.exec() == QDialog::Accepted) {
326 QString pass = dialog.getPassword();
329 enableChangePasswordAction(true);
335 void MainWindow::enableTextEdit(bool ena)
337 ui->textEdit->setEnabled(ena);
339 ui->textEdit->setPlainText(tr("Secret Notes disabled"));
341 enableResetAction(!ena);
345 void MainWindow::undoEdit()
347 ui->textEdit->undo();
350 void MainWindow::on_textEdit_undoAvailable(bool b)
352 undoEditAction->setVisible(b);
355 void MainWindow::enableResetAction(bool b)
357 resetAction->setVisible(b);
360 void MainWindow::enableChangePasswordAction(bool b)
362 changePasswordAction->setVisible(b);