<commit_before>/**
 * @file llexternaleditor.cpp
 * @brief A convenient class to run external editor.
 *
 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"
#include "llexternaleditor.h"

#include "lltrans.h"
#include "llui.h"
#include "llprocess.h"
#include "llsdutil.h"
#include <boost/foreach.hpp>

// static
const std::string LLExternalEditor::sFilenameMarker = "%s";

// static
const std::string LLExternalEditor::sSetting = "ExternalEditor";

LLExternalEditor::EErrorCode LLExternalEditor::setCommand(const std::string& env_var, const std::string& override)
{
	std::string cmd = findCommand(env_var, override);
	if (cmd.empty())
	{
		llwarns << "Editor command is empty or not set" << llendl;
		return EC_NOT_SPECIFIED;
	}

	string_vec_t tokens;
	tokenize(tokens, cmd);

	// Check executable for existence.
	std::string bin_path = tokens[0];
	if (!LLFile::isfile(bin_path))
	{
		llwarns << "Editor binary [" << bin_path << "] not found" << llendl;
		return EC_BINARY_NOT_FOUND;
	}

	// Save command.
	mProcessParams = LLProcess::Params();
	mProcessParams.executable = bin_path;
	for (size_t i = 1; i < tokens.size(); ++i)
	{
		mProcessParams.args.add(tokens[i]);
	}

	// Add the filename marker if missing.
	if (cmd.find(sFilenameMarker) == std::string::npos)
	{
		mProcessParams.args.add(sFilenameMarker);
		llinfos << "Adding the filename marker (" << sFilenameMarker << ")" << llendl;
	}

	llinfos << "Setting command [" << mProcessParams << "]" << llendl;

	return EC_SUCCESS;
}

LLExternalEditor::EErrorCode LLExternalEditor::run(const std::string& file_path)
{
	if (std::string(mProcessParams.executable).empty() || mProcessParams.args.empty())
	{
		llwarns << "Editor command not set" << llendl;
		return EC_NOT_SPECIFIED;
	}

	// Copy params block so we can replace sFilenameMarker
	LLProcess::Params params;
	params.executable = mProcessParams.executable;

	// Substitute the filename marker in the command with the actual passed file name.
	BOOST_FOREACH(const std::string& arg, mProcessParams.args)
	{
		std::string fixed(arg);
		LLStringUtil::replaceString(fixed, sFilenameMarker, file_path);
		params.args.add(fixed);
	}

	// Run the editor. Prevent killing the process in destructor.
	params.autokill = false;
	return LLProcess::create(params) ? EC_SUCCESS : EC_FAILED_TO_RUN;
}

// static
std::string LLExternalEditor::getErrorMessage(EErrorCode code)
{
	switch (code)
	{
	case EC_SUCCESS: 			return LLTrans::getString("ok");
	case EC_NOT_SPECIFIED: 		return LLTrans::getString("ExternalEditorNotSet");
	case EC_PARSE_ERROR:		return LLTrans::getString("ExternalEditorCommandParseError");
	case EC_BINARY_NOT_FOUND:	return LLTrans::getString("ExternalEditorNotFound");
	case EC_FAILED_TO_RUN:		return LLTrans::getString("ExternalEditorFailedToRun");
	}

	return LLTrans::getString("Unknown");
}

// TODO:
// - Unit-test this with tests like LLStringUtil::getTokens() (the
//   command-line overload that supports quoted tokens)
// - Unless there are significant semantic differences, eliminate this method
//   and use LLStringUtil::getTokens() instead.

// static
size_t LLExternalEditor::tokenize(string_vec_t& tokens, const std::string& str)
{
	tokens.clear();

	// Split the argument string into separate strings for each argument
	typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
	boost::char_separator<char> sep("", "\" ", boost::drop_empty_tokens);

	tokenizer tokens_list(str, sep);
	tokenizer::iterator token_iter;
	BOOL inside_quotes = FALSE;
	BOOL last_was_space = FALSE;
	for (token_iter = tokens_list.begin(); token_iter != tokens_list.end(); ++token_iter)
	{
		if (!strncmp("\"",(*token_iter).c_str(),2))
		{
			inside_quotes = !inside_quotes;
		}
		else if (!strncmp(" ",(*token_iter).c_str(),2))
		{
			if(inside_quotes)
			{
				tokens.back().append(std::string(" "));
				last_was_space = TRUE;
			}
		}
		else
		{
			std::string to_push = *token_iter;
			if (last_was_space)
			{
				tokens.back().append(to_push);
				last_was_space = FALSE;
			}
			else
			{
				tokens.push_back(to_push);
			}
		}
	}

	return tokens.size();
}

// static
std::string LLExternalEditor::findCommand(
	const std::string& env_var,
	const std::string& override)
{
	std::string cmd;

	// Get executable path.
	if (!override.empty())	// try the supplied override first
	{
		cmd = override;
		llinfos << "Using override" << llendl;
	}
	else if (!LLUI::sSettingGroups["config"]->getString(sSetting).empty())
	{
		cmd = LLUI::sSettingGroups["config"]->getString(sSetting);
		llinfos << "Using setting" << llendl;
	}
	else					// otherwise use the path specified by the environment variable
	{
		char* env_var_val = getenv(env_var.c_str());
		if (env_var_val)
		{
			cmd = env_var_val;
			llinfos << "Using env var " << env_var << llendl;
		}
	}

	llinfos << "Found command [" << cmd << "]" << llendl;
	return cmd;
}
<commit_msg>FIRE-10320 - If the user hasn't set ExternalEditor, fallback on the system generic file opener<commit_after>/**
 * @file llexternaleditor.cpp
 * @brief A convenient class to run external editor.
 *
 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"
#include "llexternaleditor.h"

#include "lltrans.h"
#include "llui.h"
#include "llprocess.h"
#include "llsdutil.h"
#include <boost/foreach.hpp>

// static
const std::string LLExternalEditor::sFilenameMarker = "%s";

// static
const std::string LLExternalEditor::sSetting = "ExternalEditor";

LLExternalEditor::EErrorCode LLExternalEditor::setCommand(const std::string& env_var, const std::string& override)
{
	std::string cmd = findCommand(env_var, override);
	if (cmd.empty())
	{
		llwarns << "Editor command is empty or not set" << llendl;
// <FS:CR> FIRE-10320 If no editor is set, fallback on the system open handler
		//return EC_NOT_SPECIFIED;
		llwarns << "Falling back on generic open handler" << llendl;
#ifdef WIN32
		cmd = "C:\Windows\System32\cmd.exe /C START \"%s\""
#endif
#ifdef DARWIN
		cmd = findCommand("", "/usr/bin/open \"%s\"")
#endif
#ifdef LINUX
		// xdg-open might not actually be installed, but it's out best shot
		cmd = findCommand("", "/usr/bin/xdg-open \"%s\"")
#endif
		if (cmd.empty())
		{
			return EC_NOT_SPECIFIED;
		}
// </FS:CR>
	}

	string_vec_t tokens;
	tokenize(tokens, cmd);

	// Check executable for existence.
	std::string bin_path = tokens[0];
	if (!LLFile::isfile(bin_path))
	{
		llwarns << "Editor binary [" << bin_path << "] not found" << llendl;
		return EC_BINARY_NOT_FOUND;
	}

	// Save command.
	mProcessParams = LLProcess::Params();
	mProcessParams.executable = bin_path;
	for (size_t i = 1; i < tokens.size(); ++i)
	{
		mProcessParams.args.add(tokens[i]);
	}

	// Add the filename marker if missing.
	if (cmd.find(sFilenameMarker) == std::string::npos)
	{
		mProcessParams.args.add(sFilenameMarker);
		llinfos << "Adding the filename marker (" << sFilenameMarker << ")" << llendl;
	}

	llinfos << "Setting command [" << mProcessParams << "]" << llendl;

	return EC_SUCCESS;
}

LLExternalEditor::EErrorCode LLExternalEditor::run(const std::string& file_path)
{
	if (std::string(mProcessParams.executable).empty() || mProcessParams.args.empty())
	{
		llwarns << "Editor command not set" << llendl;
		return EC_NOT_SPECIFIED;
	}

	// Copy params block so we can replace sFilenameMarker
	LLProcess::Params params;
	params.executable = mProcessParams.executable;

	// Substitute the filename marker in the command with the actual passed file name.
	BOOST_FOREACH(const std::string& arg, mProcessParams.args)
	{
		std::string fixed(arg);
		LLStringUtil::replaceString(fixed, sFilenameMarker, file_path);
		params.args.add(fixed);
	}

	// Run the editor. Prevent killing the process in destructor.
	params.autokill = false;
	return LLProcess::create(params) ? EC_SUCCESS : EC_FAILED_TO_RUN;
}

// static
std::string LLExternalEditor::getErrorMessage(EErrorCode code)
{
	switch (code)
	{
	case EC_SUCCESS: 			return LLTrans::getString("ok");
	case EC_NOT_SPECIFIED: 		return LLTrans::getString("ExternalEditorNotSet");
	case EC_PARSE_ERROR:		return LLTrans::getString("ExternalEditorCommandParseError");
	case EC_BINARY_NOT_FOUND:	return LLTrans::getString("ExternalEditorNotFound");
	case EC_FAILED_TO_RUN:		return LLTrans::getString("ExternalEditorFailedToRun");
	}

	return LLTrans::getString("Unknown");
}

// TODO:
// - Unit-test this with tests like LLStringUtil::getTokens() (the
//   command-line overload that supports quoted tokens)
// - Unless there are significant semantic differences, eliminate this method
//   and use LLStringUtil::getTokens() instead.

// static
size_t LLExternalEditor::tokenize(string_vec_t& tokens, const std::string& str)
{
	tokens.clear();

	// Split the argument string into separate strings for each argument
	typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
	boost::char_separator<char> sep("", "\" ", boost::drop_empty_tokens);

	tokenizer tokens_list(str, sep);
	tokenizer::iterator token_iter;
	BOOL inside_quotes = FALSE;
	BOOL last_was_space = FALSE;
	for (token_iter = tokens_list.begin(); token_iter != tokens_list.end(); ++token_iter)
	{
		if (!strncmp("\"",(*token_iter).c_str(),2))
		{
			inside_quotes = !inside_quotes;
		}
		else if (!strncmp(" ",(*token_iter).c_str(),2))
		{
			if(inside_quotes)
			{
				tokens.back().append(std::string(" "));
				last_was_space = TRUE;
			}
		}
		else
		{
			std::string to_push = *token_iter;
			if (last_was_space)
			{
				tokens.back().append(to_push);
				last_was_space = FALSE;
			}
			else
			{
				tokens.push_back(to_push);
			}
		}
	}

	return tokens.size();
}

// static
std::string LLExternalEditor::findCommand(
	const std::string& env_var,
	const std::string& override)
{
	std::string cmd;

	// Get executable path.
	if (!override.empty())	// try the supplied override first
	{
		cmd = override;
		llinfos << "Using override" << llendl;
	}
	else if (!LLUI::sSettingGroups["config"]->getString(sSetting).empty())
	{
		cmd = LLUI::sSettingGroups["config"]->getString(sSetting);
		llinfos << "Using setting" << llendl;
	}
	else					// otherwise use the path specified by the environment variable
	{
		char* env_var_val = getenv(env_var.c_str());
		if (env_var_val)
		{
			cmd = env_var_val;
			llinfos << "Using env var " << env_var << llendl;
		}
	}

	llinfos << "Found command [" << cmd << "]" << llendl;
	return cmd;
}
<|endoftext|>


<commit_before>#include <string.h>
#include <windows.h>
#include <shellapi.h>
#include "../ClientContext.h"
#include "../Config.h"
#include "../NetDb.h"
#include "../RouterContext.h"
#include "../Transports.h"
#include "../Tunnel.h"
#include "../version.h"
#include "resource.h"
#include "Win32App.h"
#include <stdio.h>

#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif

#define ID_ABOUT 2000
#define ID_EXIT 2001
#define ID_CONSOLE 2002
#define ID_APP 2003
#define ID_GRACEFUL_SHUTDOWN 2004

#define ID_TRAY_ICON 2050
#define WM_TRAYICON (WM_USER + 1)

#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100
#define FRAME_UPDATE_TIMER 2101

namespace i2p
{
namespace win32
{
	static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
	{
		HMENU hPopup = CreatePopupMenu();
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console");
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app");
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, NULL, NULL);
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit");
		SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE);
		SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0);

		POINT p;
		if (!curpos)
		{
			GetCursorPos (&p);
			curpos = &p;
		}

		WORD cmd = TrackPopupMenu (hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, curpos->x, curpos->y, 0, hWnd, NULL);
		SendMessage (hWnd, WM_COMMAND, cmd, 0);

		DestroyMenu(hPopup);
	}

	static void AddTrayIcon (HWND hWnd)
	{
		NOTIFYICONDATA nid;
		memset(&nid, 0, sizeof(nid));
		nid.cbSize = sizeof(nid);
		nid.hWnd = hWnd;
		nid.uID = ID_TRAY_ICON;
		nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
		nid.uCallbackMessage = WM_TRAYICON;
		nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON));
		strcpy (nid.szTip, "i2pd");
		strcpy (nid.szInfo, "i2pd is running");
		Shell_NotifyIcon(NIM_ADD, &nid );
	}

	static void RemoveTrayIcon (HWND hWnd)
	{
		NOTIFYICONDATA nid;
		nid.hWnd = hWnd;
		nid.uID = ID_TRAY_ICON;
		Shell_NotifyIcon (NIM_DELETE, &nid);
	}

	static void ShowUptime (std::stringstream& s, int seconds)
	{
		int num;

		if ((num = seconds / 86400) > 0) {
			s << num << " days, ";
			seconds -= num * 86400;
		}
		if ((num = seconds / 3600) > 0) {
			s << num << " hours, ";
			seconds -= num * 3600;
		}
		if ((num = seconds / 60) > 0) {
			s << num << " min, ";
			seconds -= num * 60;
		}
		s << seconds << " seconds\n";
	}

	static void ShowTransfered (std::stringstream& s, int transfer)
	{
		auto bytes = transfer & 0x03ff;
		transfer >>= 10;
		auto kbytes = transfer & 0x03ff;
		transfer >>= 10;
		auto mbytes = transfer & 0x03ff;
		transfer >>= 10;
		auto gbytes = transfer & 0x03ff;

		if (gbytes)
			s << gbytes << " GB, ";
		if (mbytes)
			s << mbytes << " MB, ";
		if (kbytes)
			s << kbytes << " KB, ";
		s << bytes << " Bytes\n";
	}

	static void PrintMainWindowText (std::stringstream& s)
	{
		s << "Status: ";
		switch (i2p::context.GetStatus())
		{
			case eRouterStatusOK: s << "OK"; break;
			case eRouterStatusTesting: s << "Testing"; break;
			case eRouterStatusFirewalled: s << "Firewalled"; break;
			case eRouterStatusError:
			{
				switch (i2p::context.GetError())
				{
					case eRouterErrorClockSkew: s << "Clock skew"; break;
					default: s << "Error";
				}
				break;
			}
			default: s << "Unknown";
		}
		s << "; ";
		s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
		s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
		s << "\n";
		s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; ";
		s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n";
		s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes());
		s << "Sent: "; ShowTransfered (s, i2p::transport::transports.GetTotalSentBytes());
		s << "\n";
		s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; ";
		s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; ";
		s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "\n";
		s << "Tunnels: ";
		s << "In: " << i2p::tunnel::tunnels.CountInboundTunnels() << "; ";
		s << "Out: " << i2p::tunnel::tunnels.CountOutboundTunnels() << "; ";
		s << "Transit: " << i2p::tunnel::tunnels.CountTransitTunnels() << "\n";
	}

	static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		switch (uMsg)
		{
			case WM_CREATE:
			{
				AddTrayIcon (hWnd);
				break;
			}
			case WM_CLOSE:
			{
				RemoveTrayIcon (hWnd);
				KillTimer (hWnd, FRAME_UPDATE_TIMER);
				KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
				PostQuitMessage (0);
				break;
			}
			case WM_COMMAND:
			{
				switch (LOWORD(wParam))
				{
					case ID_ABOUT:
					{
						std::stringstream text;
						text << "Version: " << I2PD_VERSION << " " << CODENAME;
						MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
						return 0;
					}
					case ID_EXIT:
					{
						PostMessage (hWnd, WM_CLOSE, 0, 0);
						return 0;
					}
					case ID_GRACEFUL_SHUTDOWN:
					{
						i2p::context.SetAcceptsTunnels (false);
						SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
						return 0;
					}
					case ID_CONSOLE:
					{
						char buf[30];
						std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
						uint16_t	httpPort; i2p::config::GetOption("http.port", httpPort);
						snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort);
						ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL);
						return 0;
					}
					case ID_APP:
					{
						ShowWindow(hWnd, SW_SHOW);
						SetTimer(hWnd, FRAME_UPDATE_TIMER, 3000, NULL);
						return 0;
					}
				}
				break;
			}
			case WM_SYSCOMMAND:
			{
				switch (wParam)
				{
					case SC_MINIMIZE:
					{
						ShowWindow(hWnd, SW_HIDE);
						KillTimer (hWnd, FRAME_UPDATE_TIMER);
						return 0;
					}
					case SC_CLOSE:
					{
						std::string close; i2p::config::GetOption("close", close);
						if (0 == close.compare("ask"))
						switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?"
						" You can add 'close' configuration option. Valid values are: ask, minimize, exit.",
						"Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1))
						{
							case IDYES: close = "minimize"; break;
							case IDNO: close = "exit"; break;
							default: return 0;
						}
						if (0 == close.compare("minimize"))
						{
							ShowWindow(hWnd, SW_HIDE);
							KillTimer (hWnd, FRAME_UPDATE_TIMER);
							return 0;
						}
						if (0 != close.compare("exit"))
						{
							::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING);
							return 0;
						}
					}
				}
			}
			case WM_TRAYICON:
			{
				switch (lParam)
				{
					case WM_LBUTTONUP:
					case WM_RBUTTONUP:
					{
						SetForegroundWindow (hWnd);
						ShowPopupMenu(hWnd, NULL, -1);
						PostMessage (hWnd, WM_APP + 1, 0, 0);
						break;
					}
				}
				break;
			}
			case WM_TIMER:
			{
				if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER)
				{
					PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
					return 0;
				}
				if (wParam == FRAME_UPDATE_TIMER)
				{
					InvalidateRect(hWnd, NULL, TRUE);
				}
				break;
			}
			case WM_PAINT:
			{
				HDC hDC;
				PAINTSTRUCT ps;
				RECT rp;
				HFONT hFont;
				std::stringstream s; PrintMainWindowText (s);
				hDC = BeginPaint (hWnd, &ps);
				GetClientRect(hWnd, &rp);
				SetTextColor(hDC, 0x00D43B69);
				hFont = CreateFont(18,0,0,0,0,0,0,0,DEFAULT_CHARSET,0,0,0,0,TEXT("Times New Roman"));
				SelectObject(hDC,hFont);
				DrawText(hDC, TEXT(s.str().c_str()), s.str().length(), &rp, DT_CENTER|DT_VCENTER);
				DeleteObject(hFont);
				EndPaint(hWnd, &ps);
				break;
			}
		}
		return DefWindowProc( hWnd, uMsg, wParam, lParam);
	}

	bool StartWin32App ()
	{
		if (FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")))
		{
			MessageBox(NULL, TEXT("I2Pd is running already"), TEXT("Warning"), MB_OK);
			return false;
		}
		// register main window
		auto hInst = GetModuleHandle(NULL);
		WNDCLASSEX wclx;
		memset (&wclx, 0, sizeof(wclx));
		wclx.cbSize = sizeof(wclx);
		wclx.style = 0;
		wclx.lpfnWndProc = WndProc;
		//wclx.cbClsExtra = 0;
		//wclx.cbWndExtra = 0;
		wclx.hInstance = hInst;
		wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON));
		wclx.hCursor = LoadCursor (NULL, IDC_ARROW);
		//wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
		wclx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
		wclx.lpszMenuName = NULL;
		wclx.lpszClassName = I2PD_WIN32_CLASSNAME;
		RegisterClassEx (&wclx);
		// create new window
		if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 100, 100, 350, 180, NULL, NULL, hInst, NULL))
		{
			MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST);
			return false;
		}
		return true;
	}

	int RunWin32App ()
	{
		MSG msg;
		while (GetMessage (&msg, NULL, 0, 0 ))
		{
			TranslateMessage (&msg);
			DispatchMessage (&msg);
		}
		return msg.wParam;
	}

	void StopWin32App ()
	{
		UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL));
	}

	bool GracefulShutdown ()
	{
		HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
		if (hWnd)
		PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0);
		return hWnd;
	}
}
}
<commit_msg>fix incorrect traffic counting<commit_after>#include <string.h>
#include <windows.h>
#include <shellapi.h>
#include "../ClientContext.h"
#include "../Config.h"
#include "../NetDb.h"
#include "../RouterContext.h"
#include "../Transports.h"
#include "../Tunnel.h"
#include "../version.h"
#include "resource.h"
#include "Win32App.h"
#include <stdio.h>

#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif

#define ID_ABOUT 2000
#define ID_EXIT 2001
#define ID_CONSOLE 2002
#define ID_APP 2003
#define ID_GRACEFUL_SHUTDOWN 2004

#define ID_TRAY_ICON 2050
#define WM_TRAYICON (WM_USER + 1)

#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100
#define FRAME_UPDATE_TIMER 2101

namespace i2p
{
namespace win32
{
	static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
	{
		HMENU hPopup = CreatePopupMenu();
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console");
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app");
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, NULL, NULL);
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
		InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit");
		SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE);
		SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0);

		POINT p;
		if (!curpos)
		{
			GetCursorPos (&p);
			curpos = &p;
		}

		WORD cmd = TrackPopupMenu (hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, curpos->x, curpos->y, 0, hWnd, NULL);
		SendMessage (hWnd, WM_COMMAND, cmd, 0);

		DestroyMenu(hPopup);
	}

	static void AddTrayIcon (HWND hWnd)
	{
		NOTIFYICONDATA nid;
		memset(&nid, 0, sizeof(nid));
		nid.cbSize = sizeof(nid);
		nid.hWnd = hWnd;
		nid.uID = ID_TRAY_ICON;
		nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
		nid.uCallbackMessage = WM_TRAYICON;
		nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON));
		strcpy (nid.szTip, "i2pd");
		strcpy (nid.szInfo, "i2pd is running");
		Shell_NotifyIcon(NIM_ADD, &nid );
	}

	static void RemoveTrayIcon (HWND hWnd)
	{
		NOTIFYICONDATA nid;
		nid.hWnd = hWnd;
		nid.uID = ID_TRAY_ICON;
		Shell_NotifyIcon (NIM_DELETE, &nid);
	}

	static void ShowUptime (std::stringstream& s, int seconds)
	{
		int num;

		if ((num = seconds / 86400) > 0) {
			s << num << " days, ";
			seconds -= num * 86400;
		}
		if ((num = seconds / 3600) > 0) {
			s << num << " hours, ";
			seconds -= num * 3600;
		}
		if ((num = seconds / 60) > 0) {
			s << num << " min, ";
			seconds -= num * 60;
		}
		s << seconds << " seconds\n";
	}

	template <typename size> static void ShowTransfered (std::stringstream& s, size transfer)
	{
		auto bytes = transfer & 0x03ff;
		transfer >>= 10;
		auto kbytes = transfer & 0x03ff;
		transfer >>= 10;
		auto mbytes = transfer & 0x03ff;
		transfer >>= 10;
		auto gbytes = transfer & 0x03ff;

		if (gbytes)
			s << gbytes << " GB, ";
		if (mbytes)
			s << mbytes << " MB, ";
		if (kbytes)
			s << kbytes << " KB, ";
		s << bytes << " Bytes\n";
	}

	static void PrintMainWindowText (std::stringstream& s)
	{
		s << "Status: ";
		switch (i2p::context.GetStatus())
		{
			case eRouterStatusOK: s << "OK"; break;
			case eRouterStatusTesting: s << "Testing"; break;
			case eRouterStatusFirewalled: s << "Firewalled"; break;
			case eRouterStatusError:
			{
				switch (i2p::context.GetError())
				{
					case eRouterErrorClockSkew: s << "Clock skew"; break;
					default: s << "Error";
				}
				break;
			}
			default: s << "Unknown";
		}
		s << "; ";
		s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
		s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
		s << "\n";
		s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; ";
		s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n";
		s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes());
		s << "Sent: "; ShowTransfered (s, i2p::transport::transports.GetTotalSentBytes());
		s << "\n";
		s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; ";
		s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; ";
		s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "\n";
		s << "Tunnels: ";
		s << "In: " << i2p::tunnel::tunnels.CountInboundTunnels() << "; ";
		s << "Out: " << i2p::tunnel::tunnels.CountOutboundTunnels() << "; ";
		s << "Transit: " << i2p::tunnel::tunnels.CountTransitTunnels() << "\n";
	}

	static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		switch (uMsg)
		{
			case WM_CREATE:
			{
				AddTrayIcon (hWnd);
				break;
			}
			case WM_CLOSE:
			{
				RemoveTrayIcon (hWnd);
				KillTimer (hWnd, FRAME_UPDATE_TIMER);
				KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
				PostQuitMessage (0);
				break;
			}
			case WM_COMMAND:
			{
				switch (LOWORD(wParam))
				{
					case ID_ABOUT:
					{
						std::stringstream text;
						text << "Version: " << I2PD_VERSION << " " << CODENAME;
						MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
						return 0;
					}
					case ID_EXIT:
					{
						PostMessage (hWnd, WM_CLOSE, 0, 0);
						return 0;
					}
					case ID_GRACEFUL_SHUTDOWN:
					{
						i2p::context.SetAcceptsTunnels (false);
						SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
						return 0;
					}
					case ID_CONSOLE:
					{
						char buf[30];
						std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
						uint16_t	httpPort; i2p::config::GetOption("http.port", httpPort);
						snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort);
						ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL);
						return 0;
					}
					case ID_APP:
					{
						ShowWindow(hWnd, SW_SHOW);
						SetTimer(hWnd, FRAME_UPDATE_TIMER, 3000, NULL);
						return 0;
					}
				}
				break;
			}
			case WM_SYSCOMMAND:
			{
				switch (wParam)
				{
					case SC_MINIMIZE:
					{
						ShowWindow(hWnd, SW_HIDE);
						KillTimer (hWnd, FRAME_UPDATE_TIMER);
						return 0;
					}
					case SC_CLOSE:
					{
						std::string close; i2p::config::GetOption("close", close);
						if (0 == close.compare("ask"))
						switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?"
						" You can add 'close' configuration option. Valid values are: ask, minimize, exit.",
						"Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1))
						{
							case IDYES: close = "minimize"; break;
							case IDNO: close = "exit"; break;
							default: return 0;
						}
						if (0 == close.compare("minimize"))
						{
							ShowWindow(hWnd, SW_HIDE);
							KillTimer (hWnd, FRAME_UPDATE_TIMER);
							return 0;
						}
						if (0 != close.compare("exit"))
						{
							::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING);
							return 0;
						}
					}
				}
			}
			case WM_TRAYICON:
			{
				switch (lParam)
				{
					case WM_LBUTTONUP:
					case WM_RBUTTONUP:
					{
						SetForegroundWindow (hWnd);
						ShowPopupMenu(hWnd, NULL, -1);
						PostMessage (hWnd, WM_APP + 1, 0, 0);
						break;
					}
				}
				break;
			}
			case WM_TIMER:
			{
				if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER)
				{
					PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
					return 0;
				}
				if (wParam == FRAME_UPDATE_TIMER)
				{
					InvalidateRect(hWnd, NULL, TRUE);
				}
				break;
			}
			case WM_PAINT:
			{
				HDC hDC;
				PAINTSTRUCT ps;
				RECT rp;
				HFONT hFont;
				std::stringstream s; PrintMainWindowText (s);
				hDC = BeginPaint (hWnd, &ps);
				GetClientRect(hWnd, &rp);
				SetTextColor(hDC, 0x00D43B69);
				hFont = CreateFont(18,0,0,0,0,0,0,0,DEFAULT_CHARSET,0,0,0,0,TEXT("Times New Roman"));
				SelectObject(hDC,hFont);
				DrawText(hDC, TEXT(s.str().c_str()), s.str().length(), &rp, DT_CENTER|DT_VCENTER);
				DeleteObject(hFont);
				EndPaint(hWnd, &ps);
				break;
			}
		}
		return DefWindowProc( hWnd, uMsg, wParam, lParam);
	}

	bool StartWin32App ()
	{
		if (FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")))
		{
			MessageBox(NULL, TEXT("I2Pd is running already"), TEXT("Warning"), MB_OK);
			return false;
		}
		// register main window
		auto hInst = GetModuleHandle(NULL);
		WNDCLASSEX wclx;
		memset (&wclx, 0, sizeof(wclx));
		wclx.cbSize = sizeof(wclx);
		wclx.style = 0;
		wclx.lpfnWndProc = WndProc;
		//wclx.cbClsExtra = 0;
		//wclx.cbWndExtra = 0;
		wclx.hInstance = hInst;
		wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON));
		wclx.hCursor = LoadCursor (NULL, IDC_ARROW);
		//wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
		wclx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
		wclx.lpszMenuName = NULL;
		wclx.lpszClassName = I2PD_WIN32_CLASSNAME;
		RegisterClassEx (&wclx);
		// create new window
		if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 100, 100, 350, 180, NULL, NULL, hInst, NULL))
		{
			MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST);
			return false;
		}
		return true;
	}

	int RunWin32App ()
	{
		MSG msg;
		while (GetMessage (&msg, NULL, 0, 0 ))
		{
			TranslateMessage (&msg);
			DispatchMessage (&msg);
		}
		return msg.wParam;
	}

	void StopWin32App ()
	{
		UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL));
	}

	bool GracefulShutdown ()
	{
		HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
		if (hWnd)
		PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0);
		return hWnd;
	}
}
}
<|endoftext|>


<commit_before>#ifndef VOLE_VALUE
#define VOLE_VALUE

#include "slice.hpp"

namespace Vole {

  struct Value;
  using String = Slice<char>;
  using Vector = Slice<Value>;

  struct Value {
    enum Type {
      BOOLEAN,
      NUMBER,
      // SYMBOL,
      STRING,
      // REGEXP,
      VECTOR,
      // MAPPING
    } type;

    // for garbage collection
    enum Color {
      BLACK,
      GRAY,
      WHITE
    } color;

    union Content {
      bool    boolean;
      double  number;
      // wrapper around a String, every symbol with a name is unique
      // Symbol  symbol;
      String  string;
      // Regexp regexp;
      Vector  vector;
      // Mapping mapping;

      Content(bool b)    : boolean(b) { }
      Content(double d)  : number(d)  { }
      // Content(Symbol s) { new (&symbol) Symbol(s); }
      Content(String s)  { new (&string) String(s); }
      Content(Vector v)  { new (&vector) Vector(v); }
      // Content(Mapping m) { new (&mapping) Mapping(m); }
    } content;

    template <typename T>
    Value(T thing, Color c = BLACK)
    : type(BOOLEAN), color(c), content(true)
    { }

    operator std::string() {
      std::stringstream ss;
      switch (type) {
        case BOOLEAN: {
          ss << (content.boolean ? "#t" : "#f");
        } break;
        case NUMBER: {
          ss << content.number;
        } break;
        case STRING: {
          ss << content.string;
        } break;
        case VECTOR: {
          ss << content.vector;
        } break;
      }
      return ss.str();
    }
  };

  template <>
  Value::Value(bool b, Color c)
  : type(BOOLEAN), color(c), content(b)
  { }

  template <>
  Value::Value(double d, Color c)
  : type(NUMBER), color(c), content(d)
  { }

  template <>
  Value::Value(String s, Color c)
  : type(STRING), color(c), content(s)
  { }

  template <>
  Value::Value(Vector v, Color c)
  : type(VECTOR), color(c), content(v)
  { }

  template <typename IOS>
  IOS& operator<<(IOS& ios, Value val) {
    switch (val.type) {
      case Value::BOOLEAN: {
        ios << (val.content.boolean ? "#t" : "#f");
      } break;
      case Value::NUMBER: {
        ios << val.content.number;
      } break;
      case Value::STRING: {
        ios << val.content.string;
      } break;
      case Value::VECTOR: {
        ios << val.content.vector;
      } break;
    }
    return ios;
  }

}

#endif<commit_msg>factoring out some of the serialization code in order to get rid of an ambiguous overloaded operator error<commit_after>#ifndef VOLE_VALUE
#define VOLE_VALUE

#include "slice.hpp"

namespace Vole {

  struct Value;
  using String = Slice<char>;
  using Vector = Slice<Value>;

  struct Value {
    enum Type {
      BOOLEAN,
      NUMBER,
      // SYMBOL,
      STRING,
      // REGEXP,
      VECTOR,
      // MAPPING
    } type;

    // for garbage collection
    enum Color {
      BLACK,
      GRAY,
      WHITE
    } color;

    union Content {
      bool    boolean;
      double  number;
      // wrapper around a String, every symbol with a name is unique
      // Symbol  symbol;
      String  string;
      // Regexp regexp;
      Vector  vector;
      // Mapping mapping;

      Content(bool b)    : boolean(b) { }
      Content(double d)  : number(d)  { }
      // Content(Symbol s) { new (&symbol) Symbol(s); }
      Content(String s)  { new (&string) String(s); }
      Content(Vector v)  { new (&vector) Vector(v); }
      // Content(Mapping m) { new (&mapping) Mapping(m); }
    } content;

    template <typename T>
    Value(T thing, Color c = BLACK)
    : type(BOOLEAN), color(c), content(true)
    { }

    template <typename IOS>
    IOS& serialize(IOS& ios);

    operator std::string();
  };

  template <>
  Value::Value(bool b, Color c)
  : type(BOOLEAN), color(c), content(b)
  { }

  template <>
  Value::Value(double d, Color c)
  : type(NUMBER), color(c), content(d)
  { }

  template <>
  Value::Value(String s, Color c)
  : type(STRING), color(c), content(s)
  { }

  template <>
  Value::Value(Vector v, Color c)
  : type(VECTOR), color(c), content(v)
  { }

  template <typename IOS>
  IOS& Value::serialize(IOS& ios) {
    switch (type) {
      case BOOLEAN: {
        ios << (content.boolean ? "#t" : "#f");
      } break;
      case NUMBER: {
        ios << content.number;
      } break;
      case STRING: {
        ios << content.string;
      } break;
      case VECTOR: {
        ios << content.vector;
      } break;
    }
    return ios;
  }

  template <typename IOS>
  IOS& operator<<(IOS& ios, Value val) {
    return val.serialize(ios);
  }

  Value::operator std::string() {
    std::stringstream ss;
    serialize(ss);
    return ss.str();
  }

}

#endif<|endoftext|>


<commit_before>#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

#include <signal.h>

#include <dns_sd.h>
#include <ev++.h>

#include "build_version.h"

inline void check_dnsservice_errors(DNSServiceErrorType& e, const std::string& func_name) {
    std::string error(func_name);
    error += ": ";

    switch (e) {
        case kDNSServiceErr_NoError:
            return;
        case kDNSServiceErr_NameConflict:
            error += "Name in use, please choose another";
            break;
        case kDNSServiceErr_Invalid:
            error += "An invalid index or character was passed";
            break;
        case kDNSServiceErr_BadReference:
            error += "Bad reference";
            break;
        case kDNSServiceErr_BadParam:
            error += "Bad parameter value passed to function.";
            break;
        default:
            error += "Unknown error code... ";
            error += std::to_string(e);
    }

    throw std::runtime_error(error.c_str());
}

inline void signal_int(ev::sig& sig, int) {
    std::cerr << std::endl << "Caught SIGINT..." << std::endl;

    sig.loop.break_loop();
}

int main(int argc, char *argv[]) {
    if (2 != argc) {
        std::cerr << argv[0] << " <announce file>" << std::endl;
        std::cerr << "Version: " << VERSION << std::endl;
        return -1;
    }

    std::vector<std::string> _domains;
    std::vector<DNSRecordRef> _records;

    DNSServiceRef serviceRef = 0;
    int dns_sd_fd = -1;

    try {
        std::ifstream f(argv[1], std::ios::in | std::ios::binary);

        if (!f.is_open()) {
            std::cerr << "Unable to open file: " << argv[1] << std::endl;
            return -2;
        }

        for (std::string line; std::getline(f, line); /**/) {
            if (line.length() != 0) _domains.push_back(line);
        }

        DNSServiceErrorType error = kDNSServiceErr_NoError;

        // Setup the mDNS context...
        error = DNSServiceCreateConnection(&serviceRef);
        check_dnsservice_errors(error, "DNSServiceCreateConnection");

        for (auto &d : _domains) {
            std::cout << "Registering Domain: " << d << std::endl;
            DNSRecordRef record = 0;

            const char cname[] = { 9, 'a', 'l', 'e', 'x', 'a', 'n', 'd', 'r', 'a', 5, 'l', 'o', 'c', 'a', 'l', 0 };

            error = DNSServiceRegisterRecord(
                    serviceRef,                     // DNSServiceRef
                    &record,                        // DNSRecordRef
                    kDNSServiceFlagsShareConnection | kDNSServiceFlagsUnique,         // DNSServiceFlags
                    kDNSServiceInterfaceIndexAny,   // uint32_t interfaceIndex
                    d.c_str(),                      // fullname
                    kDNSServiceType_CNAME,          // rrtype
                    kDNSServiceClass_IN,            // rrclass
                    sizeof(cname),                  // rdlen
                    cname,                          // rdata (const void)
                    0,                              // uint32_t ttl
                    CallBack,                       // Callback
                    reinterpret_cast<void*>(&d)     // Context
                    );

            check_dnsservice_errors(error, "DNSServiceRegisterRecord");
            _records.push_back(record);

        }

        // Add event for catching SIGINT, so we can do proper cleanup
        ev::sig sio;
        sio.set<signal_int>();
        sio.start(SIGINT);

        // Ignore SIGPIPE
        struct sigaction act;
        act.sa_handler = SIG_IGN;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGPIPE, &act, 0);

        // start the libev loop
        ev::default_loop loop;
        loop.run(0);
    } catch (std::exception const& e) {
        std::cerr << "Caught an exception: " << e.what() << std::endl;
    }

    DNSServiceRefDeallocate(serviceRef);

    return 0;
}
<commit_msg>Deallocate records when we are tearing it down ...<commit_after>#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <tuple>

