レポジトリ種類: Mercurial
#undef None
#include <FL/Fl_Window.H>
#include <FL/fl_ask.H>
#include <gpgme++/context.h>
#include <gpgme++/data.h>
#include <gpgme++/encryptionresult.h>
#include <fstream>
#include <unistd.h>
#include "common.hh"
#include "browser.hh"
#include "addpass.hh"
#include "../main.hh"
Addpass add;
struct InputData {
Fl_Input *txtin;
Fl_Secret_Input *pass1;
Fl_Secret_Input *pass2;
Fl_Window *dialog;
};
bool Addpass::exec(const std::string &file, const std::string &pass, bool isEdit) {
std::string basedir = Common::getbasedir(true);
std::string ext = ".gpg";
std::string gpgoutfile = (isEdit ? file : basedir + file + ext);
if (access(gpgoutfile.c_str(), F_OK) != -1) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Password already exist." :
"パスワードが既に存在しています。");
fl_alert("%s", err.c_str());
return false;
}
try {
// GPGMEライブラリを設置
std::setlocale(LC_ALL, "");
GpgME::initializeLibrary();
GpgME::setDefaultLocale(LC_CTYPE, std::setlocale(LC_CTYPE, NULL));
GpgME::Error err;
if (err) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to generate GPGME" :
"GPGエラーの設置に失敗");
fl_alert("%s: %s", ero.c_str(), gpg_strerror(err.code()));
return false;
}
// GPGMEを創作
std::unique_ptr<GpgME::Context> ctx =
GpgME::Context::create(GpgME::Protocol::OpenPGP);
// GPGMEは非対話的モードに設定
err = ctx->setPinentryMode(GpgME::Context::PinentryMode::PinentryLoopback);
if (err) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to set pinentry mode" :
"pinentryモードを設定に失敗");
fl_alert("%s: %s", ero.c_str(), gpg_strerror(err.code()));
return false;
}
// パスワードからデータオブジェクトを創作
GpgME::Data in(pass.c_str(), strlen(pass.c_str()), false);
GpgME::Data out;
// 鍵を受け取る
std::string keypath = basedir + ".gpg-id";
std::ifstream keyfile(keypath, std::ios::binary);
if (!keyfile.is_open()) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to open .gpg-id file" :
".gpg-idファイルを開くに失敗");
fl_alert("%s", ero.c_str());
return false;
}
std::string keyid;
std::string line;
while (std::getline(keyfile, line)) {
line.erase(line.find_last_not_of(" \n\r\t") + 1);
if (!line.empty()) keyid = line;
}
keyfile.close();
if (keyid.empty()) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"The .gpg-id file is empty or invalid" :
".gpg-idファイルは空か無効です");
fl_alert("%s", ero.c_str());
return false;
}
GpgME::Key key = ctx->key(keyid.c_str(), err);
if (key.isNull()) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to get key" :
"鍵を受取に失敗");
fl_alert("%s", ero.c_str());
return false;
}
// 暗号化
std::vector<GpgME::Key> keys = {key};
GpgME::EncryptionResult res =
ctx->encrypt(keys, in, out, GpgME::Context::AlwaysTrust);
if (res.error()) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to encrypt" :
"暗号化に失敗");
fl_alert("%s: %s", ero.c_str(), res.error().asString());
return false;
}
// ディレクトリを創作
std::string dirpath = (isEdit ? file : basedir + file);
auto lastsla = dirpath.find_last_of('/');
if (lastsla != std::string::npos) {
dirpath = dirpath.substr(0, lastsla);
try {
Common common;
common.mkdir_r(dirpath, 0755);
} catch (const std::runtime_error &e) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to create directory" :
"ディレクトリを創作に失敗");
fl_alert("%s: %s", ero.c_str(), e.what());
return false;
}
}
// 暗号化したファイルを開く
std::ofstream gpgpath(gpgoutfile, std::ios::binary);
if (!gpgpath.is_open()) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to write file '" + gpgoutfile + "'." :
"「" + gpgoutfile + "」ファイルを書き込めません。");
fl_alert("%s", ero.c_str());
return false;
}
// データが保存したかどうか確認
ssize_t encrypted_data_size = out.seek(0, SEEK_END);
if (encrypted_data_size <= 0) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to store the data" :
"データを保存に失敗");
fl_alert("%s", ero.c_str());
return false;
}
// 復号化したパスワードを表示する
out.seek(0, SEEK_SET);
char buffer[512];
ssize_t read_bytes;
while ((read_bytes = out.read(buffer, sizeof(buffer))) > 0) {
gpgpath.write(buffer, read_bytes);
}
gpgpath.close();
if (gpgpath.fail()) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to write encrypted data to file" :
"暗号化データを書き込めません。");
fl_alert("%s", ero.c_str());
return false;
}
} catch (const std::exception &e) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Error" :
"エラー");
fl_alert("%s: %s", err.c_str(), e.what());
return false;
}
if (isEdit) return true;
std::string msg = (lang.compare(0, 2, "en") == 0 ?
"The password got saved." :
"パスワードを保存出来ました");
fl_alert("%s", msg.c_str());
return true;
}
void Addpass::add_cb(Fl_Widget *, void *data) {
InputData *inputs = (InputData *)data;
if (inputs) {
file = inputs->txtin->value();
if (file.empty()) {
std::string err =
(lang.compare(0, 2, "en") == 0 ?
"Please fill in the path." :
"パスをご入力下さい。");
fl_alert("%s", err.c_str());
return;
}
inputpass1 = inputs->pass1->value();
if (inputpass1.empty()) {
std::string err =
(lang.compare(0, 2, "en") == 0 ?
"Please fill in the password." :
"パスワードをご入力下さい。");
fl_alert("%s", err.c_str());
return;
}
inputpass2 = inputs->pass2->value();
if (inputpass2.empty()) {
std::string err =
(lang.compare(0, 2, "en") == 0 ?
"Please fill in the password (confirm)." :
"パスワード (確認)をご入力下さい。");
fl_alert("%s", err.c_str());
return;
}
if (inputpass1 != inputpass2) {
std::string err =
(lang.compare(0, 2, "en") == 0 ?
"Password does not match." :
"パスワードが一致していません。");
fl_alert("%s", err.c_str());
return;
}
if (exec(file, inputpass1, false)) {
inputs->dialog->hide();
}
} else {
std::string err =
(lang.compare(0, 2, "en") == 0 ?
"Please fill in all the fields." :
"全てのフィールドをご入力下さい。");
fl_alert("%s", err.c_str());
}
}
void Addpass::dialog_cb(Fl_Widget *w, void *) {
(void)w;
Fl_Window *dialog = new Fl_Window(400, 200,
(lang.compare(0, 2, "en") == 0 ? "Add password" : "パスワードの追加"));
Fl_Input *txtin = new Fl_Input(150, 20, 180, 30,
(lang.compare(0, 2, "en") == 0 ? "Path:" : "パス:"));
dialog->add(txtin);
Fl_Secret_Input *pass1 = new Fl_Secret_Input(150, 60, 180, 30,
(lang.compare(0, 2, "en") == 0 ? "Password:" : "パスワード:"));
Fl_Secret_Input *pass2 = new Fl_Secret_Input(150, 100, 180, 30,
(lang.compare(0, 2, "en") == 0 ? "Password (confirm):" : "パスワード (確認):"));
dialog->add(pass1);
dialog->add(pass2);
InputData *inputs = new InputData();
inputs->txtin = txtin;
inputs->pass1 = pass1;
inputs->pass2 = pass2;
inputs->dialog = dialog;
Fl_Button *okbtn = new Fl_Button(60, 150, 80, 30, "OK");
Fl_Button *cancelbtn = new Fl_Button(160, 150, 80, 30,
(lang.compare(0, 2, "en") == 0 ? "Cancel" : "キャンセル"));
okbtn->callback(static_ok_cb, inputs);
cancelbtn->callback(static_cancel_cb, dialog);
dialog->add(okbtn);
dialog->add(cancelbtn);
dialog->end();
dialog->set_modal();
dialog->show();
}
void Addpass::static_ok_cb(Fl_Widget *w, void *data) {
(void)w;
InputData *inputs = (InputData *)data;
add.add_cb(nullptr, inputs);
std::vector<std::string> fpaths;
std::string rdir = Common::getbasedir(false);
std::string curpath = rdir + "/" + inputs->txtin->value() + ".gpg";
clearpaths(false);
Common::scandir(rdir, rdir, fpaths);
Browser::updatelist();
browse(curpath, true);
}
void Addpass::static_cancel_cb(Fl_Widget *w, void *data) {
((Addpass *)data)->cancel_cb(w, data);
}