/* Licensed to Stichting The Commons Conservancy (TCC) under one or more * contributor license agreements. See the AUTHORS file distributed with * this work for additional information regarding copyright ownership. * TCC licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "pch.h" #include "framework.h" #include "Redwax SignText (MFC).h" #include "Redwax SignText (MFC)Dlg.h" #include "afxdialogex.h" #include #pragma comment(lib, "wininet.lib") #pragma comment(lib, "version.lib") #ifdef _DEBUG #define new DEBUG_NEW #endif // CAboutDlg dialog used for App About class CAboutDlg : public CDialogEx { public: CAboutDlg(); CStatic m_Version; // Dialog Data #ifdef AFX_DESIGN_TIME enum { IDD = IDD_ABOUTBOX }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: // Generated message map functions //{{AFX_MSG(CAboutPage) virtual BOOL OnInitDialog(); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; UINT ReadStdinFunction(LPVOID pParam) { SignTextData* signtext; signtext = (SignTextData*)pParam; message_receive(signtext); return 0; } UINT HandleCryptoFunction(LPVOID pParam) { SignTextData* signtext; signtext = (SignTextData*)pParam; crypto_start(signtext); return 0; } UINT HandleSignFunction(LPVOID pParam) { SignTextInstance* instance; instance = (SignTextInstance*)pParam; crypto_sign(instance); return 0; } CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) { } BOOL CAboutDlg::OnInitDialog() { CDialog::OnInitDialog(); DWORD dwHandle; TCHAR fileName[MAX_PATH]; HINSTANCE hInst = AfxGetInstanceHandle(); GetModuleFileName(hInst, fileName, MAX_PATH); DWORD dwSize = GetFileVersionInfoSize(fileName, &dwHandle); HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE, dwSize); LPVOID lpvMem = GlobalLock(hMem); VS_FIXEDFILEINFO* pvFileInfo = NULL; UINT fiLen = 0; if ((dwSize > 0) && GetFileVersionInfo(fileName, dwHandle, dwSize, lpvMem)) { VerQueryValue(lpvMem, L"\\", (LPVOID*)&pvFileInfo, &fiLen); } if (fiLen > 0) { wchar_t szResult[MAX_PATH]; int len; HWND hDlg = GetSafeHwnd(); WORD major = HIWORD(pvFileInfo->dwFileVersionMS); WORD minor = LOWORD(pvFileInfo->dwFileVersionMS); WORD patch = HIWORD(pvFileInfo->dwFileVersionLS); WORD build = LOWORD(pvFileInfo->dwFileVersionLS); if (build) { len = swprintf(szResult, MAX_PATH, _T("Version %hu.%hu.%hu (%hu)"), major, minor, patch, build); } else { len = swprintf(szResult, MAX_PATH, _T("Version %hu.%hu.%hu"), major, minor, patch); } m_Version.SetWindowText(szResult); } GlobalUnlock(hMem); GlobalFree(hMem); return TRUE; } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_VERSION, m_Version); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() CRedwaxSignTextMFCDlg::CRedwaxSignTextMFCDlg(CWnd* pParent /*=nullptr*/) : CDialogEx(IDD_REDWAX_SIGNTEXT_MFC_DIALOG, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CRedwaxSignTextMFCDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); // m_dlg1.DoDataExchange(pDX); // m_dlg2.DoDataExchange(pDX); DDX_Control(pDX, IDC_TAB1, m_ctlTab); DDX_Control(pDX, IDSIGN, m_sign); DDX_Control(pDX, IDCANCEL, m_cancel); } BEGIN_MESSAGE_MAP(CRedwaxSignTextMFCDlg, CDialogEx) ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, OnSelchangeTab1) ON_NOTIFY(CBN_SELCHANGE, IDC_CERTIFICATEBOX, OnSelchangeCertBox) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_MESSAGE(WM_MESSAGE_RECEIVE, OnMessageReceive) ON_MESSAGE(WM_MESSAGE_RECEIVED, OnMessageReceived) ON_MESSAGE(WM_MESSAGE_SEND, OnMessageSend) ON_MESSAGE(WM_MESSAGE_SENT, OnMessageSent) ON_MESSAGE(WM_MESSAGE_SHOW, OnMessageShow) ON_MESSAGE(WM_CRYPTO_UPDATED, OnCryptoUpdated) ON_MESSAGE(WM_CRYPTO_SELECTED, OnCryptoSelected) ON_MESSAGE(WM_CRYPTO_SIGNED, OnCryptoSigned) ON_MESSAGE(WM_CRYPTO_NOTSIGNED, OnCryptoNotSigned) ON_WM_SETCURSOR() ON_WM_SIZE() ON_WM_SIZING() ON_WM_GETMINMAXINFO() ON_BN_CLICKED(IDSIGN, &CRedwaxSignTextMFCDlg::OnBnClickedSign) ON_BN_CLICKED(IDCANCEL, &CRedwaxSignTextMFCDlg::OnBnClickedCancel) ON_WM_CLOSE() ON_WM_ENTERIDLE() ON_COMMAND(IDOK, &CRedwaxSignTextMFCDlg::OnIdok) END_MESSAGE_MAP() // CRedwaxSignTextMFCDlg message handlers BOOL CRedwaxSignTextMFCDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != nullptr) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon ResizeTab(); UpdateVisibleTab(); signtext = signtext_data_new(); /* stash away the window handle */ signtext->hwnd = GetSafeHwnd(); PostMessage(WM_MESSAGE_RECEIVE, 0, 0); AfxBeginThread(HandleCryptoFunction, signtext); return TRUE; // return TRUE unless you set the focus to a control } void CRedwaxSignTextMFCDlg::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult) { UpdateVisibleTab(); *pResult = 0; } void CRedwaxSignTextMFCDlg::OnSelchangeCertBox(NMHDR* pNMHDR, LRESULT* pResult) { UpdateVisibleTab(); *pResult = 0; } void CRedwaxSignTextMFCDlg::UpdateVisibleTab() { int index = 0; int current = m_ctlTab.GetCurSel(); /* until further notice */ int can_sign = FALSE; int can_cancel = TRUE; for (std::shared_ptr& m_dlg : m_dlgs) { if (current == index) { /* should it be possible to click the sign button */ if ((m_dlg->m_certificateBox.GetCurSel() > -1) && (!m_dlg->instance->is_signing)) { can_sign = TRUE; } /* should it be possible to click the cancel button */ if ((m_dlg->instance->is_cancelled)) { can_cancel = FALSE; } /* disable PIN and certificate selector while signing */ if (!m_dlg->instance->is_signing) { m_dlg->m_certificateBox.EnableWindow(TRUE); m_dlg->m_pinText.EnableWindow(TRUE); } else { m_dlg->m_certificateBox.EnableWindow(FALSE); m_dlg->m_pinText.EnableWindow(FALSE); } int currentCert = m_dlg->m_certificateBox.GetCurSel(); if (currentCert >= 0) { SignTextCertificate* cert = (SignTextCertificate*)m_dlg->m_certificateBox.GetItemDataPtr(currentCert); assert(cert); switch (cert->secretType) { case AlphaNumericPinType: m_dlg->m_pinText.SetCueBanner(L"Enter PIN", TRUE); m_dlg->m_pinText.SetReadOnly(FALSE); break; case ExternalPinType: m_dlg->m_pinText.SetCueBanner(L"Enter PIN on pinpad", TRUE); m_dlg->m_pinText.SetReadOnly(TRUE); m_dlg->m_pinText.SetWindowText(_T("")); break; case ChallengeResponsePinType: m_dlg->m_pinText.SetCueBanner(L"Challenge response PIN", TRUE); m_dlg->m_pinText.SetReadOnly(FALSE); break; case EmptyPinType: m_dlg->m_pinText.SetCueBanner(L"No PIN required", TRUE); m_dlg->m_pinText.SetReadOnly(TRUE); m_dlg->m_pinText.SetWindowText(_T("")); break; default: m_dlg->m_pinText.SetCueBanner(L"", TRUE); m_dlg->m_pinText.SetReadOnly(TRUE); m_dlg->m_pinText.SetWindowText(_T("")); } } else { m_dlg->m_pinText.SetCueBanner(L"", TRUE); m_dlg->m_pinText.SetReadOnly(TRUE); m_dlg->m_pinText.SetWindowText(_T("")); } m_dlg->ShowWindow(SW_SHOW); } else { m_dlg->ShowWindow(SW_HIDE); } index++; } /* sign button disabled or no */ m_sign.EnableWindow(can_sign); m_cancel.EnableWindow(can_cancel); ShowWindow(m_dlgs.size() ? SW_SHOW : SW_HIDE); } void CRedwaxSignTextMFCDlg::ResizeTab() { #if 0 RECT rcTab, rcDisplay; DWORD dwDlgBase = GetDialogBaseUnits(); int cxMargin = LOWORD(dwDlgBase) / 4; int cyMargin = HIWORD(dwDlgBase) / 8; m_ctlTab.GetClientRect(&rcTab); m_ctlTab.AdjustRect(TRUE, &rcTab); OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top); // Calculate the display rectangle. CopyRect(&rcDisplay, &rcTab); m_ctlTab.AdjustRect(FALSE, &rcDisplay); for (std::shared_ptr& m_dlg : m_dlgs) { m_dlg->SetWindowPos(NULL, rcDisplay.left, rcDisplay.top + cyMargin, 0, 0, SWP_NOSIZE | SWP_NOZORDER); // m_dlg->MoveWindow(&rcDisplay, FALSE); } #endif #if 1 RECT rect; // Get size of dialog window. m_ctlTab.GetClientRect(&rect); // Adjust the rectangle to fit the tab control into the // dialog's client rectangle. m_ctlTab.AdjustRect(FALSE, &rect); /* * Magic numbers. * * The calculation above places the window too far up and * too far to the left. No amount of experimentation has * identified the magic formula for calculating the inner * rectangle given the outer rectangle. These magic numbers * seem to put the tabbed dialos in the correct place. */ rect.top += 20 - 2; rect.bottom += 20 + 4 - 1; rect.left += 16 - 2; rect.right += 16 + 4 - 1; // Move the tab control to the new position and size. for (std::shared_ptr& m_dlg : m_dlgs) { m_dlg->MoveWindow(&rect, FALSE); } #endif #if 0 CRect tabRect, itemRect; m_ctlTab.GetClientRect(&tabRect); m_ctlTab.GetItemRect(m_ctlTab.GetCurSel(), &itemRect); int x, y, xc, yc; x = itemRect.left; y = itemRect.bottom + 1; xc = tabRect.right - itemRect.left - 1; yc = tabRect.bottom - y - 1; // m_ctlTab.AdjustRect(FALSE, &rect); for (std::shared_ptr& m_dlg : m_dlgs) { m_dlg->ModifyStyle(WS_BORDER | WS_DLGFRAME | WS_CAPTION | WS_DISABLED | WS_THICKFRAME, WS_CLIPCHILDREN | WS_CHILD | WS_CLIPSIBLINGS); m_dlg->SetWindowPos(&wndTop, x, y, xc, yc, 0); } #endif } void CRedwaxSignTextMFCDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CRedwaxSignTextMFCDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } // The system calls this function to obtain the cursor to display while the user drags // the minimized window. HCURSOR CRedwaxSignTextMFCDlg::OnQueryDragIcon() { return static_cast(m_hIcon); } LRESULT CRedwaxSignTextMFCDlg::OnMessageReceive(WPARAM wParam, LPARAM lParam) { /* start the stdin thread */ AfxBeginThread(ReadStdinFunction, signtext); return NULL; } LRESULT CRedwaxSignTextMFCDlg::OnMessageReceived(WPARAM wParam, LPARAM lParam) { char* buffer = (char *)lParam; uint32_t length = (uint32_t)wParam; message_receive_do(signtext, buffer, length); return NULL; } LRESULT CRedwaxSignTextMFCDlg::OnMessageSend(WPARAM wParam, LPARAM lParam) { char* buffer = (char*)lParam; uint32_t length = (uint32_t)wParam; message_send(signtext, buffer, length); return NULL; } LRESULT CRedwaxSignTextMFCDlg::OnMessageSent(WPARAM wParam, LPARAM lParam) { int index = 0; for (std::vector>::iterator it = m_dlgs.begin(); it != m_dlgs.end(); ++it) { std::shared_ptr dlg = *it; /* * Any tabs marked done must be gracefully cleaned up. */ if (dlg->instance->response_done || dlg->instance->is_cancelled) { m_ctlTab.DeleteItem(index); m_dlgs.erase(it); if (m_ctlTab.GetItemCount() == 0) { /* * No more tabs left, we are done - close down gracefully */ ShowWindow(SW_HIDE); PostQuitMessage(0); } else { /* * Rather than messing around with iterators, re-trigger * the message to walk the rest of the loop */ PostMessage(WM_MESSAGE_SENT, 0, 0); } if (m_ctlTab.GetCurSel() == -1 && m_ctlTab.GetItemCount() > 0) { m_ctlTab.SetCurSel(0); } ResizeTab(); UpdateVisibleTab(); break; } index++; } return NULL; } LRESULT CRedwaxSignTextMFCDlg::OnMessageShow(WPARAM wParam, LPARAM lParam) { SignTextInstance* instance = (SignTextInstance *)lParam; LPWSTR wUri = signtext_utf8ToUtf16(instance->uri, strlen(instance->uri)); URL_COMPONENTS URLparts = { 0 }; TCHAR hostName[256]; URLparts.dwStructSize = sizeof(URLparts); URLparts.lpszHostName = hostName; URLparts.dwHostNameLength = sizeof(hostName); if (!InternetCrackUrl(wUri, (DWORD)_tcslen(wUri), 0, &URLparts)) { assert(0); } free(wUri); std::shared_ptr upm_dlg1 = std::make_shared(instance, this); VERIFY(upm_dlg1->Create(IDD_SIGN_PAGE, this)); VERIFY(m_ctlTab.InsertItem(TCIF_TEXT, 0, hostName, 0, 0, 0, 0) == 0); m_dlgs.insert(m_dlgs.begin(), upm_dlg1); upm_dlg1->m_signText.SetReadOnly(TRUE); upm_dlg1->m_signText.HideCaret(); /* * Insert line by line to convert line endings. */ const char* buffer = instance->in_buffer; const char* next; size_t length; do { next = strchr(buffer, '\n'); if (next) { length = next - buffer; } else { length = strlen(buffer); } LPWSTR wBuffer = signtext_utf8ToUtf16(buffer, length); int nLength = upm_dlg1->m_signText.GetWindowTextLength(); upm_dlg1->m_signText.SetSel(nLength, nLength, TRUE); upm_dlg1->m_signText.ReplaceSel(wBuffer); free(wBuffer); if (next) { int nLength = upm_dlg1->m_signText.GetWindowTextLength(); upm_dlg1->m_signText.SetSel(nLength, nLength, TRUE); upm_dlg1->m_signText.ReplaceSel(L"\r\n"); /* skip LF we found */ buffer = next + 1; } } while (next); m_ctlTab.SetCurSel(0); ResizeTab(); UpdateVisibleTab(); return NULL; } void CRedwaxSignTextMFCDlg::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); } void CRedwaxSignTextMFCDlg::OnSizing(UINT fwSide, LPRECT pRect) { CDialogEx::OnSizing(fwSide, pRect); ResizeTab(); } void CRedwaxSignTextMFCDlg::OnGetMinMaxInfo(MINMAXINFO* lpMMI) { CDialogEx::OnGetMinMaxInfo(lpMMI); } LRESULT CRedwaxSignTextMFCDlg::OnCryptoUpdated(WPARAM wParam, LPARAM lParam) { SignTextCertificate* certificates = (SignTextCertificate*)lParam; for (std::shared_ptr& m_dlg : m_dlgs) { /* remove vanished certificates */ int count = m_dlg->m_certificateBox.GetCount(); for (int i = 0; i < count;) { SignTextCertificate* cert = (SignTextCertificate*)m_dlg->m_certificateBox.GetItemDataPtr(i); if (!crypto_certificate_find(certificates, cert)) { if (i == m_dlg->m_certificateBox.GetCurSel()) { m_dlg->m_certificateBox.SetCurSel(-1); } m_dlg->m_certificateBox.DeleteString(i); count = m_dlg->m_certificateBox.GetCount(); signtext_certificate_unref(cert); } else { i++; } } /* add new certificates */ SignTextCertificate* certs = certificates; while (certs) { int found = 0; int count = m_dlg->m_certificateBox.GetCount(); for (int i = 0; i < count; i++) { SignTextCertificate* cert = (SignTextCertificate*)m_dlg->m_certificateBox.GetItemDataPtr(i); if (!crypto_certificate_compare(certs, cert)) { found = 1; } } if (!found) { signtext_certificate_ref(certs); int index = m_dlg->m_certificateBox.AddString(certs->name); m_dlg->m_certificateBox.SetItemDataPtr(index, certs); assert(count + 1 == m_dlg->m_certificateBox.GetCount()); } certs = certs->next; } } while (certificates) { SignTextCertificate* next = certificates->next; signtext_certificate_unref(certificates); certificates = next; } return NULL; } LRESULT CRedwaxSignTextMFCDlg::OnCryptoSelected(WPARAM wParam, LPARAM lParam) { UpdateVisibleTab(); return NULL; } LRESULT CRedwaxSignTextMFCDlg::OnCryptoSigned(WPARAM wParam, LPARAM lParam) { UpdateVisibleTab(); return NULL; } LRESULT CRedwaxSignTextMFCDlg::OnCryptoNotSigned(WPARAM wParam, LPARAM lParam) { UpdateVisibleTab(); return NULL; } void CRedwaxSignTextMFCDlg::OnClose() { for (std::vector>::iterator it = m_dlgs.begin(); it != m_dlgs.end(); ++it) { std::shared_ptr dlg = *it; /* * Send cancel for anything not done or already cancelled. */ if (!dlg->instance->response_done && !dlg->instance->is_cancelled) { message_send_cancel_response(dlg->instance); } } UpdateVisibleTab(); } void CRedwaxSignTextMFCDlg::OnBnClickedSign() { int current = m_ctlTab.GetCurSel(); assert(current >= 0); std::shared_ptr& m_dlg = m_dlgs.at(current); current = m_dlg->m_certificateBox.GetCurSel(); assert(current >= 0); SignTextCertificate* cert = (SignTextCertificate*)m_dlg->m_certificateBox.GetItemDataPtr(current); assert(cert); m_dlg->instance->certificate = signtext_certificate_ref(cert); m_dlg->instance->pinLen = m_dlg->m_pinText.GetWindowTextLength(); m_dlg->instance->pinBuffer = (LPWSTR)malloc((m_dlg->instance->pinLen + 1) * sizeof(wchar_t)); m_dlg->m_pinText.GetWindowText(m_dlg->instance->pinBuffer, m_dlg->instance->pinLen + 1); m_dlg->instance->is_signing = TRUE; UpdateVisibleTab(); AfxBeginThread(HandleSignFunction, m_dlg->instance); } BOOL CRedwaxSignTextMFCDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { int is_busy = FALSE; for (std::shared_ptr& m_dlg : m_dlgs) { if (m_dlg->instance->is_signing || m_dlg->instance->out_buffer) { is_busy = TRUE; break; } } if (is_busy) { ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_WAIT)); return TRUE; } return CWnd::OnSetCursor(pWnd, nHitTest, message); } void CRedwaxSignTextMFCDlg::OnBnClickedCancel() { int current = m_ctlTab.GetCurSel(); if (current >= 0) { std::shared_ptr& m_dlg = m_dlgs.at(current); message_send_cancel_response(m_dlg->instance); UpdateVisibleTab(); } } void CRedwaxSignTextMFCDlg::OnIdok() { /* ignore the ok button */ } CRedwaxSignTextMFCDlg::~CRedwaxSignTextMFCDlg() { WaitForSingleObject(hThread, INFINITE); WaitForSingleObject(cThread, INFINITE); signtext_data_free(signtext); }