#include <signal.h>

#include <dns_sd.h>
#include <ev++.h>

#include "build_version.h"

inline void check_dnsservice_errors(DNSServiceErrorType& e, const std::string& func_name) {
    std::string error(func_name);
    error += ": ";

    switch (e) {
        case kDNSServiceErr_NoError:
            return;
        case kDNSServiceErr_NameConflict:
            error += "Name in use, please choose another";
            break;
        case kDNSServiceErr_Invalid:
            error += "An invalid index or character was passed";
            break;
        case kDNSServiceErr_BadReference:
            error += "Bad reference";
            break;
        case kDNSServiceErr_BadParam:
            error += "Bad parameter value passed to function.";
            break;
        default:
            error += "Unknown error code... ";
            error += std::to_string(e);
    }

    throw std::runtime_error(error.c_str());
}

inline void signal_int(ev::sig& sig, int) {
    auto *_data = reinterpret_cast<std::tuple<DNSServiceRef, std::vector<DNSRecordRef>>*>(sig.data);

    std::cerr << std::endl << "Caught SIGINT..." << std::endl;
    std::cout << "Deallocating records";
    for (auto &rec : std::get<1>(*_data)) {
        try {
            DNSServiceErrorType error;
            error = DNSServiceRemoveRecord(
                    std::get<0>(*_data),
                    rec,
                    0
                    );

            check_dnsservice_errors(error, "DNSServiceRemoveRecord");
        } catch (std::exception const& e) {
            std::cerr << "Caught an exception: " << e.what() << std::endl;
        }
    }
    std::cout << " [done]" << std::endl;

    sig.loop.break_loop();
}

