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 /* the secret notes file is stored in user home directory */
51 filePath = QDir::fromNativeSeparators(QDir::homePath());
53 /* or in $HOME/DOCUMENTS */
54 #if defined(DOCUMENTS)
55 #define DOC_DIR_(x) #x
56 #define DOC_DIR(x) DOC_DIR_(x)
57 QString doc(filePath + QString("/") + QString(DOC_DIR(DOCUMENTS)));
62 QTimer::singleShot(1, this, SLOT(readFile()));
65 MainWindow::~MainWindow()
70 memset(password,0,passLength);
74 void MainWindow::changeEvent(QEvent *e)
76 QMainWindow::changeEvent(e);
78 case QEvent::LanguageChange:
79 ui->retranslateUi(this);
86 void MainWindow::closeEvent(QCloseEvent *event)
88 QFile file(filePath + QString("/") + filename);
89 QString text = ui->textEdit->toPlainText();
91 /* don't save the file if content hasn't changed */
92 if (((text == plaintext) && (!hasPasswordChanged)) ||
93 (!ui->textEdit->isEnabled()) ||
94 (text.length() < 1)) {
103 /* has user provided a password */
109 encode(encoded, text);
112 if (encoded.length() < 1)
115 if (!file.open(QIODevice::WriteOnly)) {
116 QMessageBox::warning(this, tr("File open failed"),
117 tr("Can't open %1").arg(filename),QMessageBox::Ok);
119 QDataStream stream(&file);
120 stream << QString("SECRETNOTES");
121 stream << (short int)1;
123 hasPasswordChanged = false;
130 void MainWindow::readFile()
132 /* we want to call readFile only once */
137 QFile file(filePath + QString("/") + filename);
138 if (!file.open(QIODevice::ReadOnly)) {
139 ui->textEdit->setPlainText("");
140 enableTextEdit(true);
142 QMessageBox::information(this,tr("Welcome"),
143 tr("Welcome to Secret Notes.\n"
144 "Your data will be saved automatically\n"
145 "when you close Secret Notes."));
151 QDataStream stream(&file);
154 if ((head != QString("SECRETNOTES")) || (version != 1))
157 if (text.length() > 0) {
158 queryPassword(false);
159 if (!decode(decoded, text)) {
162 for (i=0; i<2; i++) {
164 ok = decode(decoded, text);
173 ui->textEdit->setPlainText(plaintext);
174 enableTextEdit(true);
180 void MainWindow::queryPassword(bool retry)
182 if (password && !retry)
185 QString pass = QInputDialog::getText(this,
187 tr("Enter passphrase:"),
188 QLineEdit::PasswordEchoOnEdit,"", &ok);
195 bool MainWindow::decode(QString &output, const QByteArray &input)
197 int size = input.length();
198 int length = size - 8 - SHA_DIGEST_LENGTH;
203 char *out = new char[size+1];
206 blowfish((unsigned char*)out, (unsigned char*)input.constData(), size, 0);
208 const char *text = out + 8 + SHA_DIGEST_LENGTH;
209 /* verify SHA checksum */
211 SHA1((unsigned char *)text, length, NULL);
212 unsigned char *digest = (unsigned char *)out + 8;
213 for (int i=0; i<SHA_DIGEST_LENGTH; i++) {
214 if (md[i] != digest[i]) {
215 memset(out, 0, size);
221 output = QString::fromUtf8(qUncompress(QByteArray(text, length)));
222 memset(out, 0, size);
227 void MainWindow::encode(QByteArray &output, const QString &input)
229 QByteArray bytes = qCompress(input.toUtf8());
230 int length = bytes.size();
231 int size = 8 + SHA_DIGEST_LENGTH + length;
233 char *out = new char[size];
237 /* add 8 psuedo random bytes */
238 qsrand((uint)&bytes + size);
240 in.append((char)qrand());
242 /* add SHA checksum */
243 unsigned char *md = SHA1((unsigned char*)bytes.constData(), length, NULL);
244 for(i=0; i<SHA_DIGEST_LENGTH; i++)
245 in.append((char)md[i]);
250 blowfish((unsigned char*)out, (unsigned char*)in.constData(), size, 1);
251 output = QByteArray(out, size);
257 void MainWindow::blowfish(unsigned char *output, const unsigned char *input,
261 unsigned char ivec[8];
264 BF_set_key(&key, passLength, password);
265 BF_cfb64_encrypt(input, output, length, &key, ivec, &num, enc);
266 memset(&key, 0, sizeof(BF_KEY));
269 void MainWindow::on_actionChange_password_triggered()
271 PasswordDialog dialog(this, (char*)password);
272 if (dialog.exec() == QDialog::Accepted) {
273 QString pass = dialog.getPassword();
279 void MainWindow::on_actionReset_secret_notes_triggered()
281 if (QMessageBox::question(this, tr("Reset secret notes"),
282 tr("Do you want to reset Secret Notes?\n"
283 "This will destroy all contents!"),
284 QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
288 memset(password, 0, passLength);
292 ui->textEdit->clear();
293 enableTextEdit(true);
297 void MainWindow::setPassword(const QString &text)
299 int length = text.length();
302 memset(password, 0, passLength);
304 password = new unsigned char[length+1];
306 for(i=0; i<length; i++)
307 password[i] = text.constData()[i].toAscii();
310 hasPasswordChanged = true;
314 void MainWindow::askNewPassword()
317 PasswordDialog dialog(this);
318 dialog.hideOldPassword(true);
320 if (dialog.exec() == QDialog::Accepted) {
321 QString pass = dialog.getPassword();
329 void MainWindow::enableTextEdit(bool ena)
331 ui->textEdit->setEnabled(ena);
333 ui->textEdit->setPlainText(tr("Secret Notes disabled"));
335 enableResetAction(!ena);
339 void MainWindow::undoEdit()
341 ui->textEdit->undo();
344 void MainWindow::on_textEdit_undoAvailable(bool b)
346 undoEditAction->setVisible(b);
349 void MainWindow::enableResetAction(bool b)
351 resetAction->setVisible(b);