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

15
CHANGELOG.md Normal file
View File

@@ -0,0 +1,15 @@
# 1.1.1 (2025年03月13日)
* OTPで、秘密鍵を自動的に大文字にする様に
# 1.1.0 (2024年12月17日)
* パスワード表示で、「OpenPGP」かどうかの確認の追加
* 侵害されたパスワードの確認の追加
* 複数サイトで同じパスワードを利用かどうか、パスワードの長さ、又はパスワードの強さの確認
* パスワードを表示・非表示にする機能性の追加(デフォルトは非表示)
* ワンタイムパスワードOTPの場合、自動的に更新する様に
* アプリケーションアイコンの追加
* ライトモードの追加
* コンフィグファイルの追加
# 1.0.0 (2024年09月24日)
* 開始

13
LICENSE.txt Normal file
View File

@@ -0,0 +1,13 @@
Copyright © 2018-2024 by 076.moe
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

126
Makefile Normal file
View File

@@ -0,0 +1,126 @@
UNAME_S != uname -s
UNAME_M != uname -m
OS = ${UNAME_S}
ARCH = ${UNAME_M}
.if ${UNAME_S} == "OpenBSD"
OS = openbsd
.elif ${UNAME_S} == "NetBSD"
OS = netbsd
.elif ${UNAME_S} == "FreeBSD"
OS = freebsd
.elif ${UNAME_S} == "Dragonfly"
OS = dragonfly
.elif ${UNAME_S} == "Linux"
OS = linux
.endif
.if ${UNAME_M} == "x86_64"
ARCH = amd64
.endif
NAME != cat main.cc | grep "const char \*sofname" | awk '{print $$5}' |\
sed "s/\"//g" | sed "s/;//"
VERSION != cat main.cc | grep "const char \*version" | awk '{print $$5}' |\
sed "s/\"//g" | sed "s/;//"
PREFIX = /usr/local
.if ${OS} == "linux"
PREFIX = /usr
.endif
CC = c++
FILES = main.cc src/*.cc
.if ${OS} == "openbsd"
DEPS = pkg_add fltk gpgme gpgme-qt gnupg pinentry
.elif ${OS} == "netbsd"
DEPS = pkgin install fltk gpgme gpgmepp gnupg pinentry pinentry-fltk
.elif ${OS} == "freebsd"
DEPS = pkg install fltk gpgme gpgme-cpp gnupg pinentry pinentry-fltk
.elif ${OS} == "linux"
DEPS = xbps-install fltk fltk-devel gpgme gpgmepp gpgmepp-devel gnupg pinentry bmake
.endif
CFLAGS = -Wall -Wextra -Wno-non-c-typedef-for-linkage -Wno-unused-parameter\
-Wno-cast-function-type\
-I/usr/include -L/usr/lib
.if ${OS} == "freebsd" || ${OS} == "openbsd" || ${OS} == "netbsd" || ${OS} == "dragonfly
CFLAGS += -I/usr/local/include -L/usr/local/lib
.endif
.if ${OS} == "netbsd"
CFLAGS += -I/usr/X11R7/include -L/usr/X11R7/lib -I/usr/pkg/include -L/usr/pkg/lib
.elif ${OS} == "openbsd"
CFLAGS += -I/usr/X11R6/include -L/usr/X11R6/lib
.endif
LDFLAGS = -lfltk_images -lfltk -lX11 -lassuan -lgpgmepp -lgpgme -lcrypto -lgpg-error
.if ${OS} == "openbsd"
LDFLAGS += -lc++abi -lpthread -lm -lc\
-lXcursor -lXfixes -lXext -lXft -lfontconfig -lXinerama -lXdmcp -lXau\
-lz -lxcb -lXrender -lexpat -lfreetype -lc++ -lintl -liconv -lpng -ljpeg
.elif ${OS} == "freebsd"
LDFLAGS += -lcxxrt -lm -lXrender -lXcursor -lXfixes -lXext -lXft -lfontconfig\
-lXinerama -lthr -lz -lxcb -lfreetype -lexpat -lXau -lXdmcp\
-lbz2 -lbrotlidec -lbrotlicommon -lc++ -lgcc -lc -lgpgme -lassuan\
-lintl -lpng16
.elif ${OS} == "netbsd"
LDFLAGS += -lstdc++ -lpthread -lm -lc -lXft -lxcb -lfontconfig -lfreetype\
-lXau -lXdmcp -lXcursor -lXrandr -lXext -lXrender -lXfixes -lXinerama -lX11\
-lexpat -lz -lbz2 -lgcc -lassuan -lintl
.elif ${OS} == "linux"
LDFLAGS += -lstdc++ -lgcc -lc -lXft -lXext -lXrender -lfontconfig -lXinerama\
-lxcb -lfreetype -lpng16 -lz -lexpat -lXau -lXdmcp -lbz2\
-lbrotlidec -lbrotlicommon -lassuan
.endif
all:
${CC} -O3 ${CFLAGS} -o ${NAME}\
${FILES} -static ${LDFLAGS}
strip ${NAME}
depend:
${DEPS}
debug:
${CC} -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
clean:
rm -rf ${NAME}
dist:
mkdir -p ${NAME}-${VERSION} release/src release/desktop
cp -R LICENSE.txt Makefile README.md CHANGELOG.md logo.png\
main.* src icons ${NAME}.desktop ${NAME}-${VERSION}
tar zcfv release/src/${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION}
cp ${NAME}.desktop release/desktop
rm -rf ${NAME}-${VERSION}
release:
mkdir -p release/bin/${VERSION}/${OS}/${ARCH}
${CC} -O3 ${CFLAGS} -o release/bin/${VERSION}/${OS}/${ARCH}/${NAME} ${FILES}\
-static ${LDFLAGS}
strip release/bin/${VERSION}/${OS}/${ARCH}/${NAME}
publish:
rsync -rtvzP release/bin/${VERSION} 192.168.0.143:/zroot/repo/bin/${NAME}
rsync -rtvzP release/src/* 192.168.0.143:/zroot/repo/src/${NAME}
rsync -rtvzP release/desktop/* 192.168.0.143:/zroot/repo/desktop
rsync -rtvzP release/icons/* 192.168.0.143:/zroot/repo/icons
install:
mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${PREFIX}/share/applications
cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
cp -f ${NAME}.desktop ${DESTDIR}${PREFIX}/share/applications
.if ${OS} == "linux"
sed -i 's/\/local//' ${DESTDIR}${PREFIX}/share/applications/${NAME}.desktop
.endif
cp -rf icons/076 ${DESTDIR}${PREFIX}/share/icons
chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
uninstall:
rm -f ${DESTDIR}${PREFIX}/share/applications/${NAME}.desktop
rm -f ${DESTDIR}${PREFIX}/bin/${NAME}
.PHONY: all debug clean dist release publish install uninstall

25
README.md Normal file
View File

@@ -0,0 +1,25 @@
# simpas
シンプルなパスワードマネージャー Simple Password Manager\
GUI版のsp
## インストールする方法 | Installation
### BSD
```sh
doas make depends
make
doas make install
```
### Void Linux
```sh
sudo bmake depends
bmake
sudo bmake install
```
### Alpine Linux
```sh
sudo apk add fltk fltk-dev gpgme gpgmepp gnupg pinentry bmake
bmake
sudo bmake install
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
icons/076/16x16/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
icons/076/18x18/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
icons/076/22x22/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
icons/076/24x24/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
icons/076/32x32/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
icons/076/42x42/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
icons/076/48x48/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

BIN
icons/076/64x64/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
icons/076/84x84/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
icons/076/8x8/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

BIN
icons/076/96x96/simpas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

75
icons/076/index.theme Normal file
View File

@@ -0,0 +1,75 @@
[Icon Theme]
Name=076 Icons
Comment=076 Icons
Inherits=default
Directories=128x128,18x18,24x24,32x32,48x48,64x64,8x8,16x16,22x22,256x256,42x42,512x512,84x84,96x96
[128x128]
Size=128
Context=Icons
Type=Fixed
[18x18]
Size=18
Context=Icons
Type=Fixed
[24x24]
Size=24
Context=Icons
Type=Fixed
[32x32]
Size=32
Context=Icons
Type=Fixed
[48x48]
Size=48
Context=Icons
Type=Fixed
[64x64]
Size=64
Context=Icons
Type=Fixed
[8x8]
Size=8
Context=Icons
Type=Fixed
[16x16]
Size=16
Context=Icons
Type=Fixed
[22x22]
Size=22
Context=Icons
Type=Fixed
[256x256]
Size=256
Context=Icons
Type=Fixed
[42x42]
Size=42
Context=Icons
Type=Fixed
[512x512]
Size=512
Context=Icons
Type=Fixed
[84x84]
Size=84
Context=Icons
Type=Fixed
[96x96]
Size=96
Context=Icons
Type=Fixed

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

362
main.cc Normal file
View File

@@ -0,0 +1,362 @@
#include "src/addpass.hh"
#include "src/delpass.hh"
#include "src/editpass.hh"
#include "src/genpass.hh"
#include "src/initpass.hh"
#include "src/showpass.hh"
#include "src/vulnpass.hh"
#include "src/chkpass.hh"
#include "src/common.hh"
#include "main.hh"
#undef Status
#undef None
#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Widget.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Select_Browser.H>
#include <FL/Fl_Text_Display.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Secret_Input.H>
#include <FL/Fl_Hold_Browser.H>
#include <FL/Fl_Copy_Surface.H>
#include <FL/Fl_PNG_Image.H>
#include <dirent.h>
#include <sys/stat.h>
#include <string>
#include <vector>
Fl_Select_Browser *browser = nullptr;
Fl_Text_Display *textview = nullptr;
Fl_Text_Buffer *textbuf = nullptr;
Fl_Input *searchfield = nullptr;
Fl_Check_Button *hidechk = nullptr;
Fl_Button *showbtn = nullptr;
Addpass a;
Chkpass c;
Delpass d;
Editpass e;
Genpass g;
Initpass i;
Showpass s;
Vulnpass v;
const char *sofname = "simpas";
const char *intname = "SimPas";
const char *version = "1.1.1";
const char *basedof = "sp-1.5.1";
std::vector<std::string> fullpaths;
std::vector<std::string> dispaths;
std::vector<std::string> filterpaths;
int browseId;
bool isPassHidden = true;
std::string realpass = "";
void browse(std::string &path, bool isNew) {
std::string cont = s.exec(path.c_str(), false);
realpass = cont;
if (isNew) browseId = browser->size();
browser->value(browseId);
if (!cont.empty()) {
if (isPassHidden) {
std::string lang = Common::getlang();
if (lang.compare(0, 2, "en") == 0) {
textbuf->text("(Hidden, please click the \"show\" button to reveal)");
} else {
textbuf->text("(非表示、「表示」ボタンをクリックして確認して下さい)");
}
showbtn->activate();
showbtn->label((Common::getlang().compare(0, 2, "en") == 0) ?
"Show password" : "パスワードの表示");
} else {
textbuf->text(cont.c_str());
showbtn->label((Common::getlang().compare(0, 2, "en") == 0) ?
"Hide password" : "パスワードを隠す");
showbtn->activate();}
Editpass::setFile(path);
Delpass::setFile(path);
d.btn->activate();
e.btn->activate();
}
}
void hide_cb(Fl_Widget *w, void *) {
isPassHidden = ((Fl_Check_Button *)w)->value();
int idx = browser->value();
if (isPassHidden && idx > 0) {
showbtn->activate();
showbtn->label((Common::getlang().compare(0, 2, "en") == 0) ?
"Show password" : "パスワードの表示");
} else {
showbtn->deactivate();
showbtn->label((Common::getlang().compare(0, 2, "en") == 0) ?
"Hide password" : "パスワードを隠す");
}
showbtn->label((Common::getlang().compare(0, 2, "en") == 0) ?
"Show password" : "パスワードの表示");
if (idx > 0) {
std::string path = filterpaths[idx - 1];
browse(path, false);
}
}
void clearpaths(bool isReset, std::string &path) {
fullpaths.clear();
dispaths.clear();
if (isReset) {
std::string mockpath = "";
Editpass::setFile(mockpath);
Delpass::setFile(mockpath);
d.btn->deactivate();
e.btn->deactivate();
}
}
void updatelist() {
browser->clear();
filterpaths.clear();
std::string searchtxt = searchfield->value();
for (size_t i = 0; i < dispaths.size(); ++i) {
if (dispaths[i].find(searchtxt) != std::string::npos) {
browser->add(dispaths[i].c_str());
filterpaths.push_back(fullpaths[i]);
}
}
textbuf->text("");
}
void search_cb(Fl_Widget *, void *) {
std::string mockpath = "";
Editpass::setFile(mockpath);
Delpass::setFile(mockpath);
d.btn->deactivate();
e.btn->deactivate();
updatelist();
}
void copy_cb(Fl_Widget *, void *) {
if (!realpass.empty()) {
Fl::copy(realpass.c_str(), realpass.length(), 1, Fl::clipboard_plain_text);
} else {
Fl::copy("", 0, 1, Fl::clipboard_plain_text);
}
}
void browser_cb(Fl_Widget *w, void *) {
(void)w;
int idx = browser->value();
if (idx == 0) return;
browseId = idx;
std::string path = filterpaths[idx - 1];
browse(path, false);
}
void show_cb(Fl_Widget *, void *) {
int idx = browser->value();
if (idx == 0) return;
std::string path = filterpaths[idx - 1];
if (isPassHidden) {
std::string cont = s.exec(path.c_str(), false);
realpass = cont;
textbuf->text(cont.c_str());
std::string lang = Common::getlang();
if (lang.compare(0, 2, "en") == 0) {
showbtn->label("Hide password");
} else {
showbtn->label("パスワードを隠す");
}
isPassHidden = false;
} else {
std::string lang = Common::getlang();
if (lang.compare(0, 2, "en") == 0) {
textbuf->text("(Hidden, please click the \"show\" button to reveal)");
showbtn->label("Show password");
} else {
textbuf->text("(非表示、「表示」ボタンをクリックして確認して下さい)");
showbtn->label("パスワードの表示");
}
isPassHidden = true;
}
}
void scandir(const std::string &dpath, const std::string &rpath,
std::vector<std::string> &fpaths) {
DIR *dir = opendir(dpath.c_str());
if (!dir) return;
struct dirent *entry;
while ((entry = readdir(dir)) != nullptr) {
std::string name = entry->d_name;
if (name == "." || name == ".." || name == ".gpg-id") continue;
std::string fpath = std::string(dpath) + "/" + name;
struct stat s;
if (stat(fpath.c_str(), &s) != 0) {
closedir(dir);
return;
}
if (S_ISDIR(s.st_mode)) {
scandir(fpath, rpath, fpaths);
} else if (name.find(".gpg") != std::string::npos) {
std::string rel = fpath.substr(rpath.size() + 1);
fpaths.push_back(rel);
fullpaths.push_back(fpath);
std::string disname = rel.substr(0, rel.rfind(".gpg"));
dispaths.push_back(disname);
}
}
closedir(dir);
}
void init_cb(Fl_Widget *w, void *data) {
i.exec(i.gpgid->value());
i.btn->deactivate();
((Initpass *)data)->cancel_cb(w, data);
}
void init_dialog_cb(Fl_Widget *w, void *) {
(void)w;
std::string lang = Common::getlang();
Fl_Window *dialog = new Fl_Window(450, 120,
(lang.compare(0, 2, "en") == 0 ?
"Initialize password" : "パスワードの初期設定"));
i.gpgid = new Fl_Input(90, 20, 300, 30,
(lang.compare(0, 2, "en") == 0 ? "GPG secret key:" : "gpg秘密鍵:"));
dialog->add(i.gpgid);
Fl_Button *startbtn = new Fl_Button(185, 70, 80, 30,
(lang.compare(0, 2, "en") == 0 ? "Start" : "開始"));
startbtn->callback(init_cb, dialog);
dialog->add(startbtn);
dialog->end();
dialog->set_modal();
dialog->show();
}
void set_dark_theme() {
Fl::background(35, 32, 35);
Fl::background2(68, 59, 68);
Fl::foreground(252, 252, 252);
}
int main(int argc, char **argv) {
std::string lang = Common::getlang();
std::string windowtit = std::string(intname) + " " + version;
Fl_Window *window = new Fl_Window(790, 740, windowtit.c_str());
set_dark_theme();
#if defined(__linux)
const char *iconPath = "/usr/share/icons/076/512x512/simpas.png";
#else
const char *iconPath = "/usr/local/share/icons/076/512x512/simpas.png";
#endif
Fl_PNG_Image *icon = new Fl_PNG_Image(iconPath);
window->icon(icon);
searchfield = new Fl_Input(
(lang.compare(0, 2, "en") == 0 ? 70 : 50), 10,
(lang.compare(0, 2, "en") == 0 ? 710 : 730), 30,
(lang.compare(0, 2, "en") == 0 ? "Search:" : "検索:"));
searchfield->callback(search_cb);
browser = new Fl_Select_Browser(10, 50, 380, 500);
textview = new Fl_Text_Display(400, 50, 380, 500);
textbuf = new Fl_Text_Buffer();
textview->buffer(textbuf);
browser->callback(browser_cb);
Fl_Button *copybtn = new Fl_Button(400, 600, 150, 30,
(lang.compare(0, 2, "en") == 0 ? "Copy password" : "パスワードのコピー"));
copybtn->callback(copy_cb);
a.btn = new Fl_Button(10, 560, 150, 30,
(lang.compare(0, 2, "en") == 0 ? "Add password" : "パスワードの追加"));
a.btn->callback(a.dialog_cb);
d.btn = new Fl_Button(10, 600, 150, 30,
(lang.compare(0, 2, "en") == 0 ? "Delete password" : "パスワードの削除"));
d.btn->deactivate();
d.btn->callback(d.dialog_cb);
e.btn = new Fl_Button(400, 560, 150, 30,
(lang.compare(0, 2, "en") == 0 ? "Edit password" : "パスワードの編集"));
e.btn->deactivate();
e.btn->callback(e.dialog_cb);
hidechk = new Fl_Check_Button(560, 560, 150, 30,
(lang.compare(0, 2, "en") == 0 ? "Hide password" : "パスワードを隠す"));
hidechk->set();
hidechk->callback(hide_cb);
showbtn = new Fl_Button(560, 600, 150, 30,
(lang.compare(0, 2, "en") == 0 ? "Show password" : "パスワードの表示"));
showbtn->deactivate();
showbtn->callback(show_cb);
g.btn = new Fl_Button(10, 640, 150, 30,
(lang.compare(0, 2, "en") == 0 ? "Generate password" : "パスワードの作成"));
g.btn->callback(g.dialog_cb);
i.btn = new Fl_Button(10, 680, 150, 30,
(lang.compare(0, 2, "en") == 0 ?
"Initialize password" :
"パスワードの初期設定"));
v.btn = new Fl_Button(170, 560, 200, 30,
(lang.compare(0, 2, "en") == 0 ? "Check for breach" : "漏洩されたかの確認"));
v.btn->callback(v.dialog_cb);
c.btn = new Fl_Button(170, 600, 200, 30,
(lang.compare(0, 2, "en") == 0 ?
"Check for unsafe passwords" : "不安定的なパスワードの確認"));
c.btn->callback(c.dialog_cb);
std::string gpgidpath = Common::getbasedir(true) + ".gpg-id";
struct stat buf;
if (stat(gpgidpath.c_str(), &buf) == 0) {
i.btn->deactivate();
}
i.btn->callback(init_dialog_cb);
std::string bothver = windowtit + " (" + std::string(basedof) + ")";
Fl_Box *versionlabel = new Fl_Box(FL_NO_BOX, 620, 700, 160, 30, bothver.c_str());
versionlabel->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE);
std::vector<std::string> fpaths;
std::string rdir = Common::getbasedir(false);
scandir(rdir, rdir, fpaths);
updatelist();
window->end();
window->show(argc, argv);
return Fl::run();
}

24
main.hh Normal file
View File

@@ -0,0 +1,24 @@
#ifndef MAIN_HH
#define MAIN_HH
#include <vector>
#include <string>
extern std::vector<std::string> fullpaths;
extern std::vector<std::string> dispaths;
extern int browseId;
extern bool isPassHidden;
extern std::string realpass;
class Fl_Text_Display;
class Fl_Text_Buffer;
extern Fl_Text_Display *textview;
extern Fl_Text_Buffer *textbuf;
void browse(std::string &path, bool isNew);
void clearpaths(bool isReset, std::string &path);
void updatelist();
void scandir(const std::string& dir, const std::string& root, std::vector<std::string>& paths);
#endif

12
simpas.desktop Normal file
View File

@@ -0,0 +1,12 @@
[Desktop Entry]
Type=Application
Name=SimPas
GenericName=Simple Password Manager
GenericName[ja]=シンプルなパスワードマネージャー
Exec=simpas %F
Icon=/usr/local/share/icons/076/512x512/simpas
StartupNotify=true
Terminal=false
Type=Application
Categories=Application;Utility;System;Security;DeskopUtility;
Keywords=password;password manager;security;otp;password generator;

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