SVNからのミラー
15
CHANGELOG.md
Normal 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
@@ -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
@@ -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
@@ -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
|
||||||
|
```
|
||||||
BIN
icons/076/128x128/simpas.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
icons/076/16x16/simpas.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
icons/076/18x18/simpas.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
icons/076/22x22/simpas.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
icons/076/24x24/simpas.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
icons/076/256x256/simpas.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
icons/076/32x32/simpas.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
icons/076/42x42/simpas.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
icons/076/48x48/simpas.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
icons/076/512x512/simpas.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
icons/076/64x64/simpas.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
icons/076/84x84/simpas.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
icons/076/8x8/simpas.png
Normal file
|
After Width: | Height: | Size: 790 B |
BIN
icons/076/96x96/simpas.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
75
icons/076/index.theme
Normal 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
|
||||||
362
main.cc
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||