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),
40 enableTextEdit(false);
41 undoResetAction = ui->menuOptions->addAction(tr("Undo reset"));
42 connect(undoResetAction, SIGNAL(triggered()),this, SLOT(undoReset()));
43 enableUndoReset(false);
45 undoEditAction = ui->menuOptions->addAction(tr("Undo edit"));
46 connect(undoEditAction,SIGNAL(triggered()), this, SLOT(undoEdit()));
47 on_textEdit_undoAvailable(false);
49 /* the secret notes file is stored in user home directory */
50 filePath = QDir::fromNativeSeparators(QDir::homePath());
52 /* or in $HOME/DOCUMENTS */
53 #if defined(DOCUMENTS)
54 #define DOC_DIR_(x) #x
55 #define DOC_DIR(x) DOC_DIR_(x)
56 QString doc(filePath + QString("/") + QString(DOC_DIR(DOCUMENTS)));
61 QTimer::singleShot(1, this, SLOT(readFile()));
64 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);
291 if (plaintext.length() > 0) {
293 undoText = plaintext;
296 if (undoText.length() > 0)
297 enableUndoReset(true);
298 ui->textEdit->clear();
299 enableTextEdit(true);
303 void MainWindow::setPassword(const QString &text)
305 int length = text.length();
308 memset(password, 0, passLength);
310 password = new unsigned char[length+1];
312 for(i=0; i<length; i++)
313 password[i] = text.constData()[i].toAscii();
316 hasPasswordChanged = true;
320 void MainWindow::askNewPassword()
323 PasswordDialog dialog(this);
324 dialog.hideOldPassword(true);
326 if (dialog.exec() == QDialog::Accepted) {
327 QString pass = dialog.getPassword();
335 void MainWindow::enableTextEdit(bool ena)
337 ui->textEdit->setEnabled(ena);
339 ui->textEdit->setPlainText(tr("Secret Notes disabled"));
343 void MainWindow::enableUndoReset(bool ena)
345 undoResetAction->setVisible(ena);
348 void MainWindow::undoReset()
351 plaintext = undoText;
354 ui->textEdit->setPlainText(plaintext);
355 enableUndoReset(false);
356 enableTextEdit(true);
359 void MainWindow::undoEdit()
361 ui->textEdit->undo();
364 void MainWindow::on_textEdit_undoAvailable(bool b)
366 undoEditAction->setVisible(b);