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/>.
20 #include <QAbstractKineticScroller>
21 #endif /* Q_WS_MAEMO_5 */
22 #include "mainwindow.h"
23 #include "ui_mainwindow.h"
24 #include <qmessagebox.h>
27 #include <qdatastream.h>
28 #include <qinputdialog.h>
29 #include <openssl/blowfish.h>
30 #include <openssl/sha.h>
31 #include "passworddialog.h"
33 const char filename[] = "secretnotes.scn";
35 MainWindow::MainWindow(QWidget *parent) :
37 ui(new Ui::MainWindow),
39 hasPasswordChanged(false),
45 QAbstractKineticScroller *scroller = ui->textEdit->property("kineticScroller")
46 .value<QAbstractKineticScroller *>();
48 scroller->setEnabled(true);
49 #endif /* Q_WS_MAEMO_5 */
51 resetAction = ui->menuOptions->addAction(tr("Reset secret notes"));
52 connect(resetAction, SIGNAL(triggered()), this, SLOT(on_actionReset_secret_notes_triggered()));
54 enableTextEdit(false);
56 undoEditAction = ui->menuOptions->addAction(tr("Undo edit"));
57 connect(undoEditAction,SIGNAL(triggered()), this, SLOT(undoEdit()));
58 on_textEdit_undoAvailable(false);
60 changePasswordAction = ui->menuOptions->addAction(tr("Change password"));
61 connect(changePasswordAction, SIGNAL(triggered()), this, SLOT(on_actionChange_password_triggered()));
62 enableChangePasswordAction(false);
64 /* the secret notes file is stored in user home directory */
65 filePath = QDir::fromNativeSeparators(QDir::homePath());
67 /* or in $HOME/DOCUMENTS */
68 #if defined(DOCUMENTS)
69 #define DOC_DIR_(x) #x
70 #define DOC_DIR(x) DOC_DIR_(x)
71 QString doc(filePath + QString("/") + QString(DOC_DIR(DOCUMENTS)));
76 QTimer::singleShot(1, this, SLOT(readFile()));
79 MainWindow::~MainWindow()
84 memset(password,0,passLength);
88 void MainWindow::changeEvent(QEvent *e)
90 QMainWindow::changeEvent(e);
92 case QEvent::LanguageChange:
93 ui->retranslateUi(this);
100 void MainWindow::closeEvent(QCloseEvent *event)
102 QFile file(filePath + QString("/") + filename);
103 QString text = ui->textEdit->toPlainText();
105 /* don't save the file if content hasn't changed */
106 if (((text == plaintext) && (!hasPasswordChanged)) ||
107 (!ui->textEdit->isEnabled()) ||
108 (text.length() < 1)) {
117 /* has user provided a password */
123 encode(encoded, text);
126 if (encoded.length() < 1)
129 if (!file.open(QIODevice::WriteOnly)) {
130 QMessageBox::warning(this, tr("File open failed"),
131 tr("Can't open %1").arg(filename),QMessageBox::Ok);
133 QDataStream stream(&file);
134 stream << QString("SECRETNOTES");
135 stream << (short int)1;
137 hasPasswordChanged = false;
144 void MainWindow::readFile()
146 /* we want to call readFile only once */
151 QFile file(filePath + QString("/") + filename);
152 if (!file.open(QIODevice::ReadOnly)) {
153 ui->textEdit->setPlainText("");
154 enableTextEdit(true);
156 QMessageBox::information(this,tr("Welcome"),
157 tr("Welcome to Secret Notes.\n"
158 "Your data will be saved automatically\n"
159 "when you close Secret Notes."));
165 QDataStream stream(&file);
168 if ((head != QString("SECRETNOTES")) || (version != 1))
171 if (text.length() > 0) {
172 queryPassword(false);
173 if (!decode(decoded, text)) {
176 for (i=0; i<2; i++) {
178 ok = decode(decoded, text);
187 ui->textEdit->setPlainText(plaintext);
188 enableTextEdit(true);
189 enableChangePasswordAction(true);
195 void MainWindow::queryPassword(bool retry)
197 if (password && !retry)
200 QString pass = QInputDialog::getText(this,
202 tr("Enter passphrase:"),
203 QLineEdit::PasswordEchoOnEdit,"", &ok);
210 bool MainWindow::decode(QString &output, const QByteArray &input)
212 int size = input.length();
213 int length = size - 8 - SHA_DIGEST_LENGTH;
218 char *out = new char[size+1];
221 blowfish((unsigned char*)out, (unsigned char*)input.constData(), size, 0);
223 const char *text = out + 8 + SHA_DIGEST_LENGTH;
224 /* verify SHA checksum */
226 SHA1((unsigned char *)text, length, NULL);
227 unsigned char *digest = (unsigned char *)out + 8;
228 for (int i=0; i<SHA_DIGEST_LENGTH; i++) {
229 if (md[i] != digest[i]) {
230 memset(out, 0, size);
236 output = QString::fromUtf8(qUncompress(QByteArray(text, length)));
237 memset(out, 0, size);
242 void MainWindow::encode(QByteArray &output, const QString &input)
244 QByteArray bytes = qCompress(input.toUtf8());
245 int length = bytes.size();
246 int size = 8 + SHA_DIGEST_LENGTH + length;
248 char *out = new char[size];
252 /* add 8 psuedo random bytes */
253 qsrand((uint)&bytes + size);
255 in.append((char)qrand());
257 /* add SHA checksum */
258 unsigned char *md = SHA1((unsigned char*)bytes.constData(), length, NULL);
259 for(i=0; i<SHA_DIGEST_LENGTH; i++)
260 in.append((char)md[i]);
265 blowfish((unsigned char*)out, (unsigned char*)in.constData(), size, 1);
266 output = QByteArray(out, size);
272 void MainWindow::blowfish(unsigned char *output, const unsigned char *input,
276 unsigned char ivec[8];
279 BF_set_key(&key, passLength, password);
280 BF_cfb64_encrypt(input, output, length, &key, ivec, &num, enc);
281 memset(&key, 0, sizeof(BF_KEY));
284 void MainWindow::on_actionChange_password_triggered()
286 PasswordDialog dialog(this, (char*)password);
287 if (dialog.exec() == QDialog::Accepted) {
288 QString pass = dialog.getPassword();
294 void MainWindow::on_actionReset_secret_notes_triggered()
296 if (QMessageBox::question(this, tr("Reset secret notes"),
297 tr("Do you want to reset Secret Notes?\n"
298 "This will destroy all contents!"),
299 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel) != QMessageBox::Yes) {
303 memset(password, 0, passLength);
307 ui->textEdit->clear();
308 enableTextEdit(true);
312 void MainWindow::setPassword(const QString &text)
314 int length = text.length();
317 memset(password, 0, passLength);
319 password = new unsigned char[length+1];
321 for(i=0; i<length; i++)
322 password[i] = text.constData()[i].toAscii();
325 hasPasswordChanged = true;
329 void MainWindow::askNewPassword()
332 PasswordDialog dialog(this);
333 dialog.hideOldPassword(true);
335 if (dialog.exec() == QDialog::Accepted) {
336 QString pass = dialog.getPassword();
339 enableChangePasswordAction(true);
345 void MainWindow::enableTextEdit(bool ena)
347 ui->textEdit->setEnabled(ena);
349 ui->textEdit->setPlainText(tr("Secret Notes disabled"));
351 enableResetAction(!ena);
355 void MainWindow::undoEdit()
357 ui->textEdit->undo();
360 void MainWindow::on_textEdit_undoAvailable(bool b)
362 undoEditAction->setVisible(b);
365 void MainWindow::enableResetAction(bool b)
367 resetAction->setVisible(b);
370 void MainWindow::enableChangePasswordAction(bool b)
372 changePasswordAction->setVisible(b);