SVNからのミラー

This commit is contained in:
2026-01-21 04:47:40 +09:00
commit 81a90a99e1
47 changed files with 2619 additions and 0 deletions

307
src/addpass.cc Normal file
View File

@@ -0,0 +1,307 @@
#include "common.hh"
#include "addpass.hh"
#include "../main.hh"
#include <gpgme++/context.h>
#include <gpgme++/data.h>
#include <gpgme++/encryptionresult.h>
#undef None
#include <FL/Fl_Window.H>
#include <FL/fl_ask.H>
#include <fstream>
#include <unistd.h>
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 lang = Common::getlang();
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;
std::string lang = Common::getlang();
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;
std::string lang = Common::getlang();
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, curpath);
scandir(rdir, rdir, fpaths);
updatelist();
browse(curpath, true);
}
void Addpass::static_cancel_cb(Fl_Widget *w, void *data) {
((Addpass *)data)->cancel_cb(w, data);
}

28
src/addpass.hh Normal file
View File

@@ -0,0 +1,28 @@
#ifndef ADDPASS_HH
#define ADDPASS_HH
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Secret_Input.H>
#include "dialog.hh"
#include <string>
class Addpass : public Dialog {
public:
Fl_Button *btn = nullptr;
std::string file;
std::string inputpass1;
std::string inputpass2;
static void dialog_cb(Fl_Widget *w, void *);
void add_cb(Fl_Widget *, void *);
bool exec(const std::string &file, const std::string &pass, bool isEdit);
private:
static void static_ok_cb(Fl_Widget *w, void *data);
static void static_cancel_cb(Fl_Widget *w, void *data);
};
#endif

53
src/base32.cc Normal file
View File

@@ -0,0 +1,53 @@
#include "base32.hh"
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <iterator>
#include <stdexcept>
#include <string>
int Base32::char_to_val(char c) {
std::string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
auto it = std::find(alphabet.begin(), alphabet.end(), c);
return (it != alphabet.end()) ? std::distance(alphabet.begin(), it) : -1;
}
std::vector<unsigned char> Base32::decode(const std::string &encoded) {
std::string encoded_up = encoded;
for (auto &c : encoded_up) {
c = std::toupper(static_cast<unsigned char>(c));
}
size_t encoded_len = encoded_up.length();
size_t padding = 0;
for (int i = encoded_len - 1; i >= 0 && encoded_up[i] == '='; --i) {
++padding;
}
size_t out_len = (encoded_len - padding) * 5 / 8;
if (out_len == 0) return {};
std::vector<unsigned char> decoded(out_len);
int buffer = 0, bits_left = 0, count = 0;
Base32 b32;
for (size_t i = 0; i < encoded_len - padding; ++i) {
int val = b32.char_to_val(encoded_up[i]);
if (val < 0) {
throw std::runtime_error("Base32エンコードした文字の中に不正な文字があります。");
}
buffer <<= 5;
buffer |= val;
bits_left += 5;
if (bits_left >= 8) {
decoded[count++] = static_cast<unsigned char>(buffer >> (bits_left - 8));
bits_left -= 8;
}
}
decoded.resize(count);
return decoded;
}

15
src/base32.hh Normal file
View File

@@ -0,0 +1,15 @@
#ifndef BASE32_HH
#define BASE32_HH
#include <vector>
#include <string>
class Base32 {
public:
static std::vector<unsigned char> decode(const std::string &encoded);
private:
int char_to_val(char c);
};
#endif

279
src/chkpass.cc Normal file
View File

@@ -0,0 +1,279 @@
#include "common.hh"
#include "chkpass.hh"
#include "../main.hh"
#include "showpass.hh"
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Text_Display.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/Fl_Scrollbar.H>
#include <string>
#include <thread>
#include <algorithm>
Chkpass chk;
std::vector<std::string> foundRes;
std::vector<std::string> duppath;
std::vector<std::string> duppasswd;
struct InputData {
Fl_Radio_Button *all;
Fl_Radio_Button *len;
Fl_Radio_Button *chr;
Fl_Radio_Button *dup;
Fl_Window *dialog;
};
void Chkpass::lenPass(const std::string &path, const std::string &pass,
const std::string &lang) {
if (pass.length() > 0 && pass.length() < minimumlen) {
std::string res = "【E】";
res += (lang.compare(0, 2, "en") == 0 ?
"The password \"" + path + "\" is too short, minimum length should be " +
std::to_string(minimumlen) + " characters, recommended is " +
std::to_string(recommendlen) + " characters.":
"パスワード「" + path + "」は短すぎます。最短パスワードの長さは" +
std::to_string(minimumlen) + "文字ですが、勧めが" +
std::to_string(recommendlen) + "文字です。");
foundRes.push_back(res);
weaklencount++;
vulncount++;
} else if (pass.length() >= minimumlen && pass.length() < recommendlen) {
std::string res = "【W】";
res += (lang.compare(0, 2, "en") == 0 ?
"The password \"" + path + "\" is long enough, but for optimal security, " +
std::to_string(recommendlen) + " characters is recommended.\n" :
"パスワード「" + path + "」の長さは十分ですが、最強のセキュリティには" +
std::to_string(recommendlen) + "文字が勧めします。\n");
foundRes.push_back(res);
}
}
void Chkpass::charPass(const std::string &path, const std::string &pass,
const std::string &lang) {
bool isUpper = false;
bool isLower = false;
bool isDigit = false;
bool isSpecial = false;
for (char ch : pass) {
if (std::isupper(static_cast<unsigned char>(ch))) isUpper = true;
if (std::islower(static_cast<unsigned char>(ch))) isLower = true;
if (std::isdigit(static_cast<unsigned char>(ch))) isDigit = true;
if (std::find(spchar.begin(), spchar.end(), ch) != spchar.end()) isSpecial = true;
}
if (!isUpper || !isLower || !isDigit || !isSpecial) {
std::string res = "【E】";
res += (lang.compare(0, 2, "en") == 0 ?
"The password \"" + path + "\" is too weak! A strong password contains " +
"at least 1 uppercase, 1 lowercase, 1 digit, and 1 special character." :
"パスワード「" + path + "」は弱すぎます強いパスワードは最大1大文字、" +
"1小文字、1数字、及び1記号の文字が含みます。");
foundRes.push_back(res);
weakcharcount++;
vulncount++;
}
}
void Chkpass::dupPass(const std::string &path, const std::string &pass,
const std::string &lang) {
for (std::size_t k = 0; k < duppasswd.size(); k++) {
if (pass.compare(duppasswd[k]) == 0) {
std::string res = "【E】";
res += (lang.compare(0, 2, "en") == 0 ?
"The password \"" + path + "\" is the same as \"" + duppath[k] + "\". " +
"For security, please keep passwords unique!":
"パスワード「" + path + "」は「" + duppath[k] + "」と一致しています。" +
"セキュリティの為、各パスワードはユニークにする様にして下さい!");
foundRes.push_back(res);
duppasscount++;
vulncount++;
}
}
duppath.push_back(path);
duppasswd.push_back(pass);
}
bool Chkpass::exec() {
std::string lang = Common::getlang();
// パスワードをスキャンして
Showpass show;
for (const auto &dispath : dispaths) {
std::string fullpath = Common::getbasedir(true) + dispath + ".gpg";
std::string pass = show.exec(fullpath.c_str(), true);
if (pass.empty()) continue;
if (pass.rfind("otpauth://totp/", 0) == 0) continue;
if (isAll) {
lenPass(dispath, pass, lang);
charPass(dispath, pass, lang);
dupPass(dispath, pass, lang);
} else if (isLen) {
lenPass(dispath, pass, lang);
} else if (isChar) {
charPass(dispath, pass, lang);
} else if (isDup) {
dupPass(dispath, pass, lang);
}
}
return true;
}
void Chkpass::showRes() {
std::string lang = Common::getlang();
std::string res;
if (lang.compare(0, 2, "en") == 0) {
res = "Weak passwords:\n";
} else {
res = "不安定なパスワード:\n";
}
for (const auto &path : foundRes) {
res += path + "\n";
}
if (lang.compare(0, 2, "en") == 0) {
res += "Short password count: " + std::to_string(weaklencount) + "\n";
res += "Weak password count: " + std::to_string(weakcharcount) + "\n";
res += "Duplicate password count: " + std::to_string(duppasscount) + "\n";
res += "Total: " + std::to_string(duppath.size()) + "\n";
res += "It's advised to change any of the";
res += "weak passwords as soon as possible!";
} else {
res += "短いパスワード数: " + std::to_string(weaklencount) + "\n";
res += "弱いパスワード数: " + std::to_string(weakcharcount) + "\n";
res += "同じパスワード数: " + std::to_string(duppasscount) + "\n";
res += "合計: " + std::to_string(duppath.size()) + "\n";
res += "不安定なパスワードは出来るだけ早く変更する事をお勧めします!";
}
Fl_Window *win = new Fl_Window(500, 440, lang.compare(0, 2, "en") == 0 ?
"Results" : "結果");
Fl_Text_Display *display = new Fl_Text_Display(10, 10, 480, 380);
Fl_Text_Buffer *textbuf = new Fl_Text_Buffer();
textbuf->text(res.c_str());
display->buffer(textbuf);
display->scrollbar_width(15);
win->resizable(display);
Fl_Button *okBtn = new Fl_Button(210, 400, 80, 30, "OK");
okBtn->callback([](Fl_Widget *widget, void *win) {
(void)widget;
reinterpret_cast<Fl_Window*>(win)->hide();
}, win);
win->add(okBtn);
win->end();
win->show();
}
void Chkpass::chk_cb(Fl_Widget *, void *data) {
InputData *inputs = (InputData *)data;
std::string lang = Common::getlang();
if (inputs) {
isAll = inputs->all->value();
isLen = inputs->len->value();
isChar = inputs->chr->value();
isDup = inputs->dup->value();
}
Fl_Window *dialog = new Fl_Window(400, 50,
(lang.compare(0, 2, "en") == 0 ?
"Checking for weak password" : "不安的なパスワードの確認中"));
Fl_Box *box = new Fl_Box(10, 10, 380, 20,
(lang.compare(0, 2, "en") == 0 ?
"Checking, please wait for a while..." :
"確認中。暫くお待ち下さい・・・"));
dialog->add(box);
dialog->end();
dialog->set_modal();
dialog->show();
std::thread checker([dialog]() {
chk.exec();
Fl::lock();
dialog->hide();
chk.showRes();
Fl::unlock();
});
checker.detach();
}
void Chkpass::dialog_cb(Fl_Widget *w, void *) {
(void)w;
std::string lang = Common::getlang();
Fl_Window *dialog = new Fl_Window(390, 145,
(lang.compare(0, 2, "en") == 0 ?
"Check for weak password" : "不安的なパスワードの確認"));
Fl_Radio_Button *all = new Fl_Radio_Button(10, 10, 180, 30,
(lang.compare(0, 2, "en") == 0) ? "Everything" : "全部");
Fl_Radio_Button *len = new Fl_Radio_Button(10, 50, 180, 30,
(lang.compare(0, 2, "en") == 0) ? "Length" : "長さ");
Fl_Radio_Button *chr = new Fl_Radio_Button(200, 10, 180, 30,
(lang.compare(0, 2, "en") == 0) ? "Strength" : "強さ");
Fl_Radio_Button *dup = new Fl_Radio_Button(200, 50, 180, 30,
(lang.compare(0, 2, "en") == 0) ? "Duplicate" : "服数度");
dialog->add(all);
dialog->add(len);
dialog->add(chr);
dialog->add(dup);
InputData *inputs = new InputData();
inputs->all = all;
inputs->len = len;
inputs->chr = chr;
inputs->dup = dup;
inputs->dialog = dialog;
Fl_Button *okbtn = new Fl_Button(105, 100, 80, 30, "OK");
Fl_Button *cancelbtn = new Fl_Button(205, 100, 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 Chkpass::static_ok_cb(Fl_Widget *w, void *data) {
(void)w;
InputData *inputs = (InputData *)data;
chk.chk_cb(nullptr, inputs);
}
void Chkpass::static_cancel_cb(Fl_Widget *w, void *data) {
((Chkpass *)data)->cancel_cb(w, data);
}

45
src/chkpass.hh Normal file
View File

@@ -0,0 +1,45 @@
#ifndef CHKPASS_HH
#define CHKPASS_HH
#include <FL/Fl_Button.H>
#include <FL/Fl_Radio_Button.H>
#include "dialog.hh"
#include <string>
class Chkpass : public Dialog {
public:
Fl_Button *btn = nullptr;
Fl_Radio_Button *allChk = nullptr;
Fl_Radio_Button *lenChk = nullptr;
Fl_Radio_Button *charChk = nullptr;
Fl_Radio_Button *dupChk = nullptr;
bool isAll, isLen, isChar, isDup = false;
static void dialog_cb(Fl_Widget *w, void *);
void showRes();
void chk_cb(Fl_Widget *, void *);
bool exec();
private:
int vulncount = 0;
int weaklencount = 0;
int weakcharcount = 0;
int duppasscount = 0;
std::size_t minimumlen = 12;
std::size_t recommendlen = 64;
std::string spchar = "!@#$%^&*()-_=+[]{}|;:'\",.<>?/\\`~";
void lenPass(const std::string &path, const std::string &pass,
const std::string &lang);
void charPass(const std::string &path, const std::string &pass,
const std::string &lang);
void dupPass(const std::string &path, const std::string &pass,
const std::string &lang);
static void static_ok_cb(Fl_Widget *w, void *data);
static void static_cancel_cb(Fl_Widget *w, void *data);
};
#endif

82
src/common.cc Normal file
View File

@@ -0,0 +1,82 @@
#include "common.hh"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <errno.h>
std::string Common::getbasedir(bool trailing) {
std::string homedir = std::getenv("HOME") ? std::getenv("HOME") : "";
if (homedir.empty()) return "";
#if defined(__HAIKU__)
std::string basedir = "/config/settings/sp";
std::string slash = "/";
#elif defined(_WIN32)
std::string basedir = "\\AppData\\Local\\076\\sp";
std::string slash = "\\";
#else
std::string basedir = "/.local/share/sp";
std::string slash = "/";
#endif
return trailing ? (homedir + basedir + slash) : (homedir + basedir);
}
std::string Common::getlang() {
const char *env = std::getenv("SP_LANG");
std::string lang;
if (env) lang = env;
else lang = "ja";
return lang;
}
int Common::mkdir_r(const std::string &path, mode_t mode) {
char tmp[256];
size_t len;
std::snprintf(tmp, sizeof(tmp), "%s", path.c_str());
len = std::strlen(tmp);
if (tmp[len - 1] == '/') {
tmp[len - 1] = 0;
}
for (char *p = tmp + 1; *p; p++) {
if (*p == '/') {
*p = 0;
if (mkdir(tmp, mode) != 0 && errno != EEXIST) return -1;
*p = '/';
}
}
if (mkdir(tmp, mode) != 0 && errno != EEXIST) {
return -1;
}
return 0;
}
void Common::tmpcopy(const std::string &inpath, const std::string &outpath) {
std::ifstream src(inpath, std::ios::binary);
std::ofstream dst(outpath, std::ios::binary);
}
std::vector<std::string> Common::explode(const std::string &str, char delimiter) {
std::vector<std::string> tokens;
std::string token;
size_t start = 0, end = 0;
while ((end = str.find(delimiter, start)) != std::string::npos) {
tokens.push_back(str.substr(start, end - start));
start = end + 1;
}
tokens.push_back(str.substr(start));
return tokens;
}

18
src/common.hh Normal file
View File

@@ -0,0 +1,18 @@
#ifndef COMMON_HH
#define COMMON_HH
#include <vector>
#include <string>
#include <sys/stat.h>
class Common {
public:
static std::string getbasedir(bool trailing);
static std::string getlang();
int mkdir_r(const std::string &path, mode_t mode);
void tmpcopy(const std::string &inpath, const std::string &outpath);
static std::vector<std::string> explode(const std::string &str, char delimiter);
};
#endif

147
src/delpass.cc Normal file
View File

@@ -0,0 +1,147 @@
#include "common.hh"
#include "delpass.hh"
#include "../main.hh"
#include <FL/fl_ask.H>
#include <FL/Fl_Input.H>
#include <unistd.h>
Delpass del;
struct InputData {
Fl_Input *txtin;
Fl_Window *dialog;
};
void Delpass::setFile(std::string &f) {
del.file = f;
}
std::string Delpass::getFile() {
return file;
}
bool Delpass::exec(const std::string &file, bool force) {
std::string lang = Common::getlang();
std::string basedir = Common::getbasedir(true);
std::string ext = ".gpg";
// ファイルが既に存在するかどうか確認
if (access(file.c_str(), F_OK) != 0) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Password does not exist" :
"パスワードが存在しません");
fl_alert("%s", err.c_str());
return false;
}
if (unlink(file.c_str()) == -1) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Password cannot be deleted" :
"パスワードを削除出来ませんですた");
fl_alert("%s", err.c_str());
return false;
}
// 空のディレクトリの場合
std::vector<std::string> tokens = Common::explode(file, '/');
std::string passpath = basedir + tokens[0];
for (size_t i = 1; i < tokens.size(); ++i) {
if (i == tokens.size() - 1) continue;
passpath += "/" + tokens[i];
}
for (int i = tokens.size() - 1; i >= 0; --i) {
// ~/.local/share/sp を削除したら危険
if (passpath.compare(0, basedir.size(), basedir) == 0) {
break;
}
// ディレクトリが空じゃない場合、削除を止める
if (rmdir(passpath.c_str()) == -1) {
break;
}
size_t last_slash = passpath.find_last_of('/');
if (last_slash != std::string::npos) {
passpath.erase(last_slash);
}
}
if (force) return true;
std::string msg = (lang.compare(0, 2, "en") == 0 ?
"The password got deleted" :
"パスワードを削除しました");
fl_alert("%s", msg.c_str());
return true;
}
void Delpass::delete_cb(Fl_Widget *, void *data) {
InputData *inputs = (InputData *)data;
if (inputs) {
file = inputs->txtin->value();
}
exec(file, false);
std::vector<std::string> fpaths;
std::string rdir = Common::getbasedir(false);
std::string mockpath = "";
clearpaths(true, mockpath);
scandir(rdir, rdir, fpaths);
updatelist();
}
void Delpass::dialog_cb(Fl_Widget *w, void *data) {
(void)w;
(void)data;
std::string lang = Common::getlang();
Fl_Input *txtin = new Fl_Input(150, 20, 180, 30,
(lang.compare(0, 2, "en") == 0 ? "Path:" : "パス:"));
txtin->hide();
txtin->value(del.getFile().c_str());
InputData *inputs = new InputData();
inputs->txtin = txtin;
std::string asking =
(lang.compare(0, 2, "en") == 0 ?
"Are you sure you want to delete the password '" + del.getFile() + "'?" :
"パスワード「" + del.getFile() + "」を本当に削除する事が宜しいでしょうか?");
int confirm = fl_choice("%s",
(lang.compare(0, 2, "en") == 0 ? "Cancel" : "キャンセル"),
(lang.compare(0, 2, "en") == 0 ? "Delete" : "削除"),
nullptr, asking.c_str());
if (confirm == 0) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Not deleted" :
"削除しませんでした");
fl_alert("%s", err.c_str());
} else {
static_ok_cb(w, inputs);
}
}
void Delpass::static_ok_cb(Fl_Widget *w, void *data) {
(void)w;
InputData *inputs = (InputData *)data;
del.delete_cb(nullptr, inputs);
std::vector<std::string> fpaths;
std::string rdir = Common::getbasedir(false);
std::string curpath = "";
clearpaths(true, curpath);
scandir(rdir, rdir, fpaths);
updatelist();
}
void Delpass::static_cancel_cb(Fl_Widget *w, void *data) {
((Delpass *)data)->cancel_cb(w, data);
}

