// Example GUI for John the Ripper
// ** IMPORTANT: It is not intented to be full or even work safely.
// ** You should not rely on this program if your hash's source is not trusted.
// ** It should only show my coding skills may be with bad gui design.
// It is a simple wrapper around John command line interface. It is a reimplementation in C++. So some comments are droped.
// Implementation language: C++ with WxWidgets as a GUI toolkit.

// It could be built with: g++ john_simple_gui.001.cpp $(wx-config --cxxflags --libs)

// We include WxWidgets at the begin because we want to use '_' macros here.
#include <wx/wx.h>

// Program name
static const wxChar * const name = _("John's Example GUI");

// Version
// ** May be it is not a good decision to use floating point number to represent the version.
static const double version = 0.01;

// License
// ** It is a stub to show licence in the interface. It is not implemented in this version.
static const wxChar * const license = _(
    "Copyright © 2011 Aleksey Cherepanov\n"
    "\n"
    "Permission is hereby granted, free of charge, to any person obtaining a copy\n"
    "of this software and associated documentation files (the \"Software\"), to deal\n"
    "in the Software without restriction, including without limitation the rights\n"
    "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
    "copies of the Software, and to permit persons to whom the Software is\n"
    "furnished to do so, subject to the following conditions:\n"
    "\n"
    "The above copyright notice and this permission notice shall be included in\n"
    "all copies or substantial portions of the Software.\n"
    "\n"
    "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
    "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
    "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
    "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
    "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
    "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n"
    "THE SOFTWARE.\n");

// Signs used in comments
// '**' means a string that documents something useful, important, interesting or just strange in design.
// ' *' means other string of "**" when it should be splitted into many lines.
// '%%' means a string that documents something not finished yet or to be done as a good feature.
// ' %' is like "*" but for "%%".

////////////////////////////////////////////////////////////////////////////////
// Imports and initializations

// We make WxWidgets to be in use with many different gui elements.
// %% It is needed to support precompiled headers.
#include <wx/grid.h>
#include <wx/sizer.h>
#include <wx/filepicker.h>
#include <wx/stream.h>
#include <wx/process.h>
#include <wx/txtstrm.h>

// We will use regular expression to parse John's output
// %% We need something to parse output.

// Identificators
// ** It better to split file into two: code and header. This enumeration should be in the header.
enum {
    // %% It is not ok to use such constant. But in the wxwidgets' example it was used. Investigate!
    ID_HASHES = 100,
    ID_RESTORE,
    ID_JOHN,
    ID_SINGLE,
    ID_START,
    ID_STOP
};

////////////////////////////////////////////////////////////////////////////////
// Default settings

// Setting class
// It's constructor contains default settings.
// ** We will use inheritance here to provide our class with automatic reference counting.
class Settings : wxObject {
    // All members will be public.
public:
    // Path to John
    // ** We will use camelCase for fields because wxwidgets' example did it.
    wxString pathToJohn;
    // Value for --single option
    bool useSingle;
    // Constructor
    // Fills settings object by default values.
    // Arguments: nothing.
    Settings() {
	RestoreDefaults();
    }
    // Method to restore default settings
    // Arguments: nothing.
    // Return value: nothing.
    void RestoreDefaults() {
	// Default path to John
	// %% This string does not really need a translation.
	pathToJohn = _("/usr/sbin/john");
	// Default value for --single option is disabled.
	useSingle = false;
    }
};

////////////////////////////////////////////////////////////////////////////////
// GUI definitions

// We make a main window. We subclass wx's window.
class MyFrame : public wxFrame {
    // All members will be public as of we do need to hide them while methods should be public to be available for wxwidgets.
public:
    // Settings object for this window
    Settings settings;
    // GUI elements
    // Panel to put elements on
    wxPanel * panel;
    // We will add elements to the frame in a way WxWidgets do all size calculations for us.
    // Box will hold our elements and pack them together in a comfortable way.
    wxBoxSizer * topBox;
    // Other elements are described at initialization point.
    // %% Make all comments to be in one style.
    wxFilePickerCtrl * hashFile, * johnPath;
    wxStaticBox * settingsBoxDecoration;
    wxStaticBoxSizer * settingsBox;
    wxButton * restoreButton, * startJohn, * stopJohn;
    wxCheckBox * settingSingle;
    wxTextCtrl * cmdline, * firstLine;
    wxStaticText * output;
    // String field for John's output
    wxString outputLabel;
    // String to represent our state
    wxString hashFilePath, cmdlineString;
    // Constructor
    // It fills a window by elements, makes other housekeeping work.
    // We compute title. We set up a window in the parent class' way.
    // ** I am using my own style to describe args and i am looking for using standard system for that.
    // Arguments: nothing.
    MyFrame() : wxFrame(NULL, wxID_ANY, wxString::Format(_T("%s (ver. %.2lf)"), name, version)) {
        // Elements
        // %% This is a big field with typical calls. It should be compressed in more elegant form. May be with wxGlade.
        // %% It is very, very, very bad to write such big methods. It should be splitted.
	panel = new wxPanel(this);
	topBox = new wxBoxSizer(wxVERTICAL);
        // Label for file chooser
        // %% It seems ok to roll hash file picker into a group because there will be two file or more subsequently.
	// ** new is ok here because wxwidgets count references by itself and we do not need to keep them in mind.
        topBox-> Add(new wxStaticText(panel, wxID_ANY, wxString(_("Choose a file with hashes to crack:"))));
        // File with hashes
        hashFile = new wxFilePickerCtrl(panel, ID_HASHES);
        // ** We make all elements to fill window horizontally hence the flag.
        // %% It is copied through the code. It is not good.
        topBox-> Add(hashFile, 0, wxEXPAND);
        // Group for advanced settings
        // Sizer to manage settings
        // %% I would like to make it collapsible but it seems to be hard for me now.
	settingsBoxDecoration = new wxStaticBox(panel, wxID_ANY, _("Settings"));
        settingsBox = new wxStaticBoxSizer(settingsBoxDecoration, wxVERTICAL);
        topBox-> Add(settingsBox, 0, wxEXPAND);
        // Button to restore settings
	restoreButton = new wxButton(panel, ID_RESTORE, _("Restore settings"));
        settingsBox-> Add(restoreButton, 0, wxEXPAND);
        // Path to John
        johnPath = new wxFilePickerCtrl(panel, ID_JOHN);
        // ** I could not make it to pick up default path through initializer's 'path' argument. Hence this call exists.
        johnPath-> SetPath(settings.pathToJohn);
        settingsBox-> Add(johnPath, 0, wxEXPAND);
        // --single option
        // ** This label was borrowed from John's usage message. It seems to be possible to pick it up at runtime.
	settingSingle = new wxCheckBox(panel, ID_SINGLE, _("\"single crack\" mode (--single)"));
	settingSingle-> SetValue(settings.useSingle);
	settingsBox-> Add(settingSingle, 0, wxEXPAND);
        // Elements for settings ended.
        // We continue other elements.
        // Command line string
        cmdline = new wxTextCtrl(panel, wxID_ANY);
        topBox-> Add(cmdline, 0, wxEXPAND);
        // Buttons to start and stop John
        startJohn = new wxButton(panel, ID_START, _("Start John"));
        topBox-> Add(startJohn, 0, wxEXPAND);
        stopJohn = new wxButton(panel, ID_STOP, _("Stop John"));
        stopJohn-> Disable();
        topBox-> Add(stopJohn, 0, wxEXPAND);
        // First line of John's output
	// %% It would be better to make a static text here and to set a new label then.
        firstLine = new wxTextCtrl(panel, wxID_ANY);
        topBox-> Add(firstLine, 0, wxEXPAND);
        // Grid
	// %% It is needed to make real grid.
	// ** We do not make grid now. We use static text to just show output.
	// %% Will text be autoupdated after new label comes?
	output = new wxStaticText(panel, wxID_ANY, outputLabel);
	topBox-> Add(output, 0, wxEXPAND);
        // We finalize layout.
        panel-> SetSizer(topBox);
        panel-> Layout();
        // We call recalculation to make all frame fields correct.
        RecalculateAll();
    }
    // Method for total recalculation
    // We take all input fields, reset grid, refill settings and output fields.
    // Arguments: nothing, or possibly an event object just to support using as handler.
    // Return value: nothing.
    void RecalculateAll() {
	// We have only one input field now. It is hash file path. Let's take it.
	hashFilePath = hashFile-> GetPath();
	// We reset grid because we want to obtain new data from John.
        // ** But we have no grid. We reset label.
	outputLabel = _("There will be John's output here.");
	// We refill settings.
        johnPath-> SetPath(settings.pathToJohn);
	settingSingle-> SetValue(settings.useSingle);
	// We refill output fields.
	// We compute our command line string.
	cmdlineString = settings.pathToJohn;
	if (settings.useSingle) {
	    cmdlineString += _(" --single");
	}
	cmdlineString += _(" ") + hashFilePath;
	// We output cmdline into a field for user's pleasure using this line in his script.
        cmdline-> SetValue(cmdlineString);
    }
    // Event handlers
    // All's arguments: event object that is not used.
    // All's return value: nothing.
    // Wrapper around RecalculateAll
    void DoRecalculateAll( wxFileDirPickerEvent& WXUNUSED(ev) ) {
        RecalculateAll();
    }
    // Restoring settings
    // We restore settings and recalculate all things.
    void DoRestore( wxCommandEvent& WXUNUSED(ev) ) {
	settings.RestoreDefaults();
	RecalculateAll();
    }
    // John path selection
    // We save path into settings and recalculate everything.
    void DoJohnPathSelect( wxFileDirPickerEvent& WXUNUSED(ev) ) {
	settings.pathToJohn = johnPath-> GetPath();
	RecalculateAll();
    }
    // --single toggled
    // We save this setting and recalculate other fields.
    void DoSingleToggle( wxCommandEvent& WXUNUSED(ev) ) {
	settings.useSingle = settingSingle-> GetValue();
	RecalculateAll();
    }
    // Starting John
    // %% GUI while john runs is freezed. So stop button does not work.
    // We disable start button, create John process, and start filling grid, enable stop button.
    // After we filled grid we should enable buttons back.
    void DoStartJohn( wxCommandEvent& WXUNUSED(ev) ) {
	// %% It is highly needed to check all fields before starting John process.
	//  % For instance, user should input name of file with hashes.
	startJohn-> Disable();
	// We reset previous output.
	outputLabel = _("");
	// We start John process.
	// %% This object should be deleted later.
	wxProcess * john = new wxProcess();
	// We enable redireaction.
	john-> Redirect();
	long pid = wxExecute(cmdlineString, wxEXEC_ASYNC, john);
	// We check bad condition.
	if (!pid) {
	    delete john;
	}
	stopJohn-> Enable();
	// We make text stream around stream with John's output.
	wxInputStream * stream =  john-> GetInputStream();
	wxTextInputStream reader (*stream);
	// We read first line of John's output to show it separately.
	firstLine-> SetValue(reader.ReadLine());
	// We read other lines and parse them while we could read a line.
	while (!stream-> Eof()) {
	    wxString line = reader.ReadLine();
	    // We parse line and fill grid.
	    // %% We do not really parse it...
	    // We just add a line to output text.
	    // %% Is there 'eol' or something like?
	    outputLabel += line + _("\n");
	    // We refresh view.
	    // ** Just without anything and even with ForceRefresh it does not want to show new values for me.
	    // %% It seems to be a trick. I think it should work without this. More investigation is needed.
	    //panel-> Layout();
	    // %% Even with refresh here image will be updated only after this method finish. Multithreading is needed.
	    output-> SetLabel(outputLabel);
	}
	// We turn back our buttons.
	stopJohn-> Disable();
	startJohn-> Enable();
    }
    // Stopping John
    // We disable stop button, stop John process, enable start button.
    void DoStopJohn( wxCommandEvent& WXUNUSED(ev) ) {
	// %% Currently we just do nothing.
	stopJohn-> Disable();
	// We check are there John running.
	// %% This should be done.
	//if (john) {
	    // If John is running we kill it.
	    // %% It is not good to kill processes without a real need. We should ask it to terminate itself.
	    //  % And only if it does respond we should kill it.
	    // %% It is needed to stop John here.
	//}
	startJohn-> Enable();
    }
    // Event table to make event bindings later
    DECLARE_EVENT_TABLE()
};
////////////////////////////////////////////////////////////////////////////////
// Let's start our application

// Class for our application
class JohnApp : public wxApp {
    // All members will be public.
public:
    bool OnInit() {
	// We make a form as defined.
	MyFrame * frame = new MyFrame;
	// We make it to be visible.
	frame-> Show(true);

	return true;
    }
};

// Bindings
BEGIN_EVENT_TABLE( MyFrame, wxFrame )
    // File with hashes selection
    EVT_FILEPICKER_CHANGED(ID_HASHES, MyFrame::DoRecalculateAll)
    // Restoring settings
    EVT_BUTTON(ID_RESTORE, MyFrame::DoRestore)
    // Change John's path
    EVT_FILEPICKER_CHANGED(ID_JOHN, MyFrame::DoJohnPathSelect)
    // Enabling --single
    EVT_CHECKBOX(ID_SINGLE, MyFrame::DoSingleToggle)
    // Start John
    EVT_BUTTON(ID_START, MyFrame::DoStartJohn)
    // Stop John
    EVT_BUTTON(ID_STOP, MyFrame::DoStopJohn)
END_EVENT_TABLE()

// We make WxWidgets understand what class is our application class.
IMPLEMENT_APP( JohnApp )