int main(int argc, char *argv[]) {
    if (2 != argc) {
        std::cerr << argv[0] << " <announce file>" << std::endl;
        std::cerr << "Version: " << VERSION << std::endl;
        return -1;
    }

    std::vector<std::string> _domains;
    std::vector<DNSRecordRef> _records;

    DNSServiceRef serviceRef = 0;
    int dns_sd_fd = -1;

    std::tuple<DNSServiceRef, std::vector<DNSRecordRef>> _sigint_data;

    try {
        std::ifstream f(argv[1], std::ios::in | std::ios::binary);

        if (!f.is_open()) {
            std::cerr << "Unable to open file: " << argv[1] << std::endl;
            return -2;
        }

        for (std::string line; std::getline(f, line); /**/) {
            if (line.length() != 0) _domains.push_back(line);
        }

        DNSServiceErrorType error = kDNSServiceErr_NoError;

        // Setup the mDNS context...
        error = DNSServiceCreateConnection(&serviceRef);
        check_dnsservice_errors(error, "DNSServiceCreateConnection");

        for (auto &d : _domains) {
            std::cout << "Registering Domain: " << d << std::endl;
            DNSRecordRef record = 0;

            const char cname[] = { 9, 'a', 'l', 'e', 'x', 'a', 'n', 'd', 'r', 'a', 5, 'l', 'o', 'c', 'a', 'l', 0 };

            error = DNSServiceRegisterRecord(
                    serviceRef,                     // DNSServiceRef
                    &record,                        // DNSRecordRef
                    kDNSServiceFlagsShareConnection | kDNSServiceFlagsUnique,         // DNSServiceFlags
                    kDNSServiceInterfaceIndexAny,   // uint32_t interfaceIndex
                    d.c_str(),                      // fullname
                    kDNSServiceType_CNAME,          // rrtype
                    kDNSServiceClass_IN,            // rrclass
                    sizeof(cname),                  // rdlen
                    cname,                          // rdata (const void)
                    0,                              // uint32_t ttl
                    CallBack,                       // Callback
                    reinterpret_cast<void*>(&d)     // Context
                    );

            check_dnsservice_errors(error, "DNSServiceRegisterRecord");
            _records.push_back(record);

        }

        _sigint_data = std::make_tuple(serviceRef, _records);
        // Add event for catching SIGINT, so we can do proper cleanup
        ev::sig sio;
        sio.set<signal_int>(reinterpret_cast<void *>(&_sigint_data));
        sio.start(SIGINT);

        // Ignore SIGPIPE
        struct sigaction act;
        act.sa_handler = SIG_IGN;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGPIPE, &act, 0);

        // start the libev loop
        ev::default_loop loop;
        loop.run(0);
    } catch (std::exception const& e) {
        std::cerr << "Caught an exception: " << e.what() << std::endl;
    }

    DNSServiceRefDeallocate(serviceRef);

    return 0;
}
<|endoftext|>


<commit_before>#include "ANGLETest.h"

class VertexAttributeTest : public ANGLETest
{
protected:
    VertexAttributeTest()
    {
        setWindowWidth(128);
        setWindowHeight(128);
        setConfigRedBits(8);
        setConfigGreenBits(8);
        setConfigBlueBits(8);
        setConfigAlphaBits(8);
        setConfigDepthBits(24);

        mProgram = 0;
        mTestAttrib = -1;
        mExpectedAttrib = -1;
    }

    struct TestData
    {
        GLenum type;
        GLboolean normalized;

        const void *inputData;
        const GLfloat *expectedData;
    };

    void runTest(const TestData& test)
    {
        GLint viewportSize[4];
        glGetIntegerv(GL_VIEWPORT, viewportSize);

        GLint midPixelX = (viewportSize[0] + viewportSize[2]) / 2;
        GLint midPixelY = (viewportSize[1] + viewportSize[3]) / 2;

        for (size_t i = 0; i < 4; i++)
        {
            glBindBuffer(GL_ARRAY_BUFFER, 0);
            glVertexAttribPointer(mTestAttrib, i + 1, test.type, test.normalized, 0, test.inputData);
            glVertexAttribPointer(mExpectedAttrib, i + 1, GL_FLOAT, GL_FALSE, 0, test.expectedData);

            glEnableVertexAttribArray(mTestAttrib);
            glEnableVertexAttribArray(mExpectedAttrib);

            drawQuad(mProgram, "position", 0.5f);

            glDisableVertexAttribArray(mTestAttrib);
            glDisableVertexAttribArray(mExpectedAttrib);

            EXPECT_PIXEL_EQ(midPixelX, midPixelY, 255, 255, 255, 255);
        }
    }

    virtual void SetUp()
    {
        ANGLETest::SetUp();

        const std::string testVertexShaderSource = SHADER_SOURCE
        (
            attribute highp vec4 position;
            attribute highp vec4 test;
            attribute highp vec4 expected;

            varying highp vec4 color;

            void main(void)
            {
                gl_Position = position;
                color = vec4(lessThan(abs(test - expected), vec4(1.0 / 128.0)));
            }
        );

        const std::string testFragmentShaderSource = SHADER_SOURCE
        (
            varying highp vec4 color;
            void main(void)
            {
                gl_FragColor = color;
            }
        );

        mProgram = compileProgram(testVertexShaderSource, testFragmentShaderSource);
        if (mProgram == 0)
        {
            FAIL() << "shader compilation failed.";
        }

        mTestAttrib = glGetAttribLocation(mProgram, "test");
        mExpectedAttrib = glGetAttribLocation(mProgram, "expected");

        glUseProgram(mProgram);

        glClearColor(0, 0, 0, 0);
        glClearDepthf(0.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glDisable(GL_DEPTH_TEST);
    }

    virtual void TearDown()
    {
        glDeleteProgram(mProgram);

        ANGLETest::TearDown();
    }

    static const size_t mVertexCount = 24;

    GLuint mProgram;
    GLint mTestAttrib;
    GLint mExpectedAttrib;
};

TEST_F(VertexAttributeTest, unsigned_byte_unormalized)
{
    GLubyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, 5, 6, 7, 125, 126, 127, 128, 129, 250, 251, 252, 253, 254, 255 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i];
    }

    TestData data = { GL_UNSIGNED_BYTE, GL_FALSE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, unsigned_byte_normalized)
{
    GLubyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, 5, 6, 7, 125, 126, 127, 128, 129, 250, 251, 252, 253, 254, 255 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i] / 255.0f;
    }

    TestData data = { GL_UNSIGNED_BYTE, GL_TRUE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, byte_unnormalized)
{
    GLbyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, -1, -2, -3, -4, 125, 126, 127, -128, -127, -126 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i];
    }

    TestData data = { GL_BYTE, GL_FALSE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, byte_normalized)
{
    GLbyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, -1, -2, -3, -4, 125, 126, 127, -128, -127, -126 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = ((2.0f * inputData[i]) + 1.0f) / 255.0f;
    }

    TestData data = { GL_BYTE, GL_TRUE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, unsigned_short_unormalized)
{
    GLushort inputData[mVertexCount] = { 0, 1, 2, 3, 254, 255, 256, 32766, 32767, 32768, 65533, 65534, 65535 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i];
    }

    TestData data = { GL_UNSIGNED_SHORT, GL_FALSE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, unsigned_short_normalized)
{
    GLushort inputData[mVertexCount] = { 0, 1, 2, 3, 254, 255, 256, 32766, 32767, 32768, 65533, 65534, 65535 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i] / 65535.0f;
    }

    TestData data = { GL_UNSIGNED_SHORT, GL_TRUE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, short_unnormalized)
{
    GLshort inputData[mVertexCount] = {  0, 1, 2, 3, -1, -2, -3, -4, 32766, 32767, -32768, -32767, -32766 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i];
    }

    TestData data = { GL_SHORT, GL_FALSE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, short_normalized)
{
    GLshort inputData[mVertexCount] = {  0, 1, 2, 3, -1, -2, -3, -4, 32766, 32767, -32768, -32767, -32766 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = ((2.0f * inputData[i]) + 1.0f) / 65535.0f;
    }

    TestData data = { GL_SHORT, GL_TRUE, inputData, expectedData };
    runTest(data);
}
<commit_msg>Update the VertexAttribute test expectations.<commit_after>#include "ANGLETest.h"

class VertexAttributeTest : public ANGLETest
{
protected:
    VertexAttributeTest()
    {
        setWindowWidth(128);
        setWindowHeight(128);
        setConfigRedBits(8);
        setConfigGreenBits(8);
        setConfigBlueBits(8);
        setConfigAlphaBits(8);
        setConfigDepthBits(24);

        mProgram = 0;
        mTestAttrib = -1;
        mExpectedAttrib = -1;
    }

    struct TestData
    {
        GLenum type;
        GLboolean normalized;

        const void *inputData;
        const GLfloat *expectedData;
    };

    void runTest(const TestData& test)
    {
        GLint viewportSize[4];
        glGetIntegerv(GL_VIEWPORT, viewportSize);

        GLint midPixelX = (viewportSize[0] + viewportSize[2]) / 2;
        GLint midPixelY = (viewportSize[1] + viewportSize[3]) / 2;

        for (size_t i = 0; i < 4; i++)
        {
            glBindBuffer(GL_ARRAY_BUFFER, 0);
            glVertexAttribPointer(mTestAttrib, i + 1, test.type, test.normalized, 0, test.inputData);
            glVertexAttribPointer(mExpectedAttrib, i + 1, GL_FLOAT, GL_FALSE, 0, test.expectedData);

            glEnableVertexAttribArray(mTestAttrib);
            glEnableVertexAttribArray(mExpectedAttrib);

            drawQuad(mProgram, "position", 0.5f);

            glDisableVertexAttribArray(mTestAttrib);
            glDisableVertexAttribArray(mExpectedAttrib);

            // We need to offset our checks from triangle edges to ensure we don't fall on a single tri
            // Avoid making assumptions of drawQuad with four checks to check the four possible tri regions
            EXPECT_PIXEL_EQ((midPixelX + viewportSize[0]) / 2, midPixelY, 255, 255, 255, 255);
            EXPECT_PIXEL_EQ((midPixelX + viewportSize[2]) / 2, midPixelY, 255, 255, 255, 255);
            EXPECT_PIXEL_EQ(midPixelX, (midPixelY + viewportSize[1]) / 2, 255, 255, 255, 255);
            EXPECT_PIXEL_EQ(midPixelX, (midPixelY + viewportSize[3]) / 2, 255, 255, 255, 255);
        }
    }

    virtual void SetUp()
    {
        ANGLETest::SetUp();

        const std::string testVertexShaderSource = SHADER_SOURCE
        (
            attribute highp vec4 position;
            attribute highp vec4 test;
            attribute highp vec4 expected;

            varying highp vec4 color;

            void main(void)
            {
                gl_Position = position;
                color = vec4(lessThan(abs(test - expected), vec4(1.0 / 64.0)));
            }
        );

        const std::string testFragmentShaderSource = SHADER_SOURCE
        (
            varying highp vec4 color;
            void main(void)
            {
                gl_FragColor = color;
            }
        );

        mProgram = compileProgram(testVertexShaderSource, testFragmentShaderSource);
        if (mProgram == 0)
        {
            FAIL() << "shader compilation failed.";
        }

        mTestAttrib = glGetAttribLocation(mProgram, "test");
        mExpectedAttrib = glGetAttribLocation(mProgram, "expected");

        glUseProgram(mProgram);

        glClearColor(0, 0, 0, 0);
        glClearDepthf(0.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glDisable(GL_DEPTH_TEST);
    }

    virtual void TearDown()
    {
        glDeleteProgram(mProgram);

        ANGLETest::TearDown();
    }

    static const size_t mVertexCount = 24;

    GLuint mProgram;
    GLint mTestAttrib;
    GLint mExpectedAttrib;
};

TEST_F(VertexAttributeTest, unsigned_byte_unormalized)
{
    GLubyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, 5, 6, 7, 125, 126, 127, 128, 129, 250, 251, 252, 253, 254, 255 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i];
    }

    TestData data = { GL_UNSIGNED_BYTE, GL_FALSE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, unsigned_byte_normalized)
{
    GLubyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, 5, 6, 7, 125, 126, 127, 128, 129, 250, 251, 252, 253, 254, 255 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i] / 255.0f;
    }

    TestData data = { GL_UNSIGNED_BYTE, GL_TRUE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, byte_unnormalized)
{
    GLbyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, -1, -2, -3, -4, 125, 126, 127, -128, -127, -126 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i];
    }

    TestData data = { GL_BYTE, GL_FALSE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, byte_normalized)
{
    GLbyte inputData[mVertexCount] = { 0, 1, 2, 3, 4, -1, -2, -3, -4, 125, 126, 127, -128, -127, -126 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = ((2.0f * inputData[i]) + 1.0f) / 255.0f;
    }

    TestData data = { GL_BYTE, GL_TRUE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, unsigned_short_unormalized)
{
    GLushort inputData[mVertexCount] = { 0, 1, 2, 3, 254, 255, 256, 32766, 32767, 32768, 65533, 65534, 65535 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i];
    }

    TestData data = { GL_UNSIGNED_SHORT, GL_FALSE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, unsigned_short_normalized)
{
    GLushort inputData[mVertexCount] = { 0, 1, 2, 3, 254, 255, 256, 32766, 32767, 32768, 65533, 65534, 65535 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i] / 65535.0f;
    }

    TestData data = { GL_UNSIGNED_SHORT, GL_TRUE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, short_unnormalized)
{
    GLshort inputData[mVertexCount] = {  0, 1, 2, 3, -1, -2, -3, -4, 32766, 32767, -32768, -32767, -32766 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = inputData[i];
    }

    TestData data = { GL_SHORT, GL_FALSE, inputData, expectedData };
    runTest(data);
}

TEST_F(VertexAttributeTest, short_normalized)
{
    GLshort inputData[mVertexCount] = {  0, 1, 2, 3, -1, -2, -3, -4, 32766, 32767, -32768, -32767, -32766 };
    GLfloat expectedData[mVertexCount];
    for (size_t i = 0; i < mVertexCount; i++)
    {
        expectedData[i] = ((2.0f * inputData[i]) + 1.0f) / 65535.0f;
    }

    TestData data = { GL_SHORT, GL_TRUE, inputData, expectedData };
    runTest(data);
}
<|endoftext|>


<commit_before>#include "app-gui.h"

WARNINGS_DISABLE
#include <QFontDatabase>
#include <QMessageBox>
WARNINGS_ENABLE

#include <assert.h>

#include "debug.h"
#include "init-shared.h"
#include "translator.h"
#include "utils.h"
#include "widgets/setupdialog.h"

#include <TSettings.h>

AppGui::AppGui(int &argc, char **argv, struct optparse *opt)
    : QApplication(argc, argv),
      _mainWindow(nullptr),
      _journal(nullptr),
      _notification()
{
    // Sanity check
    assert(opt != nullptr);

    // Get values from optparse.  The (x == 1) is probably unnecessary, but
    // better safe than sorry!
    _jobsOption  = (opt->jobs == 1);
    _checkOption = (opt->check == 1);
    _configDir   = opt->config_dir;

    init_shared(this);

    setQuitOnLastWindowClosed(false);

    qRegisterMetaType<QSystemTrayIcon::ActivationReason>(
        "QSystemTrayIcon::ActivationReason");
}

AppGui::~AppGui()
{
    if(_mainWindow)
        delete _mainWindow;
    _managerThread.quit();
    _managerThread.wait();
    if(_journal)
        delete _journal;
    TSettings::destroy();
}

bool AppGui::initializeCore()
{
    struct init_info info;

    // Set up Settings.
    info = init_shared_settings(_configDir);
    if(info.status == INIT_SETTINGS_RENAMED)
        QMessageBox::information(nullptr, tr("Tarsnap info"), info.message);

    // Set up the Translator, check --dry-run, update scheduling path.
    info = init_shared_core(this);

    if(info.status == INIT_NEEDS_SETUP)
        return runSetupWizard();
    else if(info.status == INIT_DB_FAILED)
    {
        QMessageBox::warning(nullptr, tr("Tarsnap warning"),
                             tr("Cannot not initialize the database."));
        return false;
    }
    else if(info.status == INIT_DRY_RUN)
    {
        QMessageBox::warning(nullptr, tr("Tarsnap warning"), info.message);
        // There's no point trying to automatically process jobs.
        if(_jobsOption)
            return false;
    }

    if(_jobsOption || _checkOption)
    {
        if(info.status == INIT_SCHEDULE_OK)
            DEBUG << info.extra;
        else if(info.status == INIT_SCHEDULE_ERROR)
        {
            DEBUG << info.message;
            return false;
        }

        // We don't have anything else to do
        if(_checkOption)
            return true;
    }

    if(!_jobsOption)
    {
        if(info.status == INIT_SCHEDULE_OK)
            QMessageBox::information(nullptr, tr("Updated OS X launchd path"),
                                     info.message);
        else if(info.status == INIT_SCHEDULE_ERROR)
            QMessageBox::information(nullptr,
                                     tr("Failed to updated OS X launchd path"),
                                     info.message);
    }
    return true;
}

bool AppGui::prepMainLoop()
{
    // Nothing to do.
    if(_checkOption)
        return false;

    // Initialize the PersistentStore early
    PersistentStore::instance();
    _journal = new Journal();

    // Queue loading the journal when we have an event loop.
    QMetaObject::invokeMethod(_journal, "load", QUEUED);

    connect(&_taskManager, &TaskManager::displayNotification, &_notification,
            &Notification::displayNotification, QUEUED);
    connect(&_taskManager, &TaskManager::message, _journal, &Journal::log,
            QUEUED);

    if(_jobsOption)
    {
        setQuitLockEnabled(true);
        connect(&_notification, &Notification::activated, this,
                &AppGui::showMainWindow, QUEUED);
        connect(&_notification, &Notification::messageClicked, this,
                &AppGui::showMainWindow, QUEUED);
        QMetaObject::invokeMethod(&_taskManager, "runScheduledJobs", QUEUED);
    }
    else
    {
        showMainWindow();
    }

    return true;
}

void AppGui::showMainWindow()
{
    if(_mainWindow != nullptr)
        return;

    setQuitLockEnabled(false);
    disconnect(&_notification, &Notification::activated, this,
               &AppGui::showMainWindow);
    disconnect(&_notification, &Notification::messageClicked, this,
               &AppGui::showMainWindow);

    _mainWindow = new MainWindow();
    Q_ASSERT(_mainWindow != nullptr);

    connect(_mainWindow, &MainWindow::tarsnapVersionRequested, &_taskManager,
            &TaskManager::tarsnapVersionFind, QUEUED);
    connect(&_taskManager, &TaskManager::tarsnapVersionFound, _mainWindow,
            &MainWindow::tarsnapVersionResponse, QUEUED);
    connect(_mainWindow, &MainWindow::backupNow, &_taskManager,
            &TaskManager::backupNow, QUEUED);
    connect(_mainWindow, &MainWindow::getArchives, &_taskManager,
            &TaskManager::getArchives, QUEUED);
    connect(&_taskManager, &TaskManager::archiveList, _mainWindow,
            &MainWindow::archiveList, QUEUED);
    connect(&_taskManager, &TaskManager::addArchive, _mainWindow,
            &MainWindow::addArchive, QUEUED);
    connect(_mainWindow, &MainWindow::deleteArchives, &_taskManager,
            &TaskManager::deleteArchives, QUEUED);
    connect(_mainWindow, &MainWindow::loadArchiveStats, &_taskManager,
            &TaskManager::getArchiveStats, QUEUED);
    connect(_mainWindow, &MainWindow::loadArchiveContents, &_taskManager,
            &TaskManager::getArchiveContents, QUEUED);
    connect(&_taskManager, &TaskManager::idle, _mainWindow,
            &MainWindow::updateLoadingAnimation, QUEUED);
    connect(&_taskManager, &TaskManager::numTasks, _mainWindow,
            &MainWindow::updateNumTasks, QUEUED);
    connect(_mainWindow, &MainWindow::getOverallStats, &_taskManager,
            &TaskManager::getOverallStats, QUEUED);
    connect(&_taskManager, &TaskManager::overallStats, _mainWindow,
            &MainWindow::overallStatsChanged, QUEUED);
    connect(_mainWindow, &MainWindow::repairCache, &_taskManager,
            &TaskManager::fsck, QUEUED);
    connect(_mainWindow, &MainWindow::nukeArchives, &_taskManager,
            &TaskManager::nuke, QUEUED);
    connect(_mainWindow, &MainWindow::restoreArchive, &_taskManager,
            &TaskManager::restoreArchive, QUEUED);
    connect(_mainWindow, &MainWindow::runSetupWizard, this, &AppGui::reinit,
            QUEUED);
    connect(_mainWindow, &MainWindow::stopTasks, &_taskManager,
            &TaskManager::stopTasks, QUEUED);
    connect(&_taskManager, &TaskManager::jobList, _mainWindow,
            &MainWindow::jobList, QUEUED);
    connect(_mainWindow, &MainWindow::deleteJob, &_taskManager,
            &TaskManager::deleteJob, QUEUED);
    connect(_mainWindow, &MainWindow::getTaskInfo, &_taskManager,
            &TaskManager::getTaskInfo, QUEUED);
    connect(&_taskManager, &TaskManager::taskInfo, _mainWindow,
            &MainWindow::closeWithTaskInfo, QUEUED);
    connect(_mainWindow, &MainWindow::jobAdded, &_taskManager,
            &TaskManager::addJob, QUEUED);
    connect(_mainWindow, &MainWindow::getKeyId, &_taskManager,
            &TaskManager::getKeyId, QUEUED);
    connect(&_taskManager, &TaskManager::keyId, _mainWindow,
            &MainWindow::saveKeyId, QUEUED);
    connect(&_taskManager, &TaskManager::message, _mainWindow,
            &MainWindow::updateStatusMessage, QUEUED);
    connect(&_taskManager, &TaskManager::error, _mainWindow,
            &MainWindow::tarsnapError, QUEUED);
    connect(&_notification, &Notification::activated, _mainWindow,
            &MainWindow::notificationRaise, QUEUED);
    connect(&_notification, &Notification::notification_clicked, _mainWindow,
            &MainWindow::handle_notification_clicked, QUEUED);
    connect(_journal, &Journal::journal, _mainWindow, &MainWindow::setJournal,
            QUEUED);
    connect(_journal, &Journal::logEntry, _mainWindow,
            &MainWindow::appendToJournalLog, QUEUED);
    connect(_mainWindow, &MainWindow::clearJournal, _journal, &Journal::purge,
            QUEUED);
    connect(_mainWindow, &MainWindow::findMatchingArchives, &_taskManager,
            &TaskManager::findMatchingArchives, QUEUED);
    connect(&_taskManager, &TaskManager::matchingArchives, _mainWindow,
            &MainWindow::matchingArchives, QUEUED);

    QMetaObject::invokeMethod(_mainWindow, "initializeMainWindow", QUEUED);
    QMetaObject::invokeMethod(&_taskManager, "loadArchives", QUEUED);
    QMetaObject::invokeMethod(&_taskManager, "loadJobs", QUEUED);
    QMetaObject::invokeMethod(_journal, "getJournal", QUEUED);

    _mainWindow->show();
}

void AppGui::reinit()
{
    disconnect(&_taskManager, &TaskManager::displayNotification, &_notification,
               &Notification::displayNotification);
    disconnect(&_taskManager, &TaskManager::message, _journal, &Journal::log);

    if(_mainWindow)
    {
        delete _mainWindow;
        _mainWindow = nullptr;
    }

    // reset existing persistent store and app settings
    PersistentStore &store = PersistentStore::instance();
    store.purge();

    TSettings settings;
    if(settings.contains("app/wizard_done"))
    {
        settings.clear();
    }

    if(!initializeCore())
    {
        QMessageBox::critical(nullptr, tr("Failed to launch application"),
                              tr("An unknown error occurred."));
        exit(EXIT_FAILURE);
    }
    showMainWindow();
}

bool AppGui::runSetupWizard()
{
    // Show the first time setup dialog
    SetupDialog wizard;
    connect(&wizard, &SetupDialog::tarsnapVersionRequested, &_taskManager,
            &TaskManager::tarsnapVersionFind);
    connect(&_taskManager, &TaskManager::tarsnapVersionFound, &wizard,
            &SetupDialog::tarsnapVersionResponse);
    connect(&wizard, &SetupDialog::registerMachineRequested, &_taskManager,
            &TaskManager::registerMachineDo);
    connect(&_taskManager, &TaskManager::registerMachineDone, &wizard,
            &SetupDialog::registerMachineResponse);
    connect(&_taskManager, &TaskManager::idle, &wizard,
            &SetupDialog::updateLoadingAnimation);

    if(QDialog::Rejected == wizard.exec())
    {
        quit();       // if we're running in the loop
        return false; // if called from main
    }
    return true;
}
<commit_msg>Re-run initializeCore() upon completing the setup<commit_after>#include "app-gui.h"

WARNINGS_DISABLE
#include <QFontDatabase>
#include <QMessageBox>
WARNINGS_ENABLE

#include <assert.h>

#include "debug.h"
#include "init-shared.h"
#include "translator.h"
#include "utils.h"
#include "widgets/setupdialog.h"

#include <TSettings.h>

AppGui::AppGui(int &argc, char **argv, struct optparse *opt)
    : QApplication(argc, argv),
      _mainWindow(nullptr),
      _journal(nullptr),
      _notification()
{
    // Sanity check
    assert(opt != nullptr);

    // Get values from optparse.  The (x == 1) is probably unnecessary, but
    // better safe than sorry!
    _jobsOption  = (opt->jobs == 1);
    _checkOption = (opt->check == 1);
    _configDir   = opt->config_dir;

    init_shared(this);

    setQuitOnLastWindowClosed(false);

    qRegisterMetaType<QSystemTrayIcon::ActivationReason>(
        "QSystemTrayIcon::ActivationReason");
}

AppGui::~AppGui()
{
    if(_mainWindow)
        delete _mainWindow;
    _managerThread.quit();
    _managerThread.wait();
    if(_journal)
        delete _journal;
    TSettings::destroy();
}

bool AppGui::initializeCore()
{
    struct init_info info;

    // Set up Settings.
    info = init_shared_settings(_configDir);
    if(info.status == INIT_SETTINGS_RENAMED)
        QMessageBox::information(nullptr, tr("Tarsnap info"), info.message);

    // Set up the Translator, check --dry-run, update scheduling path.
    info = init_shared_core(this);

    if(info.status == INIT_NEEDS_SETUP)
    {
        if(!runSetupWizard())
            return false;
        // Remove the Translator
        Translator::destroy();
        // Restart
        return initializeCore();
    }
    else if(info.status == INIT_DB_FAILED)
    {
        QMessageBox::warning(nullptr, tr("Tarsnap warning"),
                             tr("Cannot not initialize the database."));
        return false;
    }
    else if(info.status == INIT_DRY_RUN)
    {
        QMessageBox::warning(nullptr, tr("Tarsnap warning"), info.message);
        // There's no point trying to automatically process jobs.
        if(_jobsOption)
            return false;
    }

    if(_jobsOption || _checkOption)
    {
        if(info.status == INIT_SCHEDULE_OK)
            DEBUG << info.extra;
        else if(info.status == INIT_SCHEDULE_ERROR)
        {
            DEBUG << info.message;
            return false;
        }

        // We don't have anything else to do
        if(_checkOption)
            return true;
    }

    if(!_jobsOption)
    {
        if(info.status == INIT_SCHEDULE_OK)
            QMessageBox::information(nullptr, tr("Updated OS X launchd path"),
                                     info.message);
        else if(info.status == INIT_SCHEDULE_ERROR)
            QMessageBox::information(nullptr,
                                     tr("Failed to updated OS X launchd path"),
                                     info.message);
    }
    return true;
}

bool AppGui::prepMainLoop()
{
    // Nothing to do.
    if(_checkOption)
        return false;

    // Initialize the PersistentStore early
    PersistentStore::instance();
    _journal = new Journal();

    // Queue loading the journal when we have an event loop.
    QMetaObject::invokeMethod(_journal, "load", QUEUED);

    connect(&_taskManager, &TaskManager::displayNotification, &_notification,
            &Notification::displayNotification, QUEUED);
    connect(&_taskManager, &TaskManager::message, _journal, &Journal::log,
            QUEUED);

    if(_jobsOption)
    {
        setQuitLockEnabled(true);
        connect(&_notification, &Notification::activated, this,
                &AppGui::showMainWindow, QUEUED);
        connect(&_notification, &Notification::messageClicked, this,
                &AppGui::showMainWindow, QUEUED);
        QMetaObject::invokeMethod(&_taskManager, "runScheduledJobs", QUEUED);
    }
    else
    {
        showMainWindow();
    }

    return true;
}

void AppGui::showMainWindow()
{
    if(_mainWindow != nullptr)
        return;

    setQuitLockEnabled(false);
    disconnect(&_notification, &Notification::activated, this,
               &AppGui::showMainWindow);
    disconnect(&_notification, &Notification::messageClicked, this,
               &AppGui::showMainWindow);

    _mainWindow = new MainWindow();
    Q_ASSERT(_mainWindow != nullptr);

    connect(_mainWindow, &MainWindow::tarsnapVersionRequested, &_taskManager,
            &TaskManager::tarsnapVersionFind, QUEUED);
    connect(&_taskManager, &TaskManager::tarsnapVersionFound, _mainWindow,
            &MainWindow::tarsnapVersionResponse, QUEUED);
    connect(_mainWindow, &MainWindow::backupNow, &_taskManager,
            &TaskManager::backupNow, QUEUED);
    connect(_mainWindow, &MainWindow::getArchives, &_taskManager,
            &TaskManager::getArchives, QUEUED);
    connect(&_taskManager, &TaskManager::archiveList, _mainWindow,
            &MainWindow::archiveList, QUEUED);
    connect(&_taskManager, &TaskManager::addArchive, _mainWindow,
            &MainWindow::addArchive, QUEUED);
    connect(_mainWindow, &MainWindow::deleteArchives, &_taskManager,
            &TaskManager::deleteArchives, QUEUED);
    connect(_mainWindow, &MainWindow::loadArchiveStats, &_taskManager,
            &TaskManager::getArchiveStats, QUEUED);
    connect(_mainWindow, &MainWindow::loadArchiveContents, &_taskManager,
            &TaskManager::getArchiveContents, QUEUED);
    connect(&_taskManager, &TaskManager::idle, _mainWindow,
            &MainWindow::updateLoadingAnimation, QUEUED);
    connect(&_taskManager, &TaskManager::numTasks, _mainWindow,
            &MainWindow::updateNumTasks, QUEUED);
    connect(_mainWindow, &MainWindow::getOverallStats, &_taskManager,
            &TaskManager::getOverallStats, QUEUED);
    connect(&_taskManager, &TaskManager::overallStats, _mainWindow,
            &MainWindow::overallStatsChanged, QUEUED);
    connect(_mainWindow, &MainWindow::repairCache, &_taskManager,
            &TaskManager::fsck, QUEUED);
    connect(_mainWindow, &MainWindow::nukeArchives, &_taskManager,
            &TaskManager::nuke, QUEUED);
    connect(_mainWindow, &MainWindow::restoreArchive, &_taskManager,
            &TaskManager::restoreArchive, QUEUED);
    connect(_mainWindow, &MainWindow::runSetupWizard, this, &AppGui::reinit,
            QUEUED);
    connect(_mainWindow, &MainWindow::stopTasks, &_taskManager,
            &TaskManager::stopTasks, QUEUED);
    connect(&_taskManager, &TaskManager::jobList, _mainWindow,
            &MainWindow::jobList, QUEUED);
    connect(_mainWindow, &MainWindow::deleteJob, &_taskManager,
            &TaskManager::deleteJob, QUEUED);
    connect(_mainWindow, &MainWindow::getTaskInfo, &_taskManager,
            &TaskManager::getTaskInfo, QUEUED);
    connect(&_taskManager, &TaskManager::taskInfo, _mainWindow,
            &MainWindow::closeWithTaskInfo, QUEUED);
    connect(_mainWindow, &MainWindow::jobAdded, &_taskManager,
            &TaskManager::addJob, QUEUED);
    connect(_mainWindow, &MainWindow::getKeyId, &_taskManager,
            &TaskManager::getKeyId, QUEUED);
    connect(&_taskManager, &TaskManager::keyId, _mainWindow,
            &MainWindow::saveKeyId, QUEUED);
    connect(&_taskManager, &TaskManager::message, _mainWindow,
            &MainWindow::updateStatusMessage, QUEUED);
    connect(&_taskManager, &TaskManager::error, _mainWindow,
            &MainWindow::tarsnapError, QUEUED);
    connect(&_notification, &Notification::activated, _mainWindow,
            &MainWindow::notificationRaise, QUEUED);
    connect(&_notification, &Notification::notification_clicked, _mainWindow,
            &MainWindow::handle_notification_clicked, QUEUED);
    connect(_journal, &Journal::journal, _mainWindow, &MainWindow::setJournal,
            QUEUED);
    connect(_journal, &Journal::logEntry, _mainWindow,
            &MainWindow::appendToJournalLog, QUEUED);
    connect(_mainWindow, &MainWindow::clearJournal, _journal, &Journal::purge,
            QUEUED);
    connect(_mainWindow, &MainWindow::findMatchingArchives, &_taskManager,
            &TaskManager::findMatchingArchives, QUEUED);
    connect(&_taskManager, &TaskManager::matchingArchives, _mainWindow,
            &MainWindow::matchingArchives, QUEUED);

    QMetaObject::invokeMethod(_mainWindow, "initializeMainWindow", QUEUED);
    QMetaObject::invokeMethod(&_taskManager, "loadArchives", QUEUED);
    QMetaObject::invokeMethod(&_taskManager, "loadJobs", QUEUED);
    QMetaObject::invokeMethod(_journal, "getJournal", QUEUED);

    _mainWindow->show();
}

void AppGui::reinit()
{
    disconnect(&_taskManager, &TaskManager::displayNotification, &_notification,
               &Notification::displayNotification);
    disconnect(&_taskManager, &TaskManager::message, _journal, &Journal::log);

    if(_mainWindow)
    {
        delete _mainWindow;
        _mainWindow = nullptr;
    }

    // reset existing persistent store and app settings
    PersistentStore &store = PersistentStore::instance();
    store.purge();

    TSettings settings;
    if(settings.contains("app/wizard_done"))
    {
        settings.clear();
    }

    if(!initializeCore())
    {
        QMessageBox::critical(nullptr, tr("Failed to launch application"),
                              tr("An unknown error occurred."));
        exit(EXIT_FAILURE);
    }
    showMainWindow();
}

bool AppGui::runSetupWizard()
{
    // Show the first time setup dialog
    SetupDialog wizard;
    connect(&wizard, &SetupDialog::tarsnapVersionRequested, &_taskManager,
            &TaskManager::tarsnapVersionFind);
    connect(&_taskManager, &TaskManager::tarsnapVersionFound, &wizard,
            &SetupDialog::tarsnapVersionResponse);
    connect(&wizard, &SetupDialog::registerMachineRequested, &_taskManager,
            &TaskManager::registerMachineDo);
    connect(&_taskManager, &TaskManager::registerMachineDone, &wizard,
            &SetupDialog::registerMachineResponse);
    connect(&_taskManager, &TaskManager::idle, &wizard,
            &SetupDialog::updateLoadingAnimation);

    if(QDialog::Rejected == wizard.exec())
    {
        quit();       // if we're running in the loop
        return false; // if called from main
    }
    return true;
}
<|endoftext|>