25
src/delpass.hh Normal file
View File

@@ -0,0 +1,25 @@
#ifndef DELPASS_HH
#define DELPASS_HH
#include <FL/Fl_Button.H>
#include <string>
#include "dialog.hh"
class Delpass : public Dialog {
public:
Fl_Button *btn = nullptr;
std::string file;
static void dialog_cb(Fl_Widget *w, void *);
void delete_cb(Fl_Widget *, void *data);
static void setFile(std::string &f);
std::string getFile();
bool exec(const std::string &file, bool force);
private:
static void static_ok_cb(Fl_Widget *w, void *data);
static void static_cancel_cb(Fl_Widget *w, void *data);
};
#endif

13
src/dialog.cc Normal file
View File

@@ -0,0 +1,13 @@
#include "dialog.hh"
#include <FL/Fl_Window.H>
void Dialog::ok_cb(Fl_Widget *w, void *dialog) {
(void)w;
((Fl_Window *)dialog)->hide();
}
void Dialog::cancel_cb(Fl_Widget *w, void *dialog) {
(void)w;
((Fl_Window *)dialog)->hide();
}

12
src/dialog.hh Normal file
View File

@@ -0,0 +1,12 @@
#ifndef DIALOG_HH
#define DIALOG_HH
#include <FL/Fl_Widget.H>
class Dialog {
public:
static void ok_cb(Fl_Widget *w, void *dialog);
static void cancel_cb(Fl_Widget *w, void *dialog);
};
#endif

189
src/editpass.cc Normal file
View File

@@ -0,0 +1,189 @@
#include "common.hh"
#include "editpass.hh"
#include "delpass.hh"
#include "addpass.hh"
#include "../main.hh"
#include <FL/Fl_Window.H>
#include <FL/fl_ask.H>
#include <string>
#include <unistd.h>
#include <vector>
Editpass edit;
struct InputData {
Fl_Input *txtin;
Fl_Secret_Input *pass1;
Fl_Secret_Input *pass2;
Fl_Window *dialog;
};
void Editpass::setFile(std::string &f) {
edit.file = f;
}
std::string Editpass::getFile() {
return file;
}
bool Editpass::exec(const std::string &file, const std::string &pass) {
std::string lang = Common::getlang();
Common c;
c.tmpcopy(file, "/tmp/simpas-tmp.gpg");
Delpass d;
bool isdel = d.exec(file, true);
if (!isdel) {
std::string err =
(lang.compare(0, 2, "en") == 0 ? "Failed to edit." : "編集に失敗。");
fl_alert("%s", err.c_str());
return false;
}
Addpass a;
bool isadd = a.exec(file, pass, true);
if (!isadd) {
std::string err =
(lang.compare(0, 2, "en") == 0 ? "Failed to edit." : "編集に失敗。");
fl_alert("%s", err.c_str());
// TODO: うまく動いているかの確認。それは次のバージョンから・・・
/* std::vector<std::string> tokens = Common::explode(file, '/'); */
/* std::string passpath = tokens[0]; */
/* for (size_t i = 1; i < tokens.size(); ++i) { */
/* if (i == tokens.size() - 1) continue; */
/* passpath += "/" + tokens[i]; */
/* } */
/* for (int i = tokens.size() - 1; i >= 0; ++i) { */
/* if (c.mkdir_r(passpath, 0755) == -1) break; */
/* size_t last_slash = passpath.find_last_of('/'); */
/* if (last_slash != std::string::npos) passpath.erase(last_slash); */
/* } */
c.tmpcopy("/tmp/simpas-tmp.gpg", file);
unlink("/tmp/simpas-tmp.gpg");
return false;
}
std::string msg =
(lang.compare(0, 2, "en") == 0 ? "Edit success." : "編集に成功。");
fl_alert("%s", msg.c_str());
unlink("/tmp/simpas-tmp.gpg");
return true;
}
void Editpass::edit_cb(Fl_Widget *, void *data) {
InputData *inputs = (InputData *)data;
std::string lang = Common::getlang();
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)) {
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 Editpass::dialog_cb(Fl_Widget *w, void *data) {
(void)w;
(void)data;
std::string lang = Common::getlang();
Fl_Window *dialog = new Fl_Window(400, 160,
(lang.compare(0, 2, "en") == 0 ? "Edit password" : "パスワードの編集"));
Fl_Input *txtin = new Fl_Input(150, 20, 180, 30,
(lang.compare(0, 2, "en") == 0 ? "Path:" : "パス:"));
Fl_Secret_Input *pass1 = new Fl_Secret_Input(150, 20, 180, 30,
(lang.compare(0, 2, "en") == 0 ? "Password:" : "パスワード:"));
Fl_Secret_Input *pass2 = new Fl_Secret_Input(150, 60, 180, 30,
(lang.compare(0, 2, "en") == 0 ? "Password (confirm):" : "パスワード (確認):"));
dialog->add(pass1);
dialog->add(pass2);
txtin->hide();
txtin->value(edit.getFile().c_str());
InputData *inputs = new InputData();
inputs->txtin = txtin;
inputs->pass1 = pass1;
inputs->pass2 = pass2;
inputs->dialog = dialog;
Fl_Button *okbtn = new Fl_Button(60, 110, 80, 30, "OK");
Fl_Button *cancelbtn = new Fl_Button(160, 110, 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 Editpass::static_ok_cb(Fl_Widget *w, void *data) {
(void)w;
InputData *inputs = (InputData *)data;
edit.edit_cb(nullptr, inputs);
std::vector<std::string> fpaths;
std::string rdir = Common::getbasedir(false);
std::string curpath = inputs->txtin->value();
clearpaths(false, curpath);
scandir(rdir, rdir, fpaths);
updatelist();
browse(curpath, false);
}
void Editpass::static_cancel_cb(Fl_Widget *w, void *data) {
((Editpass *)data)->cancel_cb(w, data);
}

30
src/editpass.hh Normal file
View File

@@ -0,0 +1,30 @@
#ifndef EDITPASS_HH
#define EDITPASS_HH
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Secret_Input.H>
#include "dialog.hh"
#include <string>
class Editpass : public Dialog {
public:
Fl_Button *btn = nullptr;
std::string file;
std::string inputpass1;
std::string inputpass2;
static void dialog_cb(Fl_Widget *w, void *);
void edit_cb(Fl_Widget *, void *data);
static void setFile(std::string &f);
std::string getFile();
bool exec(const std::string &file, const std::string &pass);
private:
static void static_ok_cb(Fl_Widget *w, void *data);
static void static_cancel_cb(Fl_Widget *w, void *data);
};
#endif

108
src/genpass.cc Normal file
View File

@@ -0,0 +1,108 @@
#include "common.hh"
#include "genpass.hh"
#include <FL/Fl_Window.H>
#include <FL/fl_ask.H>
#include <fstream>
#include <vector>
Genpass gen;
std::string Genpass::exec(int count, bool issecure) {
std::string lang = Common::getlang();
const std::string charset_risky =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const std::string charset_secure =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()=~-^\\|_@`[{]};:+*<>,./?";
const std::string &charset = issecure ? charset_secure : charset_risky;
std::ifstream fp("/dev/random", std::ios_base::binary);
if (!fp.is_open()) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Could not open /dev/random" :
"/dev/randomを開けられませんでした");
fl_alert("%s", err.c_str());
return "";
}
std::vector<char> password(count + 1);
for (int i = 0; i < count; ++i) {
unsigned char key;
fp.read(reinterpret_cast<char *>(&key), sizeof(key));
if (!fp) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Could not read /dev/random" :
"/dev/randomから読み込みに失敗");
fl_alert("%s", err.c_str());
fp.close();
std::exit(EXIT_FAILURE);
}
password[i] = charset[key % charset.size()];
}
password[count] = '\0';
fp.close();
return std::string(password.data());
}
void Genpass::generate_cb(Fl_Widget *, void *) {
int count = std::stoi(gen.counter->value());
bool issecure = gen.securechk->value() > 0;
std::string password = gen.exec(count, issecure);
gen.res->value(password.c_str());
}
void Genpass::dialog_cb(Fl_Widget *w, void *) {
(void)w;
std::string lang = Common::getlang();
Fl_Window *dialog = new Fl_Window(450, 250,
(lang.compare(0, 2, "en") == 0 ? "Generate password" : "パスワードの作成"));
gen.counter = new Fl_Input(120, 20, 100, 30,
(lang.compare(0, 2, "en") == 0 ? "Length:" : "長さ:"));
gen.counter->type(FL_INT_INPUT);
gen.counter->value("64");
dialog->add(gen.counter);
gen.securechk = new Fl_Check_Button(120, 70, 150, 30,
(lang.compare(0, 2, "en") == 0 ? "Secure?" : "安全化?"));
gen.securechk->value(1);
dialog->add(gen.securechk);
gen.genbtn = new Fl_Button(120, 110, 150, 30,
(lang.compare(0, 2, "en") == 0 ? "Generate" : "作成"));
gen.genbtn->callback(generate_cb);
dialog->add(gen.genbtn);
gen.res = new Fl_Output(120, 150, 300, 30,
(lang.compare(0, 2, "en") == 0 ? "Password:" : "パスワード:"));
gen.res->value("");
dialog->add(gen.res);
Fl_Button *okbtn = new Fl_Button(60, 200, 80, 30, "OK");
Fl_Button *cancelbtn = new Fl_Button(160, 200, 80, 30,
(lang.compare(0, 2, "en") == 0 ? "Cancel" : "キャンセル"));
okbtn->callback(static_ok_cb, dialog);
cancelbtn->callback(static_cancel_cb, dialog);
dialog->add(okbtn);
dialog->add(cancelbtn);
dialog->end();
dialog->set_modal();
dialog->show();
}
void Genpass::static_ok_cb(Fl_Widget *w, void *data) {
((Genpass *)data)->ok_cb(w, data);
}
void Genpass::static_cancel_cb(Fl_Widget *w, void *data) {
((Genpass *)data)->cancel_cb(w, data);
}

33
src/genpass.hh Normal file
View File

@@ -0,0 +1,33 @@
#ifndef GENPASS_HH
#define GENPASS_HH
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Secret_Input.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Output.H>
#include <string>
#include "dialog.hh"
class Genpass : public Dialog {
public:
Fl_Button *btn = nullptr;
std::string exec(int count, bool issecure);
static void dialog_cb(Fl_Widget *w, void *);
static void generate_cb(Fl_Widget *, void *data);
private:
Fl_Input *counter = nullptr;
Fl_Check_Button *securechk = nullptr;
Fl_Button *genbtn = nullptr;
Fl_Output *res = nullptr;
static void static_ok_cb(Fl_Widget *w, void *data);
static void static_cancel_cb(Fl_Widget *w, void *data);
};
#endif

58
src/initpass.cc Normal file
View File

@@ -0,0 +1,58 @@
#include "common.hh"
#include "initpass.hh"
#include <FL/Fl_Window.H>
#include <FL/fl_ask.H>
#include <cerrno>
#include <fstream>
#include <iostream>
#include <string>
#include <sys/stat.h>
Initpass init;
void Initpass::exec(const std::string &gpgid) {
std::string lang = Common::getlang();
std::string basedir = Common::getbasedir(true);
Common common;
if (common.mkdir_r(basedir, 0755) != 0 && errno != EEXIST) {
std::cout << basedir << std::endl;
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Failed to create directory." :
"ディレクトリを作成に失敗。");
fl_alert("%s", err.c_str());
return;
}
std::string gpgidpath = basedir + "/.gpg-id";
struct stat statbuf;
if (stat(gpgidpath.c_str(), &statbuf) == 0) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
".gpg-id file already exists." :
".gpg-idファイルは既に存在します。");
fl_alert("%s", err.c_str());
return;
}
std::ofstream gpgidfile(gpgidpath);
if (!gpgidfile.is_open()) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Failed to write .gpg-id file." :
".gpg-idファイルを書き込めません。");
fl_alert("%s", err.c_str());
return;
}
gpgidfile << gpgid + '\n';
gpgidfile.close();
std::string msg = (lang.compare(0, 2, "en") == 0 ?
"Initialization completed." :
"初期設定に完了しました。");
fl_alert("%s", msg.c_str());
}

19
src/initpass.hh Normal file
View File

@@ -0,0 +1,19 @@
#ifndef INITPASS_HH
#define INITPASS_HH
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <string>
#include "dialog.hh"
class Initpass : public Dialog {
public:
Fl_Button *btn = nullptr;
Fl_Input *gpgid = nullptr;
void exec(const std::string &gpgid);
};
#endif

119
src/otppass.cc Normal file
View File

@@ -0,0 +1,119 @@
#include "common.hh"
#include "base32.hh"
#include "otppass.hh"
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <FL/fl_ask.H>
#if defined(__APPLE)
#include <libkern/OSByteOrder.h>
#define htobe64(x) OSSwapHostToBigInt64(x)
#endif
std::vector<unsigned char> Otppass::extract_secret(const std::string &otpauth_url) {
std::string lang = Common::getlang();
auto secret_start = otpauth_url.find("secret=");
if (secret_start == std::string::npos) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Failed to find secret in the OTPAuth URL" :
"OTPAuth URLの中に、シークレットを見つけられませんでした");
fl_alert("%s", err.c_str());
return {};
}
secret_start += 7;
auto secret_end = otpauth_url.find('&', secret_start);
if (secret_end == std::string::npos) {
secret_end = otpauth_url.length();
}
if (secret_end < secret_start) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Incorrect secret range" :
"不正なシークレットの距離");
fl_alert("%s", err.c_str());
return {};
}
std::string secret_encoded =
otpauth_url.substr(secret_start, secret_end - secret_start);
std::vector<unsigned char> secret_decoded;
secret_decoded = Base32::decode(secret_encoded.c_str());
if (secret_decoded.empty()) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Failed to decrypt BASE32" :
"BASE32の復号化に失敗");
fl_alert("%s", err.c_str());
return {};
}
return secret_decoded;
}
#if defined(__HAIKU__) || defined(__linux)
uint64_t Otppass::htobe64(uint64_t counter) {
uint64_t res = 0;
uint8_t *dest = reinterpret_cast<uint8_t*>(&res);
const uint8_t *src = reinterpret_cast<const uint8_t*>(&counter);
for (int i = 0; i < 8; ++i) {
dest[i] = src[7 - i];
}
return res;
}
#endif
uint32_t Otppass::generate_totp(const std::vector<unsigned char> &secret,
uint64_t counter) {
counter = htobe64(counter);
unsigned char hash[SHA_DIGEST_LENGTH];
HMAC(
EVP_sha1(),
secret.data(),
secret.size(),
reinterpret_cast<unsigned char *>(&counter),
sizeof(counter),
hash,
NULL
);
int offset = hash[SHA_DIGEST_LENGTH - 1] & 0x0F;
uint32_t truncated_hash =
(hash[offset] & 0x7F) << 24 |
(hash[offset + 1] & 0xFF) << 16 |
(hash[offset + 2] & 0xFF) << 8 |
(hash[offset + 3] & 0xFF);
return truncated_hash % 1000000;
}
std::string Otppass::exec(std::string &secret) {
std::string lang = Common::getlang();
try {
std::vector<unsigned char> secret_decoded = extract_secret(secret);
time_t current_time = time(nullptr);
uint64_t counter = current_time / 30;
uint32_t otp =
generate_totp(secret_decoded, counter);
char otpres[7];
std::snprintf(otpres, sizeof(otpres), "%06u", otp);
return std::string(otpres);
} 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 "";
}
}

21
src/otppass.hh Normal file
View File

@@ -0,0 +1,21 @@
#ifndef OTPPASS_HH
#define OTPPASS_HH
#include <cstdint>
#include <string>
#include <vector>
class Otppass {
public:
std::string exec(std::string &file);
private:
std::vector<unsigned char> extract_secret(const std::string &otpauth_url);
#if defined(__HAIKU__) || defined(__linux)
uint64_t htobe64(uint64_t counter);
#endif
uint32_t generate_totp(const std::vector<unsigned char> &secret,
uint64_t counter);
};
#endif

129
src/showpass.cc Normal file
View File

@@ -0,0 +1,129 @@
#include "common.hh"
#include "showpass.hh"
#include "../main.hh"
#include "otppass.hh"
#include <gpgme++/decryptionresult.h>
#undef Status
#undef None
#include <FL/Fl.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/Fl_Text_Display.H>
void Showpass::otpupdate_cb(void *o) {
Showpass *show = static_cast<Showpass *>(o);
Otppass otp;
if (!show->otpSav.empty()) {
std::string dec = otp.exec(show->otpSav);
realpass = dec;
if (!isPassHidden) {
textbuf->text(dec.c_str());
textview->redraw();
}
}
Fl::repeat_timeout(1.0, otpupdate_cb, o);
}
std::string Showpass::exec(const char *file, bool stfu) {
std::string lang = Common::getlang();
try {
std::setlocale(LC_ALL, "");
GpgME::initializeLibrary();
gpg_error_t err = gpg_err_init();
if (err) {
if (!stfu) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to generate GPGME" :
"GPGエラーの設置に失敗");
fl_alert("%s: %s", ero.c_str(), gpg_strerror(err));
}
return "";
}
std::unique_ptr<GpgME::Context> ctx =
GpgME::Context::create(GpgME::Protocol::OpenPGP);
if (!ctx) {
if (!stfu) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to create GPGME context" :
"GPGMEコンテキストの作成に失敗");
fl_alert("%s", ero.c_str());
}
return "";
}
if (ctx->protocol() != GpgME::Protocol::OpenPGP) {
if (!stfu) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to set OpenPGP protocol" :
"OpenPGPプロトコールの設置に失敗");
fl_alert("%s", ero.c_str());
}
return "";
}
std::ifstream gpgfile(file, std::ios::binary);
if (!gpgfile.is_open()) {
if (!stfu) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Unable to open the specified file" :
"指定されたファイルを開けません");
fl_alert("%s", ero.c_str());
}
return "";
}
GpgME::Data in(file);
GpgME::Data out;
GpgME::DecryptionResult res = ctx->decrypt(in, out);
if (res.error()) {
if (!stfu) {
std::string ero = (lang.compare(0, 2, "en") == 0 ?
"Failed to decrypt" :
"復号化に失敗");
fl_alert("%s: %s", ero.c_str(), res.error().asString());
}
return "";
}
std::string dec;
char buf[512];
ssize_t read_bytes;
out.seek(0, SEEK_SET);
while ((read_bytes = out.read(buf, sizeof(buf))) > 0) {
dec.append(buf, read_bytes);
}
if (dec.rfind("otpauth://", 0) == 0 && !stfu) {
Otppass o;
otpSav = dec;
Fl::remove_timeout(otpupdate_cb, this);
Fl::add_timeout(1.0, otpupdate_cb, this);
return o.exec(dec);
}
Fl::remove_timeout(otpupdate_cb, this);
otpSav = "";
return dec;
} catch (const std::exception &e) {
if (!stfu) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Error" :
"エラー");
fl_alert("%s: %s", err.c_str(), e.what());
}
return "";
}
}

21
src/showpass.hh Normal file
View File

@@ -0,0 +1,21 @@
#ifndef SHOWPASS_HH
#define SHOWPASS_HH
#include <gpgme++/context.h>
#include <gpgme++/data.h>
#include <fstream>
class Showpass {
public:
std::string otpSav = "";
static void otpupdate_cb(void *o);
std::string exec(const char *file, bool stfu);
private:
void clean_up(GpgME::Context &ctx, GpgME::Data &in, GpgME::Data &out,
std::ifstream &gpgfile);
};
#endif

198
src/vulnpass.cc Normal file
View File

@@ -0,0 +1,198 @@
#include "common.hh"
#include "vulnpass.hh"
#include "showpass.hh"
#include "../main.hh"
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Text_Display.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/Fl_Scrollbar.H>
#include <vector>
#include <string>
#include <thread>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
Vulnpass vuln;
int vulncount = 0;
std::vector<std::string> vulnpaths;
bool Vulnpass::exec() {
std::string lang = Common::getlang();
// pwndサーバに接続
int sock;
struct sockaddr_in srv;
struct addrinfo hints, *addr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
int status = getaddrinfo("076.moe", nullptr, &hints, &addr);
if (status != 0) {
fl_alert("getaddrinfo: %s", gai_strerror(status));
return false;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
fl_alert(lang.compare(0, 2, "en") == 0 ?
"Failed to create socket" : "ソケットを作成に失敗");
return false;
}
srv.sin_addr = ((struct sockaddr_in *)(addr->ai_addr))->sin_addr;
srv.sin_family = AF_INET;
srv.sin_port = htons(9951);
freeaddrinfo(addr);
if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
fl_alert(lang.compare(0, 2, "en") == 0 ?
"Failed to connect" : "接続に失敗");
close(sock);
return false;
}
// パスワードをスキャンして
Showpass show;
for (const auto &dispath : dispaths) {
std::string fullpath = Common::getbasedir(true) + dispath + ".gpg";
std::string pass = show.exec(fullpath.c_str(), true);
if (pass.empty()) continue;
if (send(sock, pass.c_str(), pass.length(), 0) < 0) {
fl_alert(lang.compare(0, 2, "en") == 0 ?
"Failed to send" : "送信に失敗");
close(sock);
return false;
}
char res[256] = {0};
int reslen = recv(sock, res, sizeof(res) - 1, 0);
if (reslen < 0) {
fl_alert(lang.compare(0, 2, "en") == 0 ?
"Failed to retrieve" : "受取に失敗");
close(sock);
return false;
}
res[reslen] = '\0';
std::string response(res);
if (response.compare(0, 1, "0") != 0) {
vulnpaths.push_back(dispath);
vulncount++;
}
close(sock);
// 再接続
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
fl_alert(lang.compare(0, 2, "en") == 0 ?
"Failed to create socket" : "ソケットを作成に失敗");
return false;
}
if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
fl_alert(lang.compare(0, 2, "en") == 0 ?
"Failed to reconnect" : "再接続に失敗");
close(sock);
return false;
}
}
close(sock);
return true;
}
void Vulnpass::showRes() {
std::string lang = Common::getlang();
std::string res;
if (lang.compare(0, 2, "en") == 0) {
res = "Breached passwords:\n";
} else {
res = "漏洩したパスワード:\n";
}
for (const auto &path : vulnpaths) {
res += path + "\n";
}
if (lang.compare(0, 2, "en") == 0) {
res += "Total: " + std::to_string(vulnpaths.size()) + "\n";
res += "It's advised to change any of the";
res += "breached passwords as soon as possible!";
} else {
res += "合計: " + std::to_string(vulnpaths.size()) + "\n";
res += "漏洩したパスワードは出来るだけ早く変更する事をお勧めします!";
}
Fl_Window *win = new Fl_Window(500, 440, lang.compare(0, 2, "en") == 0 ?
"Results" : "結果");
Fl_Text_Display *display = new Fl_Text_Display(10, 10, 480, 380);
Fl_Text_Buffer *textbuf = new Fl_Text_Buffer();
textbuf->text(res.c_str());
display->buffer(textbuf);
display->scrollbar_width(15);
win->resizable(display);
Fl_Button *okBtn = new Fl_Button(210, 400, 80, 30, "OK");
okBtn->callback([](Fl_Widget *widget, void *win) {
(void)widget;
reinterpret_cast<Fl_Window*>(win)->hide();
}, win);
win->add(okBtn);
win->end();
win->show();
}
void Vulnpass::dialog_cb(Fl_Widget *w, void *) {
(void)w;
std::string lang = Common::getlang();
Fl_Window *dialog = new Fl_Window(400, 50,
(lang.compare(0, 2, "en") == 0 ?
"Check for branched password" : "漏洩されたパスワードの確認"));
Fl_Box *box = new Fl_Box(10, 10, 380, 20,
(lang.compare(0, 2, "en") == 0 ?
"Checking, please wait for a while..." :
"確認中。暫くお待ち下さい・・・"));
dialog->add(box);
dialog->end();
dialog->set_modal();
dialog->show();
std::thread checker([dialog]() {
vuln.exec();
Fl::lock();
dialog->hide();
vuln.showRes();
Fl::unlock();
});
checker.detach();
}

18
src/vulnpass.hh Normal file
View File

@@ -0,0 +1,18 @@
#ifndef VULNPASS_HH
#define VULNPASS_HH
#include <FL/Fl_Button.H>
#include "dialog.hh"
class Vulnpass : public Dialog {
public:
Fl_Button *btn = nullptr;
static void dialog_cb(Fl_Widget *w, void *);
void vuln_cb(Fl_Widget *, void *);
bool exec();
void showRes();
};
#endif