diff options
Diffstat (limited to 'Src/auth')
141 files changed, 28019 insertions, 0 deletions
diff --git a/Src/auth/Loginbox/addressEditbox.cpp b/Src/auth/Loginbox/addressEditbox.cpp new file mode 100644 index 00000000..9438b1e5 --- /dev/null +++ b/Src/auth/Loginbox/addressEditbox.cpp @@ -0,0 +1,660 @@ +#define OEMRESOURCE + +#include "./addressEditbox.h" +#include "./common.h" + +#include <richedit.h> +#include <strsafe.h> + +#define NAEF_USERFLAGSMASK 0x00FFFFFF +#define NAEF_UNICODE 0x01000000 + + +typedef struct __ADDRESSEDITBOX +{ + WNDPROC originalProc; + UINT flags; + DWORD dblclkTime; LPWSTR rollbackText; +} ADDRESSEDITBOX; + +#define ADDRESSEDITBOX_PROP L"NullsoftAddressEditbox" + +#define GetEditbox(__hwnd) ((ADDRESSEDITBOX*)GetProp((__hwnd), ADDRESSEDITBOX_PROP)) + +static LRESULT CALLBACK AddressEditbox_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static INT CALLBACK AddressEditbox_WordBreakProc(LPWSTR pszText, INT iCurrent, INT cchLen, INT code); +static INT CALLBACK AddressEditbox_WordBreakProc2(LPWSTR pszText, INT iCurrent, INT cchLen, INT code); + +BOOL AddressEditbox_AttachWindow(HWND hEditbox) +{ + if (!IsWindow(hEditbox)) + return FALSE; + + ADDRESSEDITBOX *editbox = (ADDRESSEDITBOX*)GetProp(hEditbox, ADDRESSEDITBOX_PROP); + if (NULL != editbox) return TRUE; + + editbox = (ADDRESSEDITBOX*)calloc(1, sizeof(ADDRESSEDITBOX)); + if (NULL == editbox) return FALSE; + + + ZeroMemory(editbox, sizeof(ADDRESSEDITBOX)); + + if (IsWindowUnicode(hEditbox)) + editbox->flags |= NAEF_UNICODE; + + editbox->originalProc = (WNDPROC)(LONG_PTR)((0 != (NAEF_UNICODE & editbox->flags)) ? + SetWindowLongPtrW(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)AddressEditbox_WindowProc) : + SetWindowLongPtrA(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)AddressEditbox_WindowProc)); + + if (NULL == editbox->originalProc || !SetProp(hEditbox, ADDRESSEDITBOX_PROP, editbox)) + { + if (NULL != editbox->originalProc) + { + if (0 != (NAEF_UNICODE & editbox->flags)) + SetWindowLongPtrW(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc); + else + SetWindowLongPtrA(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc); + } + + free(editbox); + return FALSE; + } + SendMessage(hEditbox, EM_SETWORDBREAKPROC, 0, (LPARAM)AddressEditbox_WordBreakProc); + + + if (FAILED(LoginBox_GetWindowText(hEditbox, &editbox->rollbackText, NULL))) + editbox->rollbackText = NULL; + + return TRUE; +} + + +static void AddressEditbox_Detach(HWND hwnd) +{ + ADDRESSEDITBOX *editbox = GetEditbox(hwnd); + RemoveProp(hwnd, ADDRESSEDITBOX_PROP); + + if (NULL == editbox) + return; + + if (NULL != editbox->originalProc) + { + if (0 != (NAEF_UNICODE & editbox->flags)) + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc); + else + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc); + } + + free(editbox); +} + + +static LRESULT AddressEditbox_CallOrigWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + ADDRESSEDITBOX *editbox = GetEditbox(hwnd); + + if (NULL == editbox || NULL == editbox->originalProc) + { + return (0 != (NAEF_UNICODE & editbox->flags)) ? + DefWindowProcW(hwnd, uMsg, wParam, lParam) : + DefWindowProcA(hwnd, uMsg, wParam, lParam); + } + + return (0 != (NAEF_UNICODE & editbox->flags)) ? + CallWindowProcW(editbox->originalProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(editbox->originalProc, hwnd, uMsg, wParam, lParam); +} + +static void AddressEditbox_SelectReplacementBlock(HWND hwnd, LPCWSTR pszText) +{ + INT begin(-1), end(-1); + + if (NULL != pszText) + { + LPCWSTR cursor = pszText; + while(L'\0' != *cursor) + { + if (-1 == begin) + { + if (REPLACE_MARKER_BEGIN == *cursor) + begin = (INT)(INT_PTR)(cursor - pszText); + } + else if (REPLACE_MARKER_END == *cursor) + { + end = (INT)(INT_PTR)(cursor - pszText) + 1; + break; + } + cursor = CharNext(cursor); + } + if (-1 == begin || -1 == end) + { + begin = (INT)(INT_PTR)(cursor - pszText); + end = begin + 1; + } + } + + SendMessage(hwnd, EM_SETSEL, begin, end); +} +static void AddressEditbox_ResetText(HWND hwnd) +{ + ADDRESSEDITBOX *editbox = GetEditbox(hwnd); + if (NULL != editbox) + { + AddressEditbox_CallOrigWindowProc(hwnd, WM_SETTEXT, 0, (LPARAM)editbox->rollbackText); + AddressEditbox_SelectReplacementBlock(hwnd, editbox->rollbackText); + } +} + +static BOOL AddressEditbox_IsDelimiterChar(WCHAR testChar) +{ + WORD info; + if (FALSE == GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, &testChar, 1, &info)) + return 0; + + if (0 != ((C1_SPACE | C1_PUNCT | C1_CNTRL | C1_BLANK) & info) && + REPLACE_MARKER_BEGIN != testChar && REPLACE_MARKER_END != testChar) + { + return TRUE; + } + + return FALSE; +} + +static INT AddressEditbox_FindLeft(LPCWSTR pszText, INT iCurrent, INT cchLen) +{ + if (iCurrent <= 0) + return 0; + + LPCWSTR pszCursor = &pszText[iCurrent]; + BOOL charDelim = AddressEditbox_IsDelimiterChar(*pszCursor); + + for(;;) + { + pszCursor = CharPrev(pszText, pszCursor); + if (charDelim != AddressEditbox_IsDelimiterChar(*pszCursor)) + return (INT)(INT_PTR)(CharNext(pszCursor) - pszText); + + if (pszCursor == pszText) + break; + } + return 0; +} + +static INT AddressEditbox_FindRight(LPCWSTR pszText, INT iCurrent, INT cchLen) +{ + if (iCurrent >= cchLen) + return cchLen; + + LPCWSTR pszEnd = &pszText[cchLen]; + LPCWSTR pszCursor = &pszText[iCurrent]; + + if (iCurrent > 0) + pszCursor = CharNext(pszCursor); + + BOOL charDelim = AddressEditbox_IsDelimiterChar(*pszCursor); + + for(;;) + { + pszCursor = CharNext(pszCursor); + if (pszCursor >= pszEnd) + break; + + if (charDelim != AddressEditbox_IsDelimiterChar(*pszCursor)) + return (INT)(INT_PTR)(pszCursor - pszText); + } + return cchLen; +} + +static INT AddressEditbox_FindWordLeft(LPCWSTR pszText, INT iCurrent, INT cchLen, BOOL fRightCtrl) +{ + if (iCurrent < 2) + return 0; + + LPCWSTR pszCursor = &pszText[iCurrent]; + + if (FALSE == fRightCtrl) + pszCursor = CharPrev(pszText, pszCursor); + + BOOL prevCharDelim = AddressEditbox_IsDelimiterChar(*pszCursor); + for(;;) + { + pszCursor = CharPrev(pszText, pszCursor); + if (TRUE == AddressEditbox_IsDelimiterChar(*pszCursor)) + { + if (FALSE == prevCharDelim) + return (INT)(INT_PTR)(CharNext(pszCursor) - pszText); + + prevCharDelim = TRUE; + } + else + prevCharDelim = FALSE; + + if (pszCursor == pszText) + break; + } + return 0; +} + +static INT AddressEditbox_FindWordRight(LPCWSTR pszText, INT iCurrent, INT cchLen) +{ + if ( iCurrent >= (cchLen - 1)) + return cchLen; + + LPCWSTR pszEnd = &pszText[cchLen]; + LPCWSTR pszCursor = &pszText[iCurrent]; + + BOOL prevCharDelim = AddressEditbox_IsDelimiterChar(*pszCursor); + + for(;;) + { + pszCursor = CharNext(pszCursor); + if (pszCursor >= pszEnd) + break; + + if (prevCharDelim != AddressEditbox_IsDelimiterChar(*pszCursor)) + { + prevCharDelim = TRUE; + return (INT)(INT_PTR)(pszCursor - pszText); + } + else + prevCharDelim = FALSE; + + } + return cchLen; +} + +static INT CALLBACK AddressEditbox_WordBreakProc(LPWSTR pszText, INT iCurrent, INT cchLen, INT code) +{ + switch(code) + { + case WB_ISDELIMITER: return (iCurrent < 0) ? 0 : ((iCurrent > cchLen) ? (cchLen + 1) : AddressEditbox_IsDelimiterChar(pszText[iCurrent])); + case WB_LEFT: return AddressEditbox_FindLeft(pszText, iCurrent, cchLen); + case WB_RIGHT: return AddressEditbox_FindRight(pszText, iCurrent, cchLen); + case WB_MOVEWORDLEFT: return AddressEditbox_FindWordLeft(pszText, iCurrent, cchLen, FALSE); + case WB_MOVEWORDRIGHT: return AddressEditbox_FindWordRight(pszText, iCurrent, cchLen); + } + return 0; +} + +static INT CALLBACK AddressEditbox_WordBreakProcOverrideLeft(LPWSTR pszText, INT iCurrent, INT cchLen, INT code) +{ + switch(code) + { + case WB_LEFT: return AddressEditbox_FindWordLeft(pszText, iCurrent, cchLen, FALSE); + case WB_RIGHT: return AddressEditbox_FindWordRight(pszText, iCurrent, cchLen); + } + return AddressEditbox_WordBreakProc(pszText, iCurrent, cchLen, code); +} + +static INT CALLBACK AddressEditbox_WordBreakProcOverrideRight(LPWSTR pszText, INT iCurrent, INT cchLen, INT code) +{ + switch(code) + { + case WB_LEFT: return AddressEditbox_FindWordLeft(pszText, iCurrent, cchLen, TRUE); + case WB_RIGHT: return AddressEditbox_FindWordRight(pszText, iCurrent, cchLen); + } + return AddressEditbox_WordBreakProc(pszText, iCurrent, cchLen, code); +} + + +static void AddressEditbox_OnDestroy(HWND hwnd) +{ + ADDRESSEDITBOX *editbox = GetEditbox(hwnd); + + WNDPROC originalProc = editbox->originalProc; + BOOL fUnicode = (0 != (NAEF_UNICODE & editbox->flags)); + + AddressEditbox_Detach(hwnd); + + if (NULL != originalProc) + { + if (FALSE != fUnicode) + CallWindowProcW(originalProc, hwnd, WM_DESTROY, 0, 0L); + else + CallWindowProcA(originalProc, hwnd, WM_DESTROY, 0, 0L); + } + +} + +static LRESULT AddressEditbox_OnGetDlgCode(HWND hwnd, INT vKey, MSG* pMsg) +{ + LRESULT result = AddressEditbox_CallOrigWindowProc(hwnd, WM_GETDLGCODE, (WPARAM)vKey, (LPARAM)pMsg); + + switch(vKey) + { + case VK_ESCAPE: + return result |= DLGC_WANTALLKEYS; + } + + result &= ~DLGC_HASSETSEL; + return result; +} + + +static void AddressEditbox_OnLButtonDown(HWND hwnd, UINT vKey, POINTS pts) +{ + ADDRESSEDITBOX *editbox = GetEditbox(hwnd); + if (NULL != editbox) + { + DWORD clickTime = GetTickCount(); + if (clickTime >= editbox->dblclkTime && clickTime <= (editbox->dblclkTime + GetDoubleClickTime())) + { + SendMessage(hwnd, EM_SETSEL, 0, -1); + return; + } + } + AddressEditbox_CallOrigWindowProc(hwnd, WM_LBUTTONDOWN, (WPARAM)vKey, *((LPARAM*)&pts)); +} + +static void AddressEditbox_OnLButtonDblClk(HWND hwnd, UINT vKey, POINTS pts) +{ + ADDRESSEDITBOX *editbox = GetEditbox(hwnd); + if (NULL == editbox) return; + + DWORD clickTime = GetTickCount(); + if (clickTime >= editbox->dblclkTime && clickTime <= (editbox->dblclkTime + 2*GetDoubleClickTime())) + { + INT r = (INT)SendMessage(hwnd, EM_CHARFROMPOS, 0, *(LPARAM*)&pts); + r = LOWORD(r); + SendMessage(hwnd, EM_SETSEL, (WPARAM)r, (LPARAM)r); + editbox->dblclkTime = 0; + } + else + { + editbox->dblclkTime = clickTime; + } + + INT f, l; + SendMessage(hwnd, EM_GETSEL, (WPARAM)&f, (LPARAM)&l); + if (f != l) return; + + + AddressEditbox_CallOrigWindowProc(hwnd, WM_LBUTTONDBLCLK, (WPARAM)vKey, *((LPARAM*)&pts)); + +} + + +static void AddressEditbox_DeleteWord(HWND hwnd, UINT vKey, UINT state) +{ + BOOL resetVisible = FALSE; + INT first, last; + SendMessage(hwnd, EM_GETSEL, (WPARAM)&first, (LPARAM)&last); + if (first == last) + { + UINT windowStyle = GetWindowStyle(hwnd); + if (0 != (WS_VISIBLE & windowStyle)) + { + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + resetVisible = TRUE; + } + + SendMessage(hwnd, WM_KEYDOWN, (WPARAM)vKey, (LPARAM)state); + INT newFirst, newLast; + SendMessage(hwnd, EM_GETSEL, (WPARAM)&newFirst, (LPARAM)&newLast); + if (newFirst != first || newLast != last) + SendMessage(hwnd, EM_SETSEL, (WPARAM)first, (LPARAM)newLast); + } + + SendMessage(hwnd, EM_REPLACESEL, TRUE, NULL); + if (FALSE != resetVisible) + { + UINT windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE); + + InvalidateRect(hwnd, NULL, FALSE); + } + +} +static void AddressEditbox_OnKeyDown(HWND hwnd, UINT vKey, UINT state) +{ + EDITWORDBREAKPROC fnOrigBreak = NULL; + if(0 != (0x8000 & GetAsyncKeyState(VK_CONTROL))) + { + switch(vKey) + { + case VK_LEFT: + case VK_RIGHT: + fnOrigBreak = (EDITWORDBREAKPROC)SendMessage(hwnd, EM_GETWORDBREAKPROC, 0, 0L); + if (AddressEditbox_WordBreakProc == fnOrigBreak) + { + EDITWORDBREAKPROC fnOverride = (VK_LEFT == vKey) ? + AddressEditbox_WordBreakProcOverrideLeft : AddressEditbox_WordBreakProcOverrideRight; + SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)fnOverride); + } + break; + case VK_DELETE: + AddressEditbox_DeleteWord(hwnd, VK_RIGHT, state); + return; + case VK_BACK: + AddressEditbox_DeleteWord(hwnd, VK_LEFT, state); + return; + } + } + + AddressEditbox_CallOrigWindowProc(hwnd, WM_KEYDOWN, (WPARAM)vKey, (LPARAM)state); + + if (NULL != fnOrigBreak) + SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)fnOrigBreak); +} + +static void AddressEditbox_OnChar(HWND hwnd, UINT vKey, UINT state) +{ + if (0 != (0x8000 & GetAsyncKeyState(VK_CONTROL))) + { + UINT scanCode = (HIWORD(state) & 0x00FF); + vKey = MapVirtualKey(scanCode, MAPVK_VSC_TO_VK); + } + + switch(vKey) + { + case VK_ESCAPE: AddressEditbox_ResetText(hwnd); return; + } + + + AddressEditbox_CallOrigWindowProc(hwnd, WM_CHAR, (WPARAM)vKey, (LPARAM)state); +} + + + +static BOOL AddressEditbox_RemoveBadChars(LPCWSTR pszText, LPWSTR *bufferOut) +{ + LPWSTR buffer = NULL; + if (NULL == pszText) return FALSE; + + const WCHAR szBadChars[] = { L'\r', L'\n', L'\t', L'\0'}; + BOOL fDetected = FALSE; + + for (LPCWSTR p = pszText; L'\0' != *p && FALSE == fDetected; p++) + { + for (LPCWSTR b = szBadChars; L'\0' != *b; b++) + { + if (*p == *b) + { + fDetected = TRUE; + break; + } + } + } + + if (FALSE == fDetected) + return FALSE; + + if (NULL == bufferOut) + return TRUE; + + INT cchText = lstrlen(pszText); + buffer = LoginBox_MallocString(cchText + 1); + if (NULL == buffer) return FALSE; + + LPCWSTR s = pszText; + LPWSTR d = buffer; + LPCWSTR b; + for(;;) + { + for (b = szBadChars; L'\0' != *b && *s != *b; b++); + if(L'\0' != *b) + { + if (L'\t' == *b) + { + *d = L' '; + d++; + } + } + else + { + *d = *s; + d++; + } + + if (L'\0' == *s) + break; + + s++; + + } + + *bufferOut = buffer; + return TRUE; +} + +static LRESULT AddressEditbox_OnSetText(HWND hwnd, LPCWSTR pszText) +{ + LPWSTR buffer; + if (FALSE == AddressEditbox_RemoveBadChars(pszText, &buffer)) + buffer = NULL; + else + pszText = buffer; + + LRESULT result = AddressEditbox_CallOrigWindowProc(hwnd, WM_SETTEXT, 0, (LPARAM)pszText); + + ADDRESSEDITBOX *editbox = GetEditbox(hwnd); + if (NULL != editbox) + { + LoginBox_FreeString(editbox->rollbackText); + editbox->rollbackText = LoginBox_CopyString(pszText); + AddressEditbox_SelectReplacementBlock(hwnd, pszText); + } + + if (NULL != buffer) + LoginBox_FreeString(buffer); + + return result; +} + +static LRESULT AddressEditbox_OnReplaceSel(HWND hwnd, BOOL fUndo, LPCWSTR pszText) +{ + LPWSTR buffer; + if (FALSE == AddressEditbox_RemoveBadChars(pszText, &buffer)) + buffer = NULL; + else + pszText = buffer; + + LRESULT result = AddressEditbox_CallOrigWindowProc(hwnd, EM_REPLACESEL, (WPARAM)fUndo, (LPARAM)pszText); + + if (NULL != buffer) + LoginBox_FreeString(buffer); + + return result; +} + +static void AddressEditbox_ReplaceText(HWND hwnd, LPCWSTR pszText, BOOL fUndo, BOOL fScrollCaret) +{ + UINT windowStyle = GetWindowStyle(hwnd); + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + SendMessage(hwnd, EM_REPLACESEL, (WPARAM)fUndo, (LPARAM)pszText); + if (FALSE != fScrollCaret) + { + INT f, l; + SendMessage(hwnd, EM_GETSEL, (WPARAM)&f, (LPARAM)&l); + SendMessage(hwnd, EM_SETSEL, (WPARAM)f, (LPARAM)l); + } + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE); + InvalidateRect(hwnd, NULL, FALSE); + } +} + +static void AddressEditbox_OnPaste(HWND hwnd) +{ + IDataObject *pObject; + HRESULT hr = OleGetClipboard(&pObject); + if (SUCCEEDED(hr)) + { + FORMATETC fmt = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + STGMEDIUM stgm; + hr = pObject->GetData(&fmt, &stgm); + if(S_OK == hr) + { + LPCWSTR pClipboard = (LPCWSTR)GlobalLock(stgm.hGlobal); + AddressEditbox_ReplaceText(hwnd, pClipboard, TRUE, TRUE); + GlobalUnlock(stgm.hGlobal); + ReleaseStgMedium(&stgm); + + } + else + { + fmt.cfFormat = CF_TEXT; + hr = pObject->GetData(&fmt, &stgm); + if(S_OK == hr) + { + LPCSTR pClipboardAnsi = (LPCSTR)GlobalLock(stgm.hGlobal); + LPWSTR pClipboard; + if (FAILED(LoginBox_MultiByteToWideChar(CP_ACP, 0, pClipboardAnsi, -1, &pClipboard))) + pClipboard = NULL; + + AddressEditbox_ReplaceText(hwnd, pClipboard, TRUE, TRUE); + LoginBox_FreeString(pClipboard); + GlobalUnlock(stgm.hGlobal); + ReleaseStgMedium(&stgm); + } + } + pObject->Release(); + } +} + +static LRESULT AddressEditbox_OnFindWordBreak(HWND hwnd, INT code, INT start) +{ + EDITWORDBREAKPROC fnBreak = (EDITWORDBREAKPROC)SendMessage(hwnd, EM_GETWORDBREAKPROC, 0, 0L); + if (NULL == fnBreak) return 0; + + UINT cchText = GetWindowTextLength(hwnd); + if (0 == cchText) return 0; + + LPWSTR pszText = LoginBox_MallocString(cchText + 1); + if (NULL == pszText) return 0; + + LRESULT result = 0; + cchText = GetWindowText(hwnd, pszText, cchText + 1); + if (0 != cchText) + { + result = fnBreak(pszText, start, cchText, code); + } + LoginBox_FreeString(pszText); + return result; +} + +static LRESULT CALLBACK AddressEditbox_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_DESTROY: AddressEditbox_OnDestroy(hwnd); return 0; + case WM_GETDLGCODE: return AddressEditbox_OnGetDlgCode(hwnd, (INT)wParam, (MSG*)lParam); + case WM_LBUTTONDOWN: AddressEditbox_OnLButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_LBUTTONDBLCLK: AddressEditbox_OnLButtonDblClk(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_KEYDOWN: AddressEditbox_OnKeyDown(hwnd, (UINT)wParam, (UINT)lParam); return 0; + case WM_CHAR: AddressEditbox_OnChar(hwnd, (UINT)wParam, (UINT)lParam); return 0; + case WM_SETTEXT: return AddressEditbox_OnSetText(hwnd, (LPCWSTR)lParam); + case WM_PASTE: AddressEditbox_OnPaste(hwnd); return 1; + case EM_REPLACESEL: AddressEditbox_OnReplaceSel(hwnd, (BOOL)wParam, (LPCWSTR)lParam); return 0; + case EM_FINDWORDBREAK: return AddressEditbox_OnFindWordBreak(hwnd, (INT)wParam, (INT)lParam); + } + + return AddressEditbox_CallOrigWindowProc(hwnd, uMsg, wParam, lParam); +} diff --git a/Src/auth/Loginbox/addressEditbox.h b/Src/auth/Loginbox/addressEditbox.h new file mode 100644 index 00000000..23076988 --- /dev/null +++ b/Src/auth/Loginbox/addressEditbox.h @@ -0,0 +1,15 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_ADDRESS_EDITBOX_HEADER +#define NULLSOFT_AUTH_LOGINBOX_ADDRESS_EDITBOX_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#define REPLACE_MARKER_BEGIN L'<' +#define REPLACE_MARKER_END L'>' + +BOOL AddressEditbox_AttachWindow(HWND hEditbox); + +#endif // NULLSOFT_AUTH_LOGINBOX_ADDRESS_EDITBOX_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/addressEncoder.cpp b/Src/auth/Loginbox/addressEncoder.cpp new file mode 100644 index 00000000..ed2ca8d8 --- /dev/null +++ b/Src/auth/Loginbox/addressEncoder.cpp @@ -0,0 +1,514 @@ +#include "./addressEncoder.h" +#include "./common.h" + +#include <wininet.h> +#include <strsafe.h> + + +typedef struct __ENCODEBUFFER +{ + LPWSTR buffer; + size_t bufferMax; + LPWSTR cursor; + size_t remaining; +} ENCODEBUFFER; + +HRESULT AddressEncoder_ReAllocBuffer(ENCODEBUFFER *decoder, size_t cchBufferSize) +{ + if (NULL == decoder) + return E_INVALIDARG; + + if (cchBufferSize == decoder->bufferMax) + return S_FALSE; + + if (cchBufferSize < decoder->bufferMax) + return E_FAIL; + + LPWSTR test = LoginBox_ReAllocString(decoder->buffer, cchBufferSize); + if (NULL == test) + return E_OUTOFMEMORY; + + decoder->cursor = test + (decoder->cursor - decoder->buffer); + decoder->remaining += (cchBufferSize - decoder->bufferMax); + decoder->buffer = test; + decoder->bufferMax = cchBufferSize; + + return S_OK; +} +HRESULT AddressEncoder_AppendAnsiString(ENCODEBUFFER *decoder, LPCSTR pszString, size_t cchString) +{ + if (NULL == decoder) + return E_INVALIDARG; + + INT cchConverted; + while(0 ==(cchConverted = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, pszString, (INT)cchString, decoder->cursor, (INT)decoder->remaining))) + { + DWORD errorCode = GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == errorCode) + { + INT cchNeed = MultiByteToWideChar(CP_UTF8, 0, pszString, (INT)cchString, NULL, 0) - (INT)decoder->remaining; + if (cchNeed < 32) cchNeed = 32; + HRESULT hr = AddressEncoder_ReAllocBuffer(decoder, decoder->bufferMax + cchNeed); + if (FAILED(hr)) + return hr; + } + else + { + return HRESULT_FROM_WIN32(errorCode); + } + } + + if (0 != cchConverted) + { + decoder->cursor += cchConverted; + decoder->remaining -= cchConverted; + } + + return S_OK; +} + +HRESULT AddressEncoder_AppendString(ENCODEBUFFER *decoder, LPCWSTR pszString, size_t cchString) +{ + if (NULL == decoder) + return E_INVALIDARG; + + LPWSTR cursor; + size_t remaining; + + HRESULT hr; + if (cchString >= decoder->remaining) + { + hr = AddressEncoder_ReAllocBuffer(decoder, decoder->bufferMax + (cchString - decoder->remaining) + 1); + if (FAILED(hr)) return hr; + } + + hr = StringCchCopyNEx(decoder->cursor, decoder->remaining, pszString, cchString, &cursor, &remaining, 0); + if (SUCCEEDED(hr)) + { + decoder->cursor = cursor; + decoder->remaining = remaining; + } + return hr; +} + +HRESULT AddressEncoder_GetEscapeBlock(LPCWSTR pszEscape, LPCWSTR *ppszEnd, size_t *pcchEscapeLen, LPSTR pszBuffer, UINT *pcbBufferMax) +{ + if (NULL == pszEscape || (NULL != pszBuffer && NULL == pcbBufferMax)) + return E_INVALIDARG; + + UINT cbBinary = 0; + WORD charInfo; + WCHAR szDigit[3] = {0}; + HRESULT hr = S_OK; + + LPCWSTR cursor = pszEscape; + while (L'%' == *cursor) + { + LPCWSTR testChar = CharNext(cursor); + if (L'\0' == *testChar || + FALSE == GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, testChar, 1, &charInfo) || + 0 == (C1_XDIGIT & charInfo)) + { + break; + } + szDigit[0] = *testChar; + + testChar = CharNext(testChar); + if (L'\0' == *testChar || + FALSE == GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, testChar, 1, &charInfo) || + 0 == (C1_XDIGIT & charInfo)) + { + break; + } + + szDigit[1] = *testChar; + CharUpperBuff(szDigit, 2); + + BYTE binaryData = ((szDigit[0] - ((szDigit[0] <= L'9' ? L'0' : L'A' - 10))) << 4) & 0xf0; + binaryData += (szDigit[1] - ((szDigit[1] <= L'9' ? L'0' : L'A' - 10))) & 0x0f; + if (NULL != pszBuffer) + { + if (cbBinary < *pcbBufferMax) + pszBuffer[cbBinary] = binaryData; + else + hr = E_OUTOFMEMORY; + } + cbBinary++; + + cursor = CharNext(testChar); + } + + if (cursor == pszEscape) + hr = HRESULT_FROM_WIN32(ERROR_INVALID_BLOCK_LENGTH); + + if (NULL != ppszEnd) + *ppszEnd = cursor; + + if (NULL != pcchEscapeLen) + *pcchEscapeLen = (size_t)(cursor - pszEscape); + + if (NULL != pcbBufferMax) + *pcbBufferMax = cbBinary; + + return hr; +} + +HRESULT AddressEncoder_DecodeString(LPCWSTR pszUrl, LPWSTR *ppResult) +{ + if (NULL == pszUrl) + { + *ppResult = NULL; + return S_FALSE; + } + + UINT cchUrl = 0; + UINT escapeSize = 0; + UINT escapeBlockSize,escapeBlockMaxSize = 0; + LPCWSTR escapeBlockEnd; + for (LPCWSTR cursor = pszUrl; L'\0' != *cursor;) + { + if (L'%' == *cursor && SUCCEEDED(AddressEncoder_GetEscapeBlock(cursor, &escapeBlockEnd, NULL, NULL, &escapeBlockSize))) + { + escapeSize += escapeBlockSize; + if (escapeBlockSize > escapeBlockMaxSize) + escapeBlockMaxSize = escapeBlockSize; + + cursor = escapeBlockEnd; + } + else + { + cchUrl++; + cursor = CharNext(cursor); + } + } + + if (0 == escapeSize) + { + *ppResult = LoginBox_CopyString(pszUrl); + if (NULL == *ppResult) return E_OUTOFMEMORY; + return S_FALSE; + } + + HRESULT hr = S_OK; + + ENCODEBUFFER decoder; + ZeroMemory(&decoder, sizeof(decoder)); + + LPSTR escapeBuffer = LoginBox_MallocAnsiString(escapeBlockMaxSize); + if (NULL == escapeBuffer) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = AddressEncoder_ReAllocBuffer(&decoder, cchUrl + (escapeSize + 1)* sizeof(WCHAR)); + if (SUCCEEDED(hr)) + { + LPCWSTR cursor = pszUrl; + LPCWSTR copyBlock = cursor; + + for (;;) + { + escapeBlockSize = escapeBlockMaxSize; + if (L'%' == *cursor && SUCCEEDED(AddressEncoder_GetEscapeBlock(cursor, &escapeBlockEnd, NULL, escapeBuffer, &escapeBlockSize))) + { + if (copyBlock != cursor) + { + hr = AddressEncoder_AppendString(&decoder, copyBlock, cursor - copyBlock); + if (FAILED(hr)) + break; + copyBlock = cursor; + } + + HRESULT convertResult = AddressEncoder_AppendAnsiString(&decoder, escapeBuffer, escapeBlockSize); + if (L'\0' == *cursor) + break; + + cursor = escapeBlockEnd; + if (SUCCEEDED(convertResult)) + { + copyBlock = cursor; + } + + + + continue; + } + + if (L'\0' == *cursor) + { + if (copyBlock != cursor) + hr = AddressEncoder_AppendString(&decoder, copyBlock, cursor - copyBlock); + break; + } + else + cursor = CharNext(cursor); + } + + } + + } + + if (NULL != escapeBuffer) + LoginBox_FreeAnsiString(escapeBuffer); + + if (FAILED(hr)) + { + LoginBox_FreeString(decoder.buffer); + decoder.buffer = NULL; + } + else + { + *decoder.cursor = L'\0'; + } + + *ppResult = decoder.buffer; + + return hr; +} +HRESULT AddressEncoder_GetWideBlock(LPCWSTR pszWide, LPCWSTR *pszEnd, LPWSTR pszBuffer, size_t *pcchBufferMax) +{ + LPCWSTR cursor = pszWide; + if (NULL == pszWide) + return E_INVALIDARG; + + if (NULL != pszBuffer && NULL == pcchBufferMax) + return E_INVALIDARG; + + while (L'\0' == *cursor || *cursor > 0xFF) + { + if (L'\0' == *cursor) + break; + cursor = CharNext(cursor); + } + + if (NULL != pszEnd) + *pszEnd = cursor; + + HRESULT hr = S_OK; + size_t cchBuffer = 0; + + if (cursor == pszWide) + { + hr = S_FALSE; + } + else + { + size_t bytesCount = WideCharToMultiByte(CP_UTF8, 0, pszWide, (INT)(INT_PTR)(cursor - pszWide), NULL, 0, NULL, NULL); + if (0 == bytesCount) + { + DWORD errorCode = GetLastError(); + if (ERROR_SUCCESS != errorCode) + hr = HRESULT_FROM_WIN32(errorCode); + } + else + { + cchBuffer = 3 * bytesCount; + if (NULL != pszBuffer) + { + if (*pcchBufferMax >= cchBuffer) + { + LPWSTR p = pszBuffer; + BYTE *bytes = ((BYTE*)(pszBuffer + *pcchBufferMax)) - bytesCount; + WideCharToMultiByte(CP_UTF8, 0, pszWide, (INT)(INT_PTR)(cursor - pszWide), (LPSTR)bytes, (INT)bytesCount, NULL, NULL); + for (size_t i = 0; i < bytesCount; i++) + { + BYTE b = bytes[i]; + *p++ = L'%'; + BYTE c = (b >> 4) & 0x0F; + *p++ = (c < 10) ? (L'0' + c) : (L'A' + (c -10)); + c = b & 0x0F; + *p++ = (c < 10) ? (L'0' + c) : (L'A' + (c -10)); + } + + } + else + { + hr = E_OUTOFMEMORY; + } + } + } + } + + if (NULL != pcchBufferMax) + { + *pcchBufferMax = cchBuffer; + } + + + return hr; +} +HRESULT AddressEncoder_EncodeWideChars(LPCWSTR pszAddress, size_t cchAddress, LPWSTR *ppResult) +{ + if (NULL == ppResult) + return E_POINTER; + + if (NULL == pszAddress) + { + *ppResult = NULL; + return S_FALSE; + } + + LPCWSTR blockEnd; + size_t blockSize; + size_t cchResultMax = 0; + BOOL needEncode = FALSE; + for (LPCWSTR cursor = pszAddress; L'\0' != *cursor;) + { + if (*cursor > 0xFF && SUCCEEDED(AddressEncoder_GetWideBlock(cursor, &blockEnd, NULL, &blockSize))) + { + cursor = blockEnd; + cchResultMax += blockSize; + needEncode = TRUE; + } + else + { + cursor = CharNext(cursor); + cchResultMax++; + } + } + + if (FALSE == needEncode) + { + *ppResult = NULL; + return S_FALSE; + } + + HRESULT hr; + cchResultMax++; + LPWSTR result = LoginBox_MallocString(cchResultMax); + if (NULL == result) + hr = E_OUTOFMEMORY; + else + { + LPWSTR cursor = result; + size_t remaining = cchResultMax; + LPCWSTR address = pszAddress; + LPCWSTR addressBlock = address; + + for (;;) + { + if (*address > 0xFF) + { + if (addressBlock != address) + { + hr = StringCchCopyNEx(cursor, remaining, addressBlock, (size_t)(address - addressBlock), &cursor, &remaining, 0); + if (FAILED(hr)) break; + } + + blockSize = remaining; + hr = AddressEncoder_GetWideBlock(address, &address, cursor, &blockSize); + if (FAILED(hr)) break; + + cursor += blockSize; + remaining -= blockSize; + + addressBlock = address; + continue; + } + + if (L'\0' == *address) + { + if (addressBlock != address) + { + hr = StringCchCopyNEx(cursor, remaining, addressBlock, (size_t)(address - addressBlock), &cursor, &remaining, 0); + } + break; + } + else + address = CharNext(address); + } + + *cursor = L'\0'; + } + + if (FAILED(hr)) + { + LoginBox_FreeString(result); + result = NULL; + } + + *ppResult = result; + return hr; +} + +HRESULT AddressEncoder_EncodeString(LPCWSTR pszAddress, LPWSTR pszBuffer, size_t *pcchBufferMax, UINT flags) +{ + if (NULL == pszBuffer || NULL == pcchBufferMax) + return E_INVALIDARG; + + if (NULL == pszAddress || L'\0' == *pszAddress) + { + *pszBuffer = L'\0'; + *pcchBufferMax = 0; + return S_OK; + } + + INT cchAddress = lstrlen(pszAddress); + LPCWSTR begin, end; + begin = pszAddress; + end = pszAddress + cchAddress; + WORD charType; + while (L'\0' != *begin && + FALSE != GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, begin, 1, &charType) && 0 != (C1_SPACE & charType)) + { + begin = CharNext(begin); + } + while (begin != end && + FALSE != GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, begin, 1, &charType) && 0 != (C1_SPACE & charType)) + { + end = CharPrev(begin, end); + } + + if (end <= begin) + { + *pszBuffer = L'\0'; + *pcchBufferMax = 0; + return S_OK; + } + + LPWSTR encoded; + HRESULT hr = AddressEncoder_EncodeWideChars(begin, (end - begin), &encoded); + if (FAILED(hr)) return hr; + + if (S_OK == hr) + { + begin = encoded; + end = begin + lstrlen(begin); + } + + DWORD bufferLen = (DWORD)(*pcchBufferMax); + if (FALSE == InternetCanonicalizeUrl(begin, pszBuffer, &bufferLen, flags)) + { + DWORD errorCode = GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == errorCode) + { + *pcchBufferMax = bufferLen; + hr = ENC_E_INSUFFICIENT_BUFFER; + } + else + { + size_t cchNeeded = (end - begin); + if (cchNeeded < *pcchBufferMax) + { + hr = StringCchCopyN(pszBuffer, *pcchBufferMax, begin, cchNeeded); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + hr = ENC_E_INSUFFICIENT_BUFFER; + } + } + else + { + hr = ENC_E_INSUFFICIENT_BUFFER; + } + *pcchBufferMax = cchNeeded + 1; + } + } + + else + hr = S_OK; + + + LoginBox_FreeString(encoded); + return hr; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/addressEncoder.h b/Src/auth/Loginbox/addressEncoder.h new file mode 100644 index 00000000..6cd0226b --- /dev/null +++ b/Src/auth/Loginbox/addressEncoder.h @@ -0,0 +1,15 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_ADDRESS_ENCODER_HEADER +#define NULLSOFT_AUTH_LOGINBOX_ADDRESS_ENCODER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#define ENC_E_INSUFFICIENT_BUFFER (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + +HRESULT AddressEncoder_DecodeString(LPCWSTR pszAddress, LPWSTR *ppResult); +HRESULT AddressEncoder_EncodeString(LPCWSTR pszAddress, LPWSTR pszBuffer, size_t *pcchBufferMax, UINT flags); + +#endif //NULLSOFT_AUTH_LOGINBOX_ADDRESS_ENCODER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/animation.cpp b/Src/auth/Loginbox/animation.cpp new file mode 100644 index 00000000..6ec82225 --- /dev/null +++ b/Src/auth/Loginbox/animation.cpp @@ -0,0 +1,77 @@ +#include "./animation.h" +#include "./common.h" + +BOOL Animation_Initialize(ANIMATIONDATA *animation, UINT durationMs) +{ + if (NULL == animation) + return FALSE; + + if (FALSE == QueryPerformanceFrequency(&animation->frequency)) + return FALSE; + + QueryPerformanceCounter(&animation->completion); + animation->completion.QuadPart += animation->frequency.QuadPart*durationMs/1000LL; + return TRUE; +} + +BOOL Animation_BeginStep(ANIMATIONDATA *animation) +{ + if (NULL == animation || FALSE == QueryPerformanceCounter(&animation->stepBegin)) + return FALSE; + + return TRUE; +} + +BOOL Animation_EndStep(ANIMATIONDATA *animation, size_t stepsRemaining) +{ + if (NULL == animation || FALSE == QueryPerformanceCounter(&animation->stepEnd)) + return FALSE; + + if (0 == stepsRemaining || animation->stepEnd.QuadPart >= animation->completion.QuadPart) + return TRUE; + + LARGE_INTEGER sleep; + sleep.QuadPart = (animation->completion.QuadPart - animation->stepEnd.QuadPart) - + (stepsRemaining * (animation->stepEnd.QuadPart - animation->stepBegin.QuadPart)); + + if (stepsRemaining > 1) + sleep.QuadPart /= (stepsRemaining -1); + + if (sleep.QuadPart <= 0) + return TRUE; + + sleep.QuadPart += animation->stepEnd.QuadPart; + do + { + SleepEx(0, FALSE); + QueryPerformanceCounter(&animation->stepEnd); + } while(sleep.QuadPart > animation->stepEnd.QuadPart); + + return TRUE; +} + +BOOL Animation_SetWindowPos(HWND hwnd, INT x, INT y, INT cx, INT cy, UINT flags, HDC hdc, INT contextX, INT contextY) +{ + if (NULL == hwnd || + FALSE == SetWindowPos(hwnd, NULL, x, y, cx, cy, + flags | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOCOPYBITS)) + { + return FALSE; + } + + UINT windowStyle = GetWindowStyle(hwnd); + + POINT origPoint; + SetViewportOrgEx(hdc, contextX, contextY, &origPoint); + if (0 == (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE); + + if (FALSE == LoginBox_PrintWindow(hwnd, hdc, 0)) + SendMessage(hwnd, WM_PRINT, (WPARAM)hdc, (LPARAM)(PRF_CLIENT | PRF_ERASEBKGND | PRF_CHILDREN | PRF_NONCLIENT)); + + if (0 == (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + SetViewportOrgEx(hdc, origPoint.x, origPoint.y, NULL); + return TRUE; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/animation.h b/Src/auth/Loginbox/animation.h new file mode 100644 index 00000000..7b121685 --- /dev/null +++ b/Src/auth/Loginbox/animation.h @@ -0,0 +1,23 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_ANIMATION_HEADER +#define NULLSOFT_AUTH_LOGINBOX_ANIMATION_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +typedef struct __ANIMATIONDATA +{ + LARGE_INTEGER frequency; + LARGE_INTEGER completion; + LARGE_INTEGER stepBegin; + LARGE_INTEGER stepEnd; +} ANIMATIONDATA; + +BOOL Animation_Initialize(ANIMATIONDATA *animation, UINT durationMs); +BOOL Animation_BeginStep(ANIMATIONDATA *animation); +BOOL Animation_EndStep(ANIMATIONDATA *animation, size_t stepsRemaining); +BOOL Animation_SetWindowPos(HWND hwnd, INT x, INT y, INT cx, INT cy, UINT flags, HDC hdc, INT contextX, INT contextY); + +#endif //NULLSOFT_AUTH_LOGINBOX_ANIMATION_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/browserEvent.h b/Src/auth/Loginbox/browserEvent.h new file mode 100644 index 00000000..f3398675 --- /dev/null +++ b/Src/auth/Loginbox/browserEvent.h @@ -0,0 +1,19 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_BROWSER_EVENT_HEADER +#define NULLSOFT_AUTH_LOGINBOX_BROWSER_EVENT_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class __declspec(novtable) BrowserEvent : public IUnknown +{ +public: + STDMETHOD_(void, Event_BrowserReady)(HWND hBrowser) = 0; + STDMETHOD_(void, Event_DocumentReady)(HWND hBrowser) = 0; + STDMETHOD_(void, Event_BrowserClosing)(HWND hBrowser) = 0; + STDMETHOD_(void, Event_InvokeApc)(HWND hBrowser, LPARAM param) = 0; +}; + +#endif // NULLSOFT_AUTH_LOGINBOX_BROWSER_EVENT_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/browserWindow.cpp b/Src/auth/Loginbox/browserWindow.cpp new file mode 100644 index 00000000..20b56703 --- /dev/null +++ b/Src/auth/Loginbox/browserWindow.cpp @@ -0,0 +1,198 @@ +#include "./browserWindow.h" +#include "./browserEvent.h" +#include "./common.h" + +#include "../../ombrowser/browserHost.h" + +#define NBWF_USERFLAGSMASK 0x00FFFFFF +#define NBWF_UNICODE 0x01000000 + +typedef struct __BROWSERWND +{ + WNDPROC originalProc; + UINT flags; + BrowserEvent *eventHandler; +} BROWSERWND; + +#define BROWSERWND_PROP L"NullsoftLoginboxBrowserWindow" + +#define GetBrowserWnd(__hwnd) ((BROWSERWND*)GetProp((__hwnd), BROWSERWND_PROP)) + +static UINT NBWM_QUEUEAPC = 0; + +static LRESULT CALLBACK BrowserWindow_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + +BOOL BrowserWindow_Attach(HWND hBrowser, BrowserEvent *eventHandler) +{ + if (!IsWindow(hBrowser)) + return FALSE; + + if (0 == NBWM_QUEUEAPC) + NBWM_QUEUEAPC = RegisterWindowMessage(L"NullsoftBrowserExtMessage"); + + BROWSERWND *browserWnd = (BROWSERWND*)GetProp(hBrowser, BROWSERWND_PROP); + if (NULL != browserWnd) return TRUE; + + browserWnd = (BROWSERWND*)calloc(1, sizeof(BROWSERWND)); + if (NULL == browserWnd) return FALSE; + + + ZeroMemory(browserWnd, sizeof(BROWSERWND)); + + if (IsWindowUnicode(hBrowser)) + browserWnd->flags |= NBWF_UNICODE; + + browserWnd->originalProc = (WNDPROC)(LONG_PTR)((0 != (NBWF_UNICODE & browserWnd->flags)) ? + SetWindowLongPtrW(hBrowser, GWLP_WNDPROC, (LONGX86)(LONG_PTR)BrowserWindow_WindowProc) : + SetWindowLongPtrA(hBrowser, GWLP_WNDPROC, (LONGX86)(LONG_PTR)BrowserWindow_WindowProc)); + + if (NULL == browserWnd->originalProc || !SetProp(hBrowser, BROWSERWND_PROP, browserWnd)) + { + if (NULL != browserWnd->originalProc) + { + if (0 != (NBWF_UNICODE & browserWnd->flags)) + SetWindowLongPtrW(hBrowser, GWLP_WNDPROC, (LONGX86)(LONG_PTR)browserWnd->originalProc); + else + SetWindowLongPtrA(hBrowser, GWLP_WNDPROC, (LONGX86)(LONG_PTR)browserWnd->originalProc); + } + + free(browserWnd); + return FALSE; + } + + if (NULL != eventHandler) + { + browserWnd->eventHandler = eventHandler; + eventHandler->AddRef(); + } + return TRUE; +} + +BOOL BrowserWindow_Detach(HWND hBrowser) +{ + if (NULL == hBrowser || FALSE == IsWindow(hBrowser)) + return FALSE; + + BROWSERWND *browserWnd = GetBrowserWnd(hBrowser); + RemoveProp(hBrowser, BROWSERWND_PROP); + + if (NULL == browserWnd) + return FALSE; + + if (NULL != browserWnd->originalProc) + { + if (0 != (NBWF_UNICODE & browserWnd->flags)) + SetWindowLongPtrW(hBrowser, GWLP_WNDPROC, (LONGX86)(LONG_PTR)browserWnd->originalProc); + else + SetWindowLongPtrA(hBrowser, GWLP_WNDPROC, (LONGX86)(LONG_PTR)browserWnd->originalProc); + } + + if (NULL != browserWnd->eventHandler) + browserWnd->eventHandler->Release(); + + free(browserWnd); + + return TRUE; +} + +BOOL BrowserWindow_QueueApc(HWND hBrowser, LPARAM param) +{ + if (0 == NBWM_QUEUEAPC || + NULL == hBrowser || FALSE == IsWindow(hBrowser)) + { + return FALSE; + } + return PostMessage(hBrowser, NBWM_QUEUEAPC, 0, (LPARAM)param); +} + + +static LRESULT BrowserWindow_CallOrigWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + BROWSERWND *browserWnd = GetBrowserWnd(hwnd); + + if (NULL == browserWnd || NULL == browserWnd->originalProc) + { + return (0 != (NBWF_UNICODE & browserWnd->flags)) ? + DefWindowProcW(hwnd, uMsg, wParam, lParam) : + DefWindowProcA(hwnd, uMsg, wParam, lParam); + } + + return (0 != (NBWF_UNICODE & browserWnd->flags)) ? + CallWindowProcW(browserWnd->originalProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(browserWnd->originalProc, hwnd, uMsg, wParam, lParam); +} + +static void BrowserWindow_OnDestroy(HWND hwnd) +{ + BROWSERWND *browserWnd = GetBrowserWnd(hwnd); + + WNDPROC originalProc = browserWnd->originalProc; + BOOL fUnicode = (0 != (NBWF_UNICODE & browserWnd->flags)); + + BrowserWindow_Detach(hwnd); + + if (NULL != originalProc) + { + if (FALSE != fUnicode) + CallWindowProcW(originalProc, hwnd, WM_DESTROY, 0, 0L); + else + CallWindowProcA(originalProc, hwnd, WM_DESTROY, 0, 0L); + } +} + +static void BrowserWindow_OnQueueApc(HWND hwnd, LPARAM param) +{ + BROWSERWND *browserWnd = GetBrowserWnd(hwnd); + if (NULL != browserWnd && NULL != browserWnd->eventHandler) + browserWnd->eventHandler->Event_InvokeApc(hwnd, param); +} + +static void BrowserWindow_OnBrowserNotify(HWND hwnd, NMHDR *pnmh) +{ + BROWSERWND *browserWnd = GetBrowserWnd(hwnd); + if (NULL == browserWnd || NULL == browserWnd->eventHandler) + return; + + switch(pnmh->code) + { + case NBHN_READY: + browserWnd->eventHandler->Event_BrowserReady(hwnd); + break; + case NBHN_DOCUMENTREADY: + browserWnd->eventHandler->Event_DocumentReady(hwnd); + break; + case NBHN_CLOSING: + browserWnd->eventHandler->Event_BrowserClosing(hwnd); + break; + } +} + +static LRESULT BrowserWindow_OnNotify(HWND hwnd, INT controlId, NMHDR *pnmh) +{ + LRESULT result = BrowserWindow_CallOrigWindowProc(hwnd, WM_NOTIFY, (WPARAM)controlId, (LPARAM)pnmh); + switch(controlId) + { + case 0x1000/*IDC_BROWSER*/: + BrowserWindow_OnBrowserNotify(hwnd, pnmh); + break; + } + + return result; +} + +static LRESULT CALLBACK BrowserWindow_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_DESTROY: BrowserWindow_OnDestroy(hwnd); return 0; + case WM_NOTIFY: return BrowserWindow_OnNotify(hwnd, (INT)wParam, (NMHDR*)lParam); + } + + if (NULL != NBWM_QUEUEAPC && NBWM_QUEUEAPC == uMsg) + { + BrowserWindow_OnQueueApc(hwnd, lParam); + return 0; + } + return BrowserWindow_CallOrigWindowProc(hwnd, uMsg, wParam, lParam); +} diff --git a/Src/auth/Loginbox/browserWindow.h b/Src/auth/Loginbox/browserWindow.h new file mode 100644 index 00000000..a3794239 --- /dev/null +++ b/Src/auth/Loginbox/browserWindow.h @@ -0,0 +1,16 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_BROWSER_WINDOW_HEADER +#define NULLSOFT_AUTH_LOGINBOX_BROWSER_WINDOW_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class BrowserEvent; + +BOOL BrowserWindow_Attach(HWND hBrowser, BrowserEvent *eventHandler); +BOOL BrowserWindow_Detach(HWND hBrowser); +BOOL BrowserWindow_QueueApc(HWND hBrowser, LPARAM param); + +#endif // NULLSOFT_AUTH_LOGINBOX_BROWSER_WINDOW_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/commandNodeParser.cpp b/Src/auth/Loginbox/commandNodeParser.cpp new file mode 100644 index 00000000..30ead071 --- /dev/null +++ b/Src/auth/Loginbox/commandNodeParser.cpp @@ -0,0 +1,82 @@ +#include "./commandNodeParser.h" +#include "./loginCommand.h" +#include "./loginProvider.h" + +#include "../../xml/obj_xml.h" + +LoginCommandNodeParser::LoginCommandNodeParser() + : reader(NULL), provider(NULL) +{ +} + +LoginCommandNodeParser::~LoginCommandNodeParser() +{ + End(); +} + + +HRESULT LoginCommandNodeParser::Begin(obj_xml *pReader, LoginProvider *pProvider) +{ + if (NULL != reader || NULL != provider) + return E_PENDING; + + if (NULL == pReader || NULL == pProvider) + return E_INVALIDARG; + + reader = pReader; + reader->AddRef(); + + provider = pProvider; + provider->AddRef(); + + reader->xmlreader_registerCallback(L"loginProviders\fprovider\fcommand", this); + + return S_OK; +} + +HRESULT LoginCommandNodeParser::End() +{ + if (NULL != reader) + { + reader->xmlreader_unregisterCallback(this); + reader->Release(); + reader = NULL; + } + + if (NULL != provider) + { + provider->Release(); + provider = NULL; + } + return S_OK; +} + + +void LoginCommandNodeParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementParser.Begin(reader, params); +} + +void LoginCommandNodeParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + LoginCommand *result; + if (SUCCEEDED(elementParser.End(reader, &result))) + { + if (NULL != provider) + provider->SetCommand(result); + + result->Release(); + } +} + +void LoginCommandNodeParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ +} + +#define CBCLASS LoginCommandNodeParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/Loginbox/commandNodeParser.h b/Src/auth/Loginbox/commandNodeParser.h new file mode 100644 index 00000000..bce56eef --- /dev/null +++ b/Src/auth/Loginbox/commandNodeParser.h @@ -0,0 +1,41 @@ +#ifndef NULLSOFT_AUTH_LOGIN_COMMAND_NODE_PARSER_HEADER +#define NULLSOFT_AUTH_LOGIN_COMMAND_NODE_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "./commandParser.h" + +class obj_xml; +class LoginCommand; +class LoginProvider; + +class LoginCommandNodeParser : public ifc_xmlreadercallback +{ + +public: + LoginCommandNodeParser(); + ~LoginCommandNodeParser(); + +public: + HRESULT Begin(obj_xml *reader, LoginProvider *provider); + HRESULT End(); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + obj_xml *reader; + LoginCommandParser elementParser; + LoginProvider *provider; + +protected: + RECVS_DISPATCH; +}; + +#endif //NULLSOFT_AUTH_LOGIN_COMMAND_NODE_PARSER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/commandParser.cpp b/Src/auth/Loginbox/commandParser.cpp new file mode 100644 index 00000000..06796557 --- /dev/null +++ b/Src/auth/Loginbox/commandParser.cpp @@ -0,0 +1,108 @@ +#include "./commandParser.h" +#include "./loginCommand.h" +#include "./commandWinampAuth.h" +#include "./commandWebAuth.h" + +#include "./common.h" + +#include "../../xml/obj_xml.h" + + +LoginCommandParser::LoginCommandParser() + : object(NULL) +{ +} + +LoginCommandParser::~LoginCommandParser() +{ + if (NULL != object) + object->Release(); + +} + + +HRESULT LoginCommandParser::Begin(obj_xml *reader, ifc_xmlreaderparams *params) +{ + if (NULL != object) + return E_PENDING; + + if (NULL == reader || NULL == params) + return E_INVALIDARG; + + GUID commandId; + LPCWSTR pszId = params->getItemValue(L"id"); + if (NULL == pszId || RPC_S_OK != UuidFromString((RPC_WSTR)pszId, &commandId)) + return E_INVALIDARG; + + HRESULT hr; + if (IsEqualGUID(LCUID_WINAMPAUTH, commandId)) + hr = LoginCommandWinampAuth::CreateInstance((LoginCommandWinampAuth**)&object); + else if (IsEqualGUID(LCUID_WEBAUTH, commandId)) + hr = LoginCommandWebAuth::CreateInstance((LoginCommandWebAuth**)&object); + else + hr = E_INVALIDARG; + + if (SUCCEEDED(hr)) + reader->xmlreader_registerCallback(L"loginProviders\fprovider\fcommand\f*", this); + + return hr; +} + +HRESULT LoginCommandParser::End(obj_xml *reader, LoginCommand **instance) +{ + if (NULL == object) + return E_UNEXPECTED; + + HRESULT hr; + + if (SUCCEEDED(object->IsValid())) + { + if (NULL != instance) + { + *instance = object; + object->AddRef(); + } + hr = S_OK; + } + else + hr = E_FAIL; + + object->Release(); + object = NULL; + + if (NULL != reader) + reader->xmlreader_unregisterCallback(this); + + return hr; +} + + +void LoginCommandParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementString.Clear(); +} + +void LoginCommandParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + if (NULL != object) + object->SetParameter(xmltag, elementString.Get()); +} + +void LoginCommandParser::Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value) +{ + elementString.Append(value); +} + +void LoginCommandParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ + elementString.Clear(); +} + +#define CBCLASS LoginCommandParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONCHARDATA, Event_XmlCharData) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/Loginbox/commandParser.h b/Src/auth/Loginbox/commandParser.h new file mode 100644 index 00000000..59b68baa --- /dev/null +++ b/Src/auth/Loginbox/commandParser.h @@ -0,0 +1,40 @@ +#ifndef NULLSOFT_AUTH_LOGIN_COMMAND_PARSER_HEADER +#define NULLSOFT_AUTH_LOGIN_COMMAND_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "./stringBuilder.h" + +class obj_xml; +class LoginCommand; + +class LoginCommandParser : public ifc_xmlreadercallback +{ + +public: + LoginCommandParser(); + ~LoginCommandParser(); + +public: + HRESULT Begin(obj_xml *reader, ifc_xmlreaderparams *params); + HRESULT End(obj_xml *reader, LoginCommand **instance); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + LoginCommand *object; + StringBuilder elementString; + +protected: + RECVS_DISPATCH; +}; + +#endif //NULLSOFT_AUTH_LOGIN_COMMAND_PARSER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/commandWebAuth.cpp b/Src/auth/Loginbox/commandWebAuth.cpp new file mode 100644 index 00000000..487ba748 --- /dev/null +++ b/Src/auth/Loginbox/commandWebAuth.cpp @@ -0,0 +1,159 @@ +#include "./commandWebAuth.h" +#include "./resultWebAuth.h" +#include "./common.h" + +#include "../api.h" + +#include "../../omBrowser/obj_ombrowser.h" +#include <api/service/waservicefactory.h> + +LoginCommandWebAuth::LoginCommandWebAuth() + : ref(1), targetUrl(NULL) +{ + +} + +LoginCommandWebAuth::~LoginCommandWebAuth() +{ + LoginBox_FreeString(targetUrl); +} + +HRESULT LoginCommandWebAuth::CreateInstance(LoginCommandWebAuth **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = new LoginCommandWebAuth(); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +ULONG LoginCommandWebAuth::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginCommandWebAuth::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginCommandWebAuth::GetType(GUID *commandUid) +{ + if (NULL == commandUid) return E_INVALIDARG; + *commandUid = LCUID_WEBAUTH; + return S_OK; +} + +HRESULT LoginCommandWebAuth::SetParameter(LPCWSTR pszKey, LPCWSTR pszValue) +{ + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, pszKey, -1, L"url", -1)) + { + LoginBox_FreeString(targetUrl); + targetUrl = LoginBox_CopyString(pszValue); + } + return S_OK; +} + +HRESULT LoginCommandWebAuth::IsValid() +{ + if (NULL == targetUrl || L'\0' == *targetUrl) + return S_FALSE; + + HRESULT hr = S_FALSE; + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(OBJ_OmBrowser); + if (NULL != sf) + { + obj_ombrowser *browserMngr = (obj_ombrowser*)sf->getInterface(); + if (NULL != browserMngr) + { + hr = S_OK; + browserMngr->Release(); + } + sf->Release(); + } + return hr; +} + +HRESULT LoginCommandWebAuth::IsIdentical(LoginCommand *test) +{ + if (NULL == test) + return E_INVALIDARG; + + GUID typeId; + if (FAILED(test->GetType(&typeId)) || FALSE == IsEqualGUID(LCUID_WEBAUTH, typeId)) + return S_FALSE; + + LoginCommandWebAuth *testWeb = (LoginCommandWebAuth*)test; + + if(S_OK != LoginBox_IsStrEqInvI(targetUrl, testWeb->targetUrl)) + return S_FALSE; + + return S_OK; +} + +HRESULT LoginCommandWebAuth::BeginLogin(LoginData *data, LoginResult::Callback callback, void *user, LoginResult **result) +{ + HRESULT hr; + LoginResultWebAuth *webAuth; + + hr = LoginResultWebAuth::CreateInstance(targetUrl, data, callback, user, &webAuth); + + if (SUCCEEDED(hr)) + { + if (NULL != result) + *result = webAuth; + else + webAuth->Release(); + } + else + { + if (NULL != result) + *result = NULL; + } + + return hr; +} + +HRESULT LoginCommandWebAuth::EndLogin(LoginResult *result, INT *authCode, LoginCredentials **credentials) +{ + if (NULL == result) + return E_INVALIDARG; + + HRESULT hr = result->IsCompleted(); + if (S_OK != hr) + { + HANDLE completed; + hr = result->GetWaitHandle(&completed); + if (SUCCEEDED(hr)) + { + WaitForSingleObjectEx(completed, INFINITE, TRUE); + CloseHandle(completed); + } + } + + if (SUCCEEDED(hr)) + { + LoginResultWebAuth *webAuth; + hr = result->QueryInterface(LCUID_WEBAUTH, (void**)&webAuth); + if(SUCCEEDED(hr)) + { + hr = webAuth->GetResult(authCode, credentials); + webAuth->Release(); + } + } + + return hr; +} + +HRESULT LoginCommandWebAuth::RequestAbort(LoginResult *result, BOOL drop) +{ + if (NULL == result) return E_INVALIDARG; + return result->RequestAbort(drop); +} diff --git a/Src/auth/Loginbox/commandWebAuth.h b/Src/auth/Loginbox/commandWebAuth.h new file mode 100644 index 00000000..b59e1a6b --- /dev/null +++ b/Src/auth/Loginbox/commandWebAuth.h @@ -0,0 +1,42 @@ +#ifndef NULLSOFT_AUTH_LOGINCOMMAND_WEBAUTH_HEADER +#define NULLSOFT_AUTH_LOGINCOMMAND_WEBAUTH_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginCommand.h" + +// {48F006E2-EC11-4171-833B-A9CD14F6D727} +static const GUID LCUID_WEBAUTH = +{ 0x48f006e2, 0xec11, 0x4171, { 0x83, 0x3b, 0xa9, 0xcd, 0x14, 0xf6, 0xd7, 0x27 } }; + +class LoginCommandWebAuth : public LoginCommand +{ +protected: + LoginCommandWebAuth(); + ~LoginCommandWebAuth(); + +public: + static HRESULT CreateInstance(LoginCommandWebAuth **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + HRESULT GetType(GUID *commandUid); + + HRESULT SetParameter(LPCWSTR pszKey, LPCWSTR pszValue); + HRESULT IsValid(); + HRESULT IsIdentical(LoginCommand *test); + + HRESULT BeginLogin(LoginData *data, LoginResult::Callback callback, void *user, LoginResult **result); + HRESULT EndLogin(LoginResult *result, INT *authCode, LoginCredentials **credentials); + HRESULT RequestAbort(LoginResult *result, BOOL drop); + +protected: + ULONG ref; + LPWSTR targetUrl; +}; + +#endif //NULLSOFT_AUTH_LOGINCOMMAND_WEBAUTH_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/commandWinampAuth.cpp b/Src/auth/Loginbox/commandWinampAuth.cpp new file mode 100644 index 00000000..fdcad77f --- /dev/null +++ b/Src/auth/Loginbox/commandWinampAuth.cpp @@ -0,0 +1,125 @@ +#include "./commandWinampAuth.h" +#include "./resultWinampAuth.h" + + +LoginCommandWinampAuth::LoginCommandWinampAuth() + : ref(1) +{ +} + +LoginCommandWinampAuth::~LoginCommandWinampAuth() +{ + +} + +HRESULT LoginCommandWinampAuth::CreateInstance(LoginCommandWinampAuth **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = new LoginCommandWinampAuth(); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +ULONG LoginCommandWinampAuth::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginCommandWinampAuth::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginCommandWinampAuth::GetType(GUID *commandUid) +{ + if (NULL == commandUid) return E_INVALIDARG; + *commandUid = LCUID_WINAMPAUTH; + return S_OK; +} + +HRESULT LoginCommandWinampAuth::SetParameter(LPCWSTR pszKey, LPCWSTR pszValue) +{ + return E_NOTIMPL; +} + +HRESULT LoginCommandWinampAuth::IsValid() +{ + return S_OK; +} + +HRESULT LoginCommandWinampAuth::IsIdentical(LoginCommand *test) +{ + if (NULL == test) + return E_INVALIDARG; + + GUID typeId; + if (FAILED(test->GetType(&typeId)) || FALSE == IsEqualGUID(LCUID_WINAMPAUTH, typeId)) + return S_FALSE; + + return S_OK; +} + + +HRESULT LoginCommandWinampAuth::BeginLogin(LoginData *data, LoginResult::Callback callback, void *user, LoginResult **result) +{ + LoginResultWinampAuth *winampAuth; + HRESULT hr = LoginResultWinampAuth::CreateInstance(data, callback, user, &winampAuth); + if (SUCCEEDED(hr)) + { + if (NULL != result) + *result = winampAuth; + else + winampAuth->Release(); + } + else + { + if (NULL != result) + *result = NULL; + } + + return hr; +} + +HRESULT LoginCommandWinampAuth::EndLogin(LoginResult *result, INT *authCode, LoginCredentials **credentials) +{ + if (NULL == result) + return E_INVALIDARG; + + HRESULT hr = result->IsCompleted(); + if (S_OK != hr) + { + HANDLE completed; + hr = result->GetWaitHandle(&completed); + if (SUCCEEDED(hr)) + { + WaitForSingleObjectEx(completed, INFINITE, TRUE); + CloseHandle(completed); + } + } + + if (SUCCEEDED(hr)) + { + LoginResultWinampAuth *winampAuth; + hr = result->QueryInterface(LCUID_WINAMPAUTH, (void**)&winampAuth); + if(SUCCEEDED(hr)) + { + hr = winampAuth->GetResult(authCode, credentials); + winampAuth->Release(); + } + } + + return hr; +} + +HRESULT LoginCommandWinampAuth::RequestAbort(LoginResult *result, BOOL drop) +{ + if (NULL == result) return E_INVALIDARG; + return result->RequestAbort(drop); +} diff --git a/Src/auth/Loginbox/commandWinampAuth.h b/Src/auth/Loginbox/commandWinampAuth.h new file mode 100644 index 00000000..0d06d049 --- /dev/null +++ b/Src/auth/Loginbox/commandWinampAuth.h @@ -0,0 +1,44 @@ +#ifndef NULLSOFT_AUTH_LOGINCOMMAND_WINAMPAUTH_HEADER +#define NULLSOFT_AUTH_LOGINCOMMAND_WINAMPAUTH_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginCommand.h" + +// {36B883C8-5400-4d43-89EA-96B1CBEFC605} +static const GUID LCUID_WINAMPAUTH = +{ 0x36b883c8, 0x5400, 0x4d43, { 0x89, 0xea, 0x96, 0xb1, 0xcb, 0xef, 0xc6, 0x5 } }; + + +class LoginCommandWinampAuth : public LoginCommand +{ +protected: + LoginCommandWinampAuth(); + ~LoginCommandWinampAuth(); + +public: + static HRESULT CreateInstance(LoginCommandWinampAuth **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + HRESULT GetType(GUID *commandUid); + + HRESULT SetParameter(LPCWSTR pszKey, LPCWSTR pszValue); + HRESULT IsValid(); + HRESULT IsIdentical(LoginCommand *test); + + + HRESULT BeginLogin(LoginData *data, LoginResult::Callback callback, void *user, LoginResult **result); + HRESULT EndLogin(LoginResult *result, INT *authCode, LoginCredentials **credentials); + HRESULT RequestAbort(LoginResult *result, BOOL drop); + +protected: + ULONG ref; + +}; + +#endif //NULLSOFT_AUTH_LOGINCOMMAND_WINAMPAUTH_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/common.cpp b/Src/auth/Loginbox/common.cpp new file mode 100644 index 00000000..2a92d830 --- /dev/null +++ b/Src/auth/Loginbox/common.cpp @@ -0,0 +1,618 @@ +#include "./common.h" +#include "../api.h" + +#include "../../winamp/accessibilityConfigGroup.h" + +#include <shlwapi.h> +#include <strsafe.h> + + +LPWSTR LoginBox_MallocString(size_t cchLen) +{ + return (LPWSTR)calloc(cchLen, sizeof(WCHAR)); +} + +void LoginBox_FreeString(LPWSTR pszString) +{ + if (NULL != pszString) + { + free(pszString); + } +} + +void LoginBox_FreeStringSecure(LPWSTR pszString) +{ + if (NULL != pszString) + { + size_t size = LoginBox_GetAllocSize(pszString); + if (0 != size) + SecureZeroMemory(pszString, size); + + free(pszString); + } +} + +LPWSTR LoginBox_ReAllocString(LPWSTR pszString, size_t cchLen) +{ + return (LPWSTR)realloc(pszString, sizeof(WCHAR) * cchLen); +} + +LPWSTR LoginBox_CopyString(LPCWSTR pszSource) +{ + if (NULL == pszSource) + return NULL; + + INT cchSource = lstrlenW(pszSource) + 1; + + LPWSTR copy = LoginBox_MallocString(cchSource); + if (NULL != copy) + { + CopyMemory(copy, pszSource, sizeof(WCHAR) * cchSource); + } + return copy; +} + +LPSTR LoginBox_MallocAnsiString(size_t cchLen) +{ + return (LPSTR)calloc(cchLen, sizeof(CHAR)); +} + +LPSTR LoginBox_CopyAnsiString(LPCSTR pszSource) +{ + if (NULL == pszSource) + return NULL; + + INT cchSource = lstrlenA(pszSource) + 1; + + LPSTR copy = LoginBox_MallocAnsiString(cchSource); + if (NULL != copy) + { + CopyMemory(copy, pszSource, sizeof(CHAR) * cchSource); + } + return copy; + +} +void LoginBox_FreeAnsiString(LPSTR pszString) +{ + LoginBox_FreeString((LPWSTR)pszString); +} + +void LoginBox_FreeAnsiStringSecure(LPSTR pszString) +{ + LoginBox_FreeStringSecure((LPWSTR)pszString); +} + +size_t LoginBox_GetAllocSize(void *memory) +{ + return (NULL != memory) ? _msize(memory) : 0; +} + +size_t LoginBox_GetStringMax(LPWSTR pszString) +{ + return LoginBox_GetAllocSize(pszString)/sizeof(WCHAR); +} + +size_t LoginBox_GetAnsiStringMax(LPSTR pszString) +{ + return LoginBox_GetAllocSize(pszString)/sizeof(CHAR); +} + +HRESULT LoginBox_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar, LPSTR *ppResult) +{ + if (NULL == ppResult) + return E_POINTER; + + INT resultMax = WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar); + if (0 == resultMax) + { + DWORD errorCode = GetLastError(); + *ppResult = NULL; + return HRESULT_FROM_WIN32(errorCode); + } + + if (cchWideChar > 0) + resultMax++; + + + *ppResult = LoginBox_MallocAnsiString(resultMax); + if (NULL == *ppResult) return E_OUTOFMEMORY; + resultMax = WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, *ppResult, resultMax, lpDefaultChar, lpUsedDefaultChar); + if (0 == resultMax) + { + DWORD errorCode = GetLastError(); + LoginBox_FreeAnsiString(*ppResult); + *ppResult = NULL; + return HRESULT_FROM_WIN32(errorCode); + } + + if (cchWideChar > 0) + (*ppResult)[resultMax] = '\0'; + + return S_OK; +} + +HRESULT LoginBox_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte, LPWSTR *ppResult) +{ + if (NULL == ppResult) + return E_POINTER; + + INT resultMax = MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0); + if (0 == resultMax) + { + DWORD errorCode = GetLastError(); + *ppResult = NULL; + return HRESULT_FROM_WIN32(errorCode); + } + + if (cbMultiByte > 0) + resultMax++; + + *ppResult = LoginBox_MallocString(resultMax); + if (NULL == *ppResult) return E_OUTOFMEMORY; + resultMax = MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, *ppResult, resultMax); + if (0 == resultMax) + { + DWORD errorCode = GetLastError(); + LoginBox_FreeString(*ppResult); + *ppResult = NULL; + return HRESULT_FROM_WIN32(errorCode); + } + + if (cbMultiByte > 0) + (*ppResult)[resultMax] = L'\0'; + + return S_OK; +} + +HRESULT LoginBox_GetConfigPath(LPWSTR pszConfig, BOOL fEnsureExist) +{ + if (NULL == pszConfig) + return E_INVALIDARG; + + LPCWSTR pszWinamp; + pszWinamp = (NULL != WASABI_API_APP) ? WASABI_API_APP->path_getUserSettingsPath(): NULL; + if (NULL == pszWinamp) + return E_FAIL; + + if (NULL == PathCombine(pszConfig, pszWinamp, L"Plugins\\loginBox")) + return E_FAIL; + + if (FALSE != fEnsureExist) + { + HRESULT hr; + hr = LoginBox_EnsurePathExist(pszConfig); + if (FAILED(hr)) return hr; + } + return S_OK; +} + +HRESULT LoginBox_EnsurePathExist(LPCWSTR pszDirectory) +{ + DWORD ec = ERROR_SUCCESS; + UINT errorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + + if (0 == CreateDirectory(pszDirectory, NULL)) + { + ec = GetLastError(); + if (ERROR_PATH_NOT_FOUND == ec) + { + LPCWSTR pszBlock = pszDirectory; + WCHAR szBuffer[MAX_PATH] = {0}; + + LPCTSTR pszCursor = PathFindNextComponent(pszBlock); + ec = (pszCursor == pszBlock || S_OK != StringCchCopyN(szBuffer, ARRAYSIZE(szBuffer), pszBlock, (pszCursor - pszBlock))) ? + ERROR_INVALID_NAME : ERROR_SUCCESS; + + pszBlock = pszCursor; + + while (ERROR_SUCCESS == ec && NULL != (pszCursor = PathFindNextComponent(pszBlock))) + { + if (pszCursor == pszBlock || S_OK != StringCchCatN(szBuffer, ARRAYSIZE(szBuffer), pszBlock, (pszCursor - pszBlock))) + ec = ERROR_INVALID_NAME; + + if (ERROR_SUCCESS == ec && !CreateDirectory(szBuffer, NULL)) + { + ec = GetLastError(); + if (ERROR_ALREADY_EXISTS == ec) ec = ERROR_SUCCESS; + } + pszBlock = pszCursor; + } + } + + if (ERROR_ALREADY_EXISTS == ec) + ec = ERROR_SUCCESS; + } + + SetErrorMode(errorMode); + SetLastError(ec); + return HRESULT_FROM_WIN32(ec); +} + +HRESULT LoginBox_GetWindowText(HWND hwnd, LPWSTR *ppszText, UINT *pcchText) +{ + if (NULL == ppszText) return E_POINTER; + if (NULL == hwnd) return E_INVALIDARG; + + UINT cchText = (UINT)SNDMSG(hwnd, WM_GETTEXTLENGTH, 0, 0L); + + cchText++; + *ppszText = LoginBox_MallocString(cchText); + if (NULL == *ppszText) + { + if (NULL != pcchText) *pcchText = 0; + return E_OUTOFMEMORY; + } + + cchText = (UINT)SNDMSG(hwnd, WM_GETTEXT, (WPARAM)cchText, (LPARAM)*ppszText); + if (NULL != pcchText) + *pcchText = cchText; + + return S_OK; +} + +BOOL LoginBox_PrintWindow(HWND hwnd, HDC hdc, UINT flags) +{ + typedef BOOL (WINAPI *PRINTWINDOW)(HWND /*hwnd*/, HDC /*hdc*/, UINT /*nFlags*/); + static PRINTWINDOW printWindow = NULL; + static HMODULE moduleUser32 = NULL; + if (NULL == moduleUser32) + { + moduleUser32 = GetModuleHandle(L"USER32"); + if (NULL == moduleUser32) return FALSE; + + printWindow = (PRINTWINDOW)GetProcAddress(moduleUser32, "PrintWindow"); + } + + return (NULL != printWindow && FALSE != printWindow(hwnd, hdc, flags)); + +} + + +BOOL LoginBox_MessageBeep(UINT beepType) +{ + BOOL result = FALSE; + ifc_configitem *beepEnabled = AGAVE_API_CONFIG->GetItem(accessibilityConfigGroupGUID, L"modalbeep"); + if (NULL != beepEnabled) + { + if (false != beepEnabled->GetBool()) + { + result = MessageBeep(beepType); + } + beepEnabled->Release(); + } + + return result; +} + +HRESULT LoginBox_IsStringEqualEx(LCID locale, BOOL ignoreCase, LPCWSTR str1, LPCWSTR str2) +{ + if ((NULL == str1) != (NULL == str2)) + return S_FALSE; + + if (NULL != str1 && CSTR_EQUAL != CompareString(locale, (FALSE != ignoreCase) ? NORM_IGNORECASE : 0, str1, -1, str2, -1)) + return S_FALSE; + + return S_OK; +} + +UINT LoginBox_GetCurrentTime() +{ + SYSTEMTIME st; + FILETIME ft; + + GetSystemTime(&st); + if(FALSE == SystemTimeToFileTime(&st, &ft)) + return 0; + + ULARGE_INTEGER t1; + t1.LowPart = ft.dwLowDateTime; + t1.HighPart = ft.dwHighDateTime; + + return (UINT)((t1.QuadPart - 116444736000000000) / 10000000); +} + +HRESULT LoginBox_GetCurrentLang(LPSTR *ppLang) +{ + if (NULL == ppLang) + return E_POINTER; + + if (NULL == WASABI_API_LNG) + return E_UNEXPECTED; + + LPCWSTR lang = WASABI_API_LNG->GetLanguageIdentifier(LANG_LANG_CODE); + + if (NULL != lang && L'\0' != *lang) + return LoginBox_WideCharToMultiByte(CP_UTF8, 0, lang, -1, NULL, NULL, ppLang); + + *ppLang = NULL; + return S_OK; + +} + +HDWP LoginBox_LayoutButtonBar(HDWP hdwp, HWND hwnd, const INT *buttonList, UINT buttonCount, const RECT *prcClient, LONG buttonHeight, LONG buttonSpace, BOOL fRedraw, RECT *prcResult) +{ + if (NULL == hdwp && NULL == prcClient) + return NULL; + + RECT rect; + CopyRect(&rect, prcClient); + + LONG top = rect.bottom - buttonHeight; + if (top < rect.top) top = rect.top; + LONG height = rect.bottom - top; + LONG width; + LONG right = rect.right; + + if (NULL == buttonList || 0 == buttonCount) + { + if (NULL != prcResult) + SetRect(prcResult, right, top, rect.right, top + height); + + return (NULL != hdwp) ? hdwp :(HDWP)TRUE; + } + + + UINT flags = SWP_NOACTIVATE | SWP_NOZORDER; + if (FALSE == fRedraw) flags |= SWP_NOREDRAW; + + WCHAR szText[256] = {0}; + INT cchText; + HFONT font(NULL), fontOrig; + SIZE textSize; + + RECT buttonRect; + while(buttonCount--) + { + HWND hControl = GetDlgItem(hwnd, buttonList[buttonCount]); + if (NULL == hControl || 0 == (WS_VISIBLE & GetWindowStyle(hControl)) || + FALSE == GetWindowRect(hControl, &buttonRect)) + { + continue; + } + + if (right != rect.right) + right -= buttonSpace; + + width = buttonRect.right - buttonRect.left; + + cchText = (INT)SendMessage(hControl, WM_GETTEXT, (WPARAM)ARRAYSIZE(szText), (LPARAM)szText); + if (cchText > 0) + { + HDC hdc = GetDCEx(hControl, NULL, DCX_CACHE | DCX_WINDOW | DCX_NORESETATTRS); + if (NULL != hdc) + { + if (NULL == font) + font = (HFONT)SendMessage(hControl, WM_GETFONT, 0, 0L); + + fontOrig = (HFONT)SelectObject(hdc, font); + + if (FALSE != GetTextExtentPoint32W(hdc, szText, cchText, &textSize)) + { + width = textSize.cx + 4*LoginBox_GetAveCharWidth(hdc); + } + + SelectObject(hdc, fontOrig); + ReleaseDC(hControl, hdc); + } + } + + if (width < 75) + width = 75; + + if (NULL != hdwp) + { + hdwp = DeferWindowPos(hdwp, hControl, NULL, right - width, top, width, height, flags); + if (NULL == hdwp) return NULL; + } + + right -= width; + } + + if (NULL != prcResult) + SetRect(prcResult, right, top, rect.right, top + height); + + return (NULL != hdwp) ? hdwp :(HDWP)TRUE; +} + +BYTE LoginBox_GetSysFontQuality() +{ + BOOL smoothingEnabled; + if (FALSE == SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &smoothingEnabled, 0) || + FALSE == smoothingEnabled) + { + return DEFAULT_QUALITY; + } + + OSVERSIONINFO vi; + vi.dwOSVersionInfoSize = sizeof(vi); + if (FALSE == GetVersionEx(&vi)) + return DEFAULT_QUALITY; + + if (vi.dwMajorVersion > 5 || (vi.dwMajorVersion == 5 && vi.dwMinorVersion >= 1)) + { + UINT smootingType; + if (FALSE == SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smootingType, 0)) + return DEFAULT_QUALITY; + + if (FE_FONTSMOOTHINGCLEARTYPE == smootingType) + return CLEARTYPE_QUALITY; + } + + return ANTIALIASED_QUALITY; +} + +INT LoginBox_GetAveStrWidth(HDC hdc, INT cchLen) +{ + const char szTest[] = + { + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l', 'm','n','o','p','q','r','s','t','u','v','w','x','y','z' + }; + + SIZE textSize; + if (FALSE == GetTextExtentPointA(hdc, szTest, ARRAYSIZE(szTest) -1, &textSize)) + return 0; + + INT result; + if (1 == cchLen) + { + result = (textSize.cx + ARRAYSIZE(szTest)/2)/ARRAYSIZE(szTest); + } + else + { + result = MulDiv(cchLen, textSize.cx + ARRAYSIZE(szTest)/2, ARRAYSIZE(szTest)); + if (0 != result) + { + TEXTMETRIC tm; + if (FALSE != GetTextMetrics(hdc, &tm)) + result += tm.tmOverhang; + } + } + return result; +} + +INT LoginBox_GetAveCharWidth(HDC hdc) +{ + return LoginBox_GetAveStrWidth(hdc, 1); +} + +BOOL LoginBox_GetWindowBaseUnits(HWND hwnd, INT *pBaseUnitX, INT *pBaseUnitY) +{ + INT baseunitX(0), baseunitY(0); + BOOL result = FALSE; + + if (NULL != hwnd) + { + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT font = (HFONT)SNDMSG(hwnd, WM_GETFONT, 0, 0L); + HFONT fontOrig = (HFONT)SelectObject(hdc, font); + + TEXTMETRIC tm; + if (FALSE != GetTextMetrics(hdc, &tm)) + { + baseunitY = tm.tmHeight; + baseunitX = LoginBox_GetAveCharWidth(hdc); + result = TRUE; + } + + SelectObject(hdc, fontOrig); + ReleaseDC(hwnd, hdc); + } + } + + if (NULL != pBaseUnitX) *pBaseUnitX = baseunitX; + if (NULL != pBaseUnitY) *pBaseUnitY = baseunitY; + + return result; + +} + +INT LoginBox_GetWindowTextHeight(HWND hwnd, INT paddingDlgUnit) +{ + if (NULL == hwnd) return 0; + + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL == hdc) return 0; + + INT height = 0; + + HFONT font = (HFONT)SNDMSG(hwnd, WM_GETFONT, 0, 0L); + HFONT fontOrig = (HFONT)SelectObject(hdc, font); + + TEXTMETRIC tm; + if (FALSE != GetTextMetrics(hdc, &tm)) + { + height = tm.tmHeight; + if (0 != paddingDlgUnit) + height += MulDiv(2 * paddingDlgUnit, tm.tmHeight, 8); + } + + + SelectObject(hdc, fontOrig); + ReleaseDC(hwnd, hdc); + + return height; + +} +BOOL LoginBox_GetWindowTextSize(HWND hwnd, INT idealWidth, INT *pWidth, INT *pHeight) +{ + INT width(0), height(0); + BOOL result = FALSE; + + if (NULL != hwnd) + { + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT font = (HFONT)SNDMSG(hwnd, WM_GETFONT, 0, 0L); + HFONT fontOrig = (HFONT)SelectObject(hdc, font); + + LPWSTR pszText; + UINT cchText; + if (SUCCEEDED(LoginBox_GetWindowText(hwnd, &pszText, &cchText))) + { + if (0 == cchText) + { + TEXTMETRIC tm; + if (FALSE != GetTextMetrics(hdc, &tm)) + { + height = tm.tmHeight; + width = 0; + result = TRUE; + } + } + else + { + RECT rect; + SetRect(&rect, 0, 0, idealWidth, 0); + if (0 != DrawText(hdc, pszText, cchText, &rect, DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK)) + { + width = rect.right - rect.left; + height = rect.bottom - rect.top; + result = TRUE; + } + + } + LoginBox_FreeString(pszText); + } + + SelectObject(hdc, fontOrig); + ReleaseDC(hwnd, hdc); + } + } + + if (NULL != pWidth) *pWidth = width; + if (NULL != pHeight) *pHeight = height; + + return result; +} + +BOOL LoginBox_OpenUrl(HWND hOwner, LPCWSTR pszUrl, BOOL forceExternal) +{ + if (NULL == WASABI_API_WINAMP) + return FALSE; + + HCURSOR hCursor = LoadCursor(NULL, IDC_APPSTARTING); + if (NULL != hCursor) + hCursor = SetCursor(hCursor); + + BOOL result; + + if (FALSE != forceExternal) + { + HINSTANCE hInst = ShellExecute(hOwner, L"open", pszUrl, NULL, NULL, SW_SHOWNORMAL); + result = ((INT_PTR)hInst > 32) ? TRUE: FALSE; + } + else + { + HRESULT hr = WASABI_API_WINAMP->OpenUrl(hOwner, pszUrl); + result = SUCCEEDED(hr); + } + + if (NULL != hCursor) + SetCursor(hCursor); + + return result; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/common.h b/Src/auth/Loginbox/common.h new file mode 100644 index 00000000..92b68063 --- /dev/null +++ b/Src/auth/Loginbox/common.h @@ -0,0 +1,96 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_COMMON_HEADER +#define NULLSOFT_AUTH_LOGINBOX_COMMON_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#ifndef ARRAYSIZE +#define ARRAYSIZE(blah) (sizeof(blah)/sizeof(*blah)) +#endif + +#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +#ifdef __cplusplus + #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) ::SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#else + #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#endif // __cplusplus + +#define MSGRESULT(__hwnd, __result) { SetWindowLongPtrW((__hwnd), DWLP_MSGRESULT, ((LONGX86)(LONG_PTR)(__result))); return TRUE; } + +#ifndef GetWindowStyle +#define GetWindowStyle(__hwnd) ((UINT)GetWindowLongPtr((__hwnd), GWL_STYLE)) +#endif //GetWindowStyle + +#ifndef GetWindowStyleEx +#define GetWindowStyleEx(__hwnd) ((UINT)GetWindowLongPtr((__hwnd), GWL_EXSTYLE)) +#endif //GetWindowStyleEx + + +LPWSTR LoginBox_MallocString(size_t cchLen); +void LoginBox_FreeString(LPWSTR pszString); +void LoginBox_FreeStringSecure(LPWSTR pszString); + +LPWSTR LoginBox_ReAllocString(LPWSTR pszString, size_t cchLen); +LPWSTR LoginBox_CopyString(LPCWSTR pszSource); +LPSTR LoginBox_MallocAnsiString(size_t cchLen); +LPSTR LoginBox_CopyAnsiString(LPCSTR pszSource); +void LoginBox_FreeAnsiString(LPSTR pszString); +void LoginBox_FreeAnsiStringSecure(LPSTR pszString); +size_t LoginBox_GetAllocSize(void *memory); +size_t LoginBox_GetStringMax(LPWSTR pszString); +size_t LoginBox_GetAnsiStringMax(LPSTR pszString); + +HRESULT LoginBox_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar, LPSTR *ppResult); +HRESULT LoginBox_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte, LPWSTR *ppResult); + +HRESULT LoginBox_GetWindowText(HWND hwnd, LPWSTR *ppszText, UINT *pcchText); + +HRESULT LoginBox_GetConfigPath(LPWSTR pszConfig, BOOL fEnsureExist); +HRESULT LoginBox_EnsurePathExist(LPCWSTR pszDirectory); + +BOOL LoginBox_PrintWindow(HWND hwnd, HDC hdc, UINT flags); + +BOOL LoginBox_MessageBeep(UINT beepType); + +HRESULT LoginBox_IsStringEqualEx(LCID locale, BOOL ignoreCase, LPCWSTR str1, LPCWSTR str2); + +#define LoginBox_IsStrEq(str1, str2)\ + LoginBox_IsStringEqualEx(LOCALE_USER_DEFAULT, FALSE, str1, str2) + +#define LoginBox_IsStrEqI(str1, str2)\ + LoginBox_IsStringEqualEx(LOCALE_USER_DEFAULT, TRUE, str1, str2) + +#define LoginBox_IsStrEqInv(str1, str2)\ + LoginBox_IsStringEqualEx(CSTR_INVARIANT, FALSE, str1, str2) + +#define LoginBox_IsStrEqInvI(str1, str2)\ + LoginBox_IsStringEqualEx(CSTR_INVARIANT, TRUE, str1, str2) + +UINT LoginBox_GetCurrentTime(); +HRESULT LoginBox_GetCurrentLang(LPSTR *ppLang); + +HDWP LoginBox_LayoutButtonBar(HDWP hdwp, HWND hwnd, const INT *buttonList, UINT buttonCount, const RECT *prcClient, LONG buttonHeight, LONG buttonSpace, BOOL fRedraw, RECT *prcResult); + +BYTE LoginBox_GetSysFontQuality(); +INT LoginBox_GetAveStrWidth(HDC hdc, INT cchLen); +INT LoginBox_GetAveCharWidth(HDC hdc); +BOOL LoginBox_GetWindowBaseUnits(HWND hwnd, INT *pBaseUnitX, INT *pBaseUnitY); +INT LoginBox_GetWindowTextHeight(HWND hwnd, INT paddingDlgUnit); +BOOL LoginBox_GetWindowTextSize(HWND hwnd, INT idealWidth, INT *pWidth, INT *pHeight); + +BOOL LoginBox_OpenUrl(HWND hOwner, LPCWSTR pszUrl, BOOL forceExternal); + + +#endif //NULLSOFT_AUTH_LOGINBOX_COMMON_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/dataAddress.cpp b/Src/auth/Loginbox/dataAddress.cpp new file mode 100644 index 00000000..d22db6f5 --- /dev/null +++ b/Src/auth/Loginbox/dataAddress.cpp @@ -0,0 +1,46 @@ +#include "./dataAddress.h" +#include "./common.h" + +#include <strsafe.h> + +LoginDataAddress::LoginDataAddress(const GUID *pRealm, HWND hPage, HWND hLoginbox, LPCWSTR pszAddress) + : LoginData(pRealm, hPage, hLoginbox), address(NULL) +{ + address = LoginBox_CopyString(pszAddress); +} + +LoginDataAddress::~LoginDataAddress() +{ + LoginBox_FreeString(address); +} + +HRESULT LoginDataAddress::CreateInstance(const GUID *pRealm, HWND hPage, HWND hLoginbox, LPCWSTR pszAddress, LoginDataAddress **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hPage || NULL == hLoginbox) return E_INVALIDARG; + + *instance = new LoginDataAddress(pRealm, hPage, hLoginbox, pszAddress); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +HRESULT LoginDataAddress::QueryInterface(REFIID riid, void** ppObject) +{ + if (NULL == ppObject) + return E_POINTER; + + if (IsEqualIID(riid, IID_LoginDataAddress)) + { + *ppObject = static_cast<LoginDataAddress*>(this); + if (NULL == *ppObject) return E_UNEXPECTED; + AddRef(); + return S_OK; + } + + return __super::QueryInterface(riid, ppObject); +} + +LPCWSTR LoginDataAddress::GetAddress() +{ + return address; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/dataAddress.h b/Src/auth/Loginbox/dataAddress.h new file mode 100644 index 00000000..f6adb1bc --- /dev/null +++ b/Src/auth/Loginbox/dataAddress.h @@ -0,0 +1,34 @@ +#ifndef NULLSOFT_AUTH_LOGINDATA_ADDRESS_HEADER +#define NULLSOFT_AUTH_LOGINDATA_ADDRESS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginData.h" + +// {830B9FCD-3A09-4485-8BA6-1A6B8F10ED39} +static const GUID IID_LoginDataAddress = +{ 0x830b9fcd, 0x3a09, 0x4485, { 0x8b, 0xa6, 0x1a, 0x6b, 0x8f, 0x10, 0xed, 0x39 } }; + + +class LoginDataAddress : public LoginData +{ + +protected: + LoginDataAddress(const GUID *pRealm, HWND hPage, HWND hLoginbox, LPCWSTR pszAddress); + ~LoginDataAddress(); + +public: + static HRESULT CreateInstance(const GUID *pRealm, HWND hPage, HWND hLoginbox, LPCWSTR pszAddress, LoginDataAddress **instance); + +public: + virtual HRESULT QueryInterface(REFIID riid, void** ppObject); + LPCWSTR GetAddress(); + + +protected: + LPWSTR address; +}; + +#endif //NULLSOFT_AUTH_LOGINDATA_ADDRESS_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/dataCredentials.cpp b/Src/auth/Loginbox/dataCredentials.cpp new file mode 100644 index 00000000..da2bc37f --- /dev/null +++ b/Src/auth/Loginbox/dataCredentials.cpp @@ -0,0 +1,98 @@ +#include "./dataCredentials.h" +#include "./common.h" + +#include <strsafe.h> + +LoginDataCredentials::LoginDataCredentials(const GUID *pRealm, HWND hPage, HWND hLoginbox, LPCWSTR pszUsername, LPCWSTR pszPassword) + : LoginData(pRealm, hPage, hLoginbox), username(NULL), password(NULL), context(NULL), passcode(NULL) +{ + username = LoginBox_CopyString(pszUsername); + password = LoginBox_CopyString(pszPassword); +} + +LoginDataCredentials::~LoginDataCredentials() +{ + LoginBox_FreeStringSecure(username); + LoginBox_FreeStringSecure(password); + LoginBox_FreeStringSecure(passcode); + LoginBox_FreeAnsiStringSecure(context); +} + +HRESULT LoginDataCredentials::CreateInstance(const GUID *pRealm, HWND hPage, HWND hLoginbox, LPCWSTR pszUsername, LPCWSTR pszPassword, LoginDataCredentials **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hPage || NULL == hLoginbox) return E_INVALIDARG; + *instance = new LoginDataCredentials(pRealm, hPage, hLoginbox, pszUsername, pszPassword); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +HRESULT LoginDataCredentials::QueryInterface(REFIID riid, void** ppObject) +{ + if (NULL == ppObject) + return E_POINTER; + + if (IsEqualIID(riid, IID_LoginDataCredentials)) + { + *ppObject = static_cast<LoginDataCredentials*>(this); + if (NULL == *ppObject) return E_UNEXPECTED; + AddRef(); + return S_OK; + } + + return __super::QueryInterface(riid, ppObject); +} + +LPCWSTR LoginDataCredentials::GetUsername() +{ + return username; +} + +LPCWSTR LoginDataCredentials::GetPassword() +{ + return password; +} + +HRESULT LoginDataCredentials::SetContext(LPCSTR pszContext) +{ + LoginBox_FreeAnsiStringSecure(context); + if (NULL == pszContext) + { + context = NULL; + } + else + { + context = LoginBox_CopyAnsiString(pszContext); + if (NULL == context) + return E_OUTOFMEMORY; + } + + return S_OK; +} + +LPCSTR LoginDataCredentials::GetContext() +{ + return context; +} + +HRESULT LoginDataCredentials::SetPasscode(LPCWSTR pszPasscode) +{ + LoginBox_FreeStringSecure(passcode); + if (NULL == pszPasscode) + { + passcode = NULL; + } + else + { + passcode = LoginBox_CopyString(pszPasscode); + if (NULL == passcode) + return E_OUTOFMEMORY; + } + + return S_OK; +} + +LPCWSTR LoginDataCredentials::GetPasscode() +{ + return passcode; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/dataCredentials.h b/Src/auth/Loginbox/dataCredentials.h new file mode 100644 index 00000000..a9a0bf83 --- /dev/null +++ b/Src/auth/Loginbox/dataCredentials.h @@ -0,0 +1,43 @@ +#ifndef NULLSOFT_AUTH_LOGINDATA_CREDENTIALS_HEADER +#define NULLSOFT_AUTH_LOGINDATA_CREDENTIALS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginData.h" + +// {15D82B0E-A557-4497-808D-CB68F2C9C33A} +static const GUID IID_LoginDataCredentials = +{ 0x15d82b0e, 0xa557, 0x4497, { 0x80, 0x8d, 0xcb, 0x68, 0xf2, 0xc9, 0xc3, 0x3a } }; + + +class LoginDataCredentials : public LoginData +{ + +protected: + LoginDataCredentials(const GUID *pRealm, HWND hPage, HWND hLoginbox, LPCWSTR pszUsername, LPCWSTR pszPassword); + ~LoginDataCredentials(); + +public: + static HRESULT CreateInstance(const GUID *pRealm, HWND hPage, HWND hLoginbox, LPCWSTR pszUsername, LPCWSTR pszPassword, LoginDataCredentials **instance); + +public: + virtual HRESULT QueryInterface(REFIID riid, void** ppObject); + LPCWSTR GetUsername(); + LPCWSTR GetPassword(); + + HRESULT SetContext(LPCSTR pszContext); + LPCSTR GetContext(); + + HRESULT SetPasscode(LPCWSTR pszPasscode); + LPCWSTR GetPasscode(); + +protected: + LPWSTR username; + LPWSTR password; + LPWSTR passcode; + LPSTR context; +}; + +#endif //NULLSOFT_AUTH_LOGINDATA_CREDENTIALS_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/download.cpp b/Src/auth/Loginbox/download.cpp new file mode 100644 index 00000000..ecc66042 --- /dev/null +++ b/Src/auth/Loginbox/download.cpp @@ -0,0 +1,268 @@ +#include "./common.h" +#include "./download.h" +#include "./downloadResult.h" +#include "../api.h" +#include <api/service/waservicefactory.h> + +#include "./providerLoader.h" +#include "./providerEnumerator.h" + +#include <shlwapi.h> + +LoginDownload::LoginDownload() +{ +} + +LoginDownload::~LoginDownload() +{ + +} + +HRESULT LoginDownload::Begin(LPCWSTR pszUrl, UINT type, LoginDownloadResult::Callback callback, void *data, LoginStatus *pStatus, LoginDownloadResult **result) +{ + if (NULL == result) return E_POINTER; + *result = NULL; + + if (NULL == pszUrl || L'\0' == *pszUrl) + return E_INVALIDARG; + + HRESULT hr; + + LPSTR addressAnsi; + hr = LoginBox_WideCharToMultiByte(CP_UTF8, 0, pszUrl, -1, NULL, NULL, &addressAnsi); + if (SUCCEEDED(hr)) + { + if (NULL != WASABI_API_SVC) + { + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(DownloadManagerGUID); + api_downloadManager *manager = (NULL != sf) ? (api_downloadManager *)sf->getInterface() : NULL; + if (NULL == manager) + hr = E_UNEXPECTED; + else + { + hr = LoginDownloadResult::CreateInstance(manager, type, callback, data, pStatus, result); + if (SUCCEEDED(hr)) + { + if (0 == manager->DownloadEx(addressAnsi, *result, api_downloadManager::DOWNLOADEX_TEMPFILE)) + { + (*result)->Release(); + *result = NULL; + hr = E_FAIL; + } + } + else + { + sf->releaseInterface(manager); + } + } + } + } + + LoginBox_FreeAnsiString(addressAnsi); + return hr; +} + +HRESULT LoginDownload::End(LoginDownloadResult *result, BSTR *bstrFileName) +{ + if (NULL != bstrFileName) + *bstrFileName = NULL; + + if (NULL == result) return E_INVALIDARG; + + HRESULT hr; + UINT state; + if (FAILED(result->GetState(&state)) || LoginDownloadResult::stateCompleted != state) + { + HANDLE completed; + hr = result->GetWaitHandle(&completed); + if (FAILED(hr)) return hr; + + while(WAIT_OBJECT_0 != WaitForSingleObjectEx(completed, INFINITE, TRUE)); + CloseHandle(completed); + } + + UINT type; + hr = result->GetType(&type); + if (FAILED(hr)) return hr; + + + + switch(type) + { + case LoginDownloadResult::typeProviderList: + hr = SaveProviderList(result, bstrFileName); + break; + case LoginDownloadResult::typeImage: + hr = SaveImage(result, bstrFileName); + break; + case LoginDownloadResult::typeUnknown: + hr = E_NOTIMPL; + break; + } + + return hr; +} + +HRESULT LoginDownload::SaveProviderList(LoginDownloadResult *result, BSTR *bstrFileName) +{ + if (NULL == result) + return E_INVALIDARG; + + HRESULT hr; + LPCWSTR pszSource; + hr = result->GetFile(&pszSource); + if (FAILED(hr)) return hr; + + WCHAR szTarget[2048] = {0}; + hr = LoginBox_GetConfigPath(szTarget, TRUE); + if (FAILED(hr)) return hr; + + if (FALSE == PathAppend(szTarget, L"loginProviders.xml")) + return E_FAIL; + + if (S_OK == IsBinaryEqual(pszSource, szTarget)) + hr = S_FALSE; + else + { + // validate source + LoginProviderEnumerator *enumerator; + LoginProviderLoader loader; + if (FAILED(loader.ReadXml(pszSource, &enumerator, NULL))) + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + else + { + enumerator->Release(); + + if (FALSE == CopyFile(pszSource, szTarget, FALSE)) + { + DWORD errorCode = GetLastError(); + hr = HRESULT_FROM_WIN32(errorCode); + } + else + hr = S_OK; + } + } + + if (NULL != bstrFileName) + *bstrFileName = (SUCCEEDED(hr)) ? SysAllocString(szTarget) : NULL; + + return hr; +} + +HRESULT LoginDownload::SaveImage(LoginDownloadResult *result, BSTR *bstrFileName) +{ + if (NULL == result) + return E_INVALIDARG; + + HRESULT hr; + LPCWSTR pszSource; + hr = result->GetFile(&pszSource); + if (FAILED(hr)) return hr; + + WCHAR szTarget[2048] = {0}; + hr = LoginBox_GetConfigPath(szTarget, TRUE); + if (FAILED(hr)) return hr; + + CHAR szFileAnsi[MAX_PATH] = {0}, szUrlAnsi[2096] = {0}; + hr = result->CreateDownloadFileName(szFileAnsi, ARRAYSIZE(szFileAnsi)); + if (FAILED(hr)) return hr; + + hr = result->GetUrl(szUrlAnsi, ARRAYSIZE(szUrlAnsi)); + if (FAILED(hr)) return hr; + + LPWSTR pszFile; + hr = LoginBox_MultiByteToWideChar(CP_UTF8, 0, szFileAnsi, -1, &pszFile); + if (FAILED(hr)) return hr; + + if (FALSE == PathAppend(szTarget, pszFile)) + hr = E_FAIL; + else if (FALSE == CopyFile(pszSource, szTarget, FALSE)) + { + DWORD errorCode = GetLastError(); + hr = HRESULT_FROM_WIN32(errorCode); + } + if (NULL != bstrFileName) + *bstrFileName = SysAllocString(szTarget); + + LoginBox_FreeString(pszFile); + return hr; +} + +HRESULT LoginDownload::IsBinaryEqual(LPCWSTR pszFile1, LPCWSTR pszFile2) +{ + HRESULT hr; + HANDLE hFile1, hFile2; + + hFile1 = CreateFile(pszFile1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile1) + { + DWORD errorCode = GetLastError(); + hr = HRESULT_FROM_WIN32(errorCode); + } + else hr = S_OK; + + if (SUCCEEDED(hr)) + { + hFile2 = CreateFile(pszFile2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile2) + { + DWORD errorCode = GetLastError(); + hr = HRESULT_FROM_WIN32(errorCode); + } + } + else + hFile2 = INVALID_HANDLE_VALUE; + + if (SUCCEEDED(hr)) + { + // check sizes; + LARGE_INTEGER size1, size2; + if (FALSE == GetFileSizeEx(hFile1, &size1) || FALSE == GetFileSizeEx(hFile2, &size2)) + { + DWORD errorCode = GetLastError(); + hr = HRESULT_FROM_WIN32(errorCode); + } + else + { + if (size1.QuadPart == size2.QuadPart) + { + // compare data + BYTE szBuffer1[4096] = {0}, szBuffer2[4096] = {0}; + for(;;) + { + DWORD read1 = 0, read2 = 0; + if (FALSE == ReadFile(hFile1, szBuffer1, ARRAYSIZE(szBuffer1), &read1, NULL) || + FALSE == ReadFile(hFile2, szBuffer2, ARRAYSIZE(szBuffer2), &read2, NULL)) + { + DWORD errorCode = GetLastError(); + hr = HRESULT_FROM_WIN32(errorCode); + break; + + } + + if (0 == read1 || 0 == read2) + { + hr = (read1 == read2) ? S_OK : S_FALSE; + break; + } + + if(read1 != read2 || 0 != memcmp(szBuffer1, szBuffer2, read1)) + { + hr = S_FALSE; + break; + } + } + } + else + hr = S_FALSE; + } + } + + if (INVALID_HANDLE_VALUE != hFile1) + CloseHandle(hFile1); + + if (INVALID_HANDLE_VALUE != hFile2) + CloseHandle(hFile2); + + return hr; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/download.h b/Src/auth/Loginbox/download.h new file mode 100644 index 00000000..3451976b --- /dev/null +++ b/Src/auth/Loginbox/download.h @@ -0,0 +1,30 @@ +#ifndef NULLSOFT_AUTH_LOGIN_DOWNLOAD_HEADER +#define NULLSOFT_AUTH_LOGIN_DOWNLOAD_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "./downloadResult.h" + + + +class LoginDownload +{ + +public: + LoginDownload(); + ~LoginDownload(); + +public: + HRESULT Begin(LPCWSTR pszUrl, UINT type, LoginDownloadResult::Callback callback, void *data, LoginStatus *status, LoginDownloadResult **result); + HRESULT End(LoginDownloadResult *result, BSTR *bstrFileName); // return S_FALSE if files binary indentical + +private: + HRESULT SaveProviderList(LoginDownloadResult *result, BSTR *bstrFileName); + HRESULT SaveImage(LoginDownloadResult *result, BSTR *bstrFileName); + HRESULT IsBinaryEqual(LPCWSTR pszFile1, LPCWSTR pszFile2); +}; + +#endif //NULLSOFT_AUTH_LOGIN_DOWNLOAD_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/downloadResult.cpp b/Src/auth/Loginbox/downloadResult.cpp new file mode 100644 index 00000000..33791f6c --- /dev/null +++ b/Src/auth/Loginbox/downloadResult.cpp @@ -0,0 +1,435 @@ +#include "./downloadResult.h" +#include "./loginStatus.h" +#include "../api.h" +#include "../resource.h" +#include "../jnetlib/api_httpget.h" + +#include <strsafe.h> + +LoginDownloadResult::LoginDownloadResult(api_downloadManager *pManager, UINT uType, Callback fnCallback, void *pData, LoginStatus *pStatus) + : ref(1), manager(pManager), flags(0), callback(fnCallback), data(pData), + address(NULL), result(api_downloadManager::TICK_NODATA), + cookie(0), completed(NULL), status(pStatus), statusCookie((UINT)-1) +{ + InitializeCriticalSection(&lock); + + SetType(uType); + SetState(stateInitializing); + + if (NULL != status) + status->AddRef(); + + if (NULL != manager) + manager->AddRef(); +} + +LoginDownloadResult::~LoginDownloadResult() +{ + EnterCriticalSection(&lock); + + if(NULL != manager) + { + if (0 != cookie) + { + manager->ReleaseDownload(cookie); + cookie = 0; + } + + manager->Release(); + } + + if (NULL != completed) + CloseHandle(completed); + + if (NULL != address) + free(address); + + if (NULL != status) + { + if (((UINT)-1) != statusCookie) + status->Remove(statusCookie); + + status->Release(); + } + + LeaveCriticalSection(&lock); + DeleteCriticalSection(&lock); +} + +HRESULT LoginDownloadResult::CreateInstance(api_downloadManager *pManager, UINT uType, Callback fnCallback, void *pData, LoginStatus *pStatus, LoginDownloadResult **instance) +{ + if (NULL == instance) return E_POINTER; + + if (NULL == pManager) + { + *instance = NULL; + return E_INVALIDARG; + } + *instance = new LoginDownloadResult(pManager, uType, fnCallback, pData, pStatus); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; + +} + +size_t LoginDownloadResult::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t LoginDownloadResult::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int LoginDownloadResult::QueryInterface(GUID interface_guid, void **object) +{ + if (NULL == object) return E_POINTER; + *object = NULL; + return E_NOINTERFACE; +} + +void LoginDownloadResult::SetState(UINT uState) +{ + flags = (flags & ~stateMask) | (uState & stateMask); +} + +void LoginDownloadResult::SetType(UINT uType) +{ + flags = (flags & ~typeMask) | (uType & typeMask); +} + +void LoginDownloadResult::SetFlags(UINT uFlags, UINT uMask) +{ + uMask &= flagsMask; + flags = (flags & ~uMask) | (uFlags & uMask); +} + + +HRESULT LoginDownloadResult::GetState(UINT *state) +{ + if (NULL == state) + return E_POINTER; + + EnterCriticalSection(&lock); + *state = (flags & stateMask); + LeaveCriticalSection(&lock); + + return S_OK; +} + +HRESULT LoginDownloadResult::GetType(UINT *type) +{ + if (NULL == type) + return E_POINTER; + + EnterCriticalSection(&lock); + *type = (flags & typeMask); + LeaveCriticalSection(&lock); + + return S_OK; +} + +HRESULT LoginDownloadResult::GetFile(LPCWSTR *ppszPath) +{ + EnterCriticalSection(&lock); + + HRESULT hr; + if (NULL == cookie || NULL == manager) + { + hr = E_UNEXPECTED; + } + else if (stateCompleted != (stateMask & flags)) + { + hr = E_DWNLD_BUSY; + } + else + { + switch(result) + { + case api_downloadManager::TICK_SUCCESS: hr = E_DWNLD_OK; break; + case api_downloadManager::TICK_FAILURE: hr = (0 != (flagUserAbort & flags)) ? E_DWNLD_ABORT : E_DWNLD_FAIL; break; + case api_downloadManager::TICK_TIMEOUT: hr = E_DWNLD_TIMEOUT; break; + case api_downloadManager::TICK_CANT_CONNECT: hr = E_DWNLD_CANT_CONNECT; break; + case api_downloadManager::TICK_WRITE_ERROR: hr = E_DWNLD_WRITE_ERROR; break; + default: hr = E_DWNLD_BUSY; break; + } + } + + if (NULL != ppszPath) + { + if (SUCCEEDED(hr)) + *ppszPath = manager->GetLocation(cookie); + else + *ppszPath = NULL; + } + + LeaveCriticalSection(&lock); + + return hr; +} + +HRESULT LoginDownloadResult::GetWaitHandle(HANDLE *handle) +{ + if (NULL == handle) + return E_POINTER; + + HRESULT hr = S_OK; + + EnterCriticalSection(&lock); + + if (NULL == completed) + { + completed = CreateEvent(NULL, TRUE, FALSE, NULL); + if (NULL == completed) + { + *handle = NULL; + DWORD error = GetLastError(); + hr = HRESULT_FROM_WIN32(error); + } + } + + if (SUCCEEDED(hr) && FALSE == DuplicateHandle(GetCurrentProcess(), completed, + GetCurrentProcess(), handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + *handle = NULL; + DWORD error = GetLastError(); + hr = HRESULT_FROM_WIN32(error); + } + + LeaveCriticalSection(&lock); + return hr; +} + +HRESULT LoginDownloadResult::GetData(void **data) +{ + if (NULL == data) + return E_POINTER; + + EnterCriticalSection(&lock); + *data = this->data; + LeaveCriticalSection(&lock); + + return S_OK; +} + +HRESULT LoginDownloadResult::RequestAbort(BOOL fDrop) +{ + EnterCriticalSection(&lock); + if (FALSE != fDrop) + { + data = NULL; + callback = NULL; + } + + if (0 != cookie && NULL != manager && 0 == (flagUserAbort & flags)) + { + manager->CancelDownload(cookie); + SetState(stateAborting); + SetFlags(flagUserAbort, flagUserAbort); + } + + LeaveCriticalSection(&lock); + return S_OK; + +} + +HRESULT LoginDownloadResult::GetUrl(LPSTR pszBuffer, UINT cchBufferMax) +{ + if (NULL == pszBuffer) + return E_POINTER; + + HRESULT hr; + EnterCriticalSection(&lock); + + if(NULL == manager || 0 == cookie) + hr = E_UNEXPECTED; + else + { + api_httpreceiver *receiver = manager->GetReceiver(cookie); + if (NULL == receiver) hr = E_FAIL; + else + { + LPCSTR url = receiver->get_url(); + hr = StringCchCopyExA(pszBuffer, cchBufferMax, url, NULL, NULL, STRSAFE_IGNORE_NULLS); + } + } + + LeaveCriticalSection(&lock); + return hr; +} + +void LoginDownloadResult::SetStatus() +{ + EnterCriticalSection(&lock); + + if (NULL != status && ((UINT)-1 == statusCookie)) + { + LPCWSTR pszStatus; + switch(typeMask & flags) + { + case typeProviderList: pszStatus = MAKEINTRESOURCE(IDS_STATUS_UPDATEBEGIN); break; + default: pszStatus = NULL; break; + } + + if (NULL != pszStatus) + { + BSTR bstrText; + if (FALSE == IS_INTRESOURCE(pszStatus)) + bstrText = SysAllocString(pszStatus); + else + { + WCHAR szBuffer[256] = {0}; + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszStatus, szBuffer, ARRAYSIZE(szBuffer)); + bstrText = SysAllocString(szBuffer); + } + + statusCookie = status->Add(bstrText); + if (((UINT)-1) == statusCookie) + SysFreeString(bstrText); + } + } + LeaveCriticalSection(&lock); +} + + +void LoginDownloadResult::RemoveStatus() +{ + EnterCriticalSection(&lock); + if (NULL != status) + { + if (((UINT)-1) != statusCookie) + { + status->Remove(statusCookie); + statusCookie = (UINT)-1; + } + } + LeaveCriticalSection(&lock); +} + +HRESULT LoginDownloadResult::CreateDownloadFileName(LPSTR pszBuffer, UINT cchBufferMax) +{ + if (NULL == pszBuffer) + return E_POINTER; + + HRESULT hr; + EnterCriticalSection(&lock); + + if(NULL == manager || 0 == cookie) + hr = E_UNEXPECTED; + else + { + api_httpreceiver *receiver = manager->GetReceiver(cookie); + if (NULL == receiver) hr = E_FAIL; + else + { + LPCSTR url = receiver->get_url(); + LPCSTR contentDisposition = receiver->getheader("content-disposition"); + LPCSTR contentType = receiver->getheader("content-type"); + hr = S_OK; + } + } + + LeaveCriticalSection(&lock); + return hr; +} + +void LoginDownloadResult::DownloadCompleted(int errorCode) +{ + EnterCriticalSection(&lock); + + result = errorCode; + SetState(stateCompleted); + HANDLE hEvent = completed; + Callback cb = callback; + + LeaveCriticalSection(&lock); + + if (NULL != hEvent) + SetEvent(hEvent); + + if (NULL != cb) + cb(this, data); + + RemoveStatus(); + Release(); +} + +void LoginDownloadResult::Event_DownloadInit(DownloadToken token) +{ + EnterCriticalSection(&lock); + + AddRef(); + + cookie = token; + SetState(stateConnecting); + + if (NULL != manager) + { + manager->RetainDownload(cookie); + + if (typeProviderList == (typeMask & flags)) + { + api_httpreceiver *receiver = manager->GetReceiver(token); + if (NULL != receiver) + receiver->AllowCompression(); + } + } + + LeaveCriticalSection(&lock); + + SetStatus(); + +} + +void LoginDownloadResult::Event_DownloadConnect(DownloadToken token) +{ + EnterCriticalSection(&lock); + SetState(stateReceiving); + LeaveCriticalSection(&lock); +} + +void LoginDownloadResult::Event_DownloadTick(DownloadToken token) +{ + EnterCriticalSection(&lock); + if (stateConnecting == (stateMask & flags)) + SetState(stateReceiving); + LeaveCriticalSection(&lock); +} + +void LoginDownloadResult::Event_DownloadFinish(DownloadToken token) +{ + DownloadCompleted(api_downloadManager::TICK_SUCCESS); +} + +void LoginDownloadResult::Event_DownloadError(DownloadToken token, int errorCode) +{ + DownloadCompleted(errorCode); +} + +void LoginDownloadResult::Event_DownloadCancel(DownloadToken token) +{ + DownloadCompleted(api_downloadManager::TICK_NODATA); +} + + + +#define CBCLASS LoginDownloadResult +START_DISPATCH; +CB(ADDREF, AddRef) +CB(RELEASE, Release) +CB(QUERYINTERFACE, QueryInterface) +VCB(IFC_DOWNLOADMANAGERCALLBACK_ONINIT, Event_DownloadInit) +VCB(IFC_DOWNLOADMANAGERCALLBACK_ONCONNECT, Event_DownloadConnect) +VCB(IFC_DOWNLOADMANAGERCALLBACK_ONFINISH, Event_DownloadFinish) +VCB(IFC_DOWNLOADMANAGERCALLBACK_ONERROR, Event_DownloadError) +VCB(IFC_DOWNLOADMANAGERCALLBACK_ONCANCEL, Event_DownloadCancel) +VCB(IFC_DOWNLOADMANAGERCALLBACK_ONTICK, Event_DownloadTick) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/Loginbox/downloadResult.h b/Src/auth/Loginbox/downloadResult.h new file mode 100644 index 00000000..aa4e4c5e --- /dev/null +++ b/Src/auth/Loginbox/downloadResult.h @@ -0,0 +1,110 @@ +#ifndef NULLSOFT_AUTH_LOGIN_DOWNLOAD_RESULT_HEADER +#define NULLSOFT_AUTH_LOGIN_DOWNLOAD_RESULT_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../dlmgr/api_downloadmanager.h" + +#define E_DWNLD_OK S_OK +#define E_DWNLD_BUSY E_PENDING +#define E_DWNLD_FAIL E_FAIL +#define E_DWNLD_ABORT E_ABORT +#define E_DWNLD_TIMEOUT HRESULT_FROM_WIN32(ERROR_TIMEOUT) +#define E_DWNLD_CANT_CONNECT HRESULT_FROM_WIN32(ERROR_NOT_CONNECTED) +#define E_DWNLD_WRITE_ERROR HRESULT_FROM_WIN32(ERROR_WRITE_FAULT) + +class LoginStatus; + +class LoginDownloadResult : public ifc_downloadManagerCallback +{ +public: + typedef void (CALLBACK *Callback)(LoginDownloadResult *result, void *data); + + typedef enum + { + stateMask = 0x00000FF00, + stateReady = 0x00000000, + stateInitializing = 0x00000100, + stateConnecting = 0x00000200, + stateReceiving = 0x00000300, + stateCompleted = 0x00000400, + stateAborting = 0x00000500, + } States; + + typedef enum + { + typeMask = 0x0000000FF, + typeUnknown = 0x00000000, + typeImage = 0x00000001, + typeProviderList = 0x00000002, + } Types; + + typedef enum + { + flagsMask = 0xFFFF0000, + flagUserAbort = 0x00010000, + } Flags; + +protected: + LoginDownloadResult(api_downloadManager *pManager, UINT uType, Callback fnCallback, void *pData, LoginStatus *pStatus); + ~LoginDownloadResult(); + +public: + static HRESULT CreateInstance(api_downloadManager *pManager, UINT uType, Callback fnCallback, void *pData, LoginStatus *pStatus, LoginDownloadResult **instance); + +public: + /* Dispatchable */ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + HRESULT GetWaitHandle(HANDLE *handle); + HRESULT GetData(void **data); + HRESULT GetType(UINT *type); + HRESULT GetState(UINT *state); + HRESULT GetFile(LPCWSTR *ppszPath); + + HRESULT CreateDownloadFileName(LPSTR pszBuffer, UINT cchBufferMax); + HRESULT GetUrl(LPSTR pszBuffer, UINT cchBufferMax); + HRESULT RequestAbort(BOOL fDrop); + +protected: + /* ifc_downloadManagerCallback */ + void Event_DownloadFinish(DownloadToken token); + void Event_DownloadTick(DownloadToken token); + void Event_DownloadError(DownloadToken token, int errorCode); + void Event_DownloadCancel(DownloadToken token); + void Event_DownloadConnect(DownloadToken token); + void Event_DownloadInit(DownloadToken token); + + void DownloadCompleted(int errorCode); + void SetState(UINT uState); + void SetType(UINT uType); + void SetFlags(UINT uFlags, UINT uMask); + void SetStatus(); + void RemoveStatus(); + + + +protected: + size_t ref; + UINT flags; + LPWSTR address; + INT result; + api_downloadManager *manager; + DownloadToken cookie; + HANDLE completed; + Callback callback; + void *data; + LoginStatus *status; + UINT statusCookie; + CRITICAL_SECTION lock; + +protected: + RECVS_DISPATCH; +}; + +#endif //NULLSOFT_AUTH_LOGINDOWNLOADER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/editboxExtender.cpp b/Src/auth/Loginbox/editboxExtender.cpp new file mode 100644 index 00000000..0588c22a --- /dev/null +++ b/Src/auth/Loginbox/editboxExtender.cpp @@ -0,0 +1,269 @@ +#include "./editboxExtender.h" +#include "./common.h" + +#include <commctrl.h> + +#define NAEF_USERFLAGSMASK 0x00FFFFFF +#define NAEF_UNICODE 0x01000000 + +typedef struct __EDITBOXEXTENDER +{ + WNDPROC originalProc; + UINT flags; + DWORD dblclkTime; + LPWSTR rollbackText; +} EDITBOXEXTENDER; + +#define EDITBOXEXTENDER_PROP L"NullsoftEditboxExtender" + +#define GetEditbox(__hwnd) ((EDITBOXEXTENDER*)GetProp((__hwnd), EDITBOXEXTENDER_PROP)) + +static LRESULT CALLBACK EditboxExtender_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +BOOL EditboxExtender_AttachWindow(HWND hEditbox) +{ + if (!IsWindow(hEditbox)) + return FALSE; + + EDITBOXEXTENDER *editbox = (EDITBOXEXTENDER*)GetProp(hEditbox, EDITBOXEXTENDER_PROP); + if (NULL != editbox) return TRUE; + + editbox = (EDITBOXEXTENDER*)calloc(1, sizeof(EDITBOXEXTENDER)); + if (NULL == editbox) return FALSE; + + if (IsWindowUnicode(hEditbox)) + editbox->flags |= NAEF_UNICODE; + + editbox->originalProc = (WNDPROC)(LONG_PTR)((0 != (NAEF_UNICODE & editbox->flags)) ? + SetWindowLongPtrW(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)EditboxExtender_WindowProc) : + SetWindowLongPtrA(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)EditboxExtender_WindowProc)); + + if (NULL == editbox->originalProc || !SetProp(hEditbox, EDITBOXEXTENDER_PROP, editbox)) + { + if (NULL != editbox->originalProc) + { + if (0 != (NAEF_UNICODE & editbox->flags)) + SetWindowLongPtrW(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc); + else + SetWindowLongPtrA(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc); + } + + free(editbox); + return FALSE; + } + return TRUE; +} + + +static void EditboxExtender_Detach(HWND hwnd) +{ + EDITBOXEXTENDER *editbox = GetEditbox(hwnd); + RemoveProp(hwnd, EDITBOXEXTENDER_PROP); + + if (NULL == editbox) + return; + + if (NULL != editbox->originalProc) + { + if (0 != (NAEF_UNICODE & editbox->flags)) + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc); + else + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc); + } + free(editbox); +} + + +static LRESULT EditboxExtender_CallOrigWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + EDITBOXEXTENDER *editbox = GetEditbox(hwnd); + + if (NULL == editbox || NULL == editbox->originalProc) + { + return (0 != (NAEF_UNICODE & editbox->flags)) ? + DefWindowProcW(hwnd, uMsg, wParam, lParam) : + DefWindowProcA(hwnd, uMsg, wParam, lParam); + } + + return (0 != (NAEF_UNICODE & editbox->flags)) ? + CallWindowProcW(editbox->originalProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(editbox->originalProc, hwnd, uMsg, wParam, lParam); +} + +static void EditboxExtender_OnDestroy(HWND hwnd) +{ + EDITBOXEXTENDER *editbox = GetEditbox(hwnd); + + WNDPROC originalProc = editbox->originalProc; + BOOL fUnicode = (0 != (NAEF_UNICODE & editbox->flags)); + + EditboxExtender_Detach(hwnd); + + if (NULL != originalProc) + { + if (FALSE != fUnicode) + CallWindowProcW(originalProc, hwnd, WM_DESTROY, 0, 0L); + else + CallWindowProcA(originalProc, hwnd, WM_DESTROY, 0, 0L); + } +} + +static LRESULT EditboxExtender_OnGetDlgCode(HWND hwnd, INT vKey, MSG* pMsg) +{ + LRESULT result = EditboxExtender_CallOrigWindowProc(hwnd, WM_GETDLGCODE, (WPARAM)vKey, (LPARAM)pMsg); + return result; +} + +static void EditboxExtender_OnLButtonDown(HWND hwnd, UINT vKey, POINTS pts) +{ + EDITBOXEXTENDER *editbox = GetEditbox(hwnd); + if (NULL != editbox) + { + DWORD clickTime = GetTickCount(); + if (clickTime >= editbox->dblclkTime && clickTime <= (editbox->dblclkTime + GetDoubleClickTime())) + { + SendMessage(hwnd, EM_SETSEL, 0, -1); + return; + } + } + EditboxExtender_CallOrigWindowProc(hwnd, WM_LBUTTONDOWN, (WPARAM)vKey, *((LPARAM*)&pts)); +} + +static void EditboxExtender_OnLButtonDblClk(HWND hwnd, UINT vKey, POINTS pts) +{ + EDITBOXEXTENDER *editbox = GetEditbox(hwnd); + if (NULL == editbox) return; + + DWORD clickTime = GetTickCount(); + if (clickTime >= editbox->dblclkTime && clickTime <= (editbox->dblclkTime + 2*GetDoubleClickTime())) + { + INT r = (INT)SendMessage(hwnd, EM_CHARFROMPOS, 0, *(LPARAM*)&pts); + r = LOWORD(r); + SendMessage(hwnd, EM_SETSEL, (WPARAM)r, (LPARAM)r); + editbox->dblclkTime = 0; + } + else + { + editbox->dblclkTime = clickTime; + } + + INT f, l; + SendMessage(hwnd, EM_GETSEL, (WPARAM)&f, (LPARAM)&l); + if (f != l) return; + + + EditboxExtender_CallOrigWindowProc(hwnd, WM_LBUTTONDBLCLK, (WPARAM)vKey, *((LPARAM*)&pts)); + +} + +static void EditboxExtender_OnChar(HWND hwnd, UINT vKey, UINT state) +{ + if (0 == (0x8000 & GetAsyncKeyState(VK_CONTROL)) && 0 == (0x8000 & GetAsyncKeyState(VK_MENU))) + { + HWND hParent = GetAncestor(hwnd, GA_PARENT); + if (NULL != hParent) + { + NMCHAR nmh; + nmh.hdr.hwndFrom = hwnd; + nmh.hdr.idFrom = (INT)(INT_PTR)GetWindowLongPtr(hwnd, GWLP_ID); + nmh.hdr.code = NM_CHAR; + nmh.ch = vKey; + nmh.dwItemNext = 0; + nmh.dwItemPrev = 0; + if (FALSE != (BOOL)SendMessage(hParent, WM_NOTIFY, nmh.hdr.idFrom, (LPARAM)&nmh)) + return; + } + } + EditboxExtender_CallOrigWindowProc(hwnd, WM_CHAR, (WPARAM)vKey, (LPARAM)state); +} + +static void EditboxExtender_OnKeyDown(HWND hwnd, UINT vKey, UINT state) +{ + HWND hParent = GetAncestor(hwnd, GA_PARENT); + if (NULL != hParent) + { + NMKEY nmh; + nmh.hdr.hwndFrom = hwnd; + nmh.hdr.idFrom = (INT)(INT_PTR)GetWindowLongPtr(hwnd, GWLP_ID); + nmh.hdr.code = NM_KEYDOWN; + nmh.nVKey = vKey; + nmh.uFlags = HIWORD(state); + if (FALSE != (BOOL)SendMessage(hParent, WM_NOTIFY, nmh.hdr.idFrom, (LPARAM)&nmh)) + return; + } + + EditboxExtender_CallOrigWindowProc(hwnd, WM_KEYDOWN, (WPARAM)vKey, (LPARAM)state); +} + +static void EditboxExtender_PasteText(HWND hwnd, LPCWSTR pszClipboard) +{ + HWND hParent = GetAncestor(hwnd, GA_PARENT); + if (NULL != hParent) + { + EENMPASTE nmh; + nmh.hdr.hwndFrom = hwnd; + nmh.hdr.idFrom = (INT)(INT_PTR)GetWindowLongPtr(hwnd, GWLP_ID); + nmh.hdr.code = EENM_PASTE; + nmh.text = pszClipboard; + if (FALSE != (BOOL)SendMessage(hParent, WM_NOTIFY, nmh.hdr.idFrom, (LPARAM)&nmh)) + return; + } + + SendMessage(hwnd, EM_REPLACESEL, (WPARAM)TRUE, (LPARAM)pszClipboard); +} + +static void EditboxExtender_OnPaste(HWND hwnd) +{ + IDataObject *pObject; + HRESULT hr = OleGetClipboard(&pObject); + if (SUCCEEDED(hr)) + { + FORMATETC fmt = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + STGMEDIUM stgm; + hr = pObject->GetData(&fmt, &stgm); + if(S_OK == hr) + { + LPCWSTR pClipboard = (LPCWSTR)GlobalLock(stgm.hGlobal); + EditboxExtender_PasteText(hwnd, pClipboard); + + GlobalUnlock(stgm.hGlobal); + ReleaseStgMedium(&stgm); + + } + else + { + fmt.cfFormat = CF_TEXT; + hr = pObject->GetData(&fmt, &stgm); + if(S_OK == hr) + { + LPCSTR pClipboardAnsi = (LPCSTR)GlobalLock(stgm.hGlobal); + LPWSTR pClipboard; + if (FAILED(LoginBox_MultiByteToWideChar(CP_ACP, 0, pClipboardAnsi, -1, &pClipboard))) + pClipboard = NULL; + + EditboxExtender_PasteText(hwnd, pClipboard); + + LoginBox_FreeString(pClipboard); + GlobalUnlock(stgm.hGlobal); + ReleaseStgMedium(&stgm); + } + } + pObject->Release(); + } +} + +static LRESULT CALLBACK EditboxExtender_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_DESTROY: EditboxExtender_OnDestroy(hwnd); return 0; + case WM_GETDLGCODE: return EditboxExtender_OnGetDlgCode(hwnd, (INT)wParam, (MSG*)lParam); + case WM_LBUTTONDOWN: EditboxExtender_OnLButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_LBUTTONDBLCLK: EditboxExtender_OnLButtonDblClk(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_CHAR: EditboxExtender_OnChar(hwnd, (UINT)wParam, (UINT)lParam); return 0; + case WM_KEYDOWN: EditboxExtender_OnKeyDown(hwnd, (UINT)wParam, (UINT)lParam); return 0; + case WM_PASTE: EditboxExtender_OnPaste(hwnd); return 1; + } + + return EditboxExtender_CallOrigWindowProc(hwnd, uMsg, wParam, lParam); +} diff --git a/Src/auth/Loginbox/editboxExtender.h b/Src/auth/Loginbox/editboxExtender.h new file mode 100644 index 00000000..c51574b1 --- /dev/null +++ b/Src/auth/Loginbox/editboxExtender.h @@ -0,0 +1,22 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_EDITBOX_EXTENDER_HEADER +#define NULLSOFT_AUTH_LOGINBOX_EDITBOX_EXTENDER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +BOOL EditboxExtender_AttachWindow(HWND hEditbox); + +// notification +typedef struct __EENMPASTE +{ + NMHDR hdr; + LPCWSTR text; +}EENMPASTE; + +#define EENM_FIRST 10 +#define EENM_PASTE (EENM_FIRST + 0L) + +#endif // NULLSOFT_AUTH_LOGINBOX_EDITBOX_EXTENDER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/externalMngr.cpp b/Src/auth/Loginbox/externalMngr.cpp new file mode 100644 index 00000000..10a67d30 --- /dev/null +++ b/Src/auth/Loginbox/externalMngr.cpp @@ -0,0 +1,401 @@ +#include "./externalMngr.h" +#include "./common.h" + +#include "../../winamp/jsapi.h" + +ExternalManager::ExternalManager() + : ref(1), lastDispId(0) +{ + InitializeCriticalSection(&lock); +} + +ExternalManager::~ExternalManager() +{ + EnterCriticalSection(&lock); + + size_t index= list.size(); + while(index--) + { + if (NULL != list[index].object) + list[index].object->Release(); + } + + LeaveCriticalSection(&lock); + + DeleteCriticalSection(&lock); +} + +HRESULT ExternalManager::CreateInstance(ExternalManager **instance) +{ + if (NULL == instance) return E_POINTER; + + *instance = new ExternalManager(); + if (NULL == instance) return E_OUTOFMEMORY; + + return S_OK; +} + +STDMETHODIMP_(ULONG) ExternalManager::AddRef(void) +{ + return InterlockedIncrement((LONG*)&ref); +} + +STDMETHODIMP_(ULONG) ExternalManager::Release(void) +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +STDMETHODIMP ExternalManager::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (NULL == ppvObject) + return E_POINTER; + + if (IsEqualIID(riid, IID_IDispatchEx)) + *ppvObject = static_cast<IDispatchEx*>(this); + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = static_cast<IDispatch*>(this); + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = static_cast<IUnknown*>(this); + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + if (NULL == *ppvObject) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +STDMETHODIMP ExternalManager::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + EnterCriticalSection(&lock); + UINT unknowns = 0; + size_t count = list.size(); + for (UINT i = 0; i != cNames ; i++) + { + rgdispid[i] = DISPID_UNKNOWN; + for (size_t j =0; j < count; j++) + { + if (CSTR_EQUAL == CompareString(lcid, 0, rgszNames[i], -1, list[j].name, -1)) + { + if (NULL != list[j].object) + rgdispid[i] = list[j].id; + else + unknowns++; + break; + } + if (DISPID_UNKNOWN == rgdispid[i]) + unknowns++; + } + } + + LeaveCriticalSection(&lock); + + return (0 != unknowns) ? DISP_E_UNKNOWNNAME : S_OK; +} + +STDMETHODIMP ExternalManager::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +STDMETHODIMP ExternalManager::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + +STDMETHODIMP ExternalManager::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + return InvokeEx(dispid, lcid, wFlags, pdispparams, pvarResult, pexecinfo, 0); +} + +STDMETHODIMP ExternalManager::GetDispID(BSTR bstrName, DWORD grfdex, DISPID *pid) +{ + if (NULL == pid) return E_POINTER; + *pid = DISPID_UNKNOWN; + + HRESULT hr = DISP_E_UNKNOWNNAME; + + if (NULL != bstrName && L'\0' != *bstrName) + { + UINT compareFlags = 0; + if (0 != (fdexNameCaseInsensitive & grfdex)) + compareFlags |= NORM_IGNORECASE; + + EnterCriticalSection(&lock); + + size_t index = list.size(); + while(index--) + { + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, compareFlags, bstrName, -1, list[index].name, -1)) + { + if (NULL != list[index].object) + { + *pid = list[index].id; + hr = S_OK; + } + break; + } + } + + LeaveCriticalSection(&lock); + } + + return hr; +} + +STDMETHODIMP ExternalManager::InvokeEx(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + if (DISPATCH_METHOD == wFlags || DISPATCH_PROPERTYGET == wFlags || DISPATCH_CONSTRUCT == wFlags) + JSAPI_INIT_RESULT(pvarRes, VT_DISPATCH); + + HRESULT hr(DISP_E_MEMBERNOTFOUND); + + EnterCriticalSection(&lock); + + size_t index = list.size(); + while(index--) + { + if (id == list[index].id) + { + if (NULL != list[index].object) + { + if (NULL != pvarRes && + (DISPATCH_METHOD == wFlags || DISPATCH_PROPERTYGET == wFlags || DISPATCH_CONSTRUCT == wFlags)) + { + list[index].object->AddRef(); + pvarRes->pdispVal = list[index].object; + } + hr = S_OK; + } + break; + } + } + + LeaveCriticalSection(&lock); + return hr; +} + +STDMETHODIMP ExternalManager::DeleteMemberByName(BSTR bstrName, DWORD grfdex) +{ + HRESULT hr = S_FALSE; + + if (NULL != bstrName && L'\0' != *bstrName) + { + UINT compareFlags = 0; + if (0 != (fdexNameCaseInsensitive & grfdex)) + compareFlags |= NORM_IGNORECASE; + + EnterCriticalSection(&lock); + + size_t index = list.size(); + while(index--) + { + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, compareFlags, bstrName, -1, list[index].name, -1)) + { + if (NULL != list[index].object) + { + list[index].object->Release(); + list[index].object = NULL; + hr = S_OK; + } + break; + } + } + + LeaveCriticalSection(&lock); + } + + return hr; +} + +STDMETHODIMP ExternalManager::DeleteMemberByDispID(DISPID id) +{ + HRESULT hr = S_FALSE; + + EnterCriticalSection(&lock); + + size_t index = list.size(); + while(index--) + { + if (id == list[index].id) + { + if (NULL != list[index].object) + { + list[index].object->Release(); + list[index].object = NULL; + hr = S_OK; + } + break; + } + } + + LeaveCriticalSection(&lock); + return hr; +} + +STDMETHODIMP ExternalManager::GetMemberProperties(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex) +{ + HRESULT hr(DISP_E_UNKNOWNNAME); + + EnterCriticalSection(&lock); + + size_t index = list.size(); + while(index--) + { + if (id == list[index].id) + { + if (NULL != list[index].object) + { + if (NULL != pgrfdex) + { + *pgrfdex = 0; + if (0 != (grfdexPropCanAll & grfdexFetch)) + *pgrfdex |= (fdexPropCanGet | fdexPropCanCall); + if (0 != (grfdexPropCannotAll & grfdexFetch)) + *pgrfdex |= (fdexPropCannotPut | fdexPropCanPutRef | fdexPropCannotConstruct | fdexPropCannotSourceEvents); + if (0 != (grfdexPropExtraAll & grfdexFetch)) + *pgrfdex |= (fdexPropNoSideEffects | fdexPropNoSideEffects); + } + hr = S_OK; + } + break; + } + } + + LeaveCriticalSection(&lock); + return hr; +} + +STDMETHODIMP ExternalManager::GetMemberName(DISPID id, BSTR *pbstrName) +{ + HRESULT hr(DISP_E_UNKNOWNNAME); + + EnterCriticalSection(&lock); + + size_t index = list.size(); + while(index--) + { + if (id == list[index].id) + { + if (NULL != list[index].object) + { + if (NULL != pbstrName) + { + *pbstrName = SysAllocString(list[index].name); + hr = S_OK; + } + } + break; + } + } + + LeaveCriticalSection(&lock); + return hr; +} + +STDMETHODIMP ExternalManager::GetNextDispID(DWORD grfdex, DISPID id, DISPID *pid) +{ + HRESULT hr(S_FALSE); + if (NULL == pid) return S_FALSE; + *pid = DISPID_UNKNOWN; + + EnterCriticalSection(&lock); + + size_t count = list.size(); + if (DISPID_STARTENUM == id) + { + if (count > 0) + { + *pid = list[0].id; + hr = S_OK; + } + } + else + { + for(size_t i = 0; i < count; i++) + { + if (id == list[i].id) + { + i++; + if (i < count) + { + *pid = list[i].id; + hr = S_OK; + } + break; + } + } + } + + LeaveCriticalSection(&lock); + return hr; +} + +STDMETHODIMP ExternalManager::GetNameSpaceParent(IUnknown **ppunk) +{ + return E_NOTIMPL; +} + +HRESULT ExternalManager::AddDispatch(LPCWSTR pszName, IDispatch *pDispatch, DISPID *pid) +{ + if (NULL != pid) + *pid = DISPID_UNKNOWN; + + if (NULL == pszName || L'\0' == pszName || NULL == pDispatch) + return E_INVALIDARG; + + HRESULT hr; + + EnterCriticalSection(&lock); + + size_t index = list.size(); + while(index--) + { + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, pszName, -1, list[index].name, -1)) + { + if (NULL != list[index].object) + { + hr = E_FAIL; + } + else + { + list[index].object = pDispatch; + pDispatch->AddRef(); + *pid = list[index].id; + hr = S_OK; + } + break; + } + } + + if ((size_t)-1 == index) + { + DispatchRecord r; + r.name = LoginBox_CopyString(pszName); + if (NULL == r.name) + hr = E_OUTOFMEMORY; + else + { + r.id = ++lastDispId; + r.object = pDispatch; + pDispatch->AddRef(); + list.push_back(r); + *pid = r.id; + hr = S_OK; + } + } + + LeaveCriticalSection(&lock); + return hr; +} diff --git a/Src/auth/Loginbox/externalMngr.h b/Src/auth/Loginbox/externalMngr.h new file mode 100644 index 00000000..5f8804a0 --- /dev/null +++ b/Src/auth/Loginbox/externalMngr.h @@ -0,0 +1,65 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_EXTERNAL_MANAGER_HEADER +#define NULLSOFT_AUTH_LOGINBOX_EXTERNAL_MANAGER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include <dispex.h> +#include "../nu/Vectors.h" + + +class ExternalManager : public IDispatchEx +{ +protected: + ExternalManager(); + ~ExternalManager(); + +public: + static HRESULT CreateInstance(ExternalManager **instance); + +public: + /* IUnknown */ + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + + /* IDispatch*/ + STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid); + STDMETHOD(GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); + STDMETHOD(GetTypeInfoCount)(unsigned int FAR * pctinfo); + STDMETHOD(Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); + + /* IDispatchEx*/ + STDMETHOD (GetDispID)(BSTR bstrName, DWORD grfdex, DISPID *pid); + STDMETHOD (InvokeEx)(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller); + STDMETHOD (DeleteMemberByName)(BSTR bstrName, DWORD grfdex); + STDMETHOD (DeleteMemberByDispID)(DISPID id); + STDMETHOD (GetMemberProperties)(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex); + STDMETHOD (GetMemberName)(DISPID id, BSTR *pbstrName); + STDMETHOD (GetNextDispID)(DWORD grfdex, DISPID id, DISPID *pid); + STDMETHOD (GetNameSpaceParent)(IUnknown **ppunk); + +public: + HRESULT AddDispatch(LPCWSTR pszName, IDispatch *pDispatch, DISPID *pid); + +protected: + typedef struct __DispatchRecord + { + DISPID id; + LPWSTR name; + IDispatch *object; + } DispatchRecord; + + typedef Vector<DispatchRecord> DispatchList; + +protected: + ULONG ref; + DispatchList list; + CRITICAL_SECTION lock; + DISPID lastDispId; +}; + + +#endif //NULLSOFT_AUTH_LOGINBOX_EXTERNAL_MANAGER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/graphics.cpp b/Src/auth/Loginbox/graphics.cpp new file mode 100644 index 00000000..2d338610 --- /dev/null +++ b/Src/auth/Loginbox/graphics.cpp @@ -0,0 +1,434 @@ +#include "./graphics.h" +#include "./shlwapi.h" + +BOOL Image_ColorOverEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, BOOL premult, COLORREF rgb) +{ + LONG pitch; + UINT a, r, g, b, ma, mr, mg, mb; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (step < 3) + return TRUE; + + if (cy < 0) cy -= cy; + + a = (LOBYTE((rgb)>>24)); r = GetRValue(rgb); g = GetGValue(rgb); b = GetBValue(rgb); + ma = 255 - a; mr = r * 255; mg = g * 255; mb = b * 255; + + if (0 == a) + return TRUE; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + if (0xFF == a) + { + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + cursor[0] = (BYTE)b; + cursor[1] = (BYTE)g; + cursor[2] = (BYTE)r; + // cursor[3] = 0xFF; + } + } + return TRUE; + } + + if (premult) + { + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + UINT t = (mb + ma * cursor[0] + 127) / 255; + cursor[0] = (t > 0xFF) ? 0xFF : t; + t = (mg + ma * cursor[1] + 127) / 255; + cursor[1] = (t > 0xFF) ? 0xFF : t; + t = (mr+ ma * cursor[2] + 127) / 255; + cursor[2] = (t > 0xFF) ? 0xFF : t; + } + } + } + else + { + WORD k = (((255 - a)*255 + 127)/255); + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + cursor[0] = (b*a + k*cursor[0] + 127)/255; + cursor[1] = (g*a + k*cursor[1] + 127)/255; + cursor[2] = (r*a + k*cursor[2] + 127)/255; + // cursor[3] = (a*a + k*cursor[3] + 127)/255; + } + } + } + return TRUE; +} + + +BOOL Image_ColorizeEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, BOOL premult, COLORREF rgbBottom, COLORREF rgbTop) +{ + LONG pitch; + UINT rBottom, gBottom, bBottom; + UINT rTop, gTop, bTop; + INT step = (bpp>>3); + LPBYTE startLine, line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (cy < 0) + cy = -cy; + + rBottom = GetRValue(rgbBottom); + gBottom = GetGValue(rgbBottom); + bBottom = GetBValue(rgbBottom); + + rTop = GetRValue(rgbTop); + gTop = GetGValue(rgbTop); + bTop = GetBValue(rgbTop); + + UINT a, k; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + startLine = pPixels + pitch * ofs + x*step; + + line = startLine; + + for (y = cy; y-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + a = (255- cursor[2]); + if (a < 0) a = 0; + if (a > 254) + { + cursor[0] = bTop; + cursor[1] = gTop; + cursor[2] = rTop; + } + else if (a== 0) + { + cursor[0] = bBottom; + cursor[1] = gBottom; + cursor[2] = rBottom; + } + else + { + k = (((255 - a)*255 + 127)/255); + cursor[0] = (bTop * a + k*bBottom + 127)/255; + cursor[1] = (gTop * a + k*gBottom + 127)/255; + cursor[2] = (rTop * a + k*rBottom + 127)/255; + } + } + } + + + if (32 == bpp && FALSE != premult) + { + line = startLine; + for (y = cy; y-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + a = cursor[3]; + k = MulDiv(cursor[0], a, 255); + cursor[0] = (k < 255) ? k : 255; + k = MulDiv(cursor[1], a, 255); + cursor[1] = (k < 255) ? k : 255; + k = MulDiv(cursor[2], a, 255); + cursor[2] = (k < 255) ? k : 255; + } + } + } + + + return TRUE; +} + + +BOOL Image_PremultiplyEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp) +{ + if (32 != bpp) + return FALSE; + + LONG pitch; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (cy < 0) + cy = -cy; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + UINT a,k; + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + a = cursor[3]; + if (0 == a) + { + cursor[0] = 0; + cursor[1] = 0; + cursor[2] = 0; + } + else if (255 != a) + { + k = MulDiv((UINT)cursor[0], a, 255); + cursor[0] = (k < 255) ? k : 255; + k = MulDiv((UINT)cursor[1], a, 255); + cursor[1] = (k < 255) ? k : 255; + k = MulDiv((UINT)cursor[2], a, 255); + cursor[2] = (k < 255) ? k : 255; + } + } + } + + return TRUE; +} + +BOOL Image_SaturateEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, INT n, BOOL fScale) +{ + if (32 != bpp) + return FALSE; + + LONG pitch; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (FALSE == fScale) + { + if (n < 0) n = 0; + else if (n > 1000) n = 1000; + } + else + { + if (n < -1000) n = -1000; + else if (n > 1000) n = 1000; + } + + if (cy < 0) + cy = -cy; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + COLORREF rgb; + INT k; + WORD h, l, s; + + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + rgb = RGB(cursor[2], cursor[1], cursor[0]); + ColorRGBToHLS(rgb, &h, &l, &s); + if(FALSE == fScale) + s = ((WORD)((240 * n)/1000)); + else + { + k = s; + s = (WORD)(k + (k * n) /1000); + } + + rgb = ColorHLSToRGB(h, l, s); + + cursor[0] = GetBValue(rgb); + cursor[1] = GetGValue(rgb); + cursor[2] = GetRValue(rgb); + } + } + + return TRUE; +} + + +BOOL Image_AdjustAlphaEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, INT n, BOOL fScale) +{ + if (32 != bpp) + return FALSE; + + LONG pitch; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (FALSE == fScale) + { + if (n < 0) n = 0; + else if (n > 1000) n = 1000; + } + else + { + if (n < -1000) n = -1000; + else if (n > 1000) n = 1000; + } + + if (cy < 0) + cy = -cy; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + INT k; + + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + if(FALSE == fScale) + cursor[3] = ((BYTE)((255 * n)/1000)); + else + { + k = cursor[3]; + k = k + MulDiv(k, n, 1000); + if (k > 255) k = 255; + cursor[3] = (BYTE)k; + } + } + } + + return TRUE; +} + +BOOL Image_AdjustSaturationAlphaEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, INT nSaturation, INT nAlpha) +{ + if (32 != bpp) + return FALSE; + + LONG pitch; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (nSaturation < -1000) nSaturation = -1000; + else if (nSaturation > 1000) nSaturation = 1000; + + if (nAlpha < -1000) nAlpha = -1000; + else if (nAlpha > 1000) nAlpha = 1000; + + if (cy < 0) + cy = -cy; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + INT k; + COLORREF rgb; + WORD h, l, s; + + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + k = cursor[3]; + k = k + MulDiv(k, nAlpha, 1000); + if (k > 255) k = 255; + cursor[3] = (BYTE)k; + + rgb = RGB(cursor[2], cursor[1], cursor[0]); + ColorRGBToHLS(rgb, &h, &l, &s); + + k = s; + k = k + MulDiv(k, nSaturation, 1000); + if (k > 240) k = 240; + s = (WORD)k; + + rgb = ColorHLSToRGB(h, l, s); + cursor[0] = GetBValue(rgb); + cursor[1] = GetGValue(rgb); + cursor[2] = GetRValue(rgb); + } + } + + return TRUE; +} +BOOL Image_ColorOver(HBITMAP hbmp, RECT *prcPart, BOOL premult, COLORREF rgb) +{ + DIBSECTION dibsec; + if (!hbmp || sizeof(DIBSECTION) != GetObject(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || 1 != dibsec.dsBmih.biPlanes || dibsec.dsBm.bmBitsPixel != 32) + return FALSE; + + return Image_ColorOverEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + prcPart->left, prcPart->top, + prcPart->right - prcPart->left, prcPart->bottom - prcPart->top, + dibsec.dsBm.bmBitsPixel, premult, rgb); +} + +BOOL Image_Premultiply(HBITMAP hbmp, RECT *prcPart) +{ + DIBSECTION dibsec; + if (!hbmp || sizeof(DIBSECTION) != GetObject(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || 1 != dibsec.dsBmih.biPlanes || dibsec.dsBm.bmBitsPixel != 32) + return FALSE; + + return Image_PremultiplyEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + prcPart->left, prcPart->top, + prcPart->right - prcPart->left, prcPart->bottom - prcPart->top, + dibsec.dsBm.bmBitsPixel); +} + +BOOL Image_Saturate(HBITMAP hbmp, RECT *prcPart, INT n, BOOL fScale) +{ + DIBSECTION dibsec; + if (!hbmp || sizeof(DIBSECTION) != GetObject(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || 1 != dibsec.dsBmih.biPlanes || dibsec.dsBm.bmBitsPixel != 32) + return FALSE; + + return Image_SaturateEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + prcPart->left, prcPart->top, + prcPart->right - prcPart->left, prcPart->bottom - prcPart->top, + dibsec.dsBm.bmBitsPixel, n, fScale); +} + +BOOL Image_AdjustAlpha(HBITMAP hbmp, RECT *prcPart, INT n, BOOL fScale) +{ + DIBSECTION dibsec; + if (!hbmp || sizeof(DIBSECTION) != GetObject(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || 1 != dibsec.dsBmih.biPlanes || dibsec.dsBm.bmBitsPixel != 32) + return FALSE; + + return Image_AdjustAlphaEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + prcPart->left, prcPart->top, + prcPart->right - prcPart->left, prcPart->bottom - prcPart->top, + dibsec.dsBm.bmBitsPixel, n, fScale); +} + +BOOL Image_AdjustSaturationAlpha(HBITMAP hbmp, RECT *prcPart, INT nSaturation, INT nAlpha) +{ + DIBSECTION dibsec; + if (!hbmp || sizeof(DIBSECTION) != GetObject(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || 1 != dibsec.dsBmih.biPlanes || dibsec.dsBm.bmBitsPixel != 32) + return FALSE; + + return Image_AdjustSaturationAlphaEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + prcPart->left, prcPart->top, + prcPart->right - prcPart->left, prcPart->bottom - prcPart->top, + dibsec.dsBm.bmBitsPixel, nSaturation, nAlpha); +} + +COLORREF Color_Blend(COLORREF rgbTop, COLORREF rgbBottom, INT alpha) +{ + if (alpha > 254) return rgbTop; + if (alpha < 0) return rgbBottom; + + WORD k = (((255 - alpha)*255 + 127)/255); + + return RGB( (GetRValue(rgbTop)*alpha + k*GetRValue(rgbBottom) + 127)/255, + (GetGValue(rgbTop)*alpha + k*GetGValue(rgbBottom) + 127)/255, + (GetBValue(rgbTop)*alpha + k*GetBValue(rgbBottom) + 127)/255); +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/graphics.h b/Src/auth/Loginbox/graphics.h new file mode 100644 index 00000000..7441e3f5 --- /dev/null +++ b/Src/auth/Loginbox/graphics.h @@ -0,0 +1,29 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_GRAPHICS_HEADER +#define NULLSOFT_AUTH_LOGINBOX_GRAPHICS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +BOOL Image_ColorOver(HBITMAP hbmp, RECT *prcPart, BOOL premult, COLORREF rgb); +BOOL Image_ColorOverEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, BOOL premult, COLORREF rgb); + +BOOL Image_ColorizeEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, BOOL premult, COLORREF rgbBottom, COLORREF rgbTop); + +BOOL Image_Premultiply(HBITMAP hbmp, RECT *prcPart); +BOOL Image_PremultiplyEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp); + +BOOL Image_Saturate(HBITMAP hbmp, RECT *prcPart, INT n, BOOL fScale); +BOOL Image_SaturateEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, INT n, BOOL fScale); + +BOOL Image_AdjustAlpha(HBITMAP hbmp, RECT *prcPart, INT n, BOOL fScale); +BOOL Image_AdjustAlphaEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, INT n, BOOL fScale); + +BOOL Image_AdjustSaturationAlpha(HBITMAP hbmp, RECT *prcPart, INT nSaturation, INT nAlpha); +BOOL Image_AdjustSaturationAlphaEx(BYTE *pPixels, INT bitmapCX, INT bitmapCY, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, INT nSaturation, INT nAlpha); + +COLORREF Color_Blend(COLORREF rgbTop, COLORREF rgbBottom, INT alpha); + +#endif //NULLSOFT_AUTH_LOGINBOX_GRAPHICS_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/imageCache.cpp b/Src/auth/Loginbox/imageCache.cpp new file mode 100644 index 00000000..cef2c205 --- /dev/null +++ b/Src/auth/Loginbox/imageCache.cpp @@ -0,0 +1,357 @@ +#include "./imageCache.h" +#include "./imageLoader.h" +#include "./graphics.h" +#include "./loginBox.h" + +#include <api/service/waservicefactory.h> +#include "../../ombrowser/ifc_omutility.h" +#include "../../ombrowser/ifc_omcachemanager.h" +#include "../../ombrowser/ifc_omcachegroup.h" +#include "../../ombrowser/ifc_omcacherecord.h" + +#include "../api.h" + +LoginImageCache::LoginImageCache(HWND hLoginbox) + : ref(1), hwnd(hLoginbox), group(NULL) +{ + InitializeCriticalSection(&lock); +} + +LoginImageCache::~LoginImageCache() +{ + if (NULL != group) + { + group->Release(); + } + + DeleteCriticalSection(&lock); +} + +HRESULT LoginImageCache::CreateInstance(HWND hLoginbox, LoginImageCache **instance) +{ + if (NULL == instance) + return E_POINTER; + + *instance = NULL; + if (NULL == hLoginbox) return E_INVALIDARG; + + *instance = new LoginImageCache(hLoginbox); + if (NULL == *instance) return E_OUTOFMEMORY; + + return S_OK; +} + +size_t LoginImageCache::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t LoginImageCache::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int LoginImageCache::QueryInterface(GUID interface_guid, void **object) +{ + if (NULL == object) return E_POINTER; + + if (IsEqualIID(interface_guid, IFC_OmCacheCallback)) + *object = static_cast<ifc_omcachecallback*>(this); + else + { + *object = NULL; + return E_NOINTERFACE; + } + + if (NULL == *object) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +void LoginImageCache::PathChanged(ifc_omcacherecord *record) +{ + HWND hLoginbox; + + EnterCriticalSection(&lock); + + hLoginbox = hwnd; + + LeaveCriticalSection(&lock); + + if (NULL != hLoginbox && FALSE != IsWindow(hLoginbox)) + { + IMAGECACHERESULT result; + result.imageCache = this; + result.cacheRecord = record; + SendMessage(hLoginbox, NLBM_IMAGECACHED, 0, (LPARAM)&result); + } + +} + +void LoginImageCache::Finish() +{ + EnterCriticalSection(&lock); + + ifc_omcachegroup *groupCopy = group; + if (NULL != groupCopy) + groupCopy->AddRef(); + + LeaveCriticalSection(&lock); + + if (NULL != groupCopy) + { + groupCopy->Clear(); + + WCHAR szGroup[64] = {0}; + if (SUCCEEDED(groupCopy->GetName(szGroup, ARRAYSIZE(szGroup)))) + { + waServiceFactory *serviceFactory = WASABI_API_SVC->service_getServiceByGuid(IFC_OmUtility); + if (NULL != serviceFactory) + { + ifc_omutility *omUtility = (ifc_omutility*)serviceFactory->getInterface(); + if (NULL != omUtility) + { + ifc_omcachemanager *cacheManager; + if (SUCCEEDED(omUtility->GetCacheManager(&cacheManager))) + { + cacheManager->Delete(szGroup); + cacheManager->Release(); + } + omUtility->Release(); + } + } + } + + groupCopy->Release(); + } +} + +HRESULT LoginImageCache::InitGroup() +{ + HRESULT hr; + EnterCriticalSection(&lock); + if (NULL != group) + { + hr = S_FALSE; + } + else + { + hr = S_OK; + waServiceFactory *serviceFactory = WASABI_API_SVC->service_getServiceByGuid(IFC_OmUtility); + if (NULL != serviceFactory) + { + ifc_omutility *omUtility = (ifc_omutility*)serviceFactory->getInterface(); + if (NULL != omUtility) + { + ifc_omcachemanager *cacheManager; + if (SUCCEEDED(omUtility->GetCacheManager(&cacheManager))) + { + if (FAILED(cacheManager->Find(L"loginBox", TRUE, &group, NULL))) + group = NULL; + cacheManager->Release(); + } + omUtility->Release(); + } + } + + if (NULL == group) + hr = E_NOINTERFACE; + } + + LeaveCriticalSection(&lock); + return hr; +} + +HRESULT LoginImageCache::GetImageListIndex(LPCWSTR pszPath, HIMAGELIST himl, UINT *index, UINT *indexActive, UINT *indexDisabled) +{ + if (NULL == pszPath || L'\0' == *pszPath) + return E_INVALIDARG; + + HRESULT hr; + WCHAR szBuffer[2048] = {0}; + + EnterCriticalSection(&lock); + + BOOL fCreated = FALSE; + + hr = InitGroup(); + if (SUCCEEDED(hr)) + { + + ifc_omcacherecord *record; + hr = group->Find(pszPath, TRUE, &record, &fCreated); + if (S_OK != hr) + { + if (S_FALSE == hr) + hr = E_PENDING; + } + else + { + record->RegisterCallback(this); + + hr = record->GetPath(szBuffer, ARRAYSIZE(szBuffer)); + record->Release(); + } + } + + LeaveCriticalSection(&lock); + + if (FAILED(hr)) + return hr; + + return GetImageListIndexLocal(szBuffer, himl, index, indexActive, indexDisabled); +} + +HRESULT LoginImageCache::GetImageListIndexLocal(LPCWSTR pszPath, HIMAGELIST himl, UINT *index, UINT *indexActive, UINT *indexDisabled) +{ + if (NULL == himl || NULL == pszPath || L'\0' == *pszPath) + return E_INVALIDARG; + + if (NULL == index && NULL == indexActive && NULL == indexDisabled) + return E_INVALIDARG; + + INT destWidth, destHeight; + if (0 == ImageList_GetIconSize(himl, &destWidth, &destHeight)) + return E_FAIL; + + INT imageWidth, imageHeight; + HBITMAP hbmp = ImageLoader_LoadBitmap(NULL, pszPath, FALSE, &imageWidth, &imageHeight); + if (NULL == hbmp) + return E_FAIL; + + + HRESULT hr = S_OK; + + RECT imageRect; + SetRect(&imageRect, 0, 0, imageWidth, imageHeight); + + if (NULL != indexActive) + { + *indexActive = ImageList_Add(himl, hbmp, NULL); + if (((UINT)-1) == *indexActive) hr = E_FAIL; + } + + if (NULL != index) + { + Image_AdjustSaturationAlpha(hbmp, &imageRect, -150, -100); + + *index = ImageList_Add(himl, hbmp, NULL); + if (((UINT)-1) == *index) hr = E_FAIL; + } + + if (NULL != indexDisabled) + { + Image_AdjustSaturationAlpha(hbmp, &imageRect, -600, -600); + + *indexDisabled = ImageList_Add(himl, hbmp, NULL); + if (((UINT)-1) == *indexDisabled) hr = E_FAIL; + } + + if (NULL != hbmp) + DeleteObject(hbmp); + + return hr; + +} + +HBITMAP LoginImageCache::AdjustBitmapSize(HBITMAP hBitmap, INT forceWidth, INT forceHeight) +{ + BITMAP bm; + if (sizeof(BITMAP) != GetObject(hBitmap, sizeof(bm), &bm)) + return NULL; + if (bm.bmHeight < 0) bm.bmHeight = -bm.bmHeight; + + if (bm.bmWidth == forceWidth && bm.bmHeight == forceHeight) + return hBitmap; + + HDC hdc, hdcSrc, hdcDst; + hdc = GetDCEx(NULL, NULL, DCX_CACHE | DCX_WINDOW | DCX_NORESETATTRS); + if (NULL == hdc) return NULL; + + hdcSrc = CreateCompatibleDC(hdc); + hdcDst = CreateCompatibleDC(hdc); + + BITMAPINFOHEADER bhi; + ZeroMemory(&bhi, sizeof(bhi)); + bhi.biSize = sizeof(bhi); + bhi.biCompression = BI_RGB; + bhi.biBitCount = 32; + bhi.biPlanes = 1; + bhi.biWidth = forceWidth; + bhi.biHeight = -forceHeight; + + UINT *pixelData; + HBITMAP hbmpDst = CreateDIBSection(NULL, (LPBITMAPINFO)&bhi, DIB_RGB_COLORS, (void**)&pixelData, NULL, 0); + if (NULL != hbmpDst) + { + for (INT i = 0; i < forceWidth * forceHeight; i++) + pixelData[i] = 0x00FFFFFF; + } + + ReleaseDC(NULL, hdc); + + if (NULL != hdcSrc && NULL != hdcDst && NULL != hbmpDst) + { + HBITMAP hbmpSrcOrig = (HBITMAP)SelectObject(hdcSrc, hBitmap); + HBITMAP hbmpDstOrig = (HBITMAP)SelectObject(hdcDst, hbmpDst); + + BOOL result; + if (bm.bmWidth <= forceWidth && bm.bmHeight <= forceHeight) + { + BLENDFUNCTION bf; + bf.BlendOp = AC_SRC_OVER; + bf.BlendFlags = 0; + bf.SourceConstantAlpha = 255; + bf.AlphaFormat = AC_SRC_ALPHA; + + result = GdiAlphaBlend(hdcDst, (forceWidth - bm.bmWidth)/2, (forceHeight - bm.bmHeight)/2, + bm.bmWidth, bm.bmHeight, hdcSrc, 0, 0, bm.bmWidth, bm.bmHeight, bf); + } + else + { + SetStretchBltMode(hdcDst, HALFTONE); + result = StretchBlt(hdcDst, 0, 0, forceWidth, forceHeight, + hdcSrc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); + } + + + if(FALSE != result) + { + hBitmap = hbmpDst; + hbmpDst = NULL; + } + else + hBitmap = NULL; + + SelectObject(hdcSrc, hbmpSrcOrig); + SelectObject(hdcDst, hbmpDstOrig); + } + else + { + hBitmap = NULL; + } + + if (NULL != hdcDst) DeleteDC(hdcDst); + if (NULL != hdcSrc) DeleteDC(hdcSrc); + if (NULL != hbmpDst) DeleteObject(hbmpDst); + + return hBitmap; +} + +#define CBCLASS LoginImageCache +START_DISPATCH; +CB(ADDREF, AddRef) +CB(RELEASE, Release) +CB(QUERYINTERFACE, QueryInterface) +VCB(API_PATHCHANGED, PathChanged) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/Loginbox/imageCache.h b/Src/auth/Loginbox/imageCache.h new file mode 100644 index 00000000..20674e55 --- /dev/null +++ b/Src/auth/Loginbox/imageCache.h @@ -0,0 +1,53 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_IMAGECACHE_HEADER +#define NULLSOFT_AUTH_LOGINBOX_IMAGECACHE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../ombrowser/ifc_omcachecallback.h" + +#include <commctrl.h> + +class ifc_omcachegroup; +class ifc_omcacherecord; + +class LoginImageCache : public ifc_omcachecallback +{ +protected: + LoginImageCache(HWND hLoginbox); + ~LoginImageCache(); + +public: + static HRESULT CreateInstance(HWND hLoginbox, LoginImageCache **instance); + +public: + /* Dispatchable */ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + /* ifc_omcachecallback */ + void PathChanged(ifc_omcacherecord *record); + + void Finish(); + + HRESULT GetImageListIndex(LPCWSTR pszPath, HIMAGELIST himl, UINT *index, UINT *indexActive, UINT *indexDisabled); + HRESULT GetImageListIndexLocal(LPCWSTR pszPath, HIMAGELIST himl, UINT *index, UINT *indexActive, UINT *indexDisabled); + +private: + HRESULT InitGroup(); + HBITMAP AdjustBitmapSize(HBITMAP hBitmap, INT forceWidth, INT forceHeight); +protected: + size_t ref; + HWND hwnd; + ifc_omcachegroup *group; + CRITICAL_SECTION lock; + +private: + RECVS_DISPATCH; +}; + + +#endif // NULLSOFT_AUTH_LOGINBOX_IMAGECACHE_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/imageLoader.cpp b/Src/auth/Loginbox/imageLoader.cpp new file mode 100644 index 00000000..f82128a3 --- /dev/null +++ b/Src/auth/Loginbox/imageLoader.cpp @@ -0,0 +1,331 @@ +#include "./imageLoader.h" +#include "./common.h" +#include "../api.h" + +#include <api/service/waservicefactory.h> + +#include <shlwapi.h> + +template <class api_T> +void ImageLoader_ServiceBuild(api_T *&api_t, GUID factoryGUID_t) +{ + if (WASABI_API_SVC) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t); + if (factory) + api_t = (api_T *)factory->getInterface(); + } +} + +typedef BOOL (CALLBACK *IMAGEDATAPROCESSOR)(const void * /*data*/, size_t /*dataSize*/, ULONG_PTR /*param*/); + + +typedef struct __LOADDATAPARAM +{ + BOOL premultiply; + INT cx; + INT cy; + void *pixels; +} LOADDATAPARAM; + +typedef struct __GETDIMENSIONPARAM +{ + INT cx; + INT cy; +} GETDIMENSIONPARAM; + +static BOOL CALLBACK ImageLoader_LoadDataCallback(const void *data, size_t size, ULONG_PTR param) +{ + LOADDATAPARAM *loadParam = (LOADDATAPARAM*)param; + if (NULL == loadParam) return FALSE; + + if (NULL == WASABI_API_PNGLOADER) + { + ImageLoader_ServiceBuild(WASABI_API_PNGLOADER, pngLoaderGUID); + if (NULL == WASABI_API_PNGLOADER) + return FALSE; + } + + loadParam->pixels = (FALSE == loadParam->premultiply) ? + WASABI_API_PNGLOADER->loadImageData(data, (INT)size, &loadParam->cx, &loadParam->cy) : + WASABI_API_PNGLOADER->loadImage(data, (INT)size, &loadParam->cx, &loadParam->cy); + + return (NULL != loadParam->pixels); +} + +static BOOL CALLBACK ImageLoader_GetDimensionsCallback(const void *data, size_t size, ULONG_PTR param) +{ + GETDIMENSIONPARAM *dimensionParam = (GETDIMENSIONPARAM*)param; + if (NULL == dimensionParam) return FALSE; + + if (NULL == WASABI_API_PNGLOADER) + { + ImageLoader_ServiceBuild(WASABI_API_PNGLOADER, pngLoaderGUID); + if (NULL == WASABI_API_PNGLOADER) + return FALSE; + } + + return WASABI_API_PNGLOADER->getDimensions(data, (INT)size, &dimensionParam->cx, &dimensionParam->cy); +} + +static BOOL ImageLoader_ProcessResource(HINSTANCE hInstance, LPCWSTR pszName, LPCWSTR pszType, IMAGEDATAPROCESSOR processor, ULONG_PTR param) +{ + HRSRC res = FindResourceW(hInstance, pszName, pszType); + if (NULL == res) return FALSE; + + BOOL fSucceeded = FALSE; + HANDLE handle = LoadResource(hInstance, res); + if (NULL != handle) + { + UINT resourceSize = SizeofResource(hInstance, res); + if (0 != resourceSize) + { + void *resourceData = LockResource(handle); + if (NULL != resourceData) + fSucceeded = processor(resourceData, resourceSize, param); + } + FreeResource(handle); + } + return fSucceeded; +} + +static HRESULT ImageLoader_ParseResProtocol(LPWSTR pszAddress, LPCWSTR defaultType, HINSTANCE *module, LPCWSTR *resourceName, LPCWSTR *resourceType) +{ + if (NULL == module || NULL == resourceName || NULL == resourceType) + return E_POINTER; + + if (NULL == pszAddress || L'\0' == *pszAddress) + return E_INVALIDARG; + + INT cchAddress = lstrlenW(pszAddress); + const WCHAR szPrefix[] = L"res://"; + INT cchPrefix = ARRAYSIZE(szPrefix) - 1; + if (cchAddress <= cchPrefix) + return S_FALSE; + + if (CSTR_EQUAL != CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, pszAddress, cchPrefix, szPrefix, cchPrefix)) + return S_FALSE; + + pszAddress += cchPrefix; + cchAddress -= cchPrefix; + + LPWSTR resType = NULL; + LPWSTR resName = NULL; + + LPWSTR p = pszAddress + cchAddress; + while (p != pszAddress && L'/' != *p) p--; + if (p != pszAddress && p < (pszAddress + cchAddress)) + { + resName = p + 1; + *p = L'\0'; + p--; + } + + if (NULL == resName || L'\0' == *resName) + return E_FAIL; + + + while (p != pszAddress && L'/' != *p) p--; + if (p != pszAddress && p < resName) + { + resType = p + 1; + if (L'\0' == *resType) + { + resType = NULL; + } + else + { + resType = p + 1; + *p = L'\0'; + p--; + } + } + + HINSTANCE hModule; + hModule = LoadLibraryExW(pszAddress, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); + if (NULL == hModule) + { + UINT errorCode = GetLastError(); + if (NULL != resType) + { + *(resType - 1) = L'/'; + resType = NULL; + hModule = LoadLibraryExW(pszAddress, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); + if (NULL == hModule) errorCode = GetLastError(); + } + + if (ERROR_SUCCESS != errorCode) + return HRESULT_FROM_WIN32(errorCode); + } + + if (NULL == resType) + resType = (LPWSTR)defaultType; + + if (NULL != resType && FALSE == IS_INTRESOURCE(resType) && L'#' == *resType) + { + INT typeId; + if (FALSE != StrToIntExW(resType + 1, STIF_DEFAULT, &typeId)) + resType = MAKEINTRESOURCEW(typeId); + } + + if (NULL != resName && FALSE == IS_INTRESOURCE(resName) && L'#' == *resName) + { + INT nameId; + if (FALSE != StrToIntExW(resName + 1, STIF_DEFAULT, &nameId)) + resName = MAKEINTRESOURCEW(nameId); + } + + *module = hModule; + *resourceName = resName; + *resourceType = resType; + return S_OK; +} + +static BOOL ImageLoader_ProcessFile(LPCWSTR pszPath, IMAGEDATAPROCESSOR processor, ULONG_PTR param) +{ + HINSTANCE resModule; + LPCWSTR resName, resType; + + BOOL fSucceeded = FALSE; + + LPWSTR name = LoginBox_CopyString(pszPath); + HRESULT hr = ImageLoader_ParseResProtocol(name, RT_RCDATA, &resModule, &resName, &resType); + if (S_OK == hr) + { + fSucceeded = ImageLoader_ProcessResource(resModule, resName, resType, processor, param); + LoginBox_FreeString(name); + return fSucceeded; + } + + LoginBox_FreeString(name); + + if (FAILED(hr)) + return FALSE; + + HANDLE hFile = CreateFileW(pszPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + return FALSE; + + UINT resourceSize = GetFileSize(hFile, NULL); + if (INVALID_FILE_SIZE != resourceSize) + { + void *resourceData = malloc(resourceSize); + if (NULL != resourceData) + { + DWORD readed = 0; + if (0 != ReadFile(hFile, resourceData, resourceSize, &readed, NULL) || resourceSize != readed) + fSucceeded = processor(resourceData, resourceSize, param); + free(resourceData); + } + } + CloseHandle(hFile); + + return fSucceeded; +} + +void *ImageLoader_LoadData(HINSTANCE hInstance, LPCWSTR pszName, BOOL fPremultiply, INT *widthOut, INT *heightOut) +{ + BOOL fSucceeded; + LOADDATAPARAM param; + param.premultiply = fPremultiply; + if (NULL == hInstance && !IS_INTRESOURCE(pszName)) + fSucceeded = ImageLoader_ProcessFile(pszName, ImageLoader_LoadDataCallback, (ULONG_PTR)¶m); + else + fSucceeded = ImageLoader_ProcessResource(hInstance, pszName, RT_RCDATA, ImageLoader_LoadDataCallback, (ULONG_PTR)¶m); + + if (FALSE == fSucceeded) + { + if (NULL != widthOut) *widthOut = 0; + if (NULL != heightOut) *heightOut = 0; + return NULL; + } + + if (NULL != widthOut) *widthOut = param.cx; + if (NULL != heightOut) *heightOut = param.cy; + + return param.pixels; +} + +void ImageLoader_FreeData(void *data) +{ + if (NULL == data) + return; + + if (NULL == WASABI_API_MEMMNGR) + { + ImageLoader_ServiceBuild(WASABI_API_MEMMNGR, memMgrApiServiceGuid); + if (NULL == WASABI_API_MEMMNGR) return; + } + + WASABI_API_MEMMNGR->sysFree(data); +} + +HBITMAP ImageLoader_LoadBitmapEx(HINSTANCE hInstance, LPCWSTR pszName, BOOL fPremultiply, BITMAPINFOHEADER *headerInfo, void **dataOut) +{ + INT imageCX, imageCY; + + void *data = ImageLoader_LoadData(hInstance, pszName, fPremultiply, &imageCX, &imageCY); + if (NULL == data) return NULL; + + ZeroMemory(headerInfo, sizeof(BITMAPINFOHEADER)); + headerInfo->biSize = sizeof(BITMAPINFOHEADER); + headerInfo->biCompression = BI_RGB; + headerInfo->biBitCount = 32; + headerInfo->biPlanes = 1; + headerInfo->biWidth = imageCX; + headerInfo->biHeight = -imageCY; + + void *pixelData; + HBITMAP bitmap = CreateDIBSection(NULL, (LPBITMAPINFO)headerInfo, DIB_RGB_COLORS, &pixelData, NULL, 0); + if (NULL != bitmap) + { + if (NULL != dataOut) *dataOut = pixelData; + CopyMemory(pixelData, data, headerInfo->biWidth * abs(headerInfo->biHeight) * sizeof(DWORD)); + } + else + { + if (NULL != dataOut) *dataOut = NULL; + } + + ImageLoader_FreeData(data); + return bitmap; +} + +HBITMAP ImageLoader_LoadBitmap(HINSTANCE hInstance, LPCWSTR pszName, BOOL fPremultiply, INT *widthOut, INT *heightOut) +{ + BITMAPINFOHEADER header; + HBITMAP bitmap = ImageLoader_LoadBitmapEx(hInstance, pszName, fPremultiply, &header, NULL); + if (NULL != bitmap) + { + if (NULL != widthOut) *widthOut = header.biWidth; + if (NULL != heightOut) *heightOut = header.biHeight; + } + else + { + if (NULL != widthOut) *widthOut = 0; + if (NULL != heightOut) *heightOut = 0; + } + return bitmap; +} + +BOOL ImageLoader_GetDimensions(HINSTANCE hInstance, LPCWSTR pszName, INT *widthOut, INT *heightOut) +{ + BOOL fSucceeded; + GETDIMENSIONPARAM param; + if (NULL == hInstance && !IS_INTRESOURCE(pszName)) + fSucceeded = ImageLoader_ProcessFile(pszName, ImageLoader_GetDimensionsCallback, (ULONG_PTR)¶m); + else + fSucceeded = ImageLoader_ProcessResource(hInstance, pszName, RT_RCDATA, ImageLoader_GetDimensionsCallback, (ULONG_PTR)¶m); + + if (FALSE == fSucceeded) + { + if (NULL != widthOut) *widthOut = 0; + if (NULL != heightOut) *heightOut = 0; + return FALSE; + } + + if (NULL != widthOut) *widthOut = param.cx; + if (NULL != heightOut) *heightOut = param.cy; + + return TRUE; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/imageLoader.h b/Src/auth/Loginbox/imageLoader.h new file mode 100644 index 00000000..6169693e --- /dev/null +++ b/Src/auth/Loginbox/imageLoader.h @@ -0,0 +1,16 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_IMAGELOADER_HEADER +#define NULLSOFT_AUTH_LOGINBOX_IMAGELOADER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +void *ImageLoader_LoadData(HINSTANCE hInstance, LPCWSTR pszName, BOOL fPremultiply, INT *widthOut, INT *heightOut); +void ImageLoader_FreeData(void *data); +HBITMAP ImageLoader_LoadBitmapEx(HINSTANCE hInstance, LPCWSTR pszName, BOOL fPremultiply, BITMAPINFOHEADER *headerInfo, void **dataOut); +HBITMAP ImageLoader_LoadBitmap(HINSTANCE hInstance, LPCWSTR pszName, BOOL fPremultiply, INT *widthOut, INT *heightOut); +BOOL ImageLoader_GetDimensions(HINSTANCE hInstance, LPCWSTR pszName, INT *widthOut, INT *heightOut); + +#endif //NULLSOFT_AUTH_LOGINBOX_IMAGELOADER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginCommand.h b/Src/auth/Loginbox/loginCommand.h new file mode 100644 index 00000000..7813641d --- /dev/null +++ b/Src/auth/Loginbox/loginCommand.h @@ -0,0 +1,36 @@ +#ifndef NULLSOFT_AUTH_LOGINCOMMAND_HEADER +#define NULLSOFT_AUTH_LOGINCOMMAND_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "./loginResult.h" + +class LoginData; +class LoginCredentials; + +class __declspec(novtable) LoginCommand +{ +protected: + LoginCommand() {} + ~LoginCommand() {} + +public: + virtual ULONG AddRef() = 0; + virtual ULONG Release() = 0; + + virtual HRESULT GetType(GUID *commandUid) = 0; + + virtual HRESULT SetParameter(LPCWSTR pszKey, LPCWSTR pszValue) = 0; + virtual HRESULT IsValid() = 0; + virtual HRESULT IsIdentical(LoginCommand *test) = 0; + + virtual HRESULT BeginLogin(LoginData *data, LoginResult::Callback callback, void *user, LoginResult **result) = 0; + virtual HRESULT EndLogin(LoginResult *result, INT *authCode, LoginCredentials **credentials) = 0; + virtual HRESULT RequestAbort(LoginResult *result, BOOL drop) = 0; + +}; + +#endif //NULLSOFT_AUTH_LOGINCOMMAND_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginConfig.cpp b/Src/auth/Loginbox/loginConfig.cpp new file mode 100644 index 00000000..ddac9613 --- /dev/null +++ b/Src/auth/Loginbox/loginConfig.cpp @@ -0,0 +1,124 @@ +#include "./loginConfig.h" +#include "./common.h" + +#include <shlwapi.h> +#include <strsafe.h> + +#define BOOL2HRESULT(__result) ((FALSE != (__result)) ? S_OK : S_FALSE) + + +LoginConfig::LoginConfig() + : ref(1), configPath(NULL), pathValidated(FALSE), buffer(NULL) +{ + +} + +LoginConfig::~LoginConfig() +{ + LoginBox_FreeAnsiString(configPath); + LoginBox_FreeString((LPWSTR)buffer); +} + + +HRESULT LoginConfig::CreateInstance(LoginConfig **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = NULL; + + WCHAR szFile[MAX_PATH] = {0}; + HRESULT hr; + + hr = LoginBox_GetConfigPath(szFile, FALSE); + if (FAILED(hr)) return hr; + + if (FALSE == PathAppend(szFile, L"loginBox.ini")) + return E_UNEXPECTED; + + LPSTR configPath; + hr = LoginBox_WideCharToMultiByte(CP_UTF8, 0, szFile, -1, NULL, NULL, &configPath); + if (FAILED(hr)) return hr; + + *instance = new LoginConfig(); + if (NULL == *instance) + { + LoginBox_FreeAnsiString(configPath); + return E_OUTOFMEMORY; + } + else + { + (*instance)->configPath = configPath; + } + + return S_OK; +} + +ULONG LoginConfig::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginConfig::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginConfig::GetPath(LPCSTR *ppPath) +{ + if (NULL == ppPath) + return E_POINTER; + + *ppPath = configPath; + return S_OK; +} + +DWORD LoginConfig::ReadAnsiStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpDefault, LPSTR lpReturnedString, DWORD nSize) +{ + return GetPrivateProfileStringA(lpSectionName, lpKeyName, lpDefault, lpReturnedString, nSize, configPath); +} + +UINT LoginConfig::ReadInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nDefault) +{ + return GetPrivateProfileIntA(lpSectionName, lpKeyName, nDefault, configPath); +} + + +HRESULT LoginConfig::WriteAnsiStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpString) +{ + if (NULL == configPath || L'\0' == *configPath) + return E_UNEXPECTED; + + if (FALSE == pathValidated) + { + LPWSTR pszTest; + if (SUCCEEDED(LoginBox_MultiByteToWideChar(CP_UTF8, 0, configPath, -1, &pszTest))) + { + PathRemoveFileSpec(pszTest); + LoginBox_EnsurePathExist(pszTest); + pathValidated = TRUE; + + LoginBox_FreeString(pszTest); + } + } + + if (0 != WritePrivateProfileStringA(lpSectionName, lpKeyName, lpString, configPath)) + return S_OK; + + DWORD errorCode = GetLastError(); + return HRESULT_FROM_WIN32(errorCode); +} + +HRESULT LoginConfig::WriteInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nValue) +{ + CHAR szBuffer[32] = {0}; + HRESULT hr = StringCchPrintfA(szBuffer, ARRAYSIZE(szBuffer), "%d", nValue); + if (FAILED(hr)) return hr; + + return WriteAnsiStr(lpSectionName, lpKeyName, szBuffer); +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginConfig.h b/Src/auth/Loginbox/loginConfig.h new file mode 100644 index 00000000..3e0b487d --- /dev/null +++ b/Src/auth/Loginbox/loginConfig.h @@ -0,0 +1,40 @@ +#ifndef NULLSOFT_AUTH_LOGIN_CONFIG_HEADER +#define NULLSOFT_AUTH_LOGIN_CONFIG_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class LoginConfig +{ +protected: + LoginConfig(); + ~LoginConfig(); +public: + static HRESULT CreateInstance(LoginConfig **instance); + +public: + ULONG AddRef(); + ULONG Release(); + +public: + HRESULT GetPath(LPCSTR *ppPath); + DWORD ReadAnsiStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpDefault, LPSTR lpReturnedString, DWORD nSize); + UINT ReadInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nDefault); + HRESULT WriteAnsiStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpString); + HRESULT WriteInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nValue); + + +protected: + ULONG ref; + LPSTR configPath; + BOOL pathValidated; + void *buffer; + + +}; + + +#endif //NULLSOFT_AUTH_LOGIN_CONFIG_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginCredentials.cpp b/Src/auth/Loginbox/loginCredentials.cpp new file mode 100644 index 00000000..313e41d1 --- /dev/null +++ b/Src/auth/Loginbox/loginCredentials.cpp @@ -0,0 +1,108 @@ +#include "./loginCredentials.h" +#include "./common.h" + +#include "../api_auth.h" + + +LoginCredentials::LoginCredentials(const GUID *pRealm, LPCWSTR pszName, LPCSTR pszSession, LPCSTR pszToken, __time64_t tExpire) +: ref(1), username(NULL), sessionKey(NULL), token(NULL) +{ + realm = (NULL == pRealm) ? GUID_NULL : *pRealm; + username = LoginBox_CopyString(pszName); + sessionKey = LoginBox_CopyAnsiString(pszSession); + token = LoginBox_CopyAnsiString(pszToken); + expire = tExpire; +} + +LoginCredentials::~LoginCredentials() +{ + LoginBox_FreeStringSecure(username); + LoginBox_FreeAnsiStringSecure(sessionKey); + LoginBox_FreeAnsiStringSecure(token); +} + +HRESULT LoginCredentials::CreateInstance(const GUID *pRealm, LPCWSTR pszName, LPCSTR pszSession, LPCSTR pszToken, __time64_t tExpire, LoginCredentials **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = new LoginCredentials(pRealm, pszName, pszSession, pszToken, tExpire); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +HRESULT LoginCredentials::CreateFromAuth(api_auth *authApi, const GUID *pRealm, LoginCredentials **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = NULL; + + if (NULL == authApi) return E_INVALIDARG; + + const size_t sessionKeyMax(8192), tokenMax(8192), usernameMax(8192); + LPSTR sessionKey = LoginBox_MallocAnsiString(sessionKeyMax); + LPSTR token = LoginBox_MallocAnsiString(tokenMax); + LPWSTR username = LoginBox_MallocString(usernameMax); + __time64_t expire; + + HRESULT hr; + + if (NULL == sessionKey || NULL == token || NULL == username) + hr = E_OUTOFMEMORY; + else + { + INT result = authApi->GetCredentials((NULL != pRealm) ? *pRealm : GUID_NULL, sessionKey, sessionKeyMax, token, tokenMax, username, usernameMax, &expire); + if (AUTH_SUCCESS == result) + { + hr = CreateInstance(pRealm, username, sessionKey, token, expire, instance); + } + else + { + hr = E_FAIL; + } + } + + LoginBox_FreeAnsiStringSecure(sessionKey); + LoginBox_FreeAnsiStringSecure(token); + LoginBox_FreeStringSecure(username); + + return hr; +} + +UINT LoginCredentials::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +UINT LoginCredentials::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +GUID LoginCredentials::GetRealm() +{ + return realm; +} + +__time64_t LoginCredentials::GetExpiration() +{ + return expire; +} + +LPCWSTR LoginCredentials::GetUsername() +{ + return username; +} +LPCSTR LoginCredentials::GetSessionKey() +{ + return sessionKey; +} + +LPCSTR LoginCredentials::GetToken() +{ + return token; +} diff --git a/Src/auth/Loginbox/loginCredentials.h b/Src/auth/Loginbox/loginCredentials.h new file mode 100644 index 00000000..e668c48b --- /dev/null +++ b/Src/auth/Loginbox/loginCredentials.h @@ -0,0 +1,42 @@ +#ifndef NULLSOFT_AUTH_LOGIN_CREDENTIALS_HEADER +#define NULLSOFT_AUTH_LOGIN_CREDENTIALS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class api_auth; + +class LoginCredentials +{ +protected: + LoginCredentials(const GUID *pRealm, LPCWSTR pszName, LPCSTR pszSession, LPCSTR pszToken, __time64_t tExpire); + ~LoginCredentials(); + +public: + static HRESULT CreateInstance(const GUID *pRealm, LPCWSTR pszName, LPCSTR pszSession, LPCSTR pszToken, __time64_t tExpire, LoginCredentials **instance); + static HRESULT CreateFromAuth(api_auth *authApi, const GUID *pRealm, LoginCredentials **instance); + +public: + UINT AddRef(); + UINT Release(); + + GUID GetRealm(); + LPCWSTR GetUsername(); + LPCSTR GetSessionKey(); + LPCSTR GetToken(); + __time64_t GetExpiration(); + +private: + ULONG ref; + GUID realm; + LPWSTR username; + LPSTR sessionKey; + LPSTR token; + __time64_t expire; + +}; + +#endif //NULLSOFT_AUTH_LOGIN_CREDENTIALS_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginCurtain.cpp b/Src/auth/Loginbox/loginCurtain.cpp new file mode 100644 index 00000000..e6ca29f1 --- /dev/null +++ b/Src/auth/Loginbox/loginCurtain.cpp @@ -0,0 +1,544 @@ +#include "./loginCurtain.h" +#include "./loginPopup.h" +#include "./common.h" +#include "./graphics.h" +#include "./imageLoader.h" +#include "../resource.h" +#include "../api.h" + + +#define NWC_LOGINCURTAIN L"NullsoftLoginCurtain" + +typedef struct __LOGINCURTAINCREATEPARAM +{ + HWND owner; +} LOGINCURTAINCREATEPARAM; + +#define NLPF_IMAGEINVALID 0x00000001 + +typedef struct __LOGINCURTAIN +{ + HWND owner; + HBITMAP bkImage; + UINT flags; + UINT childCount; +} LOGINCURTAIN; + +typedef struct __DRAWBORDERPARAM +{ + HWND hParent; + RECT rect; + HDC hdc; + HDC hdcSrc; + HBITMAP bitmapFrame; + HBITMAP bitmapOrig; + BLENDFUNCTION blendFunc; +} DRAWBORDERPARAM; + + +typedef struct __UPDATEPOSPARAM +{ + HWND hParent; + RECT clientRect; + RECT childRect; + HDWP hdwp; + UINT childCount; +} UPDATEPOSPARAM; + +typedef struct __EXCLUDERGNPARAM +{ + HWND hParent; + HRGN exclude; + HRGN tmp; + RECT rect; +} EXCLUDERGNPARAM; + +#define BACKGROUND_ALPHA 200 + +#define GetCurtain(__hwnd) ((LOGINCURTAIN*)(LONG_PTR)(LONGX86)GetWindowLongPtr((__hwnd), 0)) + +static LRESULT WINAPI LoginCurtain_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + + +static BOOL LoginCurtain_RegisterClass(HINSTANCE hInstance) +{ + + WNDCLASSW wc; + if (FALSE != GetClassInfo(hInstance, NWC_LOGINCURTAIN, &wc)) + return TRUE; + + ZeroMemory(&wc, sizeof(wc)); + + wc.lpszClassName = NWC_LOGINCURTAIN; + wc.lpfnWndProc = LoginCurtain_WindowProc; + wc.style = CS_PARENTDC; + wc.cbWndExtra = sizeof(LOGINCURTAIN*); + wc.hInstance = hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + + return ( 0 != RegisterClassW(&wc)); +} + +HWND LoginCurtain_CreateWindow(HWND hParent, HWND hOwner) +{ + if (FALSE == LoginCurtain_RegisterClass(WASABI_API_ORIG_HINST)) + return NULL; + + RECT rect; + if (FALSE == GetClientRect(hOwner, &rect)) + SetRectEmpty(&rect); + else + MapWindowPoints(hOwner, hParent, (POINT*)&rect, 2); + + LOGINCURTAINCREATEPARAM createParam; + createParam.owner = hOwner; + + return CreateWindowEx(WS_EX_CONTROLPARENT, NWC_LOGINCURTAIN, NULL, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | DS_CONTROL, + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + hParent, NULL, WASABI_API_ORIG_HINST, &createParam); + +} + +static BOOL CALLBACK LoginCurtain_ExcludeChildRgnCallback(HWND hwnd, LPARAM lParam) +{ + EXCLUDERGNPARAM *param = (EXCLUDERGNPARAM*)lParam; + if (NULL == param) return FALSE; + + RECT *r = ¶m->rect; + + if (0 == (WS_VISIBLE & GetWindowStyle(hwnd)) || + param->hParent != (HWND)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_HWNDPARENT) || + FALSE == GetWindowRect(hwnd, r)) + return TRUE; + + MapWindowPoints(HWND_DESKTOP, param->hParent, (POINT*)r, 2); + + if (NULL == param->tmp) + { + param->tmp = CreateRectRgn(0, 0, 0, 0); + if (NULL == param->tmp) return FALSE; + } + + if (NULL == param->exclude) + { + param->exclude = CreateRectRgn(0, 0, 0, 0); + if (NULL == param->exclude) return FALSE; + } + + INT regionType = GetWindowRgn(hwnd, param->tmp); + switch(regionType) + { + case NULLREGION: + SetRectRgn(param->tmp, r->left, r->top, r->right, r->bottom); + CombineRgn(param->exclude, param->exclude, param->tmp, RGN_OR); + break; + case SIMPLEREGION: + case COMPLEXREGION: + OffsetRgn(param->tmp, r->left, r->top); + CombineRgn(param->exclude, param->exclude, param->tmp, RGN_OR); + break; + } + + return TRUE; +} + +static void LoginCurtain_ExcludeChildren(HWND hwnd, HDC hdc) +{ + EXCLUDERGNPARAM param; + param.hParent = hwnd; + param.exclude = NULL; + param.tmp = NULL; + EnumChildWindows(hwnd, LoginCurtain_ExcludeChildRgnCallback, (LPARAM)¶m); + if (NULL != param.exclude) + { + ExtSelectClipRgn(hdc, param.exclude, RGN_DIFF); + DeleteObject(param.exclude); + } + + if (NULL != param.tmp) + DeleteObject(param.tmp); +} + +static BOOL CALLBACK LoginCurtain_DrawWindowBorderCallback(HWND hwnd, LPARAM lParam) +{ + DRAWBORDERPARAM *param = (DRAWBORDERPARAM*)lParam; + if (NULL == param) return FALSE; + + RECT *r = ¶m->rect; + + if (0 == (WS_VISIBLE & GetWindowStyle(hwnd)) || + param->hParent != (HWND)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_HWNDPARENT) || + FALSE == GetWindowRect(hwnd, r)) + return TRUE; + + MapWindowPoints(HWND_DESKTOP, param->hParent, (POINT*)r, 2); + + if (NULL == param->bitmapFrame) + { + INT frameHeight, frameWidth; + param->bitmapFrame = ImageLoader_LoadBitmap(WASABI_API_ORIG_HINST, MAKEINTRESOURCE(IDR_POPUPBORDER_IMAGE), + TRUE, &frameWidth, &frameHeight); + if (NULL == param->bitmapFrame) + return FALSE; + + param->hdcSrc = CreateCompatibleDC(param->hdc); + if (NULL == param->hdcSrc) return FALSE; + + param->bitmapOrig = (HBITMAP)SelectObject(param->hdcSrc, param->bitmapFrame); + + param->blendFunc.AlphaFormat = AC_SRC_ALPHA; + param->blendFunc.BlendFlags = 0; + param->blendFunc.BlendOp = AC_SRC_OVER; + param->blendFunc.SourceConstantAlpha = 255; + } + + r->left -= 14; + r->top -= 13; + r->right += 17; + r->bottom += 19; + + GdiAlphaBlend(param->hdc, r->left, r->top, 26, 26, param->hdcSrc, 0, 0, 26, 26, param->blendFunc); + GdiAlphaBlend(param->hdc, r->right - 28, r->top, 28, 26, param->hdcSrc, 27, 0, 28, 26, param->blendFunc); + GdiAlphaBlend(param->hdc, r->left, r->bottom - 31, 26, 31, param->hdcSrc, 0, 27, 26, 31, param->blendFunc); + GdiAlphaBlend(param->hdc, r->right - 28, r->bottom - 31, 28, 31, param->hdcSrc, 27, 27, 28, 31, param->blendFunc); + + LONG l = (r->right - r->left - (26 + 28)); + GdiAlphaBlend(param->hdc, r->left + 26, r->top, l, 26, param->hdcSrc, 25, 0, 1, 26, param->blendFunc); + GdiAlphaBlend(param->hdc, r->left + 26, r->bottom - 31, l, 31, param->hdcSrc, 25, 27, 1, 31, param->blendFunc); + + l = (r->bottom - r->top - (26 + 31)); + GdiAlphaBlend(param->hdc, r->left, r->top + 26, 26, l, param->hdcSrc, 0, 28, 26, 1, param->blendFunc); + GdiAlphaBlend(param->hdc, r->right - 28, r->top + 26, 28, l, param->hdcSrc, 27, 28, 28, 1, param->blendFunc); + + return TRUE; +} + +static void LoginCurtain_DrawChildBorders(HWND hwnd, HDC hdc) +{ + DRAWBORDERPARAM param; + ZeroMemory(¶m, sizeof(param)); + param.hdc = hdc; + param.hParent = hwnd; + + EnumChildWindows(hwnd, LoginCurtain_DrawWindowBorderCallback, (LPARAM)¶m); + + if (NULL != param.hdcSrc) + { + SelectObject(param.hdcSrc, param.bitmapOrig); + DeleteDC(param.hdcSrc); + } + + if (NULL != param.bitmapFrame) + DeleteObject(param.bitmapFrame); +} + +static HBITMAP LoginCurtain_CreateBkImage(HWND hwnd, HDC hdc, HBITMAP hBitmap, HWND hOwner) +{ + RECT rect; + if (FALSE == GetClientRect(hwnd, &rect)) + return NULL; + + LONG width = rect.right - rect.left; + LONG height = rect.bottom - rect.top; + + BITMAP bi; + + if (NULL == hBitmap || + sizeof(BITMAP) != GetObject(hBitmap, sizeof(BITMAP), &bi) || + bi.bmWidth < width || bi.bmHeight < height) + { + if (NULL != hBitmap) + DeleteObject(hBitmap); + + BITMAPINFOHEADER header; + ZeroMemory(&header, sizeof(BITMAPINFOHEADER)); + + header.biSize = sizeof(BITMAPINFOHEADER); + header.biCompression = BI_RGB; + header.biBitCount = 32; + header.biPlanes = 1; + header.biWidth = (width + 32); + header.biHeight = -(height + 32); + + bi.bmBitsPixel = header.biBitCount; + bi.bmWidth = header.biWidth; + bi.bmHeight = header.biHeight; + bi.bmPlanes = header.biPlanes; + + hBitmap = CreateDIBSection(NULL, (LPBITMAPINFO)&header, DIB_RGB_COLORS, (void**)&bi.bmBits, NULL, 0); + if (NULL == hBitmap) return NULL; + } + else + { + bi.bmHeight = -bi.bmHeight; + } + + HBRUSH bkBrush = (HBRUSH)SendMessage(hOwner, WM_CTLCOLORDLG, (WPARAM)hdc, (LPARAM)hwnd); + + HBITMAP hbo = (HBITMAP)SelectObject(hdc, hBitmap); + + if (NULL == hOwner) + { + if (NULL != bkBrush) + FillRect(hdc, &rect, bkBrush); + else + ExtTextOut(hdc, 0, 0, OPAQUE, &rect, NULL, 0, NULL); + } + else + { + if (FALSE == LoginBox_PrintWindow(hOwner, hdc, 0)) + SendMessage(hOwner, WM_PRINT, (WPARAM)hdc, (LPARAM) (PRF_NONCLIENT | PRF_CLIENT | PRF_CHILDREN | PRF_ERASEBKGND)); + + COLORREF rgbBlend = GetDCBrushColor(hdc); + if (CLR_INVALID == rgbBlend) + rgbBlend = GetBkColor(hdc); + rgbBlend |= (BACKGROUND_ALPHA << 24); + Image_ColorOverEx((BYTE*)bi.bmBits, bi.bmWidth, bi.bmHeight, 0, 0, width, height, bi.bmBitsPixel, FALSE, rgbBlend); + } + + LoginCurtain_DrawChildBorders(hwnd, hdc); + + SelectObject(hdc, hbo); + return hBitmap; +} + +static void LoginCurtain_EraseBkGround(HWND hwnd, HDC hdc, const RECT *prcPaint) +{ + LOGINCURTAIN *curtain = GetCurtain(hwnd); + if (NULL == curtain) return; + + HDC hdcSrc = CreateCompatibleDC(hdc); + if (NULL == hdcSrc) return; + + + if (NULL == curtain->bkImage || 0 != (NLPF_IMAGEINVALID & curtain->flags)) + { + HRGN clipRegion = CreateRectRgn(0,0,0,0); + if (NULL != clipRegion) + { + INT regionType = GetClipRgn(hdc, clipRegion); + if (1 == regionType) + SelectClipRgn(hdcSrc, clipRegion); + DeleteObject(clipRegion); + } + curtain->bkImage = LoginCurtain_CreateBkImage(hwnd, hdcSrc, curtain->bkImage, curtain->owner); + curtain->flags &= ~NLPF_IMAGEINVALID; + } + + if (NULL != curtain->bkImage) + { + HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcSrc, curtain->bkImage); + BitBlt(hdc, prcPaint->left, prcPaint->top, + prcPaint->right - prcPaint->left, prcPaint->bottom - prcPaint->top, + hdcSrc, prcPaint->left, prcPaint->top, SRCCOPY); + + SelectObject(hdcSrc, hbmpOld); + } + + DeleteDC(hdcSrc); + +} + +static void LoginCurtain_Paint(HWND hwnd, HDC hdc, const RECT *prcPaint, BOOL fErase) +{ + if (FALSE != fErase) + { + LoginCurtain_ExcludeChildren(hwnd, hdc); + LoginCurtain_EraseBkGround(hwnd, hdc, prcPaint); + } +} + +static BOOL CALLBACK LoginCurtain_UpdateChildPosCallback(HWND hwnd, LPARAM lParam) +{ + UPDATEPOSPARAM *param = (UPDATEPOSPARAM*)lParam; + if (NULL == param) return FALSE; + + if (param->hParent != (HWND)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_HWNDPARENT)) + return TRUE; + + if (NULL == param->hdwp) + { + param->hdwp = BeginDeferWindowPos(param->childCount); + param->childCount = 0; + + if (NULL == param->hdwp) return FALSE; + } + + LONG prevWidth(0), prevHeight(0); + if (FALSE != GetWindowRect(hwnd, ¶m->childRect)) + { + prevWidth = param->childRect.right - param->childRect.left; + prevHeight = param->childRect.bottom - param->childRect.top; + } + + + if (FALSE != LoginPopup_UpdateWindowPos(hwnd, ¶m->clientRect, ¶m->childRect)) + { + param->hdwp = DeferWindowPos(param->hdwp, hwnd, NULL, param->childRect.left, param->childRect.top, + param->childRect.right - param->childRect.left, param->childRect.bottom - param->childRect.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); + + if (NULL == param->hdwp) return FALSE; + + LONG width = param->childRect.right - param->childRect.left; + LONG height = param->childRect.bottom - param->childRect.top; + if (width != prevWidth || height != prevHeight) + { + HRGN clipRgn = CreateRoundRectRgn(0, 0, width, height, 9, 9); + SetWindowRgn(hwnd, clipRgn, FALSE); + } + } + param->childCount++; + return TRUE; +} + +static void LoginCurtain_UpdateLayout(HWND hwnd, BOOL fRedraw) +{ + LOGINCURTAIN *curtain = GetCurtain(hwnd); + if (NULL == curtain) return; + + curtain->flags |= NLPF_IMAGEINVALID; + + RECT clientRect; + GetClientRect(hwnd, &clientRect); + + UPDATEPOSPARAM param; + param.hParent = hwnd; + param.hdwp = 0; + param.childCount = curtain->childCount; + + CopyRect(¶m.clientRect, &clientRect); + param.clientRect.left += 15; + param.clientRect.top += 14; + param.clientRect.right -= 18; + param.clientRect.bottom -= 20; + + EnumChildWindows(hwnd, LoginCurtain_UpdateChildPosCallback, (LPARAM)¶m); + curtain->childCount = param.childCount; + if (NULL != param.hdwp) + EndDeferWindowPos(param.hdwp); + + if (FALSE != fRedraw) + { + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ERASENOW | RDW_ALLCHILDREN); + } +} + +static LRESULT LoginCurtain_OnCreate(HWND hwnd, CREATESTRUCT* pcs) +{ + LOGINCURTAIN *curtain = (LOGINCURTAIN*)calloc(1, sizeof(LOGINCURTAIN)); + if (NULL != curtain) + { + SetLastError(ERROR_SUCCESS); + if (!SetWindowLongPtr(hwnd, 0, (LONGX86)(LONG_PTR)curtain) && ERROR_SUCCESS != GetLastError()) + { + free(curtain); + curtain = NULL; + } + } + + if (NULL == curtain) + return -1; + + LOGINCURTAINCREATEPARAM *createParam = (LOGINCURTAINCREATEPARAM*)pcs->lpCreateParams; + if (NULL != createParam) + { + curtain->owner = createParam->owner; + } + + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED | SWP_NOREDRAW); + + return 0; +} + +static void LoginCurtain_OnDestroy(HWND hwnd) +{ + LOGINCURTAIN *curtain = GetCurtain(hwnd); + SetWindowLongPtr(hwnd, 0, 0L); + if (NULL == curtain) return; + + if (NULL != curtain->bkImage) + DeleteObject(curtain->bkImage); + + free(curtain); +} + +static void LoginCurtain_OnWindowPosChanged(HWND hwnd, WINDOWPOS *pwp) +{ + if (SWP_NOSIZE == ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags)) + return; + + LoginCurtain_UpdateLayout(hwnd, 0 == (SWP_NOREDRAW & pwp->flags)); +} + + +static void LoginCurtain_OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + if (BeginPaint(hwnd, &ps)) + { + if (ps.rcPaint.left != ps.rcPaint.right) + LoginCurtain_Paint(hwnd, ps.hdc, &ps.rcPaint, ps.fErase); + EndPaint(hwnd, &ps); + } +} + +static void LoginCurtain_OnPrintClient(HWND hwnd, HDC hdc, UINT options) +{ + RECT clientRect; + if (GetClientRect(hwnd, &clientRect)) + LoginCurtain_Paint(hwnd, hdc, &clientRect, 0 != (PRF_ERASEBKGND & options)); +} + +static void LoginCurtain_OnParentNotify(HWND hwnd, INT eventId, INT childId, LPARAM eventParam) +{ + switch(eventId) + { + case WM_CREATE: + { + HWND hChild = (HWND)eventParam; + RECT rect; + GetWindowRect(hChild, &rect); + HRGN clipRgn = CreateRoundRectRgn(0, 0, rect.right - rect.left, rect.bottom - rect.top, 9, 9); + SetWindowRgn(hChild, clipRgn, FALSE); + } + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED ); + break; + + case WM_DESTROY: + { + HWND hChild = (HWND)eventParam; + SetWindowLongPtr(hChild, GWLP_ID, 0); + } + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED); + break; + } +} + +static LRESULT LoginCurtain_OnNotify(HWND hwnd, INT controlId, NMHDR *pnmh) +{ + HWND hAncestor = GetAncestor(hwnd, GA_PARENT); + if (NULL != hAncestor) + return SendMessage(hAncestor, WM_NOTIFY, (WPARAM)controlId, (LPARAM)pnmh); + return 0; +} + +static LRESULT WINAPI LoginCurtain_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: return LoginCurtain_OnCreate(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: LoginCurtain_OnDestroy(hwnd); return 0; + case WM_ERASEBKGND: return 0; + case WM_PAINT: LoginCurtain_OnPaint(hwnd); return 0; + case WM_PRINTCLIENT: LoginCurtain_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return 0; + case WM_WINDOWPOSCHANGED: LoginCurtain_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_SIZE: return 0; + case WM_PARENTNOTIFY: LoginCurtain_OnParentNotify(hwnd, LOWORD(wParam), HIWORD(wParam), lParam); return 0; + case WM_NOTIFY: return LoginCurtain_OnNotify(hwnd, (INT)wParam, (NMHDR*)lParam); + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + diff --git a/Src/auth/Loginbox/loginCurtain.h b/Src/auth/Loginbox/loginCurtain.h new file mode 100644 index 00000000..b43586c9 --- /dev/null +++ b/Src/auth/Loginbox/loginCurtain.h @@ -0,0 +1,12 @@ +#ifndef NULLSOFT_AUTH_LOGINCURTAIN_HEADER +#define NULLSOFT_AUTH_LOGINCURTAIN_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +HWND LoginCurtain_CreateWindow(HWND hParent, HWND hOwner); + +#endif //NULLSOFT_AUTH_LOGINCURTAIN_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginData.cpp b/Src/auth/Loginbox/loginData.cpp new file mode 100644 index 00000000..01ae6540 --- /dev/null +++ b/Src/auth/Loginbox/loginData.cpp @@ -0,0 +1,149 @@ +#include "./loginData.h" +#include "./loginBox.h" +#include "./loginProvider.h" +#include "./loginStatus.h" + +#include "../api.h" + +LoginData::LoginData(const GUID *pRealm, HWND hPage, HWND hLoginbox) + : ref(1), hPage(hPage), hLoginbox(hLoginbox), provider(NULL), status(NULL), statusCookie(-1) +{ + if (NULL != pRealm) + { + realm = *pRealm; + } + else + { + if (NULL == hLoginbox || FALSE == LoginBox_GetRealm(hLoginbox, &realm)) + realm = GUID_NULL; + } + + if (FALSE == LoginBox_GetActiveProvider(hLoginbox, &provider)) + provider = NULL; + + LoginBox_GetStatus(hLoginbox, &status); +} + +LoginData::~LoginData() +{ + if (NULL != provider) + provider->Release(); + + if (NULL != status) + { + if (-1 != statusCookie) + status->Remove(statusCookie); + + status->Release(); + } +} + +HRESULT LoginData::CreateInstance(const GUID *pRealm, HWND hPage, HWND hLoginbox, LoginData **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hPage || NULL == hLoginbox) return E_INVALIDARG; + *instance = new LoginData(pRealm, hPage, hLoginbox); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +ULONG LoginData::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginData::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginData::QueryInterface(REFIID riid, void** ppObject) +{ + if (NULL == ppObject) + return E_POINTER; + + if (IsEqualIID(riid, IID_LoginData)) + *ppObject = static_cast<LoginData*>(this); + else + { + *ppObject = NULL; + return E_NOINTERFACE; + } + + if (NULL == *ppObject) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +HWND LoginData::GetLoginbox() +{ + return hLoginbox; +} + +HWND LoginData::GetPage() +{ + return hPage; +} + +HRESULT LoginData::GetRealm(GUID *pRealm) +{ + if (NULL == pRealm) return E_POINTER; + *pRealm = realm; + return S_OK; +} + +HRESULT LoginData::GetProvider(LoginProvider **ppProvider) +{ + if (NULL == ppProvider) return E_POINTER; + *ppProvider = provider; + if (NULL != provider) + provider->AddRef(); + return S_OK; +} + +HRESULT LoginData::GetStatus(LoginStatus **ppStatus) +{ + if (NULL == ppStatus) return E_POINTER; + *ppStatus = status; + if (NULL != status) + status->AddRef(); + return S_OK; +} +HRESULT LoginData::SetStatus(LPCWSTR pszStatus) +{ + if (NULL == status) + return E_FAIL; + + BSTR bstrText; + if (NULL == pszStatus || FALSE == IS_INTRESOURCE(pszStatus)) + bstrText = SysAllocString(pszStatus); + else + { + WCHAR szBuffer[256] = {0}; + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszStatus, szBuffer, ARRAYSIZE(szBuffer)); + bstrText = SysAllocString(szBuffer); + } + + if (-1 == statusCookie) + { + statusCookie = status->Add(bstrText); + if (-1 == statusCookie) + return E_FAIL; + } + else + { + if (FALSE == status->Set(statusCookie, bstrText)) + return E_FAIL; + } + + return S_OK; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginData.h b/Src/auth/Loginbox/loginData.h new file mode 100644 index 00000000..9d1dd30c --- /dev/null +++ b/Src/auth/Loginbox/loginData.h @@ -0,0 +1,50 @@ +#ifndef NULLSOFT_AUTH_LOGINDATA_HEADER +#define NULLSOFT_AUTH_LOGINDATA_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +// {69346B92-168E-452e-AA88-986AB3883920} +static const GUID IID_LoginData = +{ 0x69346b92, 0x168e, 0x452e, { 0xaa, 0x88, 0x98, 0x6a, 0xb3, 0x88, 0x39, 0x20 } }; + + +class LoginProvider; +class LoginStatus; + +class LoginData +{ + +protected: + LoginData(const GUID *pRealm, HWND hPage, HWND hLoginbox); + virtual ~LoginData(); + +public: + static HRESULT CreateInstance(const GUID *pRealm, HWND hPage, HWND hLoginbox, LoginData **instance); + +public: + virtual ULONG AddRef(); + virtual ULONG Release(); + virtual HRESULT QueryInterface(REFIID riid, void** ppObject); + + virtual HWND GetLoginbox(); + virtual HWND GetPage(); + virtual HRESULT GetRealm(GUID *pRealm); + virtual HRESULT GetProvider(LoginProvider **ppProvider); + virtual HRESULT GetStatus(LoginStatus **ppStatus); + virtual HRESULT SetStatus(LPCWSTR pszStatus); + +protected: + UINT ref; + GUID realm; + HWND hPage; + HWND hLoginbox; + LoginProvider *provider; + LoginStatus *status; + UINT statusCookie; +}; + +#endif //NULLSOFT_AUTH_LOGINDATA_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginGui.cpp b/Src/auth/Loginbox/loginGui.cpp new file mode 100644 index 00000000..7ca17ddc --- /dev/null +++ b/Src/auth/Loginbox/loginGui.cpp @@ -0,0 +1,277 @@ +#include "./loginGui.h" +#include "./common.h" +#include "./imageLoader.h" + +#include "../resource.h" +#include "../api.h" + +#include <strsafe.h> + +static size_t threadStorage = TLS_OUT_OF_INDEXES; + +LoginGuiObject::LoginGuiObject() +: ref(1), bitmapIcons(NULL), fontTitle(NULL), fontEditor(NULL), fontText(NULL) +{ +} + +LoginGuiObject::~LoginGuiObject() +{ + Reset(); +} + +HRESULT LoginGuiObject::InitializeThread() +{ + + if (TLS_OUT_OF_INDEXES == threadStorage) + { + if (NULL == WASABI_API_APP) + return E_UNEXPECTED; + + threadStorage = WASABI_API_APP->AllocateThreadStorage(); + if (TLS_OUT_OF_INDEXES == threadStorage) + return E_UNEXPECTED; + } + + LoginGuiObject *cache = (LoginGuiObject*)WASABI_API_APP->GetThreadStorage(threadStorage); + if (NULL != cache) + { + cache->AddRef(); + return S_FALSE; + } + + if (NULL == cache) + { + cache = new LoginGuiObject(); + if (NULL == cache) return E_OUTOFMEMORY; + WASABI_API_APP->SetThreadStorage(threadStorage, cache); + } + + return S_OK; +} + +HRESULT LoginGuiObject::UninitializeThread() +{ + if (TLS_OUT_OF_INDEXES == threadStorage) + return E_FAIL; + + if (NULL == WASABI_API_APP) + return E_UNEXPECTED; + + LoginGuiObject *cache = (LoginGuiObject*)WASABI_API_APP->GetThreadStorage(threadStorage); + if (NULL != cache && 0 == cache->Release()) + WASABI_API_APP->SetThreadStorage(threadStorage, NULL); + + return S_OK; +} + +HRESULT LoginGuiObject::QueryInstance(LoginGuiObject **instance) +{ + if (NULL == instance) + return E_POINTER; + + if (TLS_OUT_OF_INDEXES == threadStorage) + return E_UNEXPECTED; + + *instance = (LoginGuiObject*)WASABI_API_APP->GetThreadStorage(threadStorage); + if (NULL == *instance) return E_FAIL; + (*instance)->AddRef(); + + return S_OK; +} + +ULONG LoginGuiObject::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginGuiObject::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginGuiObject::Reset() +{ + if (NULL != bitmapIcons) + { + DeleteObject(bitmapIcons); + bitmapIcons = NULL; + } + + if (NULL != fontTitle) + { + DeleteObject(fontTitle); + fontTitle = NULL; + } + + if (NULL != fontEditor) + { + DeleteObject(fontEditor); + fontEditor = NULL; + } + + if (NULL != fontText) + { + DeleteObject(fontText); + fontText = NULL; + } + + return S_OK; +} + +HRESULT LoginGuiObject::GetIconDimensions(INT *pWidth, INT *pHeight) +{ + if (NULL == bitmapIcons) + { + INT width, height; + bitmapIcons = ImageLoader_LoadBitmap(WASABI_API_ORIG_HINST, MAKEINTRESOURCE(IDR_NOTIFIERICONS_IMAGE), + TRUE, &width, &height); + + if (NULL == bitmapIcons) + return E_FAIL; + + if (height < 0) height = -height; + + if (NULL != pWidth) *pWidth = width; + if (NULL != pHeight) *pHeight = width; + + return S_OK; + } + + BITMAP bm; + if (sizeof(bm) != GetObject(bitmapIcons, sizeof(bm), &bm)) + return E_FAIL; + + if (NULL != pWidth) *pWidth = bm.bmWidth; + if (NULL != pHeight) *pHeight = bm.bmWidth; + + return S_OK; +} + +HBITMAP LoginGuiObject::GetIcon(INT iconId, RECT *prcIcon) +{ + if (NULL == prcIcon || iconId < 0) + return NULL; + + INT width, height; + + if (NULL != bitmapIcons) + { + BITMAP bm; + if (sizeof(bm) != GetObject(bitmapIcons, sizeof(bm), &bm)) + bitmapIcons = NULL; + else + { + width = bm.bmWidth; + height = bm.bmHeight; + } + } + + if (NULL == bitmapIcons) + { + bitmapIcons = ImageLoader_LoadBitmap(WASABI_API_ORIG_HINST, MAKEINTRESOURCE(IDR_NOTIFIERICONS_IMAGE), + TRUE, &width, &height); + + if (NULL == bitmapIcons) + return NULL; + } + + if (height < 0) height = -height; + + if (width * (iconId + 1) > height) + return NULL; + + prcIcon->left = 0; + prcIcon->right = width; + prcIcon->top = width * iconId; + prcIcon->bottom = prcIcon->top + width; + + return bitmapIcons; +} + + +static HFONT LoginGuiObject_DuplicateFont(HFONT fontBase, INT heightDeltaPt) +{ + if (NULL == fontBase) return NULL; + + LOGFONT lf; + if (sizeof(lf) != GetObject(fontBase, sizeof(lf), &lf)) + return NULL; + + if (0 != heightDeltaPt) + { + HDC hdc = GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE | DCX_NORESETATTRS); + HDC hdcTmp = NULL; + + if (NULL != hdc) + { + hdcTmp = CreateCompatibleDC(hdc); + ReleaseDC(NULL, hdc); + } + + if (NULL == hdcTmp) + return NULL; + + LONG pixelsY = GetDeviceCaps (hdcTmp, LOGPIXELSY); + HFONT fontOrig = (HFONT)SelectObject(hdcTmp, fontBase); + + TEXTMETRIC tm; + if (FALSE != GetTextMetrics(hdcTmp, &tm)) + { + INT basePt = MulDiv(tm.tmHeight - tm.tmInternalLeading, 72, pixelsY); + lf.lfHeight = -MulDiv((basePt + heightDeltaPt), pixelsY, 72); + + } + + SelectObject(hdcTmp, fontOrig); + DeleteDC(hdcTmp); + } + + return CreateFontIndirect(&lf); +} + +HFONT LoginGuiObject::GetTitleFont() +{ + if (NULL == fontTitle) + { + HFONT fontBase = GetTextFont(); + if (NULL != fontBase) + { + fontTitle = LoginGuiObject_DuplicateFont(fontBase, 3); + } + } + return fontTitle; +} + +HFONT LoginGuiObject::GetEditorFont() +{ + if (NULL == fontEditor) + { + HFONT fontBase = GetTextFont(); + if (NULL != fontBase) + { + fontEditor = LoginGuiObject_DuplicateFont(fontBase, 0); + } + } + return fontEditor; +} + +HFONT LoginGuiObject::GetTextFont() +{ + if (NULL == fontText) + { + LOGFONT lf; + if (FALSE != SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0)) + { + lf.lfQuality = LoginBox_GetSysFontQuality(); + fontText = CreateFontIndirect(&lf); + } + } + return fontText; +} diff --git a/Src/auth/Loginbox/loginGui.h b/Src/auth/Loginbox/loginGui.h new file mode 100644 index 00000000..b4c279d3 --- /dev/null +++ b/Src/auth/Loginbox/loginGui.h @@ -0,0 +1,53 @@ +#ifndef NULLSOFT_AUTH_LOGIN_GUI_OBJECT_HEADER +#define NULLSOFT_AUTH_LOGIN_GUI_OBJECT_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class LoginGuiObject +{ +public: + typedef enum + { + iconNone = -1, + iconInfo = 0, + iconWarning = 1, + iconError = 2, + iconQuestion = 3, + } IconType; + +protected: + LoginGuiObject(); + ~LoginGuiObject(); + +public: + static HRESULT InitializeThread(); + static HRESULT UninitializeThread(); + + static HRESULT QueryInstance(LoginGuiObject **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + + HRESULT Reset(); + + HRESULT GetIconDimensions(INT *pWidth, INT *pHeight); + HBITMAP GetIcon(INT iconId, RECT *prcIcon); + HFONT GetTitleFont(); + HFONT GetEditorFont(); + HFONT GetTextFont(); + +private: + ULONG ref; + HBITMAP bitmapIcons; + HFONT fontTitle; + HFONT fontEditor; + HFONT fontText; +}; + +#endif //NULLSOFT_AUTH_LOGIN_GUI_OBJECT_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginNotifier.cpp b/Src/auth/Loginbox/loginNotifier.cpp new file mode 100644 index 00000000..990274b0 --- /dev/null +++ b/Src/auth/Loginbox/loginNotifier.cpp @@ -0,0 +1,663 @@ +#define OEMRESOURCE + +#include "./loginNotifier.h" +#include "./common.h" +#include "./loginGui.h" + +#include "../api.h" +#include "../resource.h" +#include "../api_auth.h" + +#include <strsafe.h> + +#define NWC_LOGINNOTIFIER L"NullsoftLoginNotifier" + +#define GRADIENT_LEFT 30 +#define GRADIENT_RIGHT 10 + +#define SPACING_TOP 2 +#define SPACING_BOTTOM 2 + +typedef struct __LOGINNOTIFIER +{ + UINT flags; + HBITMAP image; + INT type; + LPWSTR text; + HFONT font; + INT textHeight; + INT aveCharWidth; + COLORREF rgbBack; + COLORREF rgbText; +} LOGINNOTIFIER; + +#define GetNotifier(__hwnd) ((LOGINNOTIFIER*)(LONG_PTR)(LONGX86)GetWindowLongPtr((__hwnd), 0)) + +static LRESULT WINAPI LoginNotifier_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +static BOOL LoginNotifier_RegisterClass(HINSTANCE hInstance) +{ + + WNDCLASSW wc; + if (FALSE != GetClassInfo(hInstance, NWC_LOGINNOTIFIER, &wc)) + return TRUE; + + ZeroMemory(&wc, sizeof(wc)); + + wc.lpszClassName = NWC_LOGINNOTIFIER; + wc.lpfnWndProc = LoginNotifier_WindowProc; + wc.style = CS_PARENTDC; + wc.cbWndExtra = sizeof(LOGINNOTIFIER*); + wc.hInstance = hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + return ( 0 != RegisterClassW(&wc)); +} + +HWND LoginNotifier_CreateWindow(UINT styleEx, UINT style, INT x, INT y, INT cx, INT cy, HWND hParent, INT controlId) +{ + if (FALSE == LoginNotifier_RegisterClass(WASABI_API_ORIG_HINST)) + return NULL; + + return CreateWindowEx(styleEx, NWC_LOGINNOTIFIER, NULL, WS_CHILD | style, x, y, cx, cy, + hParent, (HMENU)(INT_PTR)controlId, WASABI_API_ORIG_HINST, NULL); + +} + +static HBITMAP LoginNotifier_CreateTypeImage(HDC hdc, INT type, INT height) +{ + INT iconIndex; + COLORREF rgbBack = RGB(255, 255, 255); + COLORREF rgbAlert; + + switch(type) + { + case NLNTYPE_INFORMATION: + iconIndex = LoginGuiObject::iconInfo; + rgbAlert = RGB(209, 222, 254); + break; + case NLNTYPE_WARNING: + iconIndex = LoginGuiObject::iconWarning; + rgbAlert = RGB(254, 241, 148); + break; + case NLNTYPE_ERROR: + iconIndex = LoginGuiObject::iconError; + rgbAlert = RGB(225, 105, 105); + break; + case NLNTYPE_QUESTION: + iconIndex = LoginGuiObject::iconQuestion; + rgbAlert = RGB(209, 222, 254); + break; + default: + iconIndex = LoginGuiObject::iconNone; + rgbAlert = GetSysColor(COLOR_3DLIGHT); + break; + } + + RECT iconRect; + HBITMAP bitmapIcons = NULL; + if (LoginGuiObject::iconNone != iconIndex) + { + LoginGuiObject *loginGui; + if (SUCCEEDED(LoginGuiObject::QueryInstance(&loginGui))) + { + bitmapIcons = loginGui->GetIcon(iconIndex, &iconRect); + loginGui->Release(); + } + } + + if (NULL == bitmapIcons) + SetRectEmpty(&iconRect); + + INT iconWidth = iconRect.right - iconRect.left; + INT iconHeight = iconRect.bottom - iconRect.top; + + INT width = GRADIENT_LEFT + GRADIENT_RIGHT; + width += iconWidth; + + HBITMAP bitmapDst = NULL; + HDC contextDst = CreateCompatibleDC(hdc); + if (NULL != contextDst) + { + bitmapDst = CreateCompatibleBitmap(hdc, width, height); + if (NULL != bitmapDst) + { + HBITMAP bitmapDstOrig = (HBITMAP)SelectObject(contextDst, bitmapDst); + + TRIVERTEX vertex[] = + { + { 0, 0, GetRValue(rgbBack) << 8, GetGValue(rgbBack) << 8, GetBValue(rgbBack) << 8, 0x0000 }, + { GRADIENT_LEFT, height, GetRValue(rgbAlert) << 8, GetGValue(rgbAlert) << 8, GetBValue(rgbAlert) << 8, 0x0000 }, + }; + + GRADIENT_RECT gradientRect; + gradientRect.UpperLeft = 0; + gradientRect.LowerRight = 1; + + RECT fillRect; + SetRect(&fillRect, 0, 0, width, height); + if (FALSE != GdiGradientFill(contextDst, vertex, ARRAYSIZE(vertex), &gradientRect, 1, GRADIENT_FILL_RECT_H)) + fillRect.left = GRADIENT_LEFT; + + if (fillRect.left < fillRect.right) + { + COLORREF rgbBackOrig = SetBkColor(contextDst, rgbAlert); + ExtTextOut(contextDst, 0, 0, ETO_OPAQUE, &fillRect, NULL, 0, NULL); + if (rgbBackOrig != rgbAlert) + SetBkColor(contextDst, rgbBackOrig); + } + + if (NULL != bitmapIcons) + { + HDC contextSrc = CreateCompatibleDC(hdc); + if (NULL != contextSrc) + { + HBITMAP bitmapSrcOrig = (HBITMAP)SelectObject(contextSrc, bitmapIcons); + + BLENDFUNCTION blendFunc; + blendFunc.AlphaFormat = AC_SRC_ALPHA; + blendFunc.BlendFlags = 0; + blendFunc.BlendOp = AC_SRC_OVER; + blendFunc.SourceConstantAlpha = 255; + + INT iconTop = (height - iconHeight)/2 + (height - iconHeight)%2; + if (iconTop >= 1 && (iconTop + iconHeight) < height) + { + GdiAlphaBlend(contextDst, GRADIENT_LEFT, iconTop, iconWidth, iconHeight, + contextSrc, iconRect.left, iconRect.top, iconWidth, iconHeight, blendFunc); + } + + SelectObject(contextSrc, bitmapSrcOrig); + DeleteDC(contextSrc); + } + } + SelectObject(contextDst, bitmapDstOrig); + } + DeleteDC(contextDst); + } + + return bitmapDst; +} + +static INT LoginNotifier_CalcTextHeight(HWND hwnd, HDC hdc, INT *aveCharWidth) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return 0; + + HDC contextMine(NULL); + HFONT fontOrig; + if (NULL == hdc) + { + contextMine = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL == contextMine) return 0; + + if (NULL != notifier->font) + fontOrig = (HFONT)SelectObject(contextMine, notifier->font); + + hdc = contextMine; + } + + TEXTMETRIC tm; + if (FALSE == GetTextMetrics(hdc, &tm)) + { + tm.tmHeight = 0; + tm.tmAveCharWidth = 0; + } + + if (NULL != aveCharWidth) + { + *aveCharWidth = LoginBox_GetAveCharWidth(hdc); + } + + + if (NULL != contextMine) + { + if (NULL != notifier->font) + SelectObject(contextMine, fontOrig); + + ReleaseDC(hwnd, contextMine); + } + + + + return tm.tmHeight; +} + +static void LoginNotifier_Paint(HWND hwnd, HDC hdc, const RECT *prcPaint, BOOL fErase) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return; + + RECT rect; + GetClientRect(hwnd, &rect); + + if (NULL == notifier->image) + notifier->image = LoginNotifier_CreateTypeImage(hdc, notifier->type, rect.bottom - rect.top); + + INT imageWidth = 0; + if (NULL != notifier->image) + { + BITMAP bm; + if (sizeof(BITMAP) == GetObject(notifier->image, sizeof(BITMAP), &bm)) + { + HDC contextSrc = CreateCompatibleDC(hdc); + if (NULL != contextSrc) + { + HBITMAP bitmapSrcOrig = (HBITMAP)SelectObject(contextSrc, notifier->image); + if (FALSE != BitBlt(hdc, rect.left, rect.top, bm.bmWidth, bm.bmHeight, contextSrc, 0, 0, SRCCOPY)) + imageWidth = bm.bmWidth; + SelectObject(contextSrc, bitmapSrcOrig); + DeleteDC(contextSrc); + } + } + } + + + SetBkColor(hdc, notifier->rgbBack); + SetTextColor(hdc, notifier->rgbText); + + HFONT fontOrig; + if (NULL != notifier->font) + fontOrig = (HFONT)SelectObject(hdc, notifier->font); + + LPCWSTR text = notifier->text; + INT cchText = (NULL != text) ? lstrlenW(text) : 0; + + if (-1 == notifier->textHeight) + notifier->textHeight = LoginNotifier_CalcTextHeight(hwnd, hdc, ¬ifier->aveCharWidth); + + RECT textRect; + CopyRect(&textRect, &rect); + textRect.left += imageWidth; + + INT textOffsetY = (textRect.bottom - textRect.top) - notifier->textHeight; + textOffsetY = textOffsetY/2 + textOffsetY%2; + + if (textOffsetY < SPACING_TOP) textOffsetY = SPACING_TOP; + + INT textAlignOrig = SetTextAlign(hdc, TA_TOP | TA_LEFT); + ExtTextOut(hdc, textRect.left + notifier->aveCharWidth+2, textRect.top + textOffsetY, ETO_CLIPPED | ETO_OPAQUE, &textRect, text, cchText, NULL); + + if (textAlignOrig != (TA_TOP | TA_LEFT)) + SetTextAlign(hdc, textAlignOrig); + + if (NULL != notifier->font) + SelectObject(hdc, fontOrig); +} + +static void LoginNotifier_UpdateLayout(HWND hwnd, BOOL fRedraw) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return; + + RECT rect; + GetClientRect(hwnd, &rect); + + if (NULL != notifier->image) + { + BITMAP bm; + if (sizeof(BITMAP) != GetObject(notifier->image, sizeof(BITMAP), &bm) || + bm.bmHeight != rect.bottom - rect.top) + { + DeleteObject(notifier->image); + notifier->image = NULL; + } + } + + InvalidateRect(hwnd, NULL, TRUE); + +} + +static BOOL LoginNotifier_SetNotification(HWND hwnd, INT type, LPCWSTR pszText, BOOL fInvalidate) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return FALSE; + + if (type != notifier->type) + { + notifier->type = type; + if (NULL != notifier->image) + { + DeleteObject(notifier->image); + notifier->image = NULL; + } + } + + LoginBox_FreeString(notifier->text); + + BOOL resultCode = TRUE; + + if (NULL == pszText) + { + notifier->text = NULL; + } + else + { + if (IS_INTRESOURCE(pszText)) + { + INT stringId; + switch((INT_PTR)pszText) + { + case AUTH_SUCCESS: stringId = IDS_ERR_SUCCESS; break; + case AUTH_404: stringId = IDS_ERR_404; break; + case AUTH_TIMEOUT: stringId = IDS_ERR_TIMEOUT; break; + case AUTH_NOHTTP: stringId = IDS_ERR_NOHTTP; break; + case AUTH_NOPARSER: stringId = IDS_ERR_NOPARSER; break; + case AUTH_CONNECTIONRESET: stringId = IDS_ERR_CONNECTIONRESET; break; + case AUTH_ERROR_PARSING_XML: stringId = IDS_ERR_PARSING_XML; break; + case AUTH_NOT_AUTHORIZED: stringId = IDS_ERR_NOT_AUTHORIZED; break; + case AUTH_SECURID: stringId = IDS_ERR_SECURID; break; + case AUTH_ABORT: stringId = IDS_ERR_ABORT; break; + case AUTH_INVALIDCRED: stringId = IDS_ERR_INVALIDCRED; break; + case AUTH_UNCONFIRMED: stringId = IDS_ERR_UNCONFIRMED; break; + case AUTH_UNEXPECTED: stringId = IDS_ERR_UNEXPECTED; break; + case AUTH_INVALIDPASSCODE: stringId = IDS_ERR_PASSCODE_INVALID; break; + case AUTH_USERNAME_EMPTY: stringId = IDS_ERR_USERNAME_EMPTY; break; + case AUTH_USERNAME_TOOSHORT: stringId = IDS_ERR_USERNAME_TOOSHORT; break; + case AUTH_USERNAME_TOOLONG: stringId = IDS_ERR_USERNAME_TOOLONG; break; + case AUTH_USERNAME_BADFORMAT: stringId = IDS_ERR_USERNAME_BADFORMAT; break; + case AUTH_PASSWORD_EMPTY: stringId = IDS_ERR_PASSWORD_EMPTY; break; + case AUTH_PASSWORD_TOOSHORT: stringId = IDS_ERR_PASSWORD_TOOSHORT; break; + case AUTH_PASSWORD_TOOLONG: stringId = IDS_ERR_PASSWORD_TOOLONG; break; + case AUTH_PASSWORD_BADFORMAT: stringId = IDS_ERR_PASSWORD_BADFORMAT; break; + case AUTH_PASSCODE_EMPTY: stringId = IDS_ERR_PASSCODE_EMPTY; break; + case AUTH_PASSCODE_TOOSHORT: stringId = IDS_ERR_PASSCODE_TOOSHORT; break; + case AUTH_PASSCODE_TOOLONG: stringId = IDS_ERR_PASSCODE_TOOLONG; break; + case AUTH_PASSCODE_BADFORMAT: stringId = IDS_ERR_PASSCODE_BADFORMAT; break; + default: stringId = IDS_ERR_UNKNOWN; break; + } + + WCHAR szBuffer[2048] = {0}; + LPWSTR cursor = szBuffer; + size_t remaining = ARRAYSIZE(szBuffer); + WASABI_API_LNGSTRINGW_BUF(IDS_LOGIN_FAILURE, cursor, remaining); + size_t len = lstrlen(cursor); + cursor += len; + remaining -= len; + if (cursor != szBuffer) + StringCchCopyEx(cursor, remaining, L": ", &cursor, &remaining, 0); + WASABI_API_LNGSTRINGW_BUF(stringId, cursor, remaining); + + notifier->text = LoginBox_CopyString(szBuffer); + if (NULL == notifier->text) + resultCode = FALSE; + } + else + { + notifier->text = LoginBox_CopyString(pszText); + if (NULL == notifier->text) + resultCode = FALSE; + } + } + + if (FALSE != fInvalidate) + InvalidateRect(hwnd, NULL, TRUE); + + return resultCode; +} +static LRESULT LoginNotifier_OnCreate(HWND hwnd, CREATESTRUCT* pcs) +{ + LOGINNOTIFIER *notifier = (LOGINNOTIFIER*)calloc(1, sizeof(LOGINNOTIFIER)); + if (NULL != notifier) + { + SetLastError(ERROR_SUCCESS); + if (!SetWindowLongPtr(hwnd, 0, (LONGX86)(LONG_PTR)notifier) && ERROR_SUCCESS != GetLastError()) + { + free(notifier); + notifier = NULL; + } + } + + if (NULL == notifier) + return -1; + + notifier->textHeight = -1; + notifier->rgbBack = RGB(247, 247, 247); + notifier->rgbText = RGB(0, 0, 0); + + return 0; +} + +static void LoginNotifier_OnDestroy(HWND hwnd) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + SetWindowLongPtr(hwnd, 0, 0L); + if (NULL == notifier) return; + + if (NULL != notifier->image) + DeleteObject(notifier->image); + + free(notifier); +} + +static void LoginNotifier_OnWindowPosChanged(HWND hwnd, WINDOWPOS *pwp) +{ + if (SWP_NOSIZE == ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags)) + return; + + LoginNotifier_UpdateLayout(hwnd, 0 == (SWP_NOREDRAW & pwp->flags)); +} + + +static void LoginNotifier_OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + if (BeginPaint(hwnd, &ps)) + { + if (ps.rcPaint.left != ps.rcPaint.right) + LoginNotifier_Paint(hwnd, ps.hdc, &ps.rcPaint, ps.fErase); + EndPaint(hwnd, &ps); + } +} + +static void LoginNotifier_OnPrintClient(HWND hwnd, HDC hdc, UINT options) +{ + RECT clientRect; + if (GetClientRect(hwnd, &clientRect)) + LoginNotifier_Paint(hwnd, hdc, &clientRect, 0 != (PRF_ERASEBKGND & options)); +} + +static void LoginNotifier_OnSetFont(HWND hwnd, HFONT hFont, BOOL fRedraw) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return; + + notifier->font = hFont; + + notifier->textHeight = -1; + + if (NULL != fRedraw) + InvalidateRect(hwnd, NULL, FALSE); +} + +static LRESULT LoginNotifier_OnGetFont(HWND hwnd) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + return (NULL != notifier) ? (LRESULT)notifier->font : NULL; +} + +static LRESULT LoginNotifier_OnSetText(HWND hwnd, LPCWSTR pszText) +{ + return LoginNotifier_SetNotification(hwnd, NLNTYPE_INFORMATION, pszText, TRUE); +} + +static LRESULT LoginNotifier_OnGetText(HWND hwnd, LPWSTR pszBuffer, size_t cchBufferMax) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + LPCWSTR pszText = (NULL != notifier) ? notifier->text : NULL; + + if (NULL == pszBuffer) + return 0; + + size_t remaining; + StringCchCopyEx(pszBuffer, cchBufferMax, pszText, NULL, &remaining, STRSAFE_IGNORE_NULLS); + return (cchBufferMax - remaining); +} + +static LRESULT LoginNotifier_OnGetTextLength(HWND hwnd) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + return (NULL != notifier && NULL != notifier->text) ? lstrlen(notifier->text) : 0; +} + +static LRESULT LoginNotifier_OnNotify(HWND hwnd, INT type, LPCWSTR pszText) +{ + return LoginNotifier_SetNotification(hwnd, type, pszText, TRUE); +} + +static LRESULT LoginNotifier_OnGetIdealHeight(HWND hwnd) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return 0; + + if (-1 == notifier->textHeight) + notifier->textHeight = LoginNotifier_CalcTextHeight(hwnd, NULL, ¬ifier->aveCharWidth); + + + INT iconHeight(0); + LoginGuiObject *loginGui; + if (SUCCEEDED(LoginGuiObject::QueryInstance(&loginGui))) + { + loginGui->GetIconDimensions(NULL, &iconHeight); + loginGui->Release(); + } + + INT height = (notifier->textHeight > iconHeight) ? + notifier->textHeight : iconHeight; + + height += SPACING_TOP + SPACING_BOTTOM; + return height; +} + +static LRESULT LoginNotifier_OnGetIdealSize(HWND hwnd, SIZE *sizeOut) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier || NULL == sizeOut) return FALSE; + + if (-1 == notifier->textHeight) + notifier->textHeight = LoginNotifier_CalcTextHeight(hwnd, NULL, ¬ifier->aveCharWidth); + + INT iconWidth(0), iconHeight(0); + LoginGuiObject *loginGui; + if (SUCCEEDED(LoginGuiObject::QueryInstance(&loginGui))) + { + loginGui->GetIconDimensions(&iconWidth, &iconHeight); + loginGui->Release(); + } + + sizeOut->cy = (notifier->textHeight > iconHeight) ? + notifier->textHeight : + iconHeight; + + sizeOut->cy += SPACING_TOP + SPACING_BOTTOM; + + sizeOut->cx = (0 != iconWidth) ? + (GRADIENT_LEFT + GRADIENT_RIGHT + iconWidth) : + 0; + + INT cchText = (NULL != notifier->text) ? lstrlen(notifier->text) : 0; + + BOOL resultOk = TRUE; + + if (0 != cchText) + { + sizeOut->cx += notifier->aveCharWidth+2; + + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_WINDOW | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT fontOrig = (HFONT)SelectObject(hdc, notifier->font); + + SIZE textSize; + if (FALSE != GetTextExtentPoint32W(hdc, notifier->text, cchText, &textSize)) + sizeOut->cx += textSize.cx; + else + resultOk = FALSE; + + SelectObject(hdc, fontOrig); + ReleaseDC(hwnd, hdc); + } + else + resultOk = FALSE; + } + return resultOk; +} + +static void LoginNotifier_OnSetBkColor(HWND hwnd, COLORREF rgbColor) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return; + + notifier->rgbBack = rgbColor; +} + +static LRESULT LoginNotifier_OnGetBkColor(HWND hwnd) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return RGB(255, 0, 255); + + return notifier->rgbBack; +} + +static void LoginNotifier_OnSetTextColor(HWND hwnd, COLORREF rgbColor) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return; + + notifier->rgbText = rgbColor; +} + +static LRESULT LoginNotifier_OnGetTextColor(HWND hwnd) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return RGB(255, 0, 255); + + return notifier->rgbText; +} +static void LoginNotifier_OnPlayBeep(HWND hwnd) +{ + LOGINNOTIFIER *notifier = GetNotifier(hwnd); + if (NULL == notifier) return; + + UINT beepType; + switch(notifier->type) + { + case NLNTYPE_INFORMATION: beepType = MB_ICONASTERISK; break; + case NLNTYPE_WARNING: beepType = MB_ICONEXCLAMATION; break; + case NLNTYPE_ERROR: beepType = MB_ICONHAND; break; + case NLNTYPE_QUESTION: beepType = MB_ICONQUESTION; break; + default: return; + } + LoginBox_MessageBeep(beepType); +} + +static LRESULT WINAPI LoginNotifier_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: return LoginNotifier_OnCreate(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: LoginNotifier_OnDestroy(hwnd); return 0; + case WM_ERASEBKGND: return 0; + case WM_PAINT: LoginNotifier_OnPaint(hwnd); return 0; + case WM_PRINTCLIENT: LoginNotifier_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return 0; + case WM_PRINT: return 0; + + case WM_WINDOWPOSCHANGED: LoginNotifier_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_SIZE: return 0; + case WM_SETFONT: LoginNotifier_OnSetFont(hwnd, (HFONT)wParam, (BOOL)LOWORD(lParam)); return 0; + case WM_GETFONT: return LoginNotifier_OnGetFont(hwnd); + case WM_SETTEXT: return LoginNotifier_OnSetText(hwnd, (LPCWSTR)lParam); + case WM_GETTEXT: return LoginNotifier_OnGetText(hwnd, (LPWSTR)lParam, (INT)wParam); + case WM_GETTEXTLENGTH: return LoginNotifier_OnGetTextLength(hwnd); + + case NLNM_NOTIFY: return LoginNotifier_OnNotify(hwnd, (INT)wParam, (LPCWSTR)lParam); + case NLNM_GETIDEALHEIGHT: return LoginNotifier_OnGetIdealHeight(hwnd); + case NLNM_SETBKCOLOR: LoginNotifier_OnSetBkColor(hwnd, (COLORREF)lParam); return 0; + case NLNM_GETBKCOLOR: return LoginNotifier_OnGetBkColor(hwnd); + case NLNM_SETTEXTCOLOR: LoginNotifier_OnSetTextColor(hwnd, (COLORREF)lParam); return 0; + case NLNM_GETTEXTCOLOR: return LoginNotifier_OnGetTextColor(hwnd); + case NLNM_PLAYBEEP: LoginNotifier_OnPlayBeep(hwnd); return 0; + case NLNM_GETIDEALSIZE: return LoginNotifier_OnGetIdealSize(hwnd, (SIZE*)lParam); + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + diff --git a/Src/auth/Loginbox/loginNotifier.h b/Src/auth/Loginbox/loginNotifier.h new file mode 100644 index 00000000..cb3a40d0 --- /dev/null +++ b/Src/auth/Loginbox/loginNotifier.h @@ -0,0 +1,54 @@ +#ifndef NULLSOFT_AUTH_LOGIN_NOTIFIER_HEADER +#define NULLSOFT_AUTH_LOGIN_NOTIFIER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class LoginGuiObject; + +HWND LoginNotifier_CreateWindow(UINT styleEx, UINT style, INT x, INT y, INT cx, INT cy, HWND hParent, INT controlId); + + +#define NLNTYPE_INFORMATION 0 +#define NLNTYPE_WARNING 1 +#define NLNTYPE_ERROR 2 +#define NLNTYPE_QUESTION 3 + +#define NLNM_FIRST (WM_USER + 10) + +#define NLNM_NOTIFY (NLNM_FIRST + 0) // wParam = (WPARAM)(INT)notificationType, lParam = (LPARAM)(LPCWSTR)notificationText; Return - TRUE on success; notificationText can be MAKEINTRESOURCE(authError) +#define LoginNotifier_Notify(/*HWND*/ __hwnd, /*INT*/ __notificationType, /*LPCWSTR*/ __notificationText)\ + (SNDMSG((__hwnd), NLNM_NOTIFY, (WPARAM)(__notificationType), (LPARAM)(__notificationText))) + +#define NLNM_GETIDEALHEIGHT (NLNM_FIRST + 1) // wParam - not used, lParam - not used; Return - ideal height +#define LoginNotifier_GetIdealHeight(/*HWND*/ __hwnd)\ + ((INT)SNDMSG((__hwnd), NLNM_GETIDEALHEIGHT, 0, 0L)) + +#define NLNM_SETBKCOLOR (NLNM_FIRST + 2) // wParam - not used, lParam = (LPARAM)(COLORREF)rgb; Return ignored +#define LoginNotifier_SetBkColor(/*HWND*/ __hwnd, /*COLORREF*/ __rgb)\ + (SNDMSG((__hwnd), NLNM_SETBKCOLOR, 0, (LPARAM)(__rgb))) + +#define NLNM_GETBKCOLOR (NLNM_FIRST + 3) // wParam - not used, lParam - not used; Return back color +#define LoginNotifier_GetBkColor(/*HWND*/ __hwnd)\ + ((COLORREF)SNDMSG((__hwnd), NLNM_GETBKCOLOR, 0, 0L)) + +#define NLNM_SETTEXTCOLOR (NLNM_FIRST + 4) // wParam - not used, lParam = (LPARAM)(COLORREF)rgb; Return ignored +#define LoginNotifier_SetTextColor(/*HWND*/ __hwnd, /*COLORREF*/ __rgb)\ + (SNDMSG((__hwnd), NLNM_SETTEXTCOLOR, 0, (LPARAM)(__rgb))) + +#define NLNM_GETTEXTCOLOR (NLNM_FIRST + 5) // wParam - not used, lParam - not used; Return back color +#define LoginNotifier_GetTextColor(/*HWND*/ __hwnd)\ + ((COLORREF)SNDMSG((__hwnd), NLNM_GETTEXTCOLOR, 0, 0L)) + +#define NLNM_PLAYBEEP (NLNM_FIRST + 6) // wParam - not used, lParam - not used; Return ignored +#define LoginNotifier_PlayBeep(/*HWND*/ __hwnd)\ + (SNDMSG((__hwnd), NLNM_PLAYBEEP, 0, 0L)) + +#define NLNM_GETIDEALSIZE (NLNM_FIRST + 7) // wParam - not used, lParam - (LPARAM)(SIZE*)sizeOut; Return TRUE on success +#define LoginNotifier_GetIdealSize(/*HWND*/ __hwnd, /*SIZE* */ __sizeOut)\ + ((BOOL)SNDMSG((__hwnd), NLNM_GETIDEALSIZE, 0, (LPARAM)(__sizeOut))) + +#endif //NULLSOFT_AUTH_LOGIN_NOTIFIER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginPage.cpp b/Src/auth/Loginbox/loginPage.cpp new file mode 100644 index 00000000..d5483aa1 --- /dev/null +++ b/Src/auth/Loginbox/loginPage.cpp @@ -0,0 +1,624 @@ +#define OEMRESOURCE + +#include "./loginPage.h" +#include "./loginData.h" +#include "./loginBox.h" +#include "./loginProvider.h" +#include "./loginGui.h" +#include "./common.h" +#include "../api.h" + +#include "../../nu/windowsTheme.h" + +#include <vssym32.h> +#include <vsstyle.h> +#include <windows.h> + +#define IDC_TITLE 9999 +#define IDC_HELPLINK 9998 + +typedef struct __LOGINPAGECREATEPARAM +{ + LOGINPAGECREATOR fnCreator; + LPARAM lParam; + HWND hLoginbox; +} LOGINPAGECREATEPARAM; + +LoginPage::LoginPage(HWND hwnd, HWND hLoginbox) +{ + this->hwnd = hwnd; + this->hLoginbox = hLoginbox; +} + +LoginPage::~LoginPage() +{ + +} + +HWND LoginPage::CreatePage(HWND hLoginbox, LPCWSTR pszTemplate, HWND hParent, LPARAM param, LOGINPAGECREATOR fnCreator) +{ + if (NULL == hLoginbox || NULL == hParent) + return NULL; + + if (NULL == pszTemplate || NULL == fnCreator) + return NULL; + + LOGINPAGECREATEPARAM createParam; + createParam.fnCreator = fnCreator; + createParam.lParam = param; + createParam.hLoginbox = hLoginbox; + + return WASABI_API_CREATEDIALOGPARAMW((INT)(INT_PTR)pszTemplate, hParent, LoginPage_DialogProc, (LPARAM)&createParam); +} + +void LoginPage::UpdateColors() +{ + rgbTitle = RGB(0, 51, 153); + rgbSecondaryText = GetSysColor(COLOR_WINDOWTEXT); + rgbText = GetSysColor(COLOR_WINDOWTEXT); + rgbBack = GetSysColor(COLOR_WINDOW); + hbrBack = GetSysColorBrush(COLOR_WINDOW); + + if (SUCCEEDED(UxTheme_LoadLibrary()) && FALSE != UxIsAppThemed()) + { + UXTHEME hTheme = UxOpenThemeData(hwnd, L"TEXTSTYLE"); + if (NULL != hTheme) + { + UxGetThemeColor(hTheme, TEXT_MAININSTRUCTION, 0, TMT_TEXTCOLOR, &rgbTitle); + UxGetThemeColor(hTheme, TEXT_BODYTEXT, 0, TMT_TEXTCOLOR, &rgbText); + UxGetThemeColor(hTheme, TEXT_SECONDARYTEXT, 0, TMT_TEXTCOLOR, &rgbSecondaryText); + UxCloseThemeData(hTheme); + } + } +} + +void LoginPage::UpdateMargins() +{ + SetRect(&margins, 14, 7, 7, 7); + MapDialogRect(hwnd, &margins); + + RECT controlRect; + HWND hControl = GetDlgItem(hwnd, IDC_HELPLINK); + if (NULL != hControl && GetWindowRect(hControl, &controlRect)) + { + + INT t = (controlRect.right - controlRect.left) + 1; + if (margins.right < t) margins.right = t; + + t = (controlRect.bottom - controlRect.top) + 1; + if (margins.top < t) margins.top = t; + } +} + +static HBITMAP LoginPage_GetHelpBitmap(HWND hwnd, HBRUSH hbrBack, INT *pWidth, INT *pHeight) +{ + LoginGuiObject *loginGui; + if (FAILED(LoginGuiObject::QueryInstance(&loginGui))) + return NULL; + + + RECT rectSrc; + HBITMAP hbmpSrc = loginGui->GetIcon(LoginGuiObject::iconQuestion, &rectSrc); + HBITMAP hbmpDst = NULL; + if (NULL != hbmpSrc) + { + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if( NULL != hdc) + { + HDC hdcDst = CreateCompatibleDC(hdc); + HDC hdcSrc = CreateCompatibleDC(hdc); + if (NULL != hdcDst && NULL != hdcSrc) + { + INT imageWidth = rectSrc.right - rectSrc.left; + INT imageHeight = rectSrc.bottom - rectSrc.top; + + BITMAPINFOHEADER header; + ZeroMemory(&header, sizeof(BITMAPINFOHEADER)); + + header.biSize = sizeof(BITMAPINFOHEADER); + header.biCompression = BI_RGB; + header.biBitCount = 24; + header.biPlanes = 1; + header.biWidth = imageWidth; + header.biHeight = -imageHeight; + void *pixelData; + + hbmpDst = CreateDIBSection(hdc, (LPBITMAPINFO)&header, DIB_RGB_COLORS, (void**)&pixelData, NULL, 0); + if (NULL != hbmpDst) + { + HBITMAP hbmpDstOrig = (HBITMAP)SelectObject(hdcDst, hbmpDst); + HBITMAP hbmpSrcOrig = (HBITMAP)SelectObject(hdcSrc, hbmpSrc); + + RECT fillRect; + SetRect(&fillRect, 0, 0, imageWidth, imageHeight); + FillRect(hdcDst, &fillRect, hbrBack); + + BLENDFUNCTION bf; + bf.BlendOp = AC_SRC_OVER; + bf.BlendFlags = 0; + bf.SourceConstantAlpha = 255; + bf.AlphaFormat = AC_SRC_ALPHA; + + GdiAlphaBlend(hdcDst, 0, 0, imageWidth, imageHeight, + hdcSrc, rectSrc.left, rectSrc.top, imageWidth, imageHeight, bf); + + SelectObject(hdcDst, hbmpDstOrig); + SelectObject(hdcSrc, hbmpSrcOrig); + + if (NULL != pWidth) *pWidth = imageWidth; + if (NULL != pHeight) *pHeight = imageHeight; + } + } + + if (NULL != hdcDst) DeleteDC(hdcDst); + if (NULL != hdcSrc) DeleteDC(hdcSrc); + ReleaseDC(hwnd, hdc); + } + } + loginGui->Release(); + return hbmpDst; +} + +void LoginPage::UpdateLayout(BOOL fRedraw) +{ + RECT clientRect; + GetClientRect(hwnd, &clientRect); + + UINT flags = SWP_NOACTIVATE | SWP_NOZORDER; + if (FALSE == fRedraw) flags |= SWP_NOREDRAW; + + const INT szControls[] = { IDC_HELPLINK, IDC_TITLE}; + HDWP hdwp = BeginDeferWindowPos(ARRAYSIZE(szControls)); + + RECT rect; + INT cx, cy, x, y; + + for (INT i = 0; i < ARRAYSIZE(szControls); i++) + { + HWND hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL == hControl || FALSE == GetWindowRect(hControl, &rect)) continue; + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rect, 2); + x = rect.left; + y = rect.top; + cx = rect.right - rect.left; + cy = rect.bottom - rect.top; + + switch(szControls[i]) + { + case IDC_HELPLINK: + x = clientRect.right - cx - 1; + if (x < clientRect.left + 1) x = clientRect.left + 1; + y = clientRect.top + 1; + break; + case IDC_TITLE: + x = clientRect.left + margins.left; + y = clientRect.top + margins.top; + cx = clientRect.right - margins.right - x; + cy = 0; + + LoginBox_GetWindowTextSize(hControl, cx, &cx, &cy); + + if ((cx + x) > (clientRect.right - margins.right)) + cx = clientRect.right - margins.right - x; + if ((cy + y) > (clientRect.bottom - margins.bottom)) + cy = clientRect.bottom - margins.bottom - y; + break; + } + + hdwp = DeferWindowPos(hdwp, hControl, NULL, x, y, cx, cy, flags); + if (NULL == hdwp) break; + } + + if (NULL != hdwp) + EndDeferWindowPos(hdwp); +} + +BOOL LoginPage::GetPageRect(RECT *prc) +{ + if (NULL == prc || FALSE == GetClientRect(hwnd, prc)) + return FALSE; + + prc->left += margins.left; + prc->top += margins.top; + prc->right -= margins.right; + prc->bottom -= margins.bottom; + + HWND hTitle = GetDlgItem(hwnd, IDC_TITLE); + if (NULL != hTitle) + { + UINT titleStyle = GetWindowStyle(hTitle); + if (0 != (WS_VISIBLE & titleStyle)) + { + RECT titleRect; + if (FALSE != GetWindowRect(hTitle, &titleRect)) + { + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&titleRect, 2); + + titleRect.bottom += GetTitleSpacing(); + + if (titleRect.bottom > prc->top) + { + prc->top = titleRect.bottom; + if (prc->top > prc->bottom) + prc->top = prc->bottom; + } + } + } + } + return TRUE; +} + +INT LoginPage::GetTitleSpacing() +{ + HWND hTitle = GetDlgItem(hwnd, IDC_TITLE); + if (NULL == hTitle) return 0; + + HFONT fontTitle = (HFONT)SendMessage(hTitle, WM_GETFONT, 0, 0L); + + HDC hdc = GetDCEx(hTitle, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL == hdc) return 0; + + HFONT fontOrig = (HFONT)SelectObject(hdc, fontTitle); + + TEXTMETRIC tm; + if (FALSE == GetTextMetrics(hdc, &tm)) + tm.tmHeight = 0; + + SelectObject(hdc, fontOrig); + ReleaseDC(hTitle, hdc); + + return tm.tmHeight; +} + + +BOOL LoginPage::IsHelpAvailable() +{ + LoginProvider *provider; + if (NULL == hLoginbox || + FALSE == LoginBox_GetActiveProvider(hLoginbox, &provider) || + NULL == provider) + { + return FALSE; + } + + WCHAR szBuffer[8192] = {0}; + + HRESULT hr = provider->GetHelpLink(szBuffer, ARRAYSIZE(szBuffer)); + provider->Release(); + + if (FAILED(hr) || L'\0' == szBuffer[0]) + return FALSE; + + return TRUE; +} + +BOOL LoginPage::ShowHelp() +{ + LoginProvider *provider; + if (NULL == hLoginbox || + FALSE == LoginBox_GetActiveProvider(hLoginbox, &provider) || + NULL == provider) + { + return FALSE; + } + + WCHAR szBuffer[8192] = {0}; + + HRESULT hr = provider->GetHelpLink(szBuffer, ARRAYSIZE(szBuffer)); + provider->Release(); + + if (FAILED(hr) || L'\0' == szBuffer[0]) + return FALSE; + + return LoginBox_OpenUrl(hwnd, szBuffer, TRUE); +} + +BOOL LoginPage::SetLabelText(INT controlId, LPCWSTR pszText) +{ + HWND hLabel = GetDlgItem(hwnd, controlId); + if (NULL == hLabel) return FALSE; + + LPWSTR pszTemp = NULL; + if (NULL != pszText && L'\0' != *pszText) + { + INT cchLabel = lstrlenW(pszText); + if (cchLabel > 0 && L':' != pszText[cchLabel-1]) + { + pszTemp = LoginBox_MallocString(cchLabel + 2); + if (NULL != pszTemp) + { + CopyMemory(pszTemp, pszText, sizeof(WCHAR) * cchLabel); + pszTemp[cchLabel] = L':'; + pszTemp[cchLabel + 1] = L'\0'; + pszText = pszTemp; + } + } + } + + BOOL result = SetWindowText(hLabel, pszText); + + if (NULL != pszTemp) + LoginBox_FreeString(pszTemp); + + return result; +} + +BOOL LoginPage::OnInitDialog(HWND hFocus, LPARAM param) +{ + HWND hControl = CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Static", NULL, + WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + 0, 0, 100, 24, hwnd, (HMENU)IDC_TITLE, NULL, 0L); + + if (NULL != hControl) + { + HFONT fontTitle = NULL; + LoginGuiObject *loginGui; + if (SUCCEEDED(LoginGuiObject::QueryInstance(&loginGui))) + { + fontTitle = loginGui->GetTitleFont(); + loginGui->Release(); + } + + if (NULL == fontTitle) + fontTitle = (HFONT)SNDMSG(hwnd, WM_GETFONT, 0, 0L); + + if (NULL != fontTitle) + SNDMSG(hControl, WM_SETFONT, (WPARAM)fontTitle, 0L); + } + + INT imageWidth, imageHeight; + HBITMAP bitmapHelp = LoginPage_GetHelpBitmap(hwnd, hbrBack, &imageWidth, &imageHeight); + if (NULL != bitmapHelp) + { + UINT controlStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | + SS_BITMAP | SS_NOTIFY; + + if (FALSE != IsHelpAvailable()) + controlStyle |= WS_VISIBLE; + + hControl = CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Static", NULL, controlStyle, + 0, 0, 0, 0, hwnd, (HMENU)IDC_HELPLINK, NULL, 0L); + + HBITMAP bitmapSelected = NULL; + + if (NULL != hControl) + { + bitmapSelected = (HBITMAP)SNDMSG(hControl, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)bitmapHelp); + if (NULL != bitmapSelected) + DeleteObject(bitmapSelected); + bitmapSelected = (HBITMAP)SNDMSG(hControl, STM_GETIMAGE, (WPARAM)IMAGE_BITMAP, 0L); + } + + if (bitmapSelected != bitmapHelp) + DeleteObject(bitmapHelp); + } + + + UpdateMargins(); + UpdateColors(); + UpdateLayout(FALSE); + PostMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0L); + + return FALSE; +} + +void LoginPage::OnDestroy() +{ + HWND hControl = GetDlgItem(hwnd, IDC_HELPLINK); + if (NULL != hControl) + { + HBITMAP bitmapSelected = (HBITMAP)SNDMSG(hControl, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, 0L); + if (NULL != bitmapSelected) + DeleteObject(bitmapSelected); + } +} + +void LoginPage::OnWindowPosChanged(const WINDOWPOS *pwp) +{ + if (SWP_NOSIZE != ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags)) + UpdateLayout(0 == (SWP_NOREDRAW & pwp->flags)); +} + +void LoginPage::OnCommand(UINT commandId, UINT eventType, HWND hControl) +{ + switch(commandId) + { + case IDC_HELPLINK: + switch(eventType) + { + case STN_CLICKED: + ShowHelp(); + break; + } + break; + } +} + +BOOL LoginPage::OnNotify(UINT controlId, const NMHDR *pnmh) +{ + return FALSE; +} + +BOOL LoginPage::OnGetLoginData(LoginData **ppLoginData) +{ + if (FAILED(LoginData::CreateInstance(NULL, hwnd, hLoginbox, ppLoginData))) + return FALSE; + + return TRUE; +} + +void LoginPage::OnUpdateStateChange(BOOL updateActive) +{ +} + +BOOL LoginPage::OnSetUsername(LPCWSTR pszUsername) +{ + return FALSE; +} + +BOOL LoginPage::OnSetPassword(LPCWSTR pszPassword) +{ + return FALSE; +} + +HWND LoginPage::OnGetFirstItem() +{ + return NULL; +} + +BOOL LoginPage::OnSetTitle(LPCWSTR pszTitle) +{ + HWND hControl = GetDlgItem(hwnd, IDC_TITLE); + if (NULL == hControl) return FALSE; + + BOOL result = (BOOL)SNDMSG(hControl, WM_SETTEXT, 0, (LPARAM)pszTitle); + if (FALSE != result) + UpdateLayout(TRUE); + + return result; +} + +HBRUSH LoginPage::OnGetStaticColor(HDC hdc, HWND hControl) +{ + INT controlId = (INT)GetWindowLongPtr(hControl, GWLP_ID); + switch(controlId) + { + case IDC_TITLE: + SetTextColor(hdc, rgbTitle); + break; + default: + SetTextColor(hdc, rgbText); + break; + } + + SetBkColor(hdc, rgbBack); + return hbrBack; +} + +HBRUSH LoginPage::OnGetDialogColor(HDC hdc, HWND hControl) +{ + SetTextColor(hdc, rgbText); + SetBkColor(hdc, rgbBack); + return hbrBack; +} + +BOOL LoginPage::OnSetCursor(HWND hTarget, INT hitCode, INT uMsg) +{ + HWND hControl = GetDlgItem(hwnd, IDC_HELPLINK); + if (hControl == hTarget && NULL != hControl) + { + UINT controlStyle = GetWindowStyle(hControl); + if (WS_VISIBLE == ((WS_VISIBLE | WS_DISABLED) & controlStyle)) + { + HCURSOR hCursor = LoadCursor(NULL, IDC_HAND); + if (NULL != hCursor) + { + SetCursor(hCursor); + return TRUE; + } + } + } + return FALSE; +} + +BOOL LoginPage::OnHelp(HELPINFO *phi) +{ + return ShowHelp(); +} + +void LoginPage::OnThemeChanged() +{ + UpdateColors(); +} + +void LoginPage::OnSysColorChanged() +{ + UpdateColors(); +} + +INT_PTR LoginPage::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: return OnInitDialog((HWND)wParam, lParam); + case WM_DESTROY: OnDestroy(); return TRUE; + case WM_NOTIFY: MSGRESULT(hwnd, OnNotify((INT)wParam, (NMHDR*)lParam)); + case WM_COMMAND: OnCommand(LOWORD(wParam), HIWORD(wParam), (HWND)lParam); return TRUE; + case WM_WINDOWPOSCHANGED: OnWindowPosChanged((WINDOWPOS*)lParam); return TRUE; + case WM_SIZE: return TRUE; + case WM_CTLCOLORSTATIC: return (INT_PTR)OnGetStaticColor((HDC)wParam, (HWND)lParam); + case WM_CTLCOLORDLG: return (INT_PTR)OnGetDialogColor((HDC)wParam, (HWND)lParam); + case WM_SETCURSOR: + if (FALSE != OnSetCursor((HWND)wParam, LOWORD(lParam), HIWORD(lParam))) + MSGRESULT(hwnd, TRUE); + break; + case WM_HELP: + if (FALSE != OnHelp((HELPINFO*)lParam)) + MSGRESULT(hwnd, TRUE); + break; + + case WM_THEMECHANGED: OnThemeChanged(); return TRUE; + case WM_SYSCOLORCHANGE: OnSysColorChanged(); return TRUE; + + case NLPM_GETLOGINDATA: MSGRESULT(hwnd, OnGetLoginData((LoginData**)lParam)); + case NLPM_UPDATESTATECHANGE: OnUpdateStateChange((BOOL)lParam); return TRUE; + case NLPM_SETUSERNAME: MSGRESULT(hwnd, OnSetUsername((LPCWSTR)lParam)); + case NLPM_SETPASSWORD: MSGRESULT(hwnd, OnSetPassword((LPCWSTR)lParam)); + case NLPM_GETFIRSTITEM: MSGRESULT(hwnd, OnGetFirstItem()); + case NLPM_SETTITLE: MSGRESULT(hwnd, OnSetTitle((LPCWSTR)lParam)); + + } + + return FALSE; +} + +static INT_PTR CALLBACK LoginPage_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static ATOM LOGINPAGE_PROP = 0; + LoginPage *page = (LoginPage*)GetProp(hwnd, MAKEINTATOM(LOGINPAGE_PROP)); + + if (NULL == page) + { + switch(uMsg) + { + case WM_INITDIALOG: + if (0 == LOGINPAGE_PROP) + { + LOGINPAGE_PROP = GlobalAddAtomW(L"NullsoftLoginPageProp"); + if (0 == LOGINPAGE_PROP) + return 0; + } + + if (NULL != lParam) + { + LOGINPAGECREATEPARAM *create = (LOGINPAGECREATEPARAM*)lParam; + lParam = create->lParam; + if (SUCCEEDED(create->fnCreator(hwnd, create->hLoginbox, &page))) + { + if (FALSE == SetProp(hwnd, MAKEINTATOM(LOGINPAGE_PROP), (HANDLE)page)) + { + delete(page); + page = NULL; + } + } + } + + if (NULL != page) + return page->DialogProc(uMsg, wParam, lParam); + + break; + } + return 0; + } + + INT_PTR result = page->DialogProc(uMsg, wParam, lParam); + + if (WM_DESTROY == uMsg) + { + RemoveProp(hwnd, MAKEINTATOM(LOGINPAGE_PROP)); + delete(page); + } + + return result; +} + diff --git a/Src/auth/Loginbox/loginPopup.cpp b/Src/auth/Loginbox/loginPopup.cpp new file mode 100644 index 00000000..e0fc6494 --- /dev/null +++ b/Src/auth/Loginbox/loginPopup.cpp @@ -0,0 +1,733 @@ +#include "./loginPopup.h" +#include "../api.h" +#include "../../nu/Vectors.h" +#include "./loginNotifier.h" +#include "./common.h" + +typedef Vector<HWND> WindowList; + +typedef struct __THREADPOPUPDATA +{ + HHOOK hHook; + WindowList windowList; +} THREADPOPUPDATA; + +static size_t threadStorage = TLS_OUT_OF_INDEXES; + +typedef struct __LOGINPOPUPCREATEPARAM +{ + LoginPopup::Creator fnCreator; + LPARAM lParam; +} LOGINPOPUPCREATEPARAM; + +#define IDC_NOTIFIER 10001 + +//#define COLOR_TITLE COLOR_3DLIGHT +//#define COLOR_TITLETEXT COLOR_WINDOWTEXT +#define COLOR_CLIENT COLOR_3DFACE +#define COLOR_CLIENTTEXT COLOR_WINDOWTEXT + +LoginPopup::LoginPopup(HWND hwnd, UINT popupType, LPCWSTR pszTitle) + : alertType(-1), alertMessage(NULL) +{ + buttonHeight = buttonSpace = 0; + this->hwnd = hwnd; + + if (NULL != pszTitle) + SetTitle(popupType, pszTitle); +} + +LoginPopup::~LoginPopup() +{ + RegisterPopup(hwnd, FALSE); + + if (FALSE == IS_INTRESOURCE(alertMessage)) + LoginBox_FreeString(alertMessage); +} + +HWND LoginPopup::CreatePopup(LPCWSTR pszTemplate, HWND hParent, LPARAM param, Creator fnCreator) +{ + if (NULL == hParent || NULL == pszTemplate || NULL == fnCreator) + return NULL; + + LOGINPOPUPCREATEPARAM createParam; + createParam.fnCreator = fnCreator; + createParam.lParam = param; + + return WASABI_API_CREATEDIALOGPARAMW((INT)(INT_PTR)pszTemplate, hParent, LoginPopup_DialogProc, (LPARAM)&createParam); +} + +BOOL LoginPopup::RegisterPopup(HWND hwnd, BOOL fRegister) +{ + if (NULL == hwnd || GetWindowThreadProcessId(hwnd, NULL) != GetCurrentThreadId()) + return FALSE; + + THREADPOPUPDATA *data = NULL; + if (TLS_OUT_OF_INDEXES == threadStorage) + { + if (NULL == WASABI_API_APP) + return FALSE; + + threadStorage = WASABI_API_APP->AllocateThreadStorage(); + if (TLS_OUT_OF_INDEXES == threadStorage) + return FALSE; + } + else + { + data = (THREADPOPUPDATA*)WASABI_API_APP->GetThreadStorage(threadStorage); + } + + if (NULL == data) + { + data = new THREADPOPUPDATA(); + if (NULL == data) return FALSE; + data->hHook = SetWindowsHookEx(WH_MSGFILTER, LoginPopup_MessageFilter, NULL, GetCurrentThreadId()); + if (NULL == data->hHook) + { + delete data; + return FALSE; + } + WASABI_API_APP->SetThreadStorage(threadStorage, data); + } + + size_t index = data->windowList.size(); + while(index--) + { + if (hwnd == data->windowList[index]) + { + if (FALSE == fRegister) + { + data->windowList.eraseAt(index); + if (0 == data->windowList.size()) + { + if (NULL != data->hHook) + UnhookWindowsHookEx(data->hHook); + WASABI_API_APP->SetThreadStorage(threadStorage, NULL); + delete data; + } + } + return TRUE; + } + } + + if (FALSE != fRegister) + { + data->windowList.push_back(hwnd); + return TRUE; + } + + return FALSE; +} + +BOOL LoginPopup::EnumeratePopups(HWND hHost, Enumerator callback, LPARAM param) +{ + if (NULL == callback || + NULL == hHost || GetWindowThreadProcessId(hHost, NULL) != GetCurrentThreadId()) + return FALSE; + + THREADPOPUPDATA *data = (TLS_OUT_OF_INDEXES != threadStorage && NULL != WASABI_API_APP) ? + (THREADPOPUPDATA *)WASABI_API_APP->GetThreadStorage(threadStorage) : NULL; + if (NULL == data) + return FALSE; + + size_t index = data->windowList.size(); + while(index--) + { + HWND hPopup = data->windowList[index]; + if (IsChild(hHost, hPopup) && FALSE == callback(hPopup, param)) + return FALSE; + } + + return TRUE; +} + +BOOL LoginPopup::AnyPopup(HWND hHost) +{ + if (NULL == hHost || GetWindowThreadProcessId(hHost, NULL) != GetCurrentThreadId()) + return FALSE; + + THREADPOPUPDATA *data = (TLS_OUT_OF_INDEXES != threadStorage && NULL != WASABI_API_APP) ? + (THREADPOPUPDATA *)WASABI_API_APP->GetThreadStorage(threadStorage) : NULL; + if (NULL == data) + return FALSE; + + size_t index = data->windowList.size(); + while(index--) + { + if (IsChild(hHost, data->windowList[index])) + return TRUE; + } + + return FALSE; +} + +void LoginPopup::UpdateLayout(BOOL fRedraw) +{ + RECT rect; + GetClientRect(hwnd, &rect); + + HWND hNotifier; + hNotifier = GetDlgItem(hwnd, IDC_NOTIFIER); + if (NULL != hNotifier) + { + INT height = LoginNotifier_GetIdealHeight(hNotifier); + SetWindowPos(hNotifier, NULL, rect.left, rect.top, rect.right - rect.left, height, + SWP_NOACTIVATE | SWP_NOZORDER); + } +} + +void LoginPopup::Paint(HDC hdc, const RECT *prcPaint, BOOL fErase) +{ + if (FALSE != fErase) + { + RECT clientRect; + GetClientRect(hwnd, &clientRect); + + COLORREF rgbOrig, rgbPart; + rgbOrig = GetBkColor(hdc); + + if (buttonHeight > 0) + { + RECT buttonRect; + SetRect(&buttonRect, + clientRect.left, clientRect.bottom - (2* clientMargins.bottom + buttonHeight), + clientRect.right, clientRect.bottom); + + clientRect.bottom = buttonRect.top; + + buttonRect.top++; + rgbPart = GetSysColor(COLOR_3DFACE); + SetBkColor(hdc, rgbPart); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &buttonRect, NULL, 0, NULL); + + buttonRect.bottom = buttonRect.top; + buttonRect.top--; + + rgbPart = GetSysColor(COLOR_3DLIGHT); + SetBkColor(hdc, rgbPart); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &buttonRect, NULL, 0, NULL); + } + + rgbPart = GetSysColor(COLOR_CLIENT); + SetBkColor(hdc, rgbPart); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &clientRect, NULL, 0, NULL); + + SetBkColor(hdc, rgbOrig); + } +} + +void LoginPopup::EndDialog(INT_PTR code) +{ + DestroyWindow(hwnd); +} + +void LoginPopup::UpdateMargins() +{ + SetRect(&clientMargins, 8, 6, 8, 6); + MapDialogRect(hwnd, &clientMargins); + + SetRect(&infoMargins, 6, 8, 6, 8); + MapDialogRect(hwnd, &infoMargins); + + RECT rect; + SetRect(&rect, 4, 15, 0, 0); + MapDialogRect(hwnd, &rect); + buttonHeight = rect.top; + buttonSpace = rect.left; +} + +void LoginPopup::SetTitle(UINT type, LPCWSTR title) +{ + popupType = type; + + + if (NULL == title || FALSE == IS_INTRESOURCE(title)) + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)title); + else + { + WCHAR szBuffer[256] = {0}; + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)title, szBuffer, ARRAYSIZE(szBuffer)); + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)szBuffer); + } +} + +void LoginPopup::UpdateTitle(BOOL playBeep) +{ + HWND hNotifier; + hNotifier = GetDlgItem(hwnd, IDC_NOTIFIER); + if (NULL == hNotifier) return; + + WCHAR szBuffer[512] = {0}; + LPCWSTR text; + UINT type; + + if (-1 != alertType && NULL != alertMessage) + { + type = alertType; + text = (IS_INTRESOURCE(alertMessage)) ? + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)alertMessage, szBuffer, ARRAYSIZE(szBuffer)) : + alertMessage; + } + else + { + type = popupType; + SendMessage(hwnd, WM_GETTEXT, (WPARAM)ARRAYSIZE(szBuffer), (LPARAM)szBuffer); + text = szBuffer; + } + + LoginNotifier_Notify(hNotifier, type, text); + + UINT windowStyle = GetWindowStyle(hNotifier); + if (0 == (WS_VISIBLE & windowStyle)) + { + ShowWindow(hNotifier, SW_SHOWNA); + } + + if (FALSE != playBeep) + LoginNotifier_PlayBeep(hNotifier); +} + +void LoginPopup::SetAlert(UINT type, LPCWSTR message) +{ + alertType = type; + + if (FALSE == IS_INTRESOURCE(alertMessage)) + LoginBox_FreeString(alertMessage); + + if (NULL == message) + { + alertMessage = NULL; + return; + } + + if (IS_INTRESOURCE(message)) + alertMessage = (LPWSTR)message; + else + alertMessage = LoginBox_CopyString(message); +} + +void LoginPopup::RemoveAlert() +{ + SetAlert(-1, NULL); +} + +LRESULT LoginPopup::SendNotification(UINT code, NMHDR *pnmh) +{ + if (NULL == pnmh) return 0L; + + HWND hParent = GetAncestor(hwnd, GA_PARENT); + if(NULL == hParent) return 0L; + + pnmh->code = code; + pnmh->hwndFrom = hwnd; + pnmh->idFrom = (UINT_PTR)GetWindowLongPtr(hwnd, GWLP_ID); + + return SendMessage(hParent, WM_NOTIFY, (WPARAM)pnmh->idFrom, (LPARAM)pnmh); +} + + +BOOL LoginPopup::GetInfoRect(RECT *rect) +{ + if (NULL == rect) + return FALSE; + + LONG notifierHeight = 0; + HWND hNotifier = GetDlgItem(hwnd, IDC_NOTIFIER); + if (NULL != hNotifier && 0 != (WS_VISIBLE & GetWindowStyle(hNotifier)) && + FALSE != GetWindowRect(hNotifier, rect)) + { + notifierHeight = rect->bottom - rect->top; + if (notifierHeight < 0) notifierHeight = 0; + } + + if (FALSE == GetClientRect(hwnd, rect)) + return FALSE; + + rect->left += (clientMargins.left + infoMargins.left); + rect->right -= (clientMargins.right + infoMargins.right); + + + if (0 != notifierHeight) + rect->top += (notifierHeight + infoMargins.top); + else + rect->top += (clientMargins.top + infoMargins.top); + + if (buttonHeight > 0) + rect->bottom -= (2 * clientMargins.bottom + buttonHeight + infoMargins.bottom); + else + rect->bottom -= (clientMargins.bottom + infoMargins.bottom); + + if (rect->right < rect->left) rect->right = rect->left; + if (rect->bottom < rect->top) rect->bottom = rect->top; + + return TRUE; +} + +BOOL LoginPopup::CalculateWindowRect(LONG infoWidth, LONG infoHeight, const INT *buttonList, UINT buttonCount, BOOL includeTitle, RECT *rect) +{ + if (NULL == rect) + return FALSE; + + if (infoWidth < 110) infoWidth = 110; + if (infoHeight < 32) infoHeight = 32; + + LONG minWidth = 0; + + LONG notifierHeight = 0; + + HWND hNotifier = GetDlgItem(hwnd, IDC_NOTIFIER); + if (NULL != hNotifier && 0 != (WS_VISIBLE & GetWindowStyle(hNotifier)) && + FALSE != GetWindowRect(hNotifier, rect)) + { + notifierHeight = rect->bottom - rect->top; + if (notifierHeight < 0) notifierHeight = 0; + + if (FALSE != includeTitle) + { + SIZE size; + if (FALSE != LoginNotifier_GetIdealSize(hNotifier, &size)) + minWidth = size.cx + clientMargins.right; + } + } + + if (NULL != buttonList && buttonCount > 0 && + ((HDWP)TRUE) == LayoutButtons(NULL, buttonList, buttonCount, FALSE, rect)) + { + LONG buttonWidth = (rect->right - rect->left) + clientMargins.left + clientMargins.right; + if (buttonWidth > minWidth) + minWidth = buttonWidth; + } + + rect->left = 0; + rect->top = 0; + rect->right = rect->left + infoWidth; + rect->bottom = rect->top + infoHeight; + + rect->right += (clientMargins.left + infoMargins.left + clientMargins.right + infoMargins.right); + if ((rect->right - rect->left) < minWidth) + rect->right = rect->left + minWidth; + + rect->bottom += (0 != notifierHeight) ? + (notifierHeight + infoMargins.top) : (clientMargins.top + infoMargins.top); + + rect->bottom += (buttonHeight > 0) ? + (2 * clientMargins.bottom + buttonHeight + infoMargins.bottom) : (clientMargins.bottom + infoMargins.bottom); + + return TRUE; + +} + +HDWP LoginPopup::LayoutButtons(HDWP hdwp, const INT *buttonList, UINT buttonCount, BOOL redraw, RECT *rectOut) +{ + RECT rect; + GetClientRect(hwnd, &rect); + rect.left += clientMargins.left; + rect.top += clientMargins.top; + rect.right -= clientMargins.right; + rect.bottom -= clientMargins.bottom; + + return LoginBox_LayoutButtonBar(hdwp, hwnd, buttonList, buttonCount, &rect, + buttonHeight, buttonSpace, redraw, rectOut); +} + +BOOL LoginPopup::GetTextSize(HWND hText, LONG width, SIZE *size) +{ + if (NULL == hText) return FALSE; + + WCHAR szBuffer[4096] = {0}; + INT cchLen = (INT)SendMessage(hText, WM_GETTEXT, ARRAYSIZE(szBuffer), (LPARAM)szBuffer); + if (0 == cchLen) + { + size->cx = 0; + size->cy = 0; + return TRUE; + } + + HDC hdc = GetDCEx(hText, NULL, DCX_CACHE | DCX_WINDOW | DCX_NORESETATTRS); + if (NULL == hdc) + return FALSE; + + HFONT font = (HFONT)SendMessage(hText, WM_GETFONT, 0, 0L); + HFONT fontOrig = (HFONT)SelectObject(hdc, font); + + BOOL resultOk; + RECT rect; + SetRect(&rect, 0, 0, width, 0); + resultOk = DrawText(hdc, szBuffer, cchLen, &rect, + DT_CALCRECT | DT_EXTERNALLEADING | DT_LEFT | DT_NOPREFIX | DT_WORDBREAK); + + if(FALSE != resultOk) + { + size->cx = (rect.right - rect.left); + size->cy = (rect.bottom - rect.top); + } + + SelectObject(hdc, fontOrig); + ReleaseDC(hText, hdc); + + return resultOk; +} + +BOOL LoginPopup::OnInitDialog(HWND hFocus, LPARAM param) +{ + RegisterPopup(hwnd, TRUE); + + RECT rect; + if (FALSE == GetWindowRect(hwnd, &rect)) + SetRectEmpty(&rect); + + idealSize.cx = rect.right - rect.left; + idealSize.cy = rect.bottom - rect.top; + + + HWND hNotifier = LoginNotifier_CreateWindow(0, WS_CHILD | WS_CLIPSIBLINGS, 0, 0, 0, 0, hwnd, IDC_NOTIFIER); + if (NULL != hNotifier) + { + HFONT hFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L); + if (NULL != hFont) + SendMessage(hNotifier, WM_SETFONT, (WPARAM)hFont, 0L); + +#ifdef COLOR_TITLE + LoginNotifier_SetBkColor(hNotifier, GetSysColor(COLOR_TITLE)); +#endif //COLOR_TITLE + +#ifdef COLOR_TITLETEXT + LoginNotifier_SetTextColor(hNotifier, GetSysColor(COLOR_TITLETEXT)); +#endif //COLOR_TITLETEXT + + UpdateTitle(FALSE); + } + + UpdateMargins(); + UpdateLayout(FALSE); + + PostMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0L); + return FALSE; +} + +void LoginPopup::OnDestroy() +{ +} + +void LoginPopup::OnWindowPosChanged(const WINDOWPOS *pwp) +{ + if (SWP_NOSIZE != ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags)) + UpdateLayout(0 == (SWP_NOREDRAW & pwp->flags)); +} + +void LoginPopup::OnCommand(UINT commandId, UINT eventType, HWND hControl) +{ + switch(commandId) + { + case IDOK: + case IDCANCEL: + EndDialog(commandId); + break; + } +} + +LRESULT LoginPopup::OnNotify(UINT controlId, const NMHDR *pnmh) +{ + return FALSE; +} + +void LoginPopup::OnPaint() +{ + PAINTSTRUCT ps; + if (BeginPaint(hwnd, &ps)) + { + if (ps.rcPaint.left != ps.rcPaint.right) + Paint(ps.hdc, &ps.rcPaint, ps.fErase); + EndPaint(hwnd, &ps); + } +} + +void LoginPopup::OnPrintClient(HDC hdc, UINT options) +{ + if (0 != (PRF_CLIENT & options)) + { + RECT clientRect; + if (GetClientRect(hwnd, &clientRect)) + Paint(hdc, &clientRect, TRUE); + } +} + +HBRUSH LoginPopup::OnGetStaticColor(HDC hdc, HWND hControl) +{ + HBRUSH hb = (HBRUSH)GetSysColorBrush(COLOR_CLIENT); + SetTextColor(hdc, GetSysColor(COLOR_CLIENTTEXT)); + SetBkColor(hdc, GetSysColor(COLOR_CLIENT)); + return hb; +} + +void LoginPopup::OnSetFont(HFONT font, BOOL redraw) +{ + DefDlgProc(hwnd, WM_SETFONT, (WPARAM)font, MAKELPARAM(redraw, 0)); + UpdateMargins(); +} + +void LoginPopup::OnParentNotify(UINT eventId, UINT wParam, LPARAM lParam) +{ +} + +BOOL LoginPopup::OnUpdateWindowPos(const RECT* clientRect, RECT *rectOut) +{ + if (NULL == clientRect || NULL == rectOut) + return FALSE; + + LONG width = idealSize.cx; + LONG height = idealSize.cy; + rectOut->left = clientRect->left + ((clientRect->right - clientRect->left) - width)/2; + rectOut->top = clientRect->top+ ((clientRect->bottom - clientRect->top) - height)/2; + rectOut->right = rectOut->left + width; + rectOut->bottom = rectOut->top + height; + + return TRUE; +} + +void LoginPopup::OnPlayBeep() +{ + LoginBox_MessageBeep(MB_ICONASTERISK); +} + +INT_PTR LoginPopup::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: return OnInitDialog((HWND)wParam, lParam); + case WM_DESTROY: OnDestroy(); return TRUE; + case WM_NOTIFY: MSGRESULT(hwnd, OnNotify((INT)wParam, (NMHDR*)lParam)); + case WM_COMMAND: OnCommand(LOWORD(wParam), HIWORD(wParam), (HWND)lParam); return TRUE; + case WM_WINDOWPOSCHANGED: OnWindowPosChanged((WINDOWPOS*)lParam); return TRUE; + case WM_SIZE: return TRUE; + case WM_SETFONT: OnSetFont((HFONT)wParam, (BOOL)LOWORD(lParam)); return TRUE; + case WM_ERASEBKGND: MSGRESULT(hwnd, 0); + case WM_PAINT: OnPaint(); return TRUE; + case WM_PRINTCLIENT: OnPrintClient((HDC)wParam, (UINT)lParam); return TRUE; + case WM_CTLCOLORSTATIC: return (INT_PTR)OnGetStaticColor((HDC)wParam, (HWND)lParam); + case WM_PARENTNOTIFY: OnParentNotify(LOWORD(wParam), HIWORD(wParam), lParam); return TRUE; + + case NLPOPUP_UPDATEWNDPOS: MSGRESULT(hwnd, OnUpdateWindowPos((const RECT*)wParam, (RECT*)lParam)); + case NLPOPUP_PLAYBEEP: OnPlayBeep(); return TRUE; + } + return FALSE; +} + +static INT_PTR CALLBACK LoginPopup_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static ATOM LOGINPOPUP_PROP = 0; + LoginPopup *popup = (LoginPopup*)GetProp(hwnd, MAKEINTATOM(LOGINPOPUP_PROP)); + + if (NULL == popup) + { + switch(uMsg) + { + case WM_INITDIALOG: + if (0 == LOGINPOPUP_PROP) + { + LOGINPOPUP_PROP = GlobalAddAtomW(L"NullsoftLoginPopupProp"); + if (0 == LOGINPOPUP_PROP) + return 0; + } + + if (NULL != lParam) + { + LOGINPOPUPCREATEPARAM *create = (LOGINPOPUPCREATEPARAM*)lParam; + lParam = create->lParam; + if (SUCCEEDED(create->fnCreator(hwnd, lParam, &popup))) + { + if (FALSE == SetProp(hwnd, MAKEINTATOM(LOGINPOPUP_PROP), (HANDLE)popup)) + { + delete(popup); + popup = NULL; + } + } + } + + if (NULL != popup) + return popup->DialogProc(uMsg, wParam, lParam); + + break; + } + return 0; + } + + INT_PTR result = popup->DialogProc(uMsg, wParam, lParam); + + if (WM_NCDESTROY == uMsg) + { + RemoveProp(hwnd, MAKEINTATOM(LOGINPOPUP_PROP)); + delete(popup); + } + + return result; +} + +static LRESULT CALLBACK LoginPopup_MessageFilter(INT code, WPARAM wParam, LPARAM lParam) +{ + THREADPOPUPDATA *data = (NULL != WASABI_API_APP && TLS_OUT_OF_INDEXES != threadStorage) ? + (THREADPOPUPDATA*)WASABI_API_APP->GetThreadStorage(threadStorage) : NULL; + if (NULL == data) + return 0; + + if (code >= 0) + { + MSG *pMsg = (MSG*)lParam; + if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST) + { + if (L'C' == pMsg->wParam && 0 == (0x40000000 & pMsg->lParam) && + 0 != (0x8000 & GetAsyncKeyState(VK_MENU))) + { + pMsg->wParam = VK_ESCAPE; + } + + if ((VK_ESCAPE == pMsg->wParam || VK_RETURN == pMsg->wParam) && + 0 == (0x40000000 & pMsg->lParam)) + { + size_t index = data->windowList.size(); + while(index--) + { + HWND hPopup = data->windowList[index]; + if (IsChild(hPopup, pMsg->hwnd)) + { + INT commandId; + switch(pMsg->wParam) + { + case VK_ESCAPE: + commandId = IDCANCEL; + break; + case VK_RETURN: + if (0 != (DLGC_BUTTON & SendMessage(pMsg->hwnd, WM_GETDLGCODE, 0, 0L)) && + IsWindowVisible(pMsg->hwnd) && IsWindowEnabled(pMsg->hwnd)) + { + commandId = (INT)(INT_PTR)GetWindowLongPtr(pMsg->hwnd, GWLP_ID); + } + else + { + commandId = (INT)(INT_PTR)SendMessage(hPopup, DM_GETDEFID, 0, 0L); + if (DC_HASDEFID != HIWORD(commandId)) + commandId = IDOK; + } + break; + } + SendMessage(hPopup, WM_COMMAND, MAKEWPARAM(commandId, 0), (LPARAM)pMsg->hwnd); + return 1; + } + } + } + + // add mnemonic support here (http://msdn.microsoft.com/en-us/library/ms644995%28VS.85%29.aspx) + //HWND hPopup; + //size_t index = data->windowList.size(); + //while(index--) + //{ + // hPopup = data->windowList[index]; + // if (pMsg->hwnd == hPopup || IsChild(hPopup, pMsg->hwnd)) + // { + // + // } + //} + } + } + return CallNextHookEx(data->hHook, code, wParam, lParam); +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginPopup.h b/Src/auth/Loginbox/loginPopup.h new file mode 100644 index 00000000..610b482f --- /dev/null +++ b/Src/auth/Loginbox/loginPopup.h @@ -0,0 +1,100 @@ +#ifndef NULLSOFT_AUTH_LOGINPOPUP_HEADER +#define NULLSOFT_AUTH_LOGINPOPUP_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +// messages +#define NLPOPUP_FIRST (WM_APP + 100) +#define NLPOPUP_UPDATEWNDPOS (NLPOPUP_FIRST + 0) // wParam - (WPARAM)(const RECT*)clientRect, lParam = (LPARAM)(RECT*)popupRectOut; Return TRUE if you set controlRect; +#define LoginPopup_UpdateWindowPos(/*HWND*/ __hwnd, /*const RECT* */__clientRect, /*RECT* */__popupRectOut)\ + ((BOOL)SNDMSG((__hwnd), NLPOPUP_UPDATEWNDPOS, (WPARAM)(__clientRect), (LPARAM)(__popupRectOut))) + +#define NLPOPUP_PLAYBEEP (NLPOPUP_FIRST + 1) // wParam - not used, lParam - not used; Return ignored +#define LoginPopup_PlayBeep(/*HWND*/ __hwnd)\ + (SNDMSG((__hwnd), NLPOPUP_PLAYBEEP, 0, 0L)) + +// notifications +#define NLPOPUPN_FIRST (100) + +typedef struct __NLPNRESULT +{ + NMHDR hdr; + INT_PTR exitCode; +} NLPNRESULT; + +#define NLPN_RESULT (NLPOPUPN_FIRST + 0) + +class __declspec(novtable) LoginPopup +{ +public: + typedef HRESULT (CALLBACK *Creator)(HWND /*hwnd*/, LPARAM /*param*/, LoginPopup** /*instance*/); + typedef BOOL (CALLBACK *Enumerator)(HWND /*hwnd*/, LPARAM /*param*/); + +protected: + LoginPopup(HWND hwnd, UINT popupType, LPCWSTR pszTitle); + virtual ~LoginPopup(); + +protected: + static HWND CreatePopup(LPCWSTR pszTemplate, HWND hParent, LPARAM param, Creator fnCreator); + +public: + static BOOL RegisterPopup(HWND hwnd, BOOL fRegister); + static BOOL EnumeratePopups(HWND hHost, Enumerator callback, LPARAM param); + static BOOL AnyPopup(HWND hHost); + +protected: + virtual void UpdateLayout(BOOL fRedraw); + virtual void Paint(HDC hdc, const RECT *prcPaint, BOOL fErase); + virtual void EndDialog(INT_PTR code); + virtual void UpdateMargins(); + virtual void SetTitle(UINT type, LPCWSTR title); + virtual void SetAlert(UINT type, LPCWSTR message); + virtual void RemoveAlert(); + virtual void UpdateTitle(BOOL playBeep); + + BOOL GetInfoRect(RECT *rect); + BOOL CalculateWindowRect(LONG infoWidth, LONG infoHeight, const INT *buttonList, UINT buttonCount, BOOL includeTitle, RECT *rect); + + HDWP LayoutButtons(HDWP hdwp, const INT *buttonList, UINT buttonCount, BOOL redraw, RECT *rectOut); + LRESULT SendNotification(UINT code, NMHDR *pnmh); + BOOL GetTextSize(HWND hText, LONG width, SIZE *size); + +protected: + virtual BOOL OnInitDialog(HWND hFocus, LPARAM param); + virtual void OnDestroy(); + virtual void OnCommand(UINT commandId, UINT eventType, HWND hControl); + virtual LRESULT OnNotify(UINT controlId, const NMHDR *pnmh); + virtual HBRUSH OnGetStaticColor(HDC hdc, HWND hControl); + virtual void OnParentNotify(UINT eventId, UINT wParam, LPARAM lParam); + + void OnPaint(); + void OnWindowPosChanged(const WINDOWPOS *pwp); + void OnPrintClient(HDC hdc, UINT options); + void OnSetFont(HFONT font, BOOL redraw); + + virtual BOOL OnUpdateWindowPos(const RECT* clientRect, RECT *rectOut); + virtual void OnPlayBeep(); + + virtual INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + +private: + friend static INT_PTR CALLBACK LoginPopup_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + friend static LRESULT CALLBACK LoginPopup_MessageFilter(INT code, WPARAM wParam, LPARAM lParam); + +protected: + HWND hwnd; + SIZE idealSize; + RECT clientMargins; + RECT infoMargins; + LONG buttonHeight; + LONG buttonSpace; + UINT popupType; + UINT alertType; + LPWSTR alertMessage; +}; + +#endif //NULLSOFT_AUTH_LOGINPOPUP_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginProvider.cpp b/Src/auth/Loginbox/loginProvider.cpp new file mode 100644 index 00000000..04b2b662 --- /dev/null +++ b/Src/auth/Loginbox/loginProvider.cpp @@ -0,0 +1,231 @@ +#include "./loginProvider.h" +#include "./loginTemplate.h" +#include "./loginCommand.h" +#include "./common.h" + +#include <strsafe.h> + + +static HRESULT LoginProvider_SetString(LPWSTR *ppszTarget, LPCWSTR pszSource) +{ + if (NULL == ppszTarget) + return E_POINTER; + if (NULL != *ppszTarget) + free(*ppszTarget); + + + if (NULL == pszSource) + *ppszTarget = NULL; + else + { + *ppszTarget = LoginBox_CopyString(pszSource); + if (NULL == *ppszTarget) return E_OUTOFMEMORY; + } + return S_OK; +} + +LoginProvider::LoginProvider(const GUID *providerUid) + : ref(1), name(NULL), description(NULL), imagePath(NULL), tosUrl(NULL), + privacyUrl(NULL), helpUrl(NULL), pageTemplate(NULL), command(NULL) +{ + id = (NULL != providerUid) ? *providerUid : GUID_NULL; +} + +LoginProvider::~LoginProvider() +{ + LoginBox_FreeString(name); + LoginBox_FreeString(description); + LoginBox_FreeString(imagePath); + LoginBox_FreeString(tosUrl); + LoginBox_FreeString(privacyUrl); + LoginBox_FreeString(helpUrl); + if (NULL != pageTemplate) pageTemplate->Release(); + if (NULL != command) command->Release(); +} + +HRESULT LoginProvider::CreateInstance(const GUID *providerUid, LoginProvider **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == providerUid) return E_INVALIDARG; + *instance = new LoginProvider(providerUid); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +ULONG LoginProvider::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginProvider::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginProvider::IsIdentical(LoginProvider *test) +{ + if (NULL == test) + return E_INVALIDARG; + + if (FALSE == IsEqualGUID(id, test->id)) + return S_FALSE; + + + if (S_OK != LoginBox_IsStrEq(name, test->name) || + S_OK != LoginBox_IsStrEq(description, test->description) || + S_OK != LoginBox_IsStrEqInvI(imagePath, test->imagePath) || + S_OK != LoginBox_IsStrEqInvI(tosUrl, test->tosUrl) || + S_OK != LoginBox_IsStrEqInvI(privacyUrl, test->privacyUrl) || + S_OK != LoginBox_IsStrEqInvI(helpUrl, test->helpUrl)) + { + return S_FALSE; + } + + if ((NULL == pageTemplate) != (NULL == test->pageTemplate)) + return S_FALSE; + + if (NULL != pageTemplate) + { + HRESULT hr = pageTemplate->IsIdentical(test->pageTemplate); + if (S_OK != hr) return hr; + } + + if ((NULL == command) != (NULL == test->command)) + return S_FALSE; + + if (NULL != command) + { + HRESULT hr = command->IsIdentical(test->command); + if (S_OK != hr) return hr; + } + + return S_OK; +} + +HRESULT LoginProvider::IsValid() +{ + return (NULL != pageTemplate && NULL != command) ? S_OK :S_FALSE; +} + +HRESULT LoginProvider::GetId(GUID *pId) +{ + if (NULL == pId) return E_POINTER; + *pId = id; + return S_OK; +} + +HRESULT LoginProvider::GetName(LPWSTR pszBuffer, UINT cchBufferMax) +{ + if (NULL == pszBuffer) return E_INVALIDARG; + return StringCchCopyEx(pszBuffer, cchBufferMax, name, NULL, NULL, STRSAFE_IGNORE_NULLS); +} + +HRESULT LoginProvider::GetDescription(LPWSTR pszBuffer, UINT cchBufferMax) +{ + if (NULL == pszBuffer) return E_INVALIDARG; + return StringCchCopyEx(pszBuffer, cchBufferMax, description, NULL, NULL, STRSAFE_IGNORE_NULLS); +} + +HRESULT LoginProvider::GetImagePath(LPWSTR pszBuffer, UINT cchBufferMax) +{ + if (NULL == pszBuffer) return E_INVALIDARG; + return StringCchCopyEx(pszBuffer, cchBufferMax, imagePath, NULL, NULL, STRSAFE_IGNORE_NULLS); +} + +HRESULT LoginProvider::GetTosLink(LPWSTR pszBuffer, UINT cchBufferMax) +{ + if (NULL == pszBuffer) return E_INVALIDARG; + return StringCchCopyEx(pszBuffer, cchBufferMax, tosUrl, NULL, NULL, STRSAFE_IGNORE_NULLS); +} + +HRESULT LoginProvider::GetPrivacyLink(LPWSTR pszBuffer, UINT cchBufferMax) +{ + if (NULL == pszBuffer) return E_INVALIDARG; + return StringCchCopyEx(pszBuffer, cchBufferMax, privacyUrl, NULL, NULL, STRSAFE_IGNORE_NULLS); +} + +HRESULT LoginProvider::GetHelpLink(LPWSTR pszBuffer, UINT cchBufferMax) +{ + if (NULL == pszBuffer) return E_INVALIDARG; + return StringCchCopyEx(pszBuffer, cchBufferMax, helpUrl, NULL, NULL, STRSAFE_IGNORE_NULLS); +} + +HRESULT LoginProvider::GetTemplate(LoginTemplate **ppTemplate) +{ + if (NULL == ppTemplate) + return E_POINTER; + + *ppTemplate = pageTemplate; + if (NULL != pageTemplate) + pageTemplate->AddRef(); + + return S_OK; +} + +HRESULT LoginProvider::GetCommand(LoginCommand **ppCommand) +{ + if (NULL == ppCommand) + return E_POINTER; + + *ppCommand = command; + if (NULL != command) + command->AddRef(); + + return S_OK; +} + +HRESULT LoginProvider::SetName(LPCWSTR pszName) +{ + return LoginProvider_SetString(&name, pszName); +} + +HRESULT LoginProvider::SetDescription(LPCWSTR pszDescription) +{ + return LoginProvider_SetString(&description, pszDescription); +} + +HRESULT LoginProvider::SetImagePath(LPCWSTR pszImagePath) +{ + return LoginProvider_SetString(&imagePath, pszImagePath); +} + +HRESULT LoginProvider::SetTosLink(LPCWSTR pszUrl) +{ + return LoginProvider_SetString(&tosUrl, pszUrl); +} + +HRESULT LoginProvider::SetPrivacyLink(LPCWSTR pszUrl) +{ + return LoginProvider_SetString(&privacyUrl, pszUrl); +} + +HRESULT LoginProvider::SetHelpLink(LPCWSTR pszUrl) +{ + return LoginProvider_SetString(&helpUrl, pszUrl); +} + +HRESULT LoginProvider::SetTemplate(LoginTemplate *pTemplate) +{ + if (NULL != pageTemplate) pageTemplate->Release(); + pageTemplate = pTemplate; + if (NULL != pageTemplate) pageTemplate->AddRef(); + return S_OK; +} + +HRESULT LoginProvider::SetCommand(LoginCommand *pCommand) +{ + if (NULL != command) command->Release(); + command = pCommand; + if (NULL != command) command->AddRef(); + return S_OK; +} + + + diff --git a/Src/auth/Loginbox/loginProvider.h b/Src/auth/Loginbox/loginProvider.h new file mode 100644 index 00000000..8fb8289d --- /dev/null +++ b/Src/auth/Loginbox/loginProvider.h @@ -0,0 +1,64 @@ +#ifndef NULLSOFT_AUTH_LOGINPROVIDER_HEADER +#define NULLSOFT_AUTH_LOGINPROVIDER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class LoginTemplate; +class LoginCommand; + +class LoginProvider +{ + +protected: + LoginProvider(const GUID *providerUid); + virtual ~LoginProvider(); + +public: + static HRESULT CreateInstance(const GUID *providerUid, LoginProvider **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + HRESULT IsIdentical(LoginProvider *test); + HRESULT IsValid(); + + // get + HRESULT GetId(GUID *pId); + HRESULT GetName(LPWSTR pszBuffer, UINT cchBufferMax); + HRESULT GetDescription(LPWSTR pszBuffer, UINT cchBufferMax); + HRESULT GetImagePath(LPWSTR pszBuffer, UINT cchBufferMax); + HRESULT GetTosLink(LPWSTR pszBuffer, UINT cchBufferMax); + HRESULT GetPrivacyLink(LPWSTR pszBuffer, UINT cchBufferMax); + HRESULT GetHelpLink(LPWSTR pszBuffer, UINT cchBufferMax); + HRESULT GetTemplate(LoginTemplate **ppTemplate); + HRESULT GetCommand(LoginCommand **ppCommand); + + // set + HRESULT SetName(LPCWSTR pszName); + HRESULT SetDescription(LPCWSTR pszDescription); + HRESULT SetImagePath(LPCWSTR pszImagePath); + HRESULT SetTosLink(LPCWSTR pszUrl); + HRESULT SetPrivacyLink(LPCWSTR pszUrl); + HRESULT SetHelpLink(LPCWSTR pszUrl); + HRESULT SetTemplate(LoginTemplate *pTemplate); + HRESULT SetCommand(LoginCommand *pCommand); + +protected: + ULONG ref; + GUID id; + LPWSTR name; + LPWSTR description; + LPWSTR imagePath; + LPWSTR tosUrl; + LPWSTR privacyUrl; + LPWSTR helpUrl; + LoginTemplate *pageTemplate; + LoginCommand *command; +}; + +#endif //NULLSOFT_AUTH_LOGINPROVIDER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginResult.h b/Src/auth/Loginbox/loginResult.h new file mode 100644 index 00000000..7492d128 --- /dev/null +++ b/Src/auth/Loginbox/loginResult.h @@ -0,0 +1,35 @@ +#ifndef NULLSOFT_AUTH_LOGINRESULT_HEADER +#define NULLSOFT_AUTH_LOGINRESULT_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class LoginData; + +class __declspec(novtable) LoginResult : public IUnknown +{ + +public: + typedef void (CALLBACK *Callback)(LoginResult *result); + +protected: + LoginResult() {} + ~LoginResult() {} + +public: + + virtual HRESULT GetLoginData(LoginData **loginData) = 0; + + virtual HRESULT GetWaitHandle(HANDLE *handle) = 0; + virtual HRESULT GetUser(void **user) = 0; + virtual HRESULT RequestAbort(BOOL fDrop) = 0; + virtual HRESULT IsCompleted() = 0; + virtual HRESULT IsAborting() = 0; + + +}; + +#endif //NULLSOFT_AUTH_LOGINRESULT_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginStatus.cpp b/Src/auth/Loginbox/loginStatus.cpp new file mode 100644 index 00000000..4df32bb4 --- /dev/null +++ b/Src/auth/Loginbox/loginStatus.cpp @@ -0,0 +1,159 @@ +#include "./loginStatus.h" +#include "./common.h" + + +LoginStatus::LoginStatus(HWND hTarget) + : ref(1), hwnd(hTarget) +{ + InitializeCriticalSection(&lock); +} +LoginStatus::~LoginStatus() +{ + DeleteCriticalSection(&lock); +} + +HRESULT LoginStatus::CreateInstance(HWND hTarget, LoginStatus **instance) +{ + if (NULL == instance) + return E_POINTER; + *instance = new LoginStatus(hTarget); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + + +ULONG LoginStatus::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginStatus::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + + +UINT LoginStatus::Add(BSTR status) +{ + EnterCriticalSection(&lock); + + Record r; + r.cookie = GetNextCookie(); + r.text = status; + list.push_back(r); + + LeaveCriticalSection(&lock); + + UpdateWindowText(); + return r.cookie; +} + +BOOL LoginStatus::Set(UINT cookie, BSTR status) +{ + BOOL foundOk = FALSE; + EnterCriticalSection(&lock); + + size_t index = list.size(); + while(index--) + { + if (cookie == list[index].cookie) + { + SysFreeString(list[index].text); + list[index].text = status; + foundOk = TRUE; + break; + } + } + + LeaveCriticalSection(&lock); + + UpdateWindowText(); + return foundOk; +} + +void LoginStatus::Remove(UINT cookie) +{ + EnterCriticalSection(&lock); + + size_t index = list.size(); + while(index--) + { + if (cookie == list[index].cookie) + { + SysFreeString(list[index].text); + list.eraseAt(index); + break; + } + } + + LeaveCriticalSection(&lock); + UpdateWindowText(); +} + +BOOL LoginStatus::AttachWindow(HWND hTarget) +{ + DetachWindow(); + + hwnd = hTarget; + UpdateWindowText(); + return TRUE; +} + +BOOL LoginStatus::DetachWindow() +{ + hwnd = NULL; + return TRUE; +} + +UINT LoginStatus::GetNextCookie() +{ + size_t i, count; + + count = list.size(); + UINT cookie = (UINT)count; + + do + { + for (i = 0; i < count; i++) + { + if (list[i].cookie == cookie) + { + cookie++; + break; + } + } + } while(i != count); + return cookie; +} + +BOOL LoginStatus::UpdateWindowText() +{ + + EnterCriticalSection(&lock); + + BOOL resultOk = FALSE; + if (NULL != hwnd) + { + BSTR text = NULL; + size_t index = list.size(); + while(index--) + { + if (NULL != list[index].text && L'\0' != list[index].text) + { + text = list[index].text; + break; + } + } + resultOk = (BOOL)SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)text); + } + LeaveCriticalSection(&lock); + return resultOk; +} + diff --git a/Src/auth/Loginbox/loginStatus.h b/Src/auth/Loginbox/loginStatus.h new file mode 100644 index 00000000..661205c4 --- /dev/null +++ b/Src/auth/Loginbox/loginStatus.h @@ -0,0 +1,53 @@ +#ifndef NULLSOFT_AUTH_LOGIN_STATUS_HEADER +#define NULLSOFT_AUTH_LOGIN_STATUS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../nu/Vectors.h" + +class LoginStatus +{ + +protected: + LoginStatus(HWND hTarget); + ~LoginStatus(); + +public: + static HRESULT CreateInstance(HWND hTarget, LoginStatus **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + UINT Add(BSTR status); + BOOL Set(UINT cookie, BSTR status); + void Remove(UINT cookie); + + BOOL AttachWindow(HWND hTarget); + BOOL DetachWindow(); + +protected: + BOOL UpdateWindowText(); + UINT GetNextCookie(); + +protected: + typedef struct __Record + { + UINT cookie; + BSTR text; + } Record; + + typedef Vector<Record> RecordList; + +protected: + ULONG ref; + HWND hwnd; + RecordList list; + CRITICAL_SECTION lock; + +}; + +#endif //NULLSOFT_AUTH_LOGIN_STATUS_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginTab.cpp b/Src/auth/Loginbox/loginTab.cpp new file mode 100644 index 00000000..76567cde --- /dev/null +++ b/Src/auth/Loginbox/loginTab.cpp @@ -0,0 +1,3192 @@ +#include "./loginTab.h" +#include "./common.h" +#include "./imageLoader.h" +#include "./loginGui.h" +#include "./graphics.h" + +#include "../api.h" +#include "../resource.h" +#include "../../nu/windowsTheme.h" + +#include <vssym32.h> +#include <vsstyle.h> + +#include <malloc.h> +#include <math.h> +#include <shlwapi.h> +#include <commctrl.h> +#include <strsafe.h> + +#define MAX_TEXT_AVECHAR_WIDTH 12 +#define MAX_HELP_AVECHAR_WIDTH 28 +#define IMAGE_MARGIN_CY 4 +#define IMAGE_MARGIN_CX 4 + +#define NLTDS_FOCUSED 0x00000001 +#define NLTDS_DISABLED 0x00000002 +#define NLTDS_LOCKED 0x00000004 + + +typedef struct __LOGINTABITEM +{ + LPWSTR text; + UINT iImage; + UINT iImageActive; + UINT iImageDisabled; + LPARAM param; + LONG textWidth; +} LOGINTABITEM; + +typedef struct __ITEMSTATECOLORTABLE +{ + COLORREF backTop; + COLORREF backBottom; + COLORREF backAlpha; + COLORREF text; + INT frameType; +} ITEMSTATECOLORTABLE; + +typedef struct __ITEMCOLORTABLE +{ + ITEMSTATECOLORTABLE normal; + ITEMSTATECOLORTABLE normalPressed; + ITEMSTATECOLORTABLE normalHigh; + ITEMSTATECOLORTABLE normalDisabled; + ITEMSTATECOLORTABLE selected; + ITEMSTATECOLORTABLE selectedPressed; + ITEMSTATECOLORTABLE selectedHigh; + ITEMSTATECOLORTABLE selectedDisabled; +} ITEMCOLORTABLE; + +typedef struct __COLORTABLE +{ + COLORREF backTop; + COLORREF backBottom; + COLORREF backLine; + COLORREF focus; + COLORREF focusDash; + ITEMCOLORTABLE item; +} COLORTABLE; + + +typedef struct __LOGINTAB +{ + LOGINTABITEM **items; + INT itemsCount; + INT *order; + INT iSelected; + INT iHighlighted; + INT iPressed; + INT iFocused; + HIMAGELIST imageList; + UINT drawStyle; + + COLORTABLE colors; + + HFONT fontText; + + LONG textHeight; + LONG spacing; + RECT margins; + LONG textWidthMax; + + HBITMAP chevronImage; + INT chevronWidth; + HMENU chevronMenu; + + LONG chevronLeft; + LONG visibleRight; + INT lastVisible; + + HBITMAP frameBitmap; + INT frameHeight; + INT frameWidth; + + HBITMAP itemBitmap; + + HWND hTooltip; + BSTR helpText; + +} LOGINTAB; + +typedef struct __CALCITEMWIDTH +{ + HDC hdc; + HFONT font; + HWND hwnd; + INT textWidthMax; + INT imageWidth; + INT imageHeight; + INT itemHeight; + INT frameWidth; + INT dialogPt; + HDC ownedDC; + HFONT ownedFont; +} CALCITEMWIDTH; + +#define FRAMETYPE_NONE 0 +#define FRAMETYPE_SELECTED 1 +#define FRAMETYPE_ACTIVE 2 +#define FRAMETYPE_DISABLED 0 + +typedef struct __PAINTITEMPARAM +{ + HWND hwndTab; + HDC hdc; + const RECT *prcPaint; + const RECT *prcClient; + HRGN clipRgn; + HRGN eraseRgn; + HDC hdcSrc; + HDC hdcItem; +} PAINTITEMPARAM; + +typedef struct __GETITEMRECTPARAM +{ + INT index; + RECT *rect; +} GETITEMRECTPARAM; + +typedef struct __HITTESTITEMPARAM +{ + POINT pt; + RECT *rect; +} HITTESTITEMPARAM; + +typedef struct __UPDATELAYOUTPARAM +{ + INT itemCount; + RECT visibleBox; + BOOL chevronVisible; + LONG chevronLeft; +} UPDATELAYOUTPARAM; + +typedef struct __CHEVRONMENUPAINTPARAM +{ + INT itemWidth; + INT itemHeight; + HDC hdcSrc; + HDC hdcItem; + RECT ownerRect; + HWND hwndMenu; +} CHEVRONMENUPAINTPARAM; + +#define GetTab(__hwnd) ((LOGINTAB*)(LONG_PTR)(LONGX86)GetWindowLongPtr((__hwnd), 0)) + +static LRESULT WINAPI LoginTab_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +typedef INT (CALLBACK *ITEMRECTCALLBAC)(LOGINTAB* /*tab*/, LOGINTABITEM* /*item*/, INT /*iItem*/, const RECT* /*prcItem*/, ULONG_PTR /*param*/); + +BOOL LoginTab_RegisterClass(HINSTANCE hInstance) +{ + WNDCLASSW wc; + if (FALSE != GetClassInfo(hInstance, NWC_LOGINTAB, &wc)) + return TRUE; + + ZeroMemory(&wc, sizeof(wc)); + + wc.lpszClassName = NWC_LOGINTAB; + wc.lpfnWndProc = LoginTab_WindowProc; + wc.style = CS_PARENTDC; + wc.cbWndExtra = sizeof(LOGINTAB*); + wc.hInstance = hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + + return ( 0 != RegisterClassW(&wc)); +} + +HWND LoginTab_CreateWindow(UINT styleEx, LPCWSTR pszTitle, UINT style, INT x, INT y, INT cx, INT cy, HWND hParent, INT_PTR controlId) +{ + if (FALSE == LoginTab_RegisterClass(WASABI_API_ORIG_HINST)) + return FALSE; + + HWND hwnd = CreateWindowEx(styleEx, NWC_LOGINTAB, pszTitle, WS_CHILD | style, + x, y, cx, cy, hParent, (HMENU)controlId, WASABI_API_ORIG_HINST, NULL); + + return hwnd; +} + +static BOOL LoginTab_IsLocked(HWND hwnd) +{ + UINT windowStyle = GetWindowStyle(hwnd); + return (0 != (NLTS_LOCKED & windowStyle)); +} +static BOOL LoginTab_InitCalcItemWidth(HWND hwnd, HDC hdc, CALCITEMWIDTH *pciw) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab || NULL == pciw) return FALSE; + + pciw->hdc = hdc; + pciw->font = tab->fontText; + pciw->hwnd = hwnd; + pciw->textWidthMax = tab->textWidthMax; + + if (NULL == tab->imageList || FALSE == ImageList_GetIconSize(tab->imageList, &pciw->imageWidth, &pciw->imageHeight)) + { + pciw->imageWidth = 0; + pciw->imageHeight = 0; + } + else + { + pciw->imageWidth += 2 * IMAGE_MARGIN_CX; + pciw->imageHeight += 2 * IMAGE_MARGIN_CY; + } + + + RECT rect; + GetClientRect(hwnd, &rect); + pciw->itemHeight = tab->frameHeight * 2 + tab->textHeight + pciw->imageHeight; + pciw->frameWidth = tab->frameWidth; + + pciw->dialogPt = tab->spacing; + pciw->ownedDC = NULL; + pciw->ownedFont = NULL; + + return TRUE; +} + +static BOOL LoginTab_DestroyCalcItemWidth(CALCITEMWIDTH *pciw) +{ + if (NULL == pciw) return FALSE; + if (NULL != pciw->ownedDC) + { + SelectObject(pciw->ownedDC, pciw->ownedFont); + ReleaseDC(pciw->hwnd, pciw->ownedDC); + } + return TRUE; +} +static INT LoginTab_CalculateItemWidth(CALCITEMWIDTH *pciw, LOGINTABITEM *item) +{ + if (NULL == pciw || NULL == item) return 0; + + if (-1 == item->textWidth) + { + if (NULL != item->text && L'\0' != *(item->text)) + { + if (NULL == pciw->hdc) + { + pciw->ownedDC = GetDCEx(pciw->hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL == pciw->ownedDC) return 0; + pciw->ownedFont = (HFONT)SelectObject(pciw->ownedDC, pciw->font); + pciw->hdc = pciw->ownedDC; + } + + SIZE textSize; + if (FALSE == GetTextExtentPoint(pciw->hdc, item->text, lstrlen(item->text), &textSize)) + return 0; + + item->textWidth = textSize.cx; + + } + else + item->textWidth = 0; + } + + INT width = (item->textWidth > pciw->imageWidth) ? item->textWidth : pciw->imageWidth; + if (width > pciw->textWidthMax) width = pciw->textWidthMax; + + width += 2*pciw->frameWidth; // borders + + if (width < pciw->itemHeight) + { + INT k = (pciw->itemHeight - width)/(2*pciw->dialogPt); + if (k > 2) k = 2; + width += 2*k*pciw->dialogPt; + } + + return width; + +} +static INT LoginTab_EnumerateItemRects(HWND hwnd, HDC hdc, ITEMRECTCALLBAC callback, ULONG_PTR param) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab || NULL == callback) return -1; + + RECT clientRect, tabRect; + LONG chevronLeft, limitRight; + GetClientRect(hwnd, &clientRect); + + chevronLeft = clientRect.right - tab->chevronWidth; + + clientRect.left += tab->margins.left; + clientRect.top += tab->margins.top; + clientRect.right -= tab->margins.right; + clientRect.bottom -= tab->margins.bottom; + + limitRight = (chevronLeft < clientRect.right) ? chevronLeft : clientRect.right; + + CALCITEMWIDTH calcWidth; + + if (FALSE == LoginTab_InitCalcItemWidth(hwnd, hdc, &calcWidth)) + return -1; + + SetRect(&tabRect, clientRect.left, clientRect.top, clientRect.left, clientRect.bottom); + + INT result, index, lastItem; + lastItem = tab->itemsCount - 1; + result = -1; + BOOL ignoreVisibleUpdate = FALSE; + + for (index = 0; index < tab->itemsCount; index++) + { + tabRect.left = tabRect.right; + if (tabRect.left != clientRect.left) + tabRect.left += tab->spacing; + + if (tabRect.left > limitRight) + { + tabRect.right = clientRect.right + 1; + break; + } + INT iItem = tab->order[index]; + LOGINTABITEM *item = tab->items[iItem]; + INT width = LoginTab_CalculateItemWidth(&calcWidth, item); + if (0 == width) break; + + tabRect.right = tabRect.left + width; + if ((index == lastItem && tabRect.right > clientRect.right) || + (index < lastItem && tabRect.right > limitRight)) + { + break; + } + + result = callback(tab, item, iItem, &tabRect, param); + if (-1 != result) + { + ignoreVisibleUpdate = TRUE; + break; + } + } + + if (FALSE == ignoreVisibleUpdate) + { + if ((index == lastItem && tabRect.right > clientRect.right) || + (index < lastItem && tabRect.right > limitRight)) + { + tab->lastVisible = (index - 1); + + SetRect(&tabRect, chevronLeft, clientRect.top, + clientRect.right + tab->margins.right, clientRect.bottom); + + result = callback(tab, NULL, tab->itemsCount, &tabRect, param); + } + else + tab->lastVisible = lastItem; + } + + LoginTab_DestroyCalcItemWidth(&calcWidth); + return result; +} + +static void LoginTab_NotifySelectionChanged(HWND hwnd) +{ + HWND hParent = GetAncestor(hwnd, GA_PARENT); + if (NULL == hParent) return; + + NMHDR nmhdr; + nmhdr.code = NLTN_SELCHANGE; + nmhdr.hwndFrom = hwnd; + nmhdr.idFrom = GetWindowLongPtr(hwnd, GWLP_ID); + SNDMSG(hParent, WM_NOTIFY, (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr); +} + +static HBITMAP LoginTab_GetItemBitmap(LOGINTAB *tab, HDC hdc, INT cx, INT cy) +{ + if (cx < 1) cx = 1; + if (cy < 1) cy = 1; + + BITMAP bm; + if (NULL == tab->itemBitmap || + sizeof(BITMAP) != GetObject(tab->itemBitmap, sizeof(BITMAP), &bm) || + bm.bmWidth <= cx || abs(bm.bmHeight) < cy) + { + if (NULL != tab->itemBitmap) + DeleteObject(tab->itemBitmap); + + cx++; // need +1px to compose selection fill + tab->itemBitmap = CreateCompatibleBitmap(hdc, cx, cy); + } + + return tab->itemBitmap; +} +static HBITMAP LoginTab_LoadChevronImage(HWND hwnd, INT *imageWidth, INT *imageHeight) +{ + INT width, height; + HBITMAP hbmpDst, hbmpSrc; + hbmpSrc = ImageLoader_LoadBitmap(WASABI_API_ORIG_HINST, + MAKEINTRESOURCE(IDR_ARROW_IMAGE), FALSE, &width, &height); + + if (NULL == hbmpSrc) + return NULL; + + if (height < 0) height = -height; + + INT frameHeight = height/2; + INT frameWidth = width; + + BITMAPINFOHEADER bhi; + ZeroMemory(&bhi, sizeof(bhi)); + bhi.biSize = sizeof(bhi); + bhi.biCompression = BI_RGB; + bhi.biBitCount = 32; + bhi.biPlanes = 1; + bhi.biWidth = frameWidth; + bhi.biHeight = 4 * frameHeight; + + UINT *pixelData; + hbmpDst = CreateDIBSection(NULL, (LPBITMAPINFO)&bhi, DIB_RGB_COLORS, (void**)&pixelData, NULL, 0); + if (NULL == hbmpDst) + { + DeleteObject(hbmpSrc); + return NULL; + } + + BOOL resultOk = FALSE; + + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + HDC hdcSrc = CreateCompatibleDC(hdc); + HDC hdcDst = CreateCompatibleDC(hdc); + + if (NULL != hdcSrc && NULL != hdcDst) + { + HBITMAP hbmpSrcOrig = (HBITMAP)SelectObject(hdcSrc, hbmpSrc); + HBITMAP hbmpDstOrig = (HBITMAP)SelectObject(hdcDst, hbmpDst); + + RECT imageRect; + SetRect(&imageRect, 0, 0, frameWidth, frameHeight); + + BOOL blitFailed = FALSE; + // normal + if (FALSE == blitFailed && + FALSE != BitBlt(hdcDst, 0, 0*frameHeight, frameWidth, frameHeight, hdcSrc, 0, 0, SRCCOPY)) + { + Image_AdjustSaturationAlpha(hbmpDst, &imageRect, -150, -100); + } + else blitFailed = TRUE; + + + // active + if (FALSE == blitFailed && + FALSE == BitBlt(hdcDst, 0, 1*frameHeight, frameWidth, frameHeight, hdcSrc, 0, 0, SRCCOPY)) + { + blitFailed = TRUE; + } + + // disabled + if (FALSE == blitFailed && + FALSE != BitBlt(hdcDst, 0, 2*frameHeight, frameWidth, frameHeight, hdcSrc, 0, 0, SRCCOPY)) + { + OffsetRect(&imageRect, 0, 2*frameHeight); + Image_AdjustSaturationAlpha(hbmpDst, &imageRect, -(150 + 600), -(100 + 600)); + } + else blitFailed = TRUE; + + + // pressed + if (FALSE == blitFailed && + FALSE == BitBlt(hdcDst, 0, 3*frameHeight, frameWidth, frameHeight, hdcSrc, 0, frameHeight, SRCCOPY)) + { + blitFailed = TRUE; + } + + if (FALSE == blitFailed) + { + SetRect(&imageRect, 0, 0, bhi.biWidth, -bhi.biHeight); + Image_Premultiply(hbmpDst, &imageRect); + resultOk = TRUE; + } + + SelectObject(hdcSrc, hbmpSrcOrig); + SelectObject(hdcDst, hbmpDstOrig); + } + if (NULL != hdcSrc) DeleteDC(hdcSrc); + if (NULL != hdcDst) DeleteDC(hdcDst); + ReleaseDC(hwnd, hdc); + } + + + DeleteObject(hbmpSrc); + + if (FALSE == resultOk) + { + DeleteObject(hbmpDst); + hbmpDst = NULL; + } + else + { + if (NULL != imageWidth) *imageWidth = width; + if (NULL != imageHeight) *imageHeight = height; + } + + return hbmpDst; +} +static BOOL LoginTab_GradientFillVertRect(HDC hdc, const RECT *prcFill, COLORREF rgbTop, COLORREF rgbBottom) +{ + TRIVERTEX szVertex[2]; + szVertex[0].x = prcFill->left; + szVertex[0].y = prcFill->top; + szVertex[0].Red = GetRValue(rgbTop) << 8; + szVertex[0].Green = GetGValue(rgbTop) << 8; + szVertex[0].Blue = GetBValue(rgbTop) << 8; + szVertex[0].Alpha = 0x0000; + + szVertex[1].x = prcFill->right; + szVertex[1].y = prcFill->bottom; + szVertex[1].Red = GetRValue(rgbBottom) << 8; + szVertex[1].Green = GetGValue(rgbBottom) << 8; + szVertex[1].Blue = GetBValue(rgbBottom) << 8; + szVertex[1].Alpha = 0x0000; + + GRADIENT_RECT szMesh[1]; + szMesh[0].UpperLeft = 0; + szMesh[0].LowerRight = 1; + + return GdiGradientFill(hdc, szVertex, ARRAYSIZE(szVertex), szMesh, 1, GRADIENT_FILL_RECT_V); +} + +static void LoginTab_EraseBkGround(HDC hdc, LOGINTAB *tab, LONG clientHeight, const RECT *prcPaint) +{ + RECT rect; + LONG middleY = clientHeight/2; + + COLORREF rgbOrig = SetBkColor(hdc, tab->colors.backTop); + + CopyRect(&rect, prcPaint); + rect.bottom = middleY; + if (rect.top < rect.bottom) + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + + SetRect(&rect, prcPaint->left, middleY, prcPaint->right, clientHeight - 1); + + if (FALSE == LoginTab_GradientFillVertRect(hdc, &rect, tab->colors.backTop, tab->colors.backBottom)) + { + SetBkColor(hdc, tab->colors.backBottom); + if (prcPaint->bottom < rect.bottom) + rect.bottom = prcPaint->bottom; + if (rect.top < rect.bottom) + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + } + + if (prcPaint->bottom == clientHeight) + { + SetBkColor(hdc, tab->colors.backLine); + SetRect(&rect, prcPaint->left, clientHeight - 1, prcPaint->right, clientHeight); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + } + + SetBkColor(hdc, rgbOrig); +} + +static BOOL LoginTab_PaintItemFrame(HDC hdc, LOGINTAB *tab, INT frameType, const RECT *prcItem, const RECT *prcPaint, HDC hdcSrc) +{ + if (NULL == tab->frameBitmap) + return FALSE; + + INT offsetY; + switch(frameType) + { + case FRAMETYPE_SELECTED: offsetY = 0; break; + case FRAMETYPE_ACTIVE: offsetY = 1 * (tab->frameHeight * 2 + 1); break; + case FRAMETYPE_DISABLED: offsetY = 2 * (tab->frameHeight * 2 + 1); break; + default: return FALSE; + } + + SelectObject(hdcSrc, tab->frameBitmap); + + BLENDFUNCTION bf; + bf.BlendOp = AC_SRC_OVER; + bf.BlendFlags = 0; + bf.SourceConstantAlpha = 255; + bf.AlphaFormat = AC_SRC_ALPHA; + + INT lineLength; + + // left-top + GdiAlphaBlend(hdc, prcItem->left, prcItem->top, tab->frameWidth, tab->frameHeight, + hdcSrc, 0, offsetY + 0, tab->frameWidth, tab->frameHeight, bf); + + // right-top + GdiAlphaBlend(hdc, prcItem->right - tab->frameWidth, prcItem->top, tab->frameWidth, tab->frameHeight, + hdcSrc, tab->frameWidth + 1, offsetY + 0, tab->frameWidth, tab->frameHeight, bf); + + // right-bottom + GdiAlphaBlend(hdc, prcItem->right - tab->frameWidth, prcItem->bottom - tab->frameHeight, tab->frameWidth, tab->frameHeight, + hdcSrc, tab->frameWidth + 1, offsetY + tab->frameHeight + 1, tab->frameWidth, tab->frameHeight, bf); + + // left-bottom + GdiAlphaBlend(hdc, prcItem->left, prcItem->bottom - tab->frameHeight, tab->frameWidth, tab->frameHeight, + hdcSrc, 0, offsetY + tab->frameHeight + 1, tab->frameWidth, tab->frameHeight, bf); + + lineLength = (prcItem->right - prcItem->left) - tab->frameWidth * 2; + // top + GdiAlphaBlend(hdc, prcItem->left + tab->frameWidth, prcItem->top, lineLength, tab->frameHeight, + hdcSrc, tab->frameWidth, offsetY + 0, 1, tab->frameHeight, bf); + // bottom + GdiAlphaBlend(hdc, prcItem->left + tab->frameWidth, prcItem->bottom - tab->frameHeight, lineLength, tab->frameHeight, + hdcSrc, tab->frameWidth, offsetY + tab->frameHeight + 1, 1, tab->frameHeight, bf); + + lineLength = (prcItem->bottom - prcItem->top) - tab->frameHeight * 2; + // left + GdiAlphaBlend(hdc, prcItem->left, prcItem->top + tab->frameHeight, tab->frameWidth, lineLength, + hdcSrc, 0, offsetY + tab->frameHeight, tab->frameWidth, 1, bf); + // right + GdiAlphaBlend(hdc, prcItem->right - tab->frameWidth, prcItem->top + tab->frameHeight, tab->frameWidth, lineLength, + hdcSrc, tab->frameWidth + 1, offsetY + tab->frameHeight, tab->frameWidth, 1, bf); + return TRUE; +} + +static BOOL LoginTab_FillItem(HDC hdc, const RECT *prcItem, const RECT *prcPaint, INT alpha, COLORREF rgbTop, COLORREF rgbBottom, INT tempX) +{ + + RECT rect; + SetRect(&rect, tempX, prcItem->top, tempX + 1, prcItem->bottom); + if (rgbTop == rgbBottom || FALSE == LoginTab_GradientFillVertRect(hdc, &rect, rgbTop, rgbBottom)) + { + COLORREF rgbOrig = SetBkColor(hdc, rgbBottom); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + if (rgbOrig != rgbBottom) SetBkColor(hdc, rgbOrig); + } + + INT height = prcPaint->bottom - prcPaint->top; + + BLENDFUNCTION bf; + bf.BlendOp = AC_SRC_OVER; + bf.BlendFlags = 0; + bf.SourceConstantAlpha = alpha; + bf.AlphaFormat = 0x00; + + BOOL result = GdiAlphaBlend(hdc, prcPaint->left, prcPaint->top, 1, height, + hdc, tempX, prcPaint->top, 1, height, bf); + + if (FALSE != result) + { + INT stretchModeOrig = SetStretchBltMode(hdc, COLORONCOLOR); + + result = StretchBlt(hdc, prcPaint->left + 1, prcPaint->top, prcPaint->right - prcPaint->left - 1, height, + hdc, prcPaint->left, prcPaint->top, 1, height, SRCCOPY); + + if (COLORONCOLOR != stretchModeOrig) + SetStretchBltMode(hdc, stretchModeOrig); + } + + return result; +} +static void LoginTab_DrawRect(HDC hdc, const RECT *prc) +{ + RECT rect; + SetRect(&rect, prc->left +1, prc->top, prc->right -1, prc->top + 1); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + + SetRect(&rect, prc->left + 1, prc->bottom-1, prc->right -1, prc->bottom); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + + SetRect(&rect, prc->left, prc->top + 1, prc->left + 1, prc->bottom - 1); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + + SetRect(&rect, prc->right - 1, prc->top + 1, prc->right, prc->bottom - 1); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + +} +static BOOL LoginTab_PaintChevron(HDC hdc, LOGINTAB *tab, const RECT *prcItem, const RECT *prcPaint, HDC hdcSrc) +{ + ITEMSTATECOLORTABLE *color; + INT iItem = tab->itemsCount; + + if (0 != ((NLTDS_LOCKED | NLTDS_DISABLED) & tab->drawStyle)) + color = &tab->colors.item.normalDisabled; + else + { + if (iItem == tab->iPressed) + color = &tab->colors.item.normalPressed; + else if (iItem == tab->iHighlighted) + color = &tab->colors.item.normalHigh; + else + color = &tab->colors.item.normal; + } + + RECT rect, itemRect; + CopyRect(&itemRect, prcItem); + + if (iItem == tab->iFocused && 0 != (NLTDS_FOCUSED & tab->drawStyle)) + { + CopyRect(&rect, prcItem); + InflateRect(&rect, -(tab->frameWidth - 2), -(tab->frameWidth - 2)); + COLORREF rgbBkOrig = SetBkColor(hdc, tab->colors.focus); + COLORREF rgbTextOrig = SetTextColor(hdc, tab->colors.focusDash); + DrawFocusRect(hdc, &rect); + //LoginTab_DrawRect(hdc, &rect); + if (rgbBkOrig != tab->colors.focus) SetBkColor(hdc, rgbBkOrig); + if (rgbTextOrig != tab->colors.focusDash) SetTextColor(hdc, rgbTextOrig); + } + + INT frameType = color->frameType; + if (iItem == tab->iFocused && 0 != (NLTDS_FOCUSED & tab->drawStyle)) + frameType = FRAMETYPE_ACTIVE; + + + if (FRAMETYPE_NONE != frameType && + FALSE != LoginTab_PaintItemFrame(hdc, tab, frameType, prcItem, prcPaint, hdcSrc)) + { + InflateRect(&itemRect, -(tab->frameWidth -1), -(tab->frameHeight -1)); + } + + if (0 != color->backAlpha && FALSE != IntersectRect(&rect, &itemRect, prcPaint)) + { + LoginTab_FillItem(hdc, &itemRect, &rect, color->backAlpha, color->backTop, color->backBottom, prcPaint->right); + } + + + + if (NULL != tab->chevronImage) + { + BITMAP bm; + if (sizeof(bm) == GetObject(tab->chevronImage, sizeof(bm), &bm)) + { + if (bm.bmHeight < 0) bm.bmHeight = -bm.bmHeight; + bm.bmHeight = bm.bmHeight/4; + + + INT cx = bm.bmWidth; + INT cy = bm.bmHeight; + + INT offsetY; + if (0 != ((NLTDS_LOCKED | NLTDS_DISABLED) & tab->drawStyle)) + offsetY = 2*bm.bmHeight; + else if (iItem == tab->iPressed) + offsetY = 3*bm.bmHeight; + else if (iItem == tab->iHighlighted || iItem == tab->iSelected) + offsetY = 1*bm.bmHeight; + else + offsetY = 0; + + INT x = prcItem->left + ((prcItem->right - prcItem->left) - cx)/2; + if (x < prcItem->left) x = prcItem->left; + + INT y = prcItem->top + ((prcItem->bottom - prcItem->top) - cy)/2; + if (y < prcItem->top) y = prcItem->top; + + if ((x + cx) > prcItem->right) cx = prcItem->right - x; + if ((y + cy) > prcItem->bottom) cy = prcItem->bottom - y; + + if (iItem == tab->iPressed) y++; + + SelectObject(hdcSrc, tab->chevronImage); + + BLENDFUNCTION bf; + bf.BlendOp = AC_SRC_OVER; + bf.BlendFlags = 0; + bf.SourceConstantAlpha = 255; + bf.AlphaFormat = AC_SRC_ALPHA; + + GdiAlphaBlend(hdc, x, y, cx, cy, hdcSrc, 0, offsetY, bm.bmWidth, bm.bmHeight, bf); + } + } + + return TRUE; +} +static void LoginTab_ResolveImageIndex(HWND hwnd, HIMAGELIST imageList, LOGINTABITEM *item, INT iItem, UINT requestMask) +{ + HWND hParent = GetAncestor(hwnd, GA_PARENT); + if (NULL == hParent) return; + + NMLOGINTABIMAGE request; + request.hdr.code = NLTN_GETITEMIMAGE; + request.hdr.hwndFrom = hwnd; + request.hdr.idFrom = GetWindowLongPtr(hwnd, GWLP_ID); + request.iItem = iItem; + request.param = item->param; + request.imageList = imageList; + request.maskRequest = requestMask; + request.maskUpdate = 0; + request.iImage = item->iImage; + request.iImageActive = item->iImageActive; + request.iImageDisabled = item->iImageDisabled; + + SNDMSG(hParent, WM_NOTIFY, (WPARAM)request.hdr.idFrom, (LPARAM)&request); + + if (0 != request.maskUpdate) + { + if (0 != (NLTIF_IMAGE & request.maskUpdate)) + item->iImage = request.iImage; + + if (0 != (NLTIF_IMAGE_ACTIVE & request.maskUpdate)) + item->iImageActive = request.iImageActive; + + if (0 != (NLTIF_IMAGE_DISABLED & request.maskUpdate)) + item->iImageDisabled = request.iImageDisabled; + } + +} +static UINT LoginTab_GetImageIndex(HWND hwnd, HIMAGELIST imageList, LOGINTABITEM *item, INT iItem, UINT imageType) +{ + if (NULL == item) return NLTM_IMAGE_NONE; + switch(imageType & NLTIF_IMAGE_MASK) + { + case NLTIF_IMAGE_ACTIVE: + if (NLTM_IMAGE_CALLBACK == item->iImageActive) + { + LoginTab_ResolveImageIndex(hwnd, imageList, item, iItem, NLTIF_IMAGE_ACTIVE); + if (NLTM_IMAGE_CALLBACK == item->iImageActive) + break; + } + + if (NLTM_IMAGE_NONE != item->iImageActive) + return item->iImageActive; + + break; + case NLTIF_IMAGE_DISABLED: + if (NLTM_IMAGE_CALLBACK == item->iImageDisabled) + { + LoginTab_ResolveImageIndex(hwnd, imageList, item, iItem, NLTIF_IMAGE_DISABLED); + if (NLTM_IMAGE_CALLBACK == item->iImageDisabled) + break; + } + + if (NLTM_IMAGE_NONE != item->iImageDisabled) + return item->iImageDisabled; + + break; + } + + if (NLTM_IMAGE_CALLBACK == item->iImage) + LoginTab_ResolveImageIndex(hwnd, imageList, item, iItem, NLTIF_IMAGE); + + return item->iImage; +} + +static BOOL LoginTab_PaintItem(HWND hwndTab, HDC hdc, LOGINTAB *tab, LOGINTABITEM *item, INT iItem, const RECT *prcItem, const RECT *prcPaint, HDC hdcSrc) +{ + + ITEMSTATECOLORTABLE *color; + UINT imageType; + + if (iItem == tab->iSelected) + { + if (0 != (NLTDS_DISABLED & tab->drawStyle)) + { + color = &tab->colors.item.selectedDisabled; + imageType = NLTIF_IMAGE_DISABLED; + } + else + { + if (0 != (NLTDS_LOCKED & tab->drawStyle)) + { + color = &tab->colors.item.selected; + } + else + { + if (iItem == tab->iPressed) + color = &tab->colors.item.selectedPressed; + else if (iItem == tab->iHighlighted) + color = &tab->colors.item.selectedHigh; + else + color = &tab->colors.item.selected; + } + + imageType = NLTIF_IMAGE_ACTIVE; + } + } + else + { + if (0 != ((NLTDS_DISABLED | NLTDS_LOCKED) & tab->drawStyle)) + { + color = &tab->colors.item.normalDisabled; + imageType = NLTIF_IMAGE_DISABLED; + } + else if (iItem == tab->iPressed) + { + color = &tab->colors.item.normalPressed; + imageType = NLTIF_IMAGE_ACTIVE; + } + else if (iItem == tab->iHighlighted) + { + color = &tab->colors.item.normalHigh; + imageType = NLTIF_IMAGE_ACTIVE; + } + else + { + color = &tab->colors.item.normal; + imageType = NLTIF_IMAGE; + } + } + + RECT rect, itemRect; + CopyRect(&itemRect, prcItem); + + + + if (FRAMETYPE_NONE != color->frameType && + FALSE != LoginTab_PaintItemFrame(hdc, tab, color->frameType, prcItem, prcPaint, hdcSrc)) + { + InflateRect(&itemRect, -(tab->frameWidth -1), -(tab->frameHeight -1)); + } + + if (0 != color->backAlpha && FALSE != IntersectRect(&rect, &itemRect, prcPaint)) + { + LoginTab_FillItem(hdc, &itemRect, &rect, color->backAlpha, color->backTop, color->backBottom, prcPaint->right); + } + + if (iItem == tab->iFocused && 0 != (NLTDS_FOCUSED & tab->drawStyle)) + { + CopyRect(&rect, prcItem); + InflateRect(&rect, -(tab->frameWidth - 2), -(tab->frameWidth - 2)); + COLORREF rgbBkOrig = SetBkColor(hdc, tab->colors.focus); + COLORREF rgbTextOrig = SetTextColor(hdc, tab->colors.focusDash); + DrawFocusRect(hdc, &rect); + //LoginTab_DrawRect(hdc, &rect); + if (rgbBkOrig != tab->colors.focus) SetBkColor(hdc, rgbBkOrig); + if (rgbTextOrig != tab->colors.focusDash) SetTextColor(hdc, rgbTextOrig); + } + + if (NULL != tab->imageList) + { + UINT iImage = LoginTab_GetImageIndex(hwndTab, tab->imageList, item, iItem, imageType); + if (NLTM_IMAGE_NONE != iImage) + { + IMAGELISTDRAWPARAMS dp; + dp.cbSize = 56/*sizeof(IMAGELISTDRAWPARAMS) - sizeof(DWORD) * 3*/; + dp.himl = tab->imageList; + dp.i = iImage; + dp.hdcDst = hdc; + + ImageList_GetIconSize(tab->imageList, &dp.cx, &dp.cy); + dp.x = prcItem->left + ((prcItem->right - prcItem->left) - dp.cx)/2; + if (dp.x < (prcItem->left + tab->frameWidth)) dp.x = prcItem->left + tab->frameWidth; + if ((dp.x + dp.cx) > (prcItem->right - tab->frameWidth)) + { + dp.cx = prcItem->right - tab->frameWidth - dp.x; + if (dp.cx < 0) dp.cx = 0; + } + + dp.y = prcItem->top + tab->frameHeight + IMAGE_MARGIN_CY; + if ((dp.y + dp.cy) > (prcItem->bottom - tab->frameHeight)) + { + dp.cy = prcItem->bottom - tab->frameHeight- dp.y; + if (dp.cy < 0) dp.cy = 0; + } + + dp.xBitmap = 0; + dp.yBitmap = 0; + dp.rgbBk = CLR_NONE; + dp.rgbFg = CLR_NONE; + dp.fStyle = ILD_NORMAL; + dp.dwRop = SRCCOPY; + dp.fState = ILS_NORMAL /*| ILS_SATURATE*/ /*| ILS_ALPHA*/; + dp.Frame = 255; + dp.crEffect = 0; + + if (dp.cx > 0 && dp.cy > 0) + ImageList_DrawIndirect(&dp); + } + + } + if (NULL != item->text && L'\0' != *item->text) + { + LONG left = prcItem->left + ((prcItem->right - prcItem->left) - item->textWidth) / 2; + if (left < (prcItem->left + tab->frameWidth)) left = prcItem->left + tab->frameWidth; + + LONG top = prcItem->bottom - tab->textHeight - tab->frameHeight + 1; + + SetRect(&rect, left, top, left + item->textWidth, top + tab->textHeight); + if (rect.right > (prcItem->right - tab->frameWidth)) rect.right = prcItem->right - tab->frameWidth; + + if (rect.bottom > prcPaint->bottom) rect.bottom = prcPaint->bottom; + if (rect.top < prcPaint->top) rect.top = prcPaint->top; + if (rect.right > prcPaint->right) rect.right = prcPaint->right; + if (rect.left < prcPaint->left) rect.left = prcPaint->left; + + if (rect.left < rect.right && rect.top < rect.bottom) + { + SetTextColor(hdc, color->text); + INT cchText = lstrlen(item->text); + ExtTextOut(hdc, left, top, ETO_CLIPPED, &rect, item->text, cchText, NULL); + } + } + + return TRUE; +} + +static INT CALLBACK LoginTab_PaintItemCallback(LOGINTAB *tab, LOGINTABITEM *item, INT iItem, const RECT *prcItem, ULONG_PTR param) +{ + PAINTITEMPARAM *pip = (PAINTITEMPARAM*)param; + if (NULL == pip) return -2; + + RECT paintRect; + if (FALSE != IntersectRect(&paintRect, pip->prcPaint, prcItem)) + { + + SetRectRgn(pip->clipRgn, paintRect.left, paintRect.top, paintRect.right, paintRect.bottom); + CombineRgn(pip->eraseRgn, pip->eraseRgn, pip->clipRgn, RGN_DIFF); + + HBITMAP hbmp = LoginTab_GetItemBitmap(tab, pip->hdc, + prcItem->right - prcItem->left, + prcItem->bottom - prcItem->top); + + if (NULL != hbmp) + { + SelectObject(pip->hdcItem, hbmp); + SetViewportOrgEx(pip->hdcItem, -paintRect.left, -paintRect.top, NULL); + } + + LoginTab_EraseBkGround(pip->hdcItem, tab, pip->prcClient->bottom - pip->prcClient->top, &paintRect); + + if (iItem == tab->itemsCount) + LoginTab_PaintChevron(pip->hdcItem, tab, prcItem, &paintRect, pip->hdcSrc); + else + LoginTab_PaintItem(pip->hwndTab, pip->hdcItem, tab, item, iItem, prcItem, &paintRect, pip->hdcSrc); + + BitBlt(pip->hdc, paintRect.left, paintRect.top, paintRect.right - paintRect.left, paintRect.bottom - paintRect.top, + pip->hdcItem, paintRect.left, paintRect.top, SRCCOPY); + } + return -1; +} + +static UINT LoginTab_GetDrawStyles(HWND hwnd) +{ + UINT windowStyle = GetWindowStyle(hwnd); + UINT drawStyle = 0; + + if (0 != (WS_DISABLED & windowStyle)) + drawStyle |= NLTDS_DISABLED; + else if (hwnd == GetFocus()) + { + UINT uiState = (UINT)SendMessage(hwnd, WM_QUERYUISTATE, 0, 0L); + if (0 == (UISF_HIDEFOCUS & uiState)) + drawStyle |= NLTDS_FOCUSED; + } + + if (0 != (NLTS_LOCKED & windowStyle)) + drawStyle |= NLTDS_LOCKED; + + return drawStyle; +} + +static void LoginTab_Paint(HWND hwnd, HDC hdc, const RECT *prcPaint, BOOL fErase) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + RECT clientRect; + GetClientRect(hwnd, &clientRect); + + tab->drawStyle = LoginTab_GetDrawStyles(hwnd); + + HBITMAP bitmapSrcOrig, bitmapItemOrig; + HFONT fontItemOrig; + + PAINTITEMPARAM param; + param.hwndTab = hwnd; + param.hdc = hdc; + param.prcPaint = prcPaint; + param.prcClient = &clientRect; + param.clipRgn = CreateRectRgn(0,0,0,0); + param.eraseRgn = CreateRectRgnIndirect(&clientRect); + param.hdcSrc = CreateCompatibleDC(hdc); + param.hdcItem = CreateCompatibleDC(hdc); + + + if (NULL != param.hdcSrc) + bitmapSrcOrig = (HBITMAP)GetCurrentObject(param.hdcSrc, OBJ_BITMAP); + + if (NULL != param.hdcItem) + { + bitmapItemOrig = (HBITMAP)GetCurrentObject(param.hdcItem, OBJ_BITMAP); + SetBkMode(param.hdcItem, TRANSPARENT); + fontItemOrig = (HFONT)SelectObject(param.hdcItem, tab->fontText); + } + + LoginTab_EnumerateItemRects(hwnd, param.hdcItem, LoginTab_PaintItemCallback, (ULONG_PTR)¶m); + + if (FALSE != fErase) + { + SelectClipRgn(hdc, param.eraseRgn); + LoginTab_EraseBkGround(hdc, tab, clientRect.bottom - clientRect.top, prcPaint); + //if (SUCCEEDED(UxTheme_LoadLibrary()) && FALSE != UxIsAppThemed()) + //{ + // // CONTROLPANEL, CPANEL_NAVIGATIONPANE, 0 + // // + // UXTHEME hTheme = UxOpenThemeData(hwnd, L"MENU"); + // if (NULL != hTheme) + // { + // clientRect.right++; + // UxDrawThemeBackground(hTheme, hdc, MENU_BARBACKGROUND, MB_ACTIVE, &clientRect, prcPaint); + // UxCloseThemeData(hTheme); + // } + //} + } + + if (NULL != param.hdcSrc) + { + SelectObject(param.hdcSrc, bitmapSrcOrig); + DeleteDC(param.hdcSrc); + } + + if (NULL != param.hdcItem) + { + SelectObject(param.hdcItem, bitmapItemOrig); + SelectObject(param.hdcItem, fontItemOrig); + DeleteDC(param.hdcItem); + } + + DeleteObject(param.clipRgn); + DeleteObject(param.eraseRgn); +} + +static INT CALLBACK LoginTab_GetItemRectCallback(LOGINTAB *tab, LOGINTABITEM *item, INT iItem, const RECT *prcItem, ULONG_PTR param) +{ + GETITEMRECTPARAM *gip = (GETITEMRECTPARAM*)param; + if (NULL == gip) return -2; + + if (iItem == gip->index) + { + CopyRect(gip->rect, prcItem); + return iItem; + } + + return -1; +} + +static BOOL LoginTab_GetItemRect(HWND hwnd, INT iItem, RECT *itemRect) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab || NULL == itemRect || iItem < 0 || iItem > tab->itemsCount) + return FALSE; + + GETITEMRECTPARAM param; + param.index = iItem; + param.rect = itemRect; + + INT result = LoginTab_EnumerateItemRects(hwnd, NULL, LoginTab_GetItemRectCallback, (ULONG_PTR)¶m); + + return (result == iItem); +} + +static INT CALLBACK LoginTab_HitTestCallback(LOGINTAB *tab, LOGINTABITEM *item, INT iItem, const RECT *prcItem, ULONG_PTR param) +{ + HITTESTITEMPARAM *htp = (HITTESTITEMPARAM*)param; + if (NULL == htp) return -2; + + if (FALSE != PtInRect(prcItem, htp->pt)) + { + if (NULL != htp->rect) + CopyRect(htp->rect, prcItem); + return iItem; + } + + return -1; +} + +static INT LoginTab_HitTest(HWND hwnd, INT x, INT y, RECT *itemRect) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return -1; + + HITTESTITEMPARAM param; + param.pt.x = x; + param.pt.y = y; + param.rect = itemRect; + + return LoginTab_EnumerateItemRects(hwnd, NULL, LoginTab_HitTestCallback, (ULONG_PTR)¶m); +} + +static INT CALLBACK LoginTab_UpdateLayoutCallback(LOGINTAB *tab, LOGINTABITEM *item, INT iItem, const RECT *prcItem, ULONG_PTR param) +{ + UPDATELAYOUTPARAM *ulp = (UPDATELAYOUTPARAM*)param; + if (NULL == ulp) return -2; + + if (iItem != tab->itemsCount) + { + if (0 == ulp->itemCount) + CopyRect(&ulp->visibleBox, prcItem); + else + ulp->visibleBox.right = prcItem->right; + + ulp->itemCount++; + } + else + { + ulp->chevronLeft = prcItem->left; + + if (ulp->visibleBox.right > ulp->chevronLeft) + ulp->visibleBox.right = ulp->chevronLeft; + + ulp->chevronVisible = TRUE; + } + + return -1; +} + +static void LoginTab_UpdateLayout(HWND hwnd, BOOL fRedraw) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + RECT clientRect, rect; + GetClientRect(hwnd, &clientRect); + + UPDATELAYOUTPARAM param; + ZeroMemory(¶m, sizeof(param)); + param.chevronLeft = clientRect.right; + + INT *orderCopy = NULL; + if (tab->itemsCount > 0) + { + orderCopy = (INT*)calloc(tab->itemsCount, sizeof(INT)); + if (NULL == orderCopy) return; + CopyMemory(orderCopy, tab->order, tab->itemsCount * sizeof(INT)); + } + + +// for (INT i = 0; i < tab->itemsCount; i++) +// tab->order[i] = i; // reset order + + LoginTab_EnumerateItemRects(hwnd, NULL, LoginTab_UpdateLayoutCallback, (ULONG_PTR)¶m); + + INT selectPos = -1; + if (-1 != tab->iSelected) + { + for (INT i = 0; i < tab->itemsCount; i++) + { + if (tab->order[i] == tab->iSelected) + { + selectPos = i; + break; + } + } + } + + if (tab->lastVisible < selectPos && tab->lastVisible >= 0 && selectPos >= 0) + { + CALCITEMWIDTH calcWidth; + if (FALSE != LoginTab_InitCalcItemWidth(hwnd, NULL, &calcWidth)) + { + INT selectWidth = LoginTab_CalculateItemWidth(&calcWidth, tab->items[tab->iSelected]); + INT limit = param.chevronLeft - selectWidth; + INT right = param.visibleBox.right + tab->spacing; + INT pos = tab->lastVisible + 1; + + while(right > limit && pos-- > 0) + { + right -= LoginTab_CalculateItemWidth(&calcWidth, tab->items[tab->order[pos]]); + if (pos > 0) + right -= tab->spacing; + } + + if (pos < selectPos) + MoveMemory(tab->order + (pos + 1), tab->order + pos, (selectPos - pos) * sizeof(INT)); + + tab->order[pos] = tab->iSelected; + tab->lastVisible = pos; + + right += selectWidth; + //if (param.visibleBox.right > right) + param.visibleBox.right = right; + + LoginTab_DestroyCalcItemWidth(&calcWidth); + } + + } + + INT invalidLeft = clientRect.right; + if(NULL != orderCopy) + { + for (INT i = 0; i < tab->itemsCount; i++) + { + if (tab->order[i] != orderCopy[i]) + { + if (FALSE != LoginTab_GetItemRect(hwnd, tab->order[i], &rect)) + invalidLeft = rect.left; + break; + } + } + free(orderCopy); + } + + if (tab->chevronLeft != param.chevronLeft) + { + SetRect(&rect, param.chevronLeft, clientRect.top, clientRect.right, clientRect.bottom); + if (rect.left > tab->chevronLeft) + rect.left = tab->chevronLeft; + + InvalidateRect(hwnd, &rect, FALSE); + tab->chevronLeft = param.chevronLeft; + } + + if (tab->visibleRight != param.visibleBox.right || invalidLeft != clientRect.right) + { + CopyRect(&rect, ¶m.visibleBox); + rect.left = min(param.visibleBox.right, tab->visibleRight); + if (invalidLeft < rect.left) rect.left = invalidLeft; + rect.right = max(param.visibleBox.right, tab->visibleRight); + + InvalidateRect(hwnd, &rect, FALSE); + tab->visibleRight = param.visibleBox.right; + } + +} +static void LoginTab_SetItemFocus(HWND hwnd, INT iFocus) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + INT focusPos = -1; + if (iFocus >= tab->itemsCount) + focusPos = iFocus; + else if (-1 != iFocus) + { + for (INT i = 0; i < tab->itemsCount; i++) + { + if (tab->order[i] == iFocus) + { + focusPos = i; + break; + } + } + } + + if (focusPos > tab->lastVisible) + { + if (tab->lastVisible != (tab->itemsCount -1)) + { + iFocus = tab->itemsCount; + } + else + { + iFocus = tab->order[tab->lastVisible]; + } + } + + if (iFocus < 0) + iFocus = (tab->itemsCount > 0) ? 0 : -1; + + if (iFocus != tab->iFocused) + { + INT iFocused = tab->iFocused; + INT iSelected = tab->iSelected; + + tab->iFocused = iFocus; + if (iFocus < tab->itemsCount) + tab->iSelected = iFocus; + + RECT rect; + if (-1 != tab->iFocused && FALSE != LoginTab_GetItemRect(hwnd, tab->iFocused, &rect)) + InvalidateRect(hwnd, &rect, FALSE); + + if (-1 != iFocused && iFocused != tab->iFocused && + FALSE != LoginTab_GetItemRect(hwnd, iFocused, &rect)) + { + InvalidateRect(hwnd, &rect, FALSE); + } + + if (-1 != iSelected && iSelected != tab->iSelected && iSelected != iFocused && + FALSE != LoginTab_GetItemRect(hwnd, iSelected, &rect)) + { + InvalidateRect(hwnd, &rect, FALSE); + } + + if (iSelected != tab->iSelected) + { + LoginTab_NotifySelectionChanged(hwnd); + } + } +} +static BOOL LoginTab_ShowHiddenTabs(HWND hwnd, const RECT *ownerRect) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return FALSE; + + HMENU hMenu = CreatePopupMenu(); + if (NULL == hMenu) return FALSE; + + MENUINFO mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIM_STYLE | MIIM_FTYPE; + mi.dwStyle = /*MNS_MODELESS | */ MNS_NOCHECK; + SetMenuInfo(hMenu, &mi); + + UINT insertedCount = 0; + MENUITEMINFO mii; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_STRING | MIIM_ID | MIIM_STATE | MIIM_FTYPE | MIIM_DATA; + mii.fState = MFS_UNHILITE | MFS_ENABLED; + + CHEVRONMENUPAINTPARAM menuPaint; + ZeroMemory(&menuPaint, sizeof(menuPaint)); + + CALCITEMWIDTH calcItem; + LoginTab_InitCalcItemWidth(hwnd, NULL, &calcItem); + + menuPaint.itemHeight = calcItem.itemHeight; + if (NULL != ownerRect) + CopyRect(&menuPaint.ownerRect, ownerRect); + + INT width = tab->itemsCount - tab->lastVisible - 1; + if (width < 2) width = 1; + else if (width < 9) width = 2; + else width = (INT)sqrt((float)(width)); + + for(INT offset = 0; offset < width; offset++) + { + mii.fType = MFT_OWNERDRAW | MFT_MENUBREAK; + for(INT i = tab->lastVisible + 1 + offset; i < tab->itemsCount; i += width) + { + LOGINTABITEM *item = tab->items[tab->order[i]]; + + INT itemWidth = LoginTab_CalculateItemWidth(&calcItem, item); + if (menuPaint.itemWidth < itemWidth) menuPaint.itemWidth = itemWidth; + + mii.wID = tab->order[i] + 1; + mii.dwTypeData = item->text; + mii.dwItemData = (ULONG_PTR)item; + + if (FALSE != InsertMenuItem(hMenu, insertedCount, TRUE, &mii)) + insertedCount++; + + mii.fType = MFT_OWNERDRAW; + } + } + + LoginTab_DestroyCalcItemWidth(&calcItem); + + if (NULL != hMenu && insertedCount > 0) + { + RECT windowRect; + GetWindowRect(hwnd, &windowRect); + + POINT menuOrig; + menuOrig.x = windowRect.right; + menuOrig.y = windowRect.bottom - 1; + + UINT menuStyle = TPM_RIGHTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION | TPM_NONOTIFY | TPM_RETURNCMD; + + + TRACKMOUSEEVENT tm; + tm.cbSize = sizeof(tm); + tm.dwFlags = TME_CANCEL | TME_LEAVE; + tm.hwndTrack = hwnd; + TrackMouseEvent(&tm); + + tab->chevronMenu = hMenu; + + HBITMAP bitmapSrcOrig, bitmapItemOrig; + HFONT fontItemOrig; + + HBRUSH hbrBack = NULL; + + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + menuPaint.hdcSrc = CreateCompatibleDC(hdc); + menuPaint.hdcItem = CreateCompatibleDC(hdc); + + + if (NULL != menuPaint.hdcSrc) + { + bitmapSrcOrig = (HBITMAP)GetCurrentObject(menuPaint.hdcSrc, OBJ_BITMAP); + + INT menuHeight = (insertedCount /width + insertedCount%width) * menuPaint.itemHeight + 2 * 4; + HBITMAP hbmpPattern = CreateCompatibleBitmap(hdc, 1, menuHeight); + if (NULL != hbmpPattern) + { + HBITMAP bmpOrig = (HBITMAP)SelectObject(menuPaint.hdcSrc, hbmpPattern); + RECT rect; + SetRect(&rect, 0, 0, 1, menuHeight); + LoginTab_EraseBkGround(menuPaint.hdcSrc, tab, menuHeight, &rect); + SelectObject(menuPaint.hdcSrc, bmpOrig); + hbrBack = CreatePatternBrush(hbmpPattern); + DeleteObject(hbmpPattern); + } + } + + if (NULL != menuPaint.hdcItem) + { + bitmapItemOrig = (HBITMAP)GetCurrentObject(menuPaint.hdcItem, OBJ_BITMAP); + SetBkMode(menuPaint.hdcItem, TRANSPARENT); + fontItemOrig = (HFONT)SelectObject(menuPaint.hdcItem, tab->fontText); + } + + + + ReleaseDC(hwnd, hdc); + } + + + MENUINFO mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIM_MENUDATA | MIM_BACKGROUND; + mi.dwMenuData = (ULONG_PTR)&menuPaint; + mi.hbrBack = hbrBack; + SetMenuInfo(hMenu, &mi); + + TPMPARAMS tpm; + tpm.cbSize =sizeof(tpm); + GetWindowRect(hwnd, &tpm.rcExclude); + tpm.rcExclude.bottom -= 2; + tpm.rcExclude.left = -20000; + tpm.rcExclude.right = 20000; + INT commandId = TrackPopupMenuEx(hMenu, menuStyle, menuOrig.x, menuOrig.y, hwnd, &tpm); + commandId--; + + if ( hbrBack != NULL ) + DeleteObject( hbrBack ); + + if (NULL != menuPaint.hdcSrc) + { + SelectObject(menuPaint.hdcSrc, bitmapSrcOrig); + DeleteDC(menuPaint.hdcSrc); + } + + if (NULL != menuPaint.hdcItem) + { + SelectObject(menuPaint.hdcItem, bitmapItemOrig); + SelectObject(menuPaint.hdcItem, fontItemOrig); + DeleteDC(menuPaint.hdcItem); + } + + tab->chevronMenu = NULL; + + if (commandId >= 0 && commandId < tab->itemsCount && tab->iSelected != commandId) + { + LoginTab_SetCurSel(hwnd, commandId); + LoginTab_NotifySelectionChanged(hwnd); + } + } + + if (NULL != hMenu) + DestroyMenu(hMenu); + + return TRUE; +} + +static void LoginTab_TrackMouseLeave(HWND hwnd) +{ + TRACKMOUSEEVENT tm; + tm.cbSize = sizeof(TRACKMOUSEEVENT); + tm.dwFlags = TME_QUERY; + tm.hwndTrack = hwnd; + if (TrackMouseEvent(&tm) && 0 == (TME_LEAVE & tm.dwFlags)) + { + tm.cbSize = sizeof(TRACKMOUSEEVENT); + tm.dwFlags = TME_LEAVE; + tm.hwndTrack = hwnd; + TrackMouseEvent(&tm); + } +} + + +static void LoginTab_UpdateColors(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + tab->colors.backTop = GetSysColor(COLOR_WINDOW); + + WORD h, l, s, lBottom, lLine; + INT k; + ColorRGBToHLS(tab->colors.backTop, &h, &l, &s); + k = MulDiv(240, 50, 1000); + lBottom = l + ((l > 0) ? -k : k); + if (lBottom > 240) lBottom = 240; + + k = MulDiv(240, 75, 1000); + lLine = l + ((l > 0) ? -k : k); + if (lLine > 240) lLine = 240; + + tab->colors.backBottom = ColorHLSToRGB(h, lBottom, s); + tab->colors.backLine = ColorHLSToRGB(h, lLine, s); + + tab->colors.focus = RGB(255, 255, 255); + tab->colors.focusDash = RGB(0, 0, 0); + + COLORREF rgbTextActive = GetSysColor(COLOR_WINDOWTEXT); + COLORREF rgbText = Color_Blend(rgbTextActive, tab->colors.backBottom, 210); + + + tab->colors.item.normal.backAlpha = 0; + tab->colors.item.normal.backTop = 0; + tab->colors.item.normal.backBottom = 0; + tab->colors.item.normal.frameType = FRAMETYPE_NONE; + tab->colors.item.normal.text = rgbText; + + tab->colors.item.normalHigh.backAlpha = tab->colors.item.normal.backAlpha; + tab->colors.item.normalHigh.backTop = tab->colors.item.normal.backTop; + tab->colors.item.normalHigh.backBottom = tab->colors.item.normal.backBottom; + tab->colors.item.normalHigh.frameType = tab->colors.item.normal.frameType; + tab->colors.item.normalHigh.text = rgbTextActive; + + tab->colors.item.normalPressed.backAlpha = tab->colors.item.normal.backAlpha; + tab->colors.item.normalPressed.backTop = tab->colors.item.normal.backTop; + tab->colors.item.normalPressed.backBottom = tab->colors.item.normal.backBottom; + tab->colors.item.normalPressed.frameType = tab->colors.item.normal.frameType; + tab->colors.item.normalPressed.text = rgbTextActive; + + tab->colors.item.normalDisabled.backAlpha = tab->colors.item.normal.backAlpha; + tab->colors.item.normalDisabled.backTop = tab->colors.item.normal.backTop; + tab->colors.item.normalDisabled.backBottom = tab->colors.item.normal.backBottom; + tab->colors.item.normalDisabled.frameType = tab->colors.item.normal.frameType; + tab->colors.item.normalDisabled.text = Color_Blend(GetSysColor(COLOR_GRAYTEXT), tab->colors.backBottom, 160); + + tab->colors.item.selected.backAlpha = 100; + tab->colors.item.selected.backTop = Color_Blend(GetSysColor(COLOR_HIGHLIGHT), GetSysColor(COLOR_WINDOW), 100); + tab->colors.item.selected.backBottom = Color_Blend(GetSysColor(COLOR_HIGHLIGHT), GetSysColor(COLOR_WINDOW), 140); + + tab->colors.item.selected.frameType = FRAMETYPE_SELECTED; + tab->colors.item.selected.text = rgbTextActive; + + tab->colors.item.selectedHigh.backAlpha = tab->colors.item.selected.backAlpha; + tab->colors.item.selectedHigh.backTop = tab->colors.item.selected.backTop; + tab->colors.item.selectedHigh.backBottom = tab->colors.item.selected.backBottom; + tab->colors.item.selectedHigh.frameType = tab->colors.item.selected.frameType; + tab->colors.item.selectedHigh.text = tab->colors.item.selected.text; + + tab->colors.item.selectedPressed.backAlpha = tab->colors.item.selected.backAlpha; + tab->colors.item.selectedPressed.backTop = tab->colors.item.selected.backTop; + tab->colors.item.selectedPressed.backBottom = tab->colors.item.selected.backBottom; + tab->colors.item.selectedPressed.frameType = tab->colors.item.selected.frameType; + tab->colors.item.selectedPressed.text = tab->colors.item.selected.text; + + tab->colors.item.selectedDisabled.backAlpha = tab->colors.item.selected.backAlpha; + tab->colors.item.selectedDisabled.backTop = Color_Blend(GetSysColor(COLOR_3DLIGHT), GetSysColor(COLOR_WINDOW), 100); + tab->colors.item.selectedDisabled.backBottom = Color_Blend(GetSysColor(COLOR_3DLIGHT), GetSysColor(COLOR_WINDOW), 140); + tab->colors.item.selectedDisabled.frameType = FRAMETYPE_DISABLED; + tab->colors.item.selectedDisabled.text = tab->colors.item.normalDisabled.text; + + if (NULL != tab->frameBitmap) + DeleteObject(tab->frameBitmap); + + BITMAPINFOHEADER frameHeader; + BYTE *framePixels; + tab->frameBitmap = ImageLoader_LoadBitmapEx(WASABI_API_ORIG_HINST, + MAKEINTRESOURCE(IDR_SELECTIONFRAME_IMAGE), FALSE, &frameHeader, (void**)&framePixels); + if (NULL == tab->frameBitmap) + { + tab->frameWidth = 0; + tab->frameHeight = 0; + } + else + { + if (frameHeader.biHeight < 0) + frameHeader.biHeight = -frameHeader.biHeight; + + tab->frameWidth = (frameHeader.biWidth - 1)/2; + tab->frameHeight = (frameHeader.biHeight/2 - 1)/2; + + COLORREF rgbTop = Color_Blend(GetSysColor(COLOR_HIGHLIGHT), GetSysColor(COLOR_WINDOW), 200); + COLORREF rgbBottom = GetSysColor(COLOR_WINDOW); + + Image_ColorizeEx(framePixels, frameHeader.biWidth, frameHeader.biHeight, 0, 0, + frameHeader.biWidth, frameHeader.biHeight, frameHeader.biBitCount, TRUE, rgbBottom, rgbTop); + } + + if (NULL != tab->chevronImage) + { + DeleteObject(tab->chevronImage); + tab->chevronImage = NULL; + } + + tab->chevronImage = LoginTab_LoadChevronImage(hwnd, &tab->chevronWidth, NULL); + if (NULL == tab->chevronImage) + { + tab->chevronWidth = 0; + } + else + { + INT k = tab->frameWidth - 2; + if (k < 0) k = 0; + tab->chevronWidth += 2 * k; + } + +} + +static void LoginTab_UpdateFonts(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + + tab->fontText = NULL; + LoginGuiObject *loginGui; + if (SUCCEEDED(LoginGuiObject::QueryInstance(&loginGui))) + { + tab->fontText = loginGui->GetTextFont(); + loginGui->Release(); + } + + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT fontOrig = (HFONT)SelectObject(hdc, tab->fontText); + + TEXTMETRIC tm; + if (FALSE != GetTextMetrics(hdc, &tm)) + { + tab->textHeight = tm.tmHeight; + + LONG baseunitY = tm.tmHeight; + LONG baseunitX = LoginBox_GetAveCharWidth(hdc); + + tab->textWidthMax = baseunitX * MAX_TEXT_AVECHAR_WIDTH + tm.tmOverhang; + tab->margins.left = MulDiv(1, baseunitX, 4); + tab->margins.right = MulDiv(1, baseunitX, 4); + tab->margins.top = MulDiv(1, baseunitY, 8); + tab->margins.bottom = MulDiv(1, baseunitY, 8); + tab->spacing = MulDiv(1, baseunitX, 4); + + for (INT i = 0; i < tab->itemsCount; i++) + tab->items[i]->textWidth = -1; + } + + + SelectObject(hdc, fontOrig); + ReleaseDC(hwnd, hdc); + } +} +static void LoginTab_UpdateMouseInfo(HWND hwnd) +{ + POINT pt; + RECT rect; + GetCursorPos(&pt); + MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1); + + GetClientRect(hwnd, &rect); + if (FALSE == PtInRect(&rect, pt)) + { + SendMessage(hwnd, WM_MOUSELEAVE, 0, 0L); + } + else + { + UINT vKey = 0; + if (0 != (0x8000 & GetAsyncKeyState(VK_CONTROL))) + vKey |= MK_CONTROL; + if (0 != (0x8000 & GetAsyncKeyState(VK_LBUTTON))) + vKey |= MK_LBUTTON; + if (0 != (0x8000 & GetAsyncKeyState(VK_RBUTTON))) + vKey |= MK_RBUTTON; + if (0 != (0x8000 & GetAsyncKeyState(VK_SHIFT))) + vKey |= MK_SHIFT; + if (0 != (0x8000 & GetAsyncKeyState(VK_XBUTTON1))) + vKey |= MK_XBUTTON1; + if (0 != (0x8000 & GetAsyncKeyState(VK_XBUTTON2))) + vKey |= MK_XBUTTON2; + + SendMessage(hwnd, WM_MOUSEMOVE, vKey, MAKELPARAM(pt.x, pt.y)); + } +} +static HWND LoginTab_CreateTooltip(HWND hwnd) +{ + HWND hTooltip = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT | WS_EX_LAYERED, + TOOLTIPS_CLASS, NULL, WS_CLIPSIBLINGS | WS_POPUP | TTS_NOPREFIX /*| TTS_ALWAYSTIP*/, + 0, 0, 0, 0, hwnd, NULL, NULL, NULL); + + if (NULL == hTooltip) + return NULL; + + SendMessage(hTooltip, CCM_SETVERSION, 6, 0L); + SetWindowPos(hTooltip, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); + SendMessage(hTooltip, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELPARAM(1000, 0)); + + TOOLINFO ti; + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(TOOLINFO); + ti.hwnd = hwnd; + ti.lpszText = LPSTR_TEXTCALLBACK; + SendMessage(hTooltip, TTM_ADDTOOL, 0, (LPARAM)&ti); + + + INT helpWidthMax = 260; + + NONCLIENTMETRICS ncm; + ncm.cbSize = sizeof(ncm); + + OSVERSIONINFO vi; + vi.dwOSVersionInfoSize = sizeof(vi); + if (FALSE == GetVersionEx(&vi)) + ZeroMemory(&vi, sizeof(vi)); + + if (vi.dwMajorVersion < 6) + ncm.cbSize -= sizeof(ncm.iPaddedBorderWidth); + + RECT marginRect; + SetRect(&marginRect, 3, 1, 3, 1); + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0)) + { + HFONT font = CreateFontIndirect(&ncm.lfStatusFont); + if (NULL != font) + { + HDC hdc = GetDCEx(hTooltip, NULL, DCX_CACHE); + if (NULL != hdc) + { + HFONT fontOrig = (HFONT)SelectObject(hdc, font); + TEXTMETRIC tm; + if (FALSE != GetTextMetrics(hdc, &tm)) + { + INT baseunitX = LoginBox_GetAveCharWidth(hdc); + if (NULL != baseunitX) + { + helpWidthMax = baseunitX * MAX_HELP_AVECHAR_WIDTH + tm.tmOverhang; + marginRect.left = MulDiv(2, baseunitX, 4); + marginRect.right = marginRect.left; + marginRect.top = MulDiv(1, tm.tmHeight, 8); + marginRect.bottom = MulDiv(1, tm.tmHeight, 8); + } + } + SelectObject(hdc, fontOrig); + ReleaseDC(hTooltip, hdc); + } + DeleteObject(font); + } + } + + SendMessage(hTooltip, TTM_SETMAXTIPWIDTH, 0, helpWidthMax); + SendMessage(hTooltip, TTM_SETMARGIN, 0, (LPARAM)&marginRect); + + return hTooltip; +} + +static void LoginTab_RelayTooltipMouseMsg(HWND hwnd, UINT uMsg, UINT vKey, POINTS pts) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab || NULL == tab->hTooltip || -1 == tab->iHighlighted) + return; + + MSG msg; + msg.hwnd = hwnd; + msg.message = uMsg; + msg.wParam = (WPARAM)vKey; + msg.lParam = MAKELPARAM(pts.x, pts.y); + + SendMessage(tab->hTooltip, TTM_RELAYEVENT, 0, (LPARAM)&msg); +} + +static void LoginTab_UpdateTooltip(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + TOOLINFO ti; + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(ti); + ti.hwnd = hwnd; + ti.uId = 0; + + if (FALSE == SendMessage(tab->hTooltip, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) + return; + + INT iItem = tab->iHighlighted; + RECT rect; + if (-1 == iItem || FALSE == LoginTab_GetItemRect(hwnd, iItem, &rect)) + { + SendMessage(tab->hTooltip, TTM_ACTIVATE, FALSE, 0L); + SysFreeString(tab->helpText); + tab->helpText = NULL; + + return; + } + + if (ti.lParam != (LPARAM)iItem || FALSE == EqualRect(&ti.rect, &rect)) + { + SendMessage(tab->hTooltip, TTM_ACTIVATE, FALSE, 0L); + + CopyRect(&ti.rect, &rect); + if (ti.lParam != iItem) + { + LPCWSTR pszTitle = NULL; + if (iItem >= 0 && iItem < tab->itemsCount) + pszTitle = tab->items[iItem]->text; + SendMessage(tab->hTooltip, TTM_SETTITLE, (WPARAM)TTI_NONE, (LPARAM)pszTitle); + + ti.lParam = (LPARAM)iItem; + ti.lpszText = LPSTR_TEXTCALLBACK; + + SysFreeString(tab->helpText); + tab->helpText = NULL; + } + SendMessage(tab->hTooltip, TTM_SETTOOLINFO, 0, (LPARAM)&ti); + + } + + SendMessage(tab->hTooltip, TTM_ACTIVATE, TRUE, 0L); +} + +static void LoginTab_FreeItem(LOGINTABITEM *item) +{ + if (NULL == item) return; + + LoginBox_FreeString(item->text); + free(item); +} + +static BOOL LoginTab_SetItemInternal(LOGINTABITEM *dst, const NLTITEM *src) +{ + if (NULL == dst || NULL == src) + return FALSE; + + BOOL succeeded = TRUE; + + if (0 != (NLTIF_TEXT & src->mask)) + { + dst->textWidth = -1; + LoginBox_FreeString(dst->text); + if (NULL != src->pszText) + { + dst->text = LoginBox_CopyString(src->pszText); + if (NULL == dst->text) succeeded = FALSE; + } + else + { + dst->text = NULL; + } + } + + if (0 != (NLTIF_PARAM & src->mask)) + dst->param = src->param; + + if (0 != (NLTIF_IMAGE & src->mask)) + dst->iImage = src->iImage; + + if (0 != (NLTIF_IMAGE_ACTIVE & src->mask)) + dst->iImageActive = src->iImageActive; + + if (0 != (NLTIF_IMAGE_DISABLED & src->mask)) + dst->iImageDisabled = src->iImageDisabled; + + return succeeded; +} + +static LOGINTABITEM *LoginTab_CreateItem(const NLTITEM *pItem) +{ + LOGINTABITEM *item = (LOGINTABITEM*)calloc(1, sizeof(LOGINTABITEM)); + if (NULL == item) return NULL; + + item->textWidth = -1; + item->iImage = NLTM_IMAGE_NONE; + item->iImageActive = NLTM_IMAGE_NONE; + item->iImageDisabled = NLTM_IMAGE_NONE; + + if (NULL != pItem && FALSE == LoginTab_SetItemInternal(item, pItem)) + { + LoginTab_FreeItem(item); + item = NULL; + } + return item; +} + +static INT LoginTab_DeleteAllItemsReal(HWND hwnd, LOGINTABITEM **itemsList, INT itemsCount) +{ + if (NULL == itemsList || itemsCount < 1) + return 0; + + NMLOGINTAB nmp; + nmp.hdr.hwndFrom = hwnd; + nmp.hdr.idFrom = GetWindowLongPtr(hwnd, GWLP_ID); + HWND hParent = GetAncestor(hwnd, GA_PARENT); + + BOOL fNotifyItem = FALSE; + + if (NULL != hParent) + { + nmp.hdr.code = NLTN_DELETEALLITEMS; + nmp.iItem = -1; + fNotifyItem = (FALSE == (BOOL)SNDMSG(hParent, WM_NOTIFY, (WPARAM)nmp.hdr.idFrom, (LPARAM)&nmp)); + if (FALSE != fNotifyItem) + nmp.hdr.code = NLTN_DELETEITEM; + } + + INT deleted = 0; + for(; itemsCount > 0; itemsCount--) + { + nmp.iItem = itemsCount - 1; + if (FALSE != fNotifyItem) + SNDMSG(hParent, WM_NOTIFY, (WPARAM)nmp.hdr.idFrom, (LPARAM)&nmp); + + LoginTab_FreeItem(itemsList[nmp.iItem]); + deleted++; + } + + return deleted; +} + +static LRESULT LoginTab_OnCreate(HWND hwnd, CREATESTRUCT* pcs) +{ + LOGINTAB *tab = (LOGINTAB*)calloc(1, sizeof(LOGINTAB)); + if (NULL != tab) + { + SetLastError(ERROR_SUCCESS); + if (!SetWindowLongPtr(hwnd, 0, (LONGX86)(LONG_PTR)tab) && ERROR_SUCCESS != GetLastError()) + { + free(tab); + tab = NULL; + } + } + + if (NULL == tab) + return -1; + + tab->iPressed = -1; + tab->iSelected = -1; + tab->iHighlighted = -1; + + tab->hTooltip = LoginTab_CreateTooltip(hwnd); + + LoginTab_UpdateColors(hwnd); + LoginTab_UpdateFonts(hwnd); + + return 0; +} + +static void LoginTab_OnDestroy(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + SetWindowLongPtr(hwnd, 0, 0L); + if (NULL == tab) return; + + if (NULL != tab->items) + { + tab->itemsCount -= LoginTab_DeleteAllItemsReal(hwnd, tab->items, tab->itemsCount); + free(tab->items); + } + + if (NULL != tab->order) + free(tab->order); + + if (NULL != tab->frameBitmap) + DeleteObject(tab->frameBitmap); + + if (NULL != tab->itemBitmap) + DeleteObject(tab->itemBitmap); + + if (NULL != tab->chevronImage) + DeleteObject(tab->chevronImage); + + if (NULL != tab->hTooltip) + DestroyWindow(tab->hTooltip); + + SysFreeString(tab->helpText); + + free(tab); +} + +static void LoginTab_OnWindowPosChanged(HWND hwnd, WINDOWPOS *pwp) +{ + if (SWP_NOSIZE == ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags)) + return; + + LoginTab_UpdateLayout(hwnd, 0 == (SWP_NOREDRAW & pwp->flags)); +} + + +static void LoginTab_OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + if (BeginPaint(hwnd, &ps)) + { + if (ps.rcPaint.left != ps.rcPaint.right) + LoginTab_Paint(hwnd, ps.hdc, &ps.rcPaint, ps.fErase); + EndPaint(hwnd, &ps); + } +} + +static void LoginTab_OnPrintClient(HWND hwnd, HDC hdc, UINT options) +{ + RECT clientRect; + if (GetClientRect(hwnd, &clientRect)) + LoginTab_Paint(hwnd, hdc, &clientRect, 0 != (PRF_ERASEBKGND & options)); +} + + +static void LoginTab_OnMouseMove(HWND hwnd, UINT vKey, POINTS pts) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + if (FALSE != LoginTab_IsLocked(hwnd)) + return; + + RECT rect; + + INT iHighlighted = tab->iHighlighted; + + tab->iHighlighted = (-1 == tab->iPressed) ? + LoginTab_HitTest(hwnd, pts.x, pts.y, &rect) : -1; + + + + if (iHighlighted != tab->iHighlighted) + LoginTab_UpdateTooltip(hwnd); + + LoginTab_RelayTooltipMouseMsg(hwnd, WM_MOUSEMOVE, vKey, pts); + + + if (iHighlighted != tab->iHighlighted) + { + InvalidateRect(hwnd, &rect, FALSE); + + if (-1 != iHighlighted && FALSE != LoginTab_GetItemRect(hwnd, iHighlighted, &rect)) + InvalidateRect(hwnd, &rect, FALSE); + } + + if (-1 != tab->iHighlighted) + LoginTab_TrackMouseLeave(hwnd); + + +} + +static void LoginTab_OnMouseLeave(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + INT iPressed = tab->iPressed; + INT iHighlighted = tab->iHighlighted; + + tab->iPressed = -1; + tab->iHighlighted = -1; + + RECT rect; + if (-1 != iHighlighted && FALSE != LoginTab_GetItemRect(hwnd, iHighlighted, &rect)) + InvalidateRect(hwnd, &rect, FALSE); + + if (-1 != iPressed && iPressed != iHighlighted && FALSE != LoginTab_GetItemRect(hwnd, iPressed, &rect)) + InvalidateRect(hwnd, &rect, FALSE); + +} + +static void LoginTab_OnLButtonDown(HWND hwnd, UINT vKey, POINTS pts) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + if (FALSE != LoginTab_IsLocked(hwnd)) + return; + + LoginTab_RelayTooltipMouseMsg(hwnd, WM_LBUTTONDOWN, vKey, pts); + + RECT rect, rect2; + INT iPressed, iHighlighted; + INT iItem = LoginTab_HitTest(hwnd, pts.x, pts.y, &rect); + + iPressed = tab->iPressed; + iHighlighted = tab->iHighlighted; + + tab->iPressed = iItem; + tab->iHighlighted = -1; + + if (iPressed != iItem && -1 != iPressed && + FALSE != LoginTab_GetItemRect(hwnd, iPressed, &rect2)) + { + InvalidateRect(hwnd, &rect2, FALSE); + } + + if (iHighlighted != iItem && -1 != iHighlighted && iHighlighted != iPressed && + FALSE != LoginTab_GetItemRect(hwnd, iHighlighted, &rect2)) + { + InvalidateRect(hwnd, &rect2, FALSE); + } + + if (-1 != iItem && iPressed != iItem) + { + if (iItem == tab->itemsCount && tab->iFocused != iItem) + { + INT iFocused = tab->iFocused; + tab->iFocused = iItem; + + if (-1 != iFocused && FALSE != LoginTab_GetItemRect(hwnd, iFocused, &rect2)) + InvalidateRect(hwnd, &rect2, FALSE); + } + InvalidateRect(hwnd, &rect, FALSE); + + if (iItem == tab->itemsCount) + { + + LoginTab_ShowHiddenTabs(hwnd, &rect); + + if (0 == (0x8000 & GetAsyncKeyState((FALSE == GetSystemMetrics(SM_SWAPBUTTON)) ? VK_LBUTTON : VK_RBUTTON))) + tab->iPressed = -1; + tab->iFocused = tab->itemsCount; + InvalidateRect(hwnd, &rect, FALSE); + LoginTab_UpdateMouseInfo(hwnd); + + } + else if (hwnd != GetCapture()) + SetCapture(hwnd); + } +} + +static void LoginTab_OnLButtonUp(HWND hwnd, UINT vKey, POINTS pts) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + if (FALSE != LoginTab_IsLocked(hwnd)) + return; + + LoginTab_RelayTooltipMouseMsg(hwnd, WM_LBUTTONUP, vKey, pts); + + RECT rect, rect2; + + INT iPressed = tab->iPressed; + INT iHighlighted = tab->iHighlighted; + INT iSelected = tab->iSelected; + INT iFocused = tab->iFocused; + + INT iItem = LoginTab_HitTest(hwnd, pts.x, pts.y, &rect); + + + tab->iPressed = -1; + tab->iHighlighted = iItem; + + if (iItem == iPressed && -1 != iItem) + { + if (iItem < tab->itemsCount) + tab->iSelected = iItem; + tab->iFocused = iItem; + } + + if (-1 != iHighlighted && iHighlighted != tab->iHighlighted && + iHighlighted != iPressed && iHighlighted != iFocused && iHighlighted != iSelected && + FALSE != LoginTab_GetItemRect(hwnd, iHighlighted, &rect2)) + { + InvalidateRect(hwnd, &rect2, FALSE); + } + + + if (-1 != iPressed && iPressed != tab->iPressed && + (iPressed != iFocused || tab->iFocused == iFocused) && + (iPressed != iSelected || tab->iSelected == iSelected) && + FALSE != LoginTab_GetItemRect(hwnd, iPressed, &rect2)) + { + InvalidateRect(hwnd, &rect2, FALSE); + } + + + if (-1 != iSelected && iSelected != tab->iSelected && + iSelected != iFocused && + FALSE != LoginTab_GetItemRect(hwnd, iSelected, &rect2)) + { + InvalidateRect(hwnd, &rect2, FALSE); + } + + if (-1 != iFocused && iFocused != tab->iFocused && + FALSE != LoginTab_GetItemRect(hwnd, iFocused, &rect2)) + { + InvalidateRect(hwnd, &rect2, FALSE); + } + + + + if (-1 != iItem) + InvalidateRect(hwnd, &rect, FALSE); + + if (hwnd == GetCapture()) + ReleaseCapture(); + + if (-1 != tab->iSelected && tab->iSelected != iSelected) + LoginTab_NotifySelectionChanged(hwnd); +} + +static void LoginTab_OnRButtonDown(HWND hwnd, UINT vKey, POINTS pts) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + if (FALSE != LoginTab_IsLocked(hwnd)) + return; + + LoginTab_RelayTooltipMouseMsg(hwnd, WM_RBUTTONDOWN, vKey, pts); + + HWND hParent = GetAncestor(hwnd, GA_PARENT); + if (NULL != hParent) + { + NMLOGINTABCLICK ntc; + ntc.hdr.code = NM_RCLICK; + ntc.hdr.hwndFrom = hwnd; + ntc.hdr.idFrom = GetWindowLongPtr(hwnd, GWLP_ID); + ntc.pt.x = pts.x; + ntc.pt.y = pts.y; + SNDMSG(hParent, WM_NOTIFY, (WPARAM)ntc.hdr.idFrom, (LPARAM)&ntc); + } +} + +static void LoginTab_OnRButtonUp(HWND hwnd, UINT vKey, POINTS pts) +{ + if (FALSE != LoginTab_IsLocked(hwnd)) + return; + + LoginTab_RelayTooltipMouseMsg(hwnd, WM_RBUTTONUP, vKey, pts); +} + +static void LoginTab_OnMButtonDown(HWND hwnd, UINT vKey, POINTS pts) +{ + if (FALSE != LoginTab_IsLocked(hwnd)) + return; + + LoginTab_RelayTooltipMouseMsg(hwnd, WM_MBUTTONDOWN, vKey, pts); +} + +static void LoginTab_OnMButtonUp(HWND hwnd, UINT vKey, POINTS pts) +{ + if (FALSE != LoginTab_IsLocked(hwnd)) + return; + + LoginTab_RelayTooltipMouseMsg(hwnd, WM_MBUTTONUP, vKey, pts); +} + +static void LoginTab_OnCaptureChanged(HWND hwnd, HWND hCapture) +{ + if (hwnd != hCapture) + LoginTab_TrackMouseLeave(hwnd); +} + +static void LoginTab_OnSetFocus(HWND hwnd, HWND hFocus) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + INT iFocus = tab->iSelected; + tab->iFocused = -1; + LoginTab_SetItemFocus(hwnd, iFocus); +} + +static void LoginTab_OnKillFocus(HWND hwnd, HWND hFocus) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + RECT rect; + if (-1 != tab->iFocused && FALSE != LoginTab_GetItemRect(hwnd, tab->iFocused, &rect)) + { + InvalidateRect(hwnd, &rect, FALSE); + } +} + +static void LoginTab_OnEnable(HWND hwnd, BOOL fEnabled) +{ + InvalidateRect(hwnd, NULL, FALSE); +} + +static void LoginTab_OnKeyDown(HWND hwnd, INT vKey, UINT flags) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + if (FALSE != LoginTab_IsLocked(hwnd)) + return; + + INT focusPos = -1; + if (tab->iFocused >= tab->itemsCount) + { + focusPos = tab->iFocused; + } + else if (-1 != tab->iFocused) + { + for (INT i = 0; i < tab->itemsCount; i++) + { + if (tab->order[i] == tab->iFocused) + { + focusPos = i; + break; + } + } + } + + switch(vKey) + { + case VK_LEFT: focusPos = (focusPos > tab->lastVisible) ? tab->lastVisible : (focusPos - 1); break; + case VK_RIGHT: focusPos++; break; + case VK_PRIOR: + case VK_HOME: focusPos = 0; break; + case VK_END: focusPos = tab->itemsCount -1; break; + case VK_NEXT: focusPos = tab->lastVisible; break; + + case VK_SPACE: + case VK_RETURN: + if (tab->iFocused == tab->itemsCount) + { + tab->iPressed = tab->iFocused; + RECT rect; + if (FALSE == LoginTab_GetItemRect(hwnd, tab->iPressed, &rect)) + SetRectEmpty(&rect); + + LoginTab_ShowHiddenTabs(hwnd, &rect); + tab->iFocused = tab->itemsCount; + tab->iPressed = -1; + InvalidateRect(hwnd, &rect, FALSE); + LoginTab_UpdateMouseInfo(hwnd); + return; + } + break; + } + + INT iFocus; + if (focusPos >= tab->itemsCount) + iFocus = tab->itemsCount; + else if (focusPos < 0) + iFocus = 0; + else + iFocus = tab->order[focusPos]; + + LoginTab_SetItemFocus(hwnd, iFocus); +} + + +static LRESULT LoginTab_OnGetDlgCode(HWND hwnd, INT vKey, MSG *pMsg) +{ + switch(vKey) + { + case VK_TAB: return 0; + } + return DLGC_WANTALLKEYS; +} + +static LRESULT LoginTab_OnMenuChar(HWND hwnd, INT vkCode, INT menuType, HMENU hMenu) +{ + switch(vkCode) + { + case VK_SPACE: + case VK_RETURN: + for (INT i = GetMenuItemCount(hMenu) - 1; i >= 0; i--) + { + UINT r = GetMenuState(hMenu, i, MF_BYPOSITION); + if (-1 != r && 0 != (MF_HILITE & LOWORD(r))) + return MAKELRESULT(i, MNC_EXECUTE); + } + return MAKELRESULT(0, MNC_SELECT); + } + return MAKELRESULT(0, MNC_IGNORE); +} + +static void LoginTab_OnMenuSelect(HWND hwnd, INT iItem, UINT flags, HMENU hMenu) +{ + MENUINFO mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIM_MENUDATA | MIM_BACKGROUND; + + if (FALSE != GetMenuInfo(hMenu, &mi) && NULL != mi.dwMenuData) + { + CHEVRONMENUPAINTPARAM *pmp= (CHEVRONMENUPAINTPARAM*)mi.dwMenuData; + if (NULL != pmp->hwndMenu) + { + UINT stateOrig = (UINT)SendMessage(pmp->hwndMenu, WM_QUERYUISTATE, 0, 0L); + + SendMessage(pmp->hwndMenu, WM_CHANGEUISTATE, + MAKEWPARAM(UISF_HIDEACCEL | UISF_HIDEFOCUS, UIS_INITIALIZE), 0L); + + UINT stateCurrent = (UINT)SendMessage(pmp->hwndMenu, WM_QUERYUISTATE, 0, 0L); + if ((UISF_HIDEFOCUS & stateOrig) != (UISF_HIDEFOCUS & stateCurrent)) + { + INT menuCount = GetMenuItemCount(hMenu); + while(menuCount--) + { + if (iItem == GetMenuItemID(hMenu, menuCount)) + { + RECT rect; + if (FALSE != GetMenuItemRect(NULL, hMenu, menuCount, &rect)) + { + MapWindowPoints(HWND_DESKTOP, pmp->hwndMenu, (POINT*)&rect, 2); + InvalidateRect(pmp->hwndMenu, &rect, FALSE); + } + break; + } + } + } + } + } +} + + +static void LoginTab_GetTootipDispInfo(HWND hwnd, NMTTDISPINFO *pdisp) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return; + + if(NULL == tab->helpText) + { + INT iItem = (INT)pdisp->lParam; + if (iItem >= 0 && iItem < tab->itemsCount) + { + HWND hParent = GetAncestor(hwnd, GA_PARENT); + if (NULL != hParent) + { + NMLOGINTABHELP help; + help.hdr.code = NLTN_GETITEMHELP; + help.hdr.hwndFrom = hwnd; + help.hdr.idFrom = GetWindowLongPtr(hwnd, GWLP_ID); + help.iItem = iItem; + help.param = tab->items[iItem]->param; + help.bstrHelp = NULL; + SNDMSG(hParent, WM_NOTIFY, (WPARAM)help.hdr.idFrom, (LPARAM)&help); + tab->helpText = help.bstrHelp; + } + } + else if (iItem == tab->itemsCount) + { + WCHAR szBuffer[256] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_LOGINTAB_MOREPROVIDERS, szBuffer, ARRAYSIZE(szBuffer)); + tab->helpText = SysAllocString(szBuffer); + } + + if (NULL == tab->helpText) + tab->helpText = SysAllocString(L" "); + } + + if (NULL != tab->helpText && L'\0' != tab->helpText) + { + pdisp->lpszText = (LPWSTR)tab->helpText; + pdisp->uFlags = TTF_DI_SETITEM; + } +} + +static BOOL LoginTab_OnShow(HWND hwnd, HWND hTooltip) +{ + TOOLINFO ti; + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(ti); + ti.hwnd = hwnd; + ti.uId = 0; + + if (FALSE == SendMessage(hTooltip, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) + return FALSE; + + MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&ti.rect, 2); + + RECT windowRect, tooltipRect; + GetWindowRect(hwnd, &windowRect); + GetWindowRect(hTooltip, &tooltipRect); + + ti.rect.right = ti.rect.left + (tooltipRect.right - tooltipRect.left); + ti.rect.top = windowRect.bottom; + ti.rect.bottom = ti.rect.top + (tooltipRect.bottom - tooltipRect.top); + + HMONITOR hMonitor = MonitorFromRect(&ti.rect, MONITOR_DEFAULTTONEAREST); + if (NULL != hMonitor) + { + MONITORINFO mi; + mi.cbSize = sizeof(mi); + if (FALSE != GetMonitorInfo(hMonitor, &mi)) + { + INT offsetX = 0; + INT offsetY = 0; + + if (ti.rect.right > mi.rcWork.right) + offsetX += (mi.rcWork.right - ti.rect.right); + + if (ti.rect.bottom > mi.rcWork.bottom) + { + offsetY += (mi.rcWork.bottom - ti.rect.bottom); + if ((ti.rect.top + offsetY) < windowRect.bottom && (ti.rect.bottom + offsetY) > windowRect.top) + offsetY = (windowRect.top - (ti.rect.bottom - ti.rect.top)) - ti.rect.top; + } + + if ((ti.rect.left + offsetX) < mi.rcWork.left) + offsetX += (mi.rcWork.left - (ti.rect.left + offsetX)); + if ((ti.rect.top + offsetY) < mi.rcWork.top) + offsetY += (mi.rcWork.top - (ti.rect.top + offsetY)); + + + + + OffsetRect(&ti.rect, offsetX, offsetY); + } + } + + return SetWindowPos(hTooltip, NULL, ti.rect.left, ti.rect.top, 0, 0, + SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOREDRAW); +} + +static LRESULT LoginTab_OnTooltipNotify(HWND hwnd, NMHDR *pnmh) +{ + switch(pnmh->code) + { + case TTN_GETDISPINFO: + LoginTab_GetTootipDispInfo(hwnd, (NMTTDISPINFO*)pnmh); + break; + case TTN_SHOW: + return LoginTab_OnShow(hwnd, pnmh->hwndFrom); + + } + return 0; +} + +static LRESULT LoginTab_OnNotify(HWND hwnd, INT controlId, NMHDR *pnmh) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return 0; + + if (tab->hTooltip == pnmh->hwndFrom && NULL != tab->hTooltip) + return LoginTab_OnTooltipNotify(hwnd, pnmh); + + return 0; +} +static INT LoginTab_OnGetIdealHeight(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return 0; + + INT height = 0; + + INT iconCX, iconCY; + if (NULL != tab->imageList && FALSE != ImageList_GetIconSize(tab->imageList, &iconCX, &iconCY)) + { + height += (iconCY + 2 * IMAGE_MARGIN_CY); + } + + height += tab->textHeight; + + height += tab->margins.top + tab->margins.bottom; + height += 2 * tab->frameHeight; + + return height; +} + +static INT LoginTab_OnGetIdealWidth(HWND hwnd, INT itemsCount) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return 0; + + INT width = 0; + + if (itemsCount < 0) itemsCount = 0; + if (itemsCount > tab->itemsCount) itemsCount = tab->itemsCount; + + if (itemsCount == 0) + { + width = tab->margins.left + tab->margins.right; + return width; + } + itemsCount--; + if (itemsCount <= tab->lastVisible) + { + RECT rect; + if (FALSE != LoginTab_GetItemRect(hwnd, itemsCount, &rect)) + width = rect.right; + } + else + { + RECT rect; + GetWindowRect(hwnd, &rect); + LONG origWidth = rect.right - rect.left; + LONG origHeight = rect.bottom - rect.top; + SetWindowPos(hwnd, NULL, 0, 0, 40000, origHeight, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING); + + if (FALSE != LoginTab_GetItemRect(hwnd, itemsCount, &rect)) + width = rect.right; + + SetWindowPos(hwnd, NULL, 0, 0, origWidth, origHeight, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING); + } + + if (0 != width) + { + width += (itemsCount < (tab->itemsCount - 1) && tab->chevronWidth > tab->margins.right) ? + tab->chevronWidth : tab->margins.right; + } + return width; +} +static INT LoginTab_OnInsertItem(HWND hwnd, INT iItem, NLTITEM *pItem) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab || NULL == pItem) return -1; + if (iItem < 0) iItem = 0; + + + INT itemsMax = (0 != tab->items) ? (INT)(_msize(tab->items)/sizeof(LOGINTABITEM*)) : 0; + if (tab->itemsCount >= itemsMax) + { + INT k = tab->itemsCount + 1 - itemsMax; + if (k < 8) k = 8; + itemsMax += k; + void *data = realloc(tab->items, itemsMax * sizeof(LOGINTABITEM*)); + if (NULL == data) return -1; + tab->items = (LOGINTABITEM**)data; + data = realloc(tab->order, itemsMax * sizeof(INT)); + if (NULL == data) return -1; + tab->order = (INT*)data; + } + + LOGINTABITEM *item = LoginTab_CreateItem(pItem); + if (NULL == item) return -1; + + if (iItem >= tab->itemsCount) + { + iItem = tab->itemsCount; + tab->items[iItem] = item; + tab->order[iItem] = iItem; + } + else + { + MoveMemory((tab->items + iItem + 1), (tab->items + iItem), sizeof(LOGINTABITEM*) * (tab->itemsCount - iItem)); + tab->items[iItem] = item; + + MoveMemory((tab->order + iItem + 1), (tab->order + iItem), sizeof(INT) * (tab->itemsCount - iItem)); + tab->order[iItem] = iItem; + + for (INT i = 0; i <= tab->itemsCount; i++) + { + if (iItem != i && tab->order[i] >= iItem) + ++(tab->order[i]); + } + } + + tab->itemsCount++; + + + return iItem; +} + +static BOOL LoginTab_OnSetItem(HWND hwnd, INT iItem, NLTITEM *pItem) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab || NULL == pItem) return FALSE; + if (iItem < 0 || iItem >= tab->itemsCount) return FALSE; + + BOOL result = LoginTab_SetItemInternal(tab->items[iItem], pItem); + + RECT rect; + GetClientRect(hwnd, &rect); + LONG clientRight = rect.right; + + if (FALSE != LoginTab_GetItemRect(hwnd, iItem, &rect)) + { + rect.right = clientRight; + InvalidateRect(hwnd, &rect, FALSE); + } + else if (NULL != tab->chevronMenu) + { + INT menuCount = GetMenuItemCount(tab->chevronMenu); + while(menuCount--) + { + if (iItem == (GetMenuItemID(tab->chevronMenu, menuCount) - 1)) + { + if (FALSE != GetMenuItemRect(NULL, tab->chevronMenu, menuCount, &rect)) + { + POINT ptTest; + ptTest.x = rect.left + (rect.right - rect.left)/2; + ptTest.y = rect.top + (rect.bottom - rect.top)/2; + HWND hwndMenu = WindowFromPoint(ptTest); + if (NULL != hwndMenu) + { + MapWindowPoints(HWND_DESKTOP, hwndMenu, (POINT*)&rect, 2); + InvalidateRect(hwndMenu, &rect, FALSE); + } + } + break; + } + } + + } + + return result; +} + +static BOOL LoginTab_OnGetItem(HWND hwnd, INT iItem, NLTITEM *pItem) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab || NULL == pItem) return FALSE; + if (iItem < 0 || iItem >= tab->itemsCount) return FALSE; + + LOGINTABITEM *item = tab->items[iItem]; + + BOOL succeeded = TRUE; + + if (0 != (NLTIF_TEXT & pItem->mask)) + { + if (NULL == pItem->pszText || + FAILED(StringCchCopyEx(pItem->pszText, pItem->cchTextMax, item->text, NULL, NULL, STRSAFE_IGNORE_NULLS))) + { + succeeded = FALSE; + } + } + + if (0 != (NLTIF_PARAM & pItem->mask)) + pItem->param = item->param; + + if (0 != (NLTIF_IMAGE & pItem->mask)) + pItem->iImage = item->iImage; + + if (0 != (NLTIF_IMAGE_ACTIVE & pItem->mask)) + pItem->iImageActive = item->iImageActive; + + if (0 != (NLTIF_IMAGE_DISABLED & pItem->mask)) + pItem->iImageDisabled = item->iImageDisabled; + + return succeeded; +} + +static BOOL LoginTab_OnDeleteItem(HWND hwnd, INT iItem) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return FALSE; + + if (iItem < 0 || iItem >= tab->itemsCount) + return FALSE; + + HWND hParent = GetAncestor(hwnd, GA_PARENT); + if (NULL != hParent) + { + NMLOGINTAB nmp; + nmp.hdr.code = NLTN_DELETEITEM; + nmp.hdr.hwndFrom = hwnd; + nmp.hdr.idFrom = GetWindowLongPtr(hwnd, GWLP_ID); + nmp.iItem = iItem; + SNDMSG(hParent, WM_NOTIFY, (WPARAM)nmp.hdr.idFrom, (LPARAM)&nmp); + } + + LOGINTABITEM *item = tab->items[iItem]; + + INT shiftLen = tab->itemsCount - iItem - 1; + if (shiftLen > 0) + { + + MoveMemory((tab->items + iItem), (tab->items + (iItem + 1)), sizeof(LOGINTABITEM*)*shiftLen); + + INT iOrder = tab->itemsCount - 1; + while(iOrder--) + { + if (iItem == tab->order[iOrder]) + { + MoveMemory((tab->order + iOrder), (tab->order + (iOrder + 1)), + sizeof(INT)*(tab->itemsCount - iOrder - 1)); + break; + } + } + + } + + LoginTab_FreeItem(item); + tab->itemsCount--; + + return TRUE; +} + + +static BOOL LoginTab_OnDeleteAllItems(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return FALSE; + + tab->itemsCount -= LoginTab_DeleteAllItemsReal(hwnd, tab->items, tab->itemsCount); + return TRUE; +} + +static INT LoginTab_OnGetItemCount(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + return (NULL != tab) ? tab->itemsCount : -1; +} + +static INT LoginTab_OnGetCurSel(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + return (NULL != tab) ? tab->iSelected : -1; +} + +static INT LoginTab_OnSetCurSel(HWND hwnd, INT iItem) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return -1; + + if (iItem < 0 || iItem >= tab->itemsCount) + return -1; + + if (iItem == tab->iSelected) + return tab->iSelected; + + INT iSelected = tab->iSelected; + INT iFocused = tab->iFocused; + + tab->iSelected = iItem; + + if (tab->iFocused != tab->itemsCount) + tab->iFocused = iItem; + + RECT rect; + if (-1 != iSelected && + FALSE != LoginTab_GetItemRect(hwnd, iSelected, &rect)) + { + InvalidateRect(hwnd, &rect, FALSE); + } + + if (iFocused != tab->iFocused && -1 != iFocused && iFocused != iSelected && + FALSE != LoginTab_GetItemRect(hwnd, iFocused, &rect)) + { + InvalidateRect(hwnd, &rect, FALSE); + } + + if (-1 != tab->iSelected && + FALSE != LoginTab_GetItemRect(hwnd, tab->iSelected, &rect)) + { + InvalidateRect(hwnd, &rect, FALSE); + } + + for(INT i = (tab->lastVisible + 1); i < tab->itemsCount; i++) + { + if (tab->order[i] == tab->iSelected) + { + LoginTab_UpdateLayout(hwnd, TRUE); + break; + } + } + + return iSelected; +} + + +static HIMAGELIST LoginTab_OnSetImageList(HWND hwnd, HIMAGELIST himl) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return NULL; + + HIMAGELIST old = tab->imageList; + tab->imageList = himl; + + LoginTab_UpdateLayout(hwnd, TRUE); + return old; +} + +static HIMAGELIST LoginTab_OnGetImageList(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + return (NULL != tab) ? tab->imageList : NULL; +} + +static BOOL LoginTab_OnResetOrder(HWND hwnd) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return FALSE; + + for (INT i = 0; i < tab->itemsCount; i++) + tab->order[i] = i; + + LoginTab_UpdateLayout(hwnd, FALSE); + InvalidateRect(hwnd, NULL, FALSE); + return TRUE; +} + +static void LoginTab_OnLockSelection(HWND hwnd, BOOL fLock) +{ + UINT windowStyle = GetWindowStyle(hwnd); + if ((FALSE != fLock) != (0 != (NLTS_LOCKED & windowStyle))) + { + if (FALSE != fLock) + windowStyle |= NLTS_LOCKED; + else + windowStyle &= ~NLTS_LOCKED; + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle); + InvalidateRect(hwnd, NULL, FALSE); + } +} +static BOOL LoginTab_OnMeasureChevronItem(HWND hwnd, MEASUREITEMSTRUCT* pmis) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return FALSE; + + MENUINFO mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIM_MENUDATA; + + if (FALSE != GetMenuInfo(tab->chevronMenu, &mi) && NULL != mi.dwMenuData) + { + CHEVRONMENUPAINTPARAM *pmp= (CHEVRONMENUPAINTPARAM*)mi.dwMenuData; + pmis->itemWidth = pmp->itemWidth; + pmis->itemHeight = pmp->itemHeight; + + return TRUE; + } + return FALSE; +} + + +static BOOL LoginTab_OnDrawChevronItem(HWND hwnd, DRAWITEMSTRUCT* pdis) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return FALSE; + + + MENUINFO mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIM_MENUDATA | MIM_BACKGROUND; + + if (FALSE == GetMenuInfo(tab->chevronMenu, &mi) || NULL == mi.dwMenuData) + return FALSE; + + CHEVRONMENUPAINTPARAM *pmp= (CHEVRONMENUPAINTPARAM*)mi.dwMenuData; + + if (NULL == pmp->hwndMenu) + { + pmp->hwndMenu = WindowFromDC(pdis->hDC); + if (NULL != pmp->hwndMenu) + { + SendMessage(pmp->hwndMenu, WM_CHANGEUISTATE, + MAKEWPARAM(UISF_HIDEACCEL | UISF_HIDEFOCUS, UIS_INITIALIZE), 0L); + } + } + + tab->drawStyle = LoginTab_GetDrawStyles(hwnd); + + if (NULL != pmp->hwndMenu) + { + UINT uiState = (UINT)SendMessage(pmp->hwndMenu, WM_QUERYUISTATE, 0, 0L); + if (0 == (UISF_HIDEFOCUS & uiState)) + tab->drawStyle |= NLTDS_FOCUSED; + else + tab->drawStyle &= ~NLTDS_FOCUSED; + } + + + HBITMAP hbmp = LoginTab_GetItemBitmap(tab, pdis->hDC, + pdis->rcItem.right - pdis->rcItem.left, + pdis->rcItem.bottom - pdis->rcItem.top); + + HBITMAP hbmpOrig = NULL; + POINT viewportOrig; + if (NULL != hbmp) + { + hbmpOrig = (HBITMAP)SelectObject(pmp->hdcItem, hbmp); + SetViewportOrgEx(pmp->hdcItem, -pdis->rcItem.left, -pdis->rcItem.top, &viewportOrig); + } + + if (NULL == mi.hbrBack) + mi.hbrBack = GetSysColorBrush(COLOR_MENU); + + INT itemWidth = pdis->rcItem.right - pdis->rcItem.left; + INT itemHeight = pdis->rcItem.bottom - pdis->rcItem.top; + + POINT brushOrgEx; + SetBrushOrgEx(pmp->hdcItem, 0, -pdis->rcItem.top, &brushOrgEx); + FillRect(pmp->hdcItem, &pdis->rcItem, mi.hbrBack); + SetBrushOrgEx(pmp->hdcItem, brushOrgEx.x, brushOrgEx.y, NULL); + + LOGINTABITEM *item = (LOGINTABITEM*)pdis->itemData; + INT iItem = pdis->itemID - 1; + + + INT iHighlighted = tab->iHighlighted; + + if (0 != (ODS_SELECTED & pdis->itemState)) + { + tab->iHighlighted = iItem; + if (tab->iFocused != iItem && -1 != tab->iFocused) + { + RECT rect; + if (FALSE != LoginTab_GetItemRect(hwnd, tab->iFocused, &rect)) + InvalidateRect(hwnd, &rect, FALSE); + } + tab->iFocused = iItem; + } + else + tab->iFocused = -1; + + LoginTab_PaintItem(hwnd, pmp->hdcItem, tab, item, iItem, &pdis->rcItem, &pdis->rcItem, pmp->hdcSrc); + + tab->iHighlighted = iHighlighted; + + BitBlt(pdis->hDC, pdis->rcItem.left, pdis->rcItem.top, itemWidth, itemHeight, + pmp->hdcItem, pdis->rcItem.left, pdis->rcItem.top, SRCCOPY); + + if (NULL != hbmp) + { + SelectObject(pmp->hdcItem, hbmpOrig); + SetViewportOrgEx(pmp->hdcItem, viewportOrig.x, viewportOrig.y, NULL); + } + + return TRUE; +} +static BOOL LoginTab_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT* pmis) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return FALSE; + + switch(pmis->CtlType) + { + case ODT_MENU: + if (NULL != tab->chevronMenu) + return LoginTab_OnMeasureChevronItem(hwnd, pmis); + break; + } + + return FALSE; +} + +static BOOL LoginTab_OnDrawItem(HWND hwnd, DRAWITEMSTRUCT* pdis) +{ + LOGINTAB *tab = GetTab(hwnd); + if (NULL == tab) return FALSE; + + switch(pdis->CtlType) + { + case ODT_MENU: + if (NULL != tab->chevronMenu) + return LoginTab_OnDrawChevronItem(hwnd, pdis); + break; + } + + return FALSE; +} + +void LoginTab_OnThemeChanged(HWND hwnd) +{ + LoginTab_UpdateColors(hwnd); + OutputDebugStringA("Theme changed received\r\n"); +} + +void LoginTab_OnSysColorChanged(HWND hwnd) +{ + LoginTab_UpdateColors(hwnd); + OutputDebugStringA("Color changed received\r\n"); +} + + +static LRESULT WINAPI LoginTab_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: return LoginTab_OnCreate(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: LoginTab_OnDestroy(hwnd); return 0; + case WM_ERASEBKGND: return 0; + case WM_PAINT: LoginTab_OnPaint(hwnd); return 0; + case WM_PRINTCLIENT: LoginTab_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return 0; + case WM_WINDOWPOSCHANGED: LoginTab_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_SIZE: return 0; + case WM_MOUSEMOVE: LoginTab_OnMouseMove(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_LBUTTONDOWN: LoginTab_OnLButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_LBUTTONUP: LoginTab_OnLButtonUp(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_RBUTTONDOWN: LoginTab_OnRButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_RBUTTONUP: LoginTab_OnRButtonUp(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_MBUTTONDOWN: LoginTab_OnMButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_MBUTTONUP: LoginTab_OnMButtonUp(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_MOUSELEAVE: LoginTab_OnMouseLeave(hwnd); return 0; + case WM_CAPTURECHANGED: LoginTab_OnCaptureChanged(hwnd, (HWND)lParam); return 0; + case WM_SETFOCUS: LoginTab_OnSetFocus(hwnd, (HWND)wParam); return 0; + case WM_KILLFOCUS: LoginTab_OnKillFocus(hwnd, (HWND)wParam); return 0; + case WM_ENABLE: LoginTab_OnEnable(hwnd, (BOOL)wParam); return 0; + case WM_KEYDOWN: LoginTab_OnKeyDown(hwnd, (INT)wParam, (UINT)lParam); return 0; + case WM_GETDLGCODE: return LoginTab_OnGetDlgCode(hwnd, (INT)wParam, (MSG*)lParam); + case WM_MENUCHAR: return LoginTab_OnMenuChar(hwnd, LOWORD(wParam), HIWORD(wParam), (HMENU)lParam); + case WM_MENUSELECT: LoginTab_OnMenuSelect(hwnd, LOWORD(wParam), HIWORD(wParam), (HMENU)lParam); return 0; + case WM_MEASUREITEM: return LoginTab_OnMeasureItem(hwnd, (MEASUREITEMSTRUCT*)lParam); + case WM_DRAWITEM: return LoginTab_OnDrawItem(hwnd, (DRAWITEMSTRUCT*)lParam); + case WM_NOTIFY: return LoginTab_OnNotify(hwnd, (INT)wParam, (NMHDR*)lParam); + case WM_THEMECHANGED: LoginTab_OnThemeChanged(hwnd); return TRUE; + case WM_SYSCOLORCHANGE: LoginTab_OnSysColorChanged(hwnd); return TRUE; + + + case NLTM_GETIDEALHEIGHT: return LoginTab_OnGetIdealHeight(hwnd); + case NLTM_INSERTITEM: return LoginTab_OnInsertItem(hwnd, (INT)wParam, (NLTITEM*)lParam); + case NLTM_SETITEM: return LoginTab_OnSetItem(hwnd, (INT)wParam, (NLTITEM*)lParam); + case NLTM_GETITEM: return LoginTab_OnGetItem(hwnd, (INT)wParam, (NLTITEM*)lParam); + case NLTM_DELETEITEM: return LoginTab_OnDeleteItem(hwnd, (INT)wParam); + case NLTM_DELETEALLITEMS: return LoginTab_OnDeleteAllItems(hwnd); + case NLTM_GETITEMCOUNT: return LoginTab_OnGetItemCount(hwnd); + case NLTM_GETCURSEL: return LoginTab_OnGetCurSel(hwnd); + case NLTM_SETCURSEL: return LoginTab_OnSetCurSel(hwnd, (INT)wParam); + case NLTM_SETIMAGELIST: return (LRESULT)LoginTab_OnSetImageList(hwnd, (HIMAGELIST)lParam); + case NLTM_GETIMAGELIST: return (LRESULT)LoginTab_OnGetImageList(hwnd); + case NLTM_RESETORDER: LoginTab_OnResetOrder(hwnd); return 0; + case NLTM_LOCKSELECTION: LoginTab_OnLockSelection(hwnd, (BOOL)wParam); return 0; + case NLTM_GETIDEALWIDTH: return LoginTab_OnGetIdealWidth(hwnd, (INT)wParam); + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginTab.h b/Src/auth/Loginbox/loginTab.h new file mode 100644 index 00000000..c1f2f8a7 --- /dev/null +++ b/Src/auth/Loginbox/loginTab.h @@ -0,0 +1,157 @@ +#ifndef NULLSOFT_AUTH_LOGIN_TAB_HEADER +#define NULLSOFT_AUTH_LOGIN_TAB_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include <commctrl.h> + +#define NWC_LOGINTAB L"NullsoftLoginTab" + +BOOL LoginTab_RegisterClass(HINSTANCE hInstance); +HWND LoginTab_CreateWindow(UINT styleEx, LPCWSTR pszTitle, UINT style, INT x, INT y, INT cx, INT cy, HWND hParent, INT_PTR controlId); + +typedef struct __NLTITEM +{ + UINT mask; + UINT dwState; + UINT dwStateMask; + LPWSTR pszText; + UINT cchTextMax; + UINT iImage; + UINT iImageActive; + UINT iImageDisabled; + LPARAM param; +} NLTITEM; + +// Item mask flags +#define NLTIF_STATE 0x00000001 +#define NLTIF_TEXT 0x00000002 +#define NLTIF_PARAM 0x00000004 +#define NLTIF_IMAGE_MASK (NLTIF_IMAGE | NLTIF_IMAGE_ACTIVE | NLTIF_IMAGE_DISABLED) +#define NLTIF_IMAGE 0x00000010 +#define NLTIF_IMAGE_ACTIVE 0x00000020 +#define NLTIF_IMAGE_DISABLED 0x00000040 + +// Item states +#define NLTIS_PRESSED 0x00000001 +#define NLTIS_HIGHLIGHTED 0x00000002 +#define NLTIS_SELECTED 0x00000004 +#define NLTIS_DISABLED 0x00000008 + +// image index values +#define NLTM_IMAGE_NONE ((UINT)-1) +#define NLTM_IMAGE_CALLBACK ((UINT)-2) + +// Messages +#define NLTM_FIRST (WM_USER + 10) + +#define NLTM_GETIDEALHEIGHT (NLTM_FIRST + 0) // wParam - not used, lParam - not used; Return ideal height. +#define LoginTab_GetIdealHeight(/*HWND*/ __hwnd)\ + ((INT)SNDMSG((__hwnd), NLTM_GETIDEALHEIGHT, 0, 0L)) + +#define NLTM_INSERTITEM (NLTM_FIRST + 1) // wParam = (WPARAM)(INT)iItem, lParam = (LPARAM)(NLTITEM*)pItem; Return = index of new item or -1. +#define LoginTab_InsertItem(/*HWND*/ __hwnd, /*INT*/ __iItem, /*NLTITEM* */ __pItem)\ + ((INT)SNDMSG((__hwnd), NLTM_INSERTITEM, (WPARAM)(__iItem), (LPARAM)(__pItem))) + +#define NLTM_SETITEM (NLTM_FIRST + 2) // wParam = (WPARAM)(INT)iItem, lParam = (LPARAM)(NLTITEM*)pItem; Return = TRUE on success. +#define LoginTab_SetItem(/*HWND*/ __hwnd, /*INT*/ __iItem, /*NLTITEM* */ __pItem)\ + ((BOOL)SNDMSG((__hwnd), NLTM_SETITEM, (WPARAM)(__iItem), (LPARAM)(__pItem))) + +#define NLTM_GETITEM (NLTM_FIRST + 3) // wParam = (WPARAM)(INT)iItem, lParam = (LPARAM)(NLTITEM*)pItem; Return = TRUE on success. +#define LoginTab_GetItem(/*HWND*/ __hwnd, /*INT*/ __iItem, /*NLTITEM* */ __pItem)\ + ((BOOL)SNDMSG((__hwnd), NLTM_GETITEM, (WPARAM)(__iItem), (LPARAM)(__pItem))) + +#define NLTM_DELETEITEM (NLTM_FIRST + 4) // wParam = (WPARAM)(INT)iItem, lParam - not used; Return = TRUE on success. +#define LoginTab_DeleteItem(/*HWND*/ __hwnd, /*INT*/ __iItem)\ + ((BOOL)SNDMSG((__hwnd), NLTM_DELETEITEM, (WPARAM)(__iItem), 0L)) + +#define NLTM_DELETEALLITEMS (NLTM_FIRST + 5) // wParam - not used, lParam - not used; Return = TRUE on success. +#define LoginTab_DeleteAllItems(/*HWND*/ __hwnd)\ + ((BOOL)SNDMSG((__hwnd), NLTM_DELETEALLITEMS, 0, 0L)) + +#define NLTM_GETITEMCOUNT (NLTM_FIRST + 6) // wParam - not used, lParam - not used; Return item count. +#define LoginTab_GetItemCount(/*HWND*/ __hwnd)\ + ((INT)SNDMSG((__hwnd), NLTM_GETITEMCOUNT, 0, 0L)) + +#define NLTM_GETCURSEL (NLTM_FIRST + 7) // wParam - not used, lParam - not used; Return item index or -1 +#define LoginTab_GetCurSel(/*HWND*/ __hwnd)\ + ((INT)SNDMSG((__hwnd), NLTM_GETCURSEL, 0, 0L)) + +#define NLTM_SETCURSEL (NLTM_FIRST + 8) // wParam = (WPARAM)(INT)iItem, lParam - not used; Return index of previously selected item if successful, or -1. +#define LoginTab_SetCurSel(/*HWND*/ __hwnd, /*INT*/ __iItem)\ + ((INT)SNDMSG((__hwnd), NLTM_SETCURSEL, (WPARAM)(__iItem), 0L)) + +#define NLTM_SETIMAGELIST (NLTM_FIRST + 9) // wParam - not used, lParam - (LPARAM)(HIMAGELIST)himl; Returns the handle to the previous image list, or NULL if there is no previous image list. +#define LoginTab_SetImageList(/*HWND*/ __hwnd, /*HIMAGELIST*/ __himl)\ + ((HIMAGELIST)SNDMSG((__hwnd), NLTM_SETIMAGELIST, 0, (LPARAM)(__himl))) + +#define NLTM_GETIMAGELIST (NLTM_FIRST + 10) // wParam - not used, lParam - not used; Returns the handle to the image list if successful, or NULL otherwise. +#define LoginTab_GetImageList(/*HWND*/ __hwnd)\ + ((HIMAGELIST)SNDMSG((__hwnd), NLTM_GETIMAGELIST, 0, 0L)) + +#define NLTM_RESETORDER (NLTM_FIRST + 11) // wParam - not used, lParam - not used; Return - ignored +#define LoginTab_ResetOrder(/*HWND*/ __hwnd)\ + (SNDMSG((__hwnd), NLTM_RESETORDER, 0, 0L)) + +#define NLTM_LOCKSELECTION (NLTM_FIRST + 12) // wParam - (BOOL)fLock, lParam - not used; Return - ignored. +#define LoginTab_LockSelection(/*HWND*/ __hwnd, /*BOOL*/ __fLock)\ + (SNDMSG((__hwnd), NLTM_LOCKSELECTION, (WPARAM)(__fLock), 0L)) + + +#define NLTM_GETIDEALWIDTH (NLTM_FIRST + 13) // wParam = (WPARAM)(INT)itemCount, lParam - not used; Return ideal width. +#define LoginTab_GetIdealWidth(/*HWND*/ __hwnd, /*INT*/ __itemCount)\ + ((INT)SNDMSG((__hwnd), NLTM_GETIDEALWIDTH, (WPARAM)(__itemCount), 0L)) + +// Notifications + +typedef struct __NMLOGINTAB +{ + NMHDR hdr; + INT iItem; +} NMLOGINTAB; + +typedef struct __NMLOGINTABHELP +{ + NMHDR hdr; + INT iItem; + LPARAM param; + BSTR bstrHelp; +} NMLOGINTABHELP; + +typedef struct __NMLOGINTABCLICK +{ + NMHDR hdr; + POINT pt; +} NMLOGINTABCLICK; + +typedef struct __NMLOGINTABIMAGE +{ + NMHDR hdr; + INT iItem; + LPARAM param; + HIMAGELIST imageList; + UINT maskRequest; + UINT maskUpdate; + UINT iImage; + UINT iImageActive; + UINT iImageDisabled; +} NMLOGINTABIMAGE; + +#define NLTN_FIRST (0 + 10) + +#define NLTN_SELCHANGE (NLTN_FIRST + 0) // pnmh = (NMHDR*)lParam; +#define NLTN_DELETEITEM (NLTN_FIRST + 1) // pnmh = (NMLOGINTAB*)lParam; +#define NLTN_DELETEALLITEMS (NLTN_FIRST + 2) // pnmh = (NMLOGINTAB*)lParam; iItem = -1, return TRUE if you don't want to receive NLTN_DELETEITEM +#define NLTN_GETITEMHELP (NLTN_FIRST + 3) // pnmh = (NMLOGINTABHELP*)lParam; +#define NLTN_GETITEMIMAGE (NLTN_FIRST + 4) // pnmh = (NMLOGINTABIMAGE*)lParam; + +// common notifications +//NM_RCLICK - pnmh = (NMLOGINTABCLICK*)lParam; + + +//styles +#define NLTS_LOCKED 0x00000001 +#endif //NULLSOFT_AUTH_LOGIN_TAB_HEADER diff --git a/Src/auth/Loginbox/loginTemplate.h b/Src/auth/Loginbox/loginTemplate.h new file mode 100644 index 00000000..0c490488 --- /dev/null +++ b/Src/auth/Loginbox/loginTemplate.h @@ -0,0 +1,30 @@ +#ifndef NULLSOFT_AUTH_LOGIN_TEMPLATE_HEADER +#define NULLSOFT_AUTH_LOGIN_TEMPLATE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class __declspec(novtable) LoginTemplate +{ +protected: + LoginTemplate() {} + ~LoginTemplate(){} + +public: + virtual ULONG AddRef() = 0; + virtual ULONG Release() = 0; + + virtual HRESULT GetType(GUID *templateUid) = 0; + + virtual HRESULT SetParameter(LPCWSTR pszKey, LPCWSTR pszValue) = 0; + virtual HRESULT IsValid() = 0; + virtual HRESULT IsIdentical(LoginTemplate *test) = 0; + + virtual HWND CreatePage(HWND hLoginbox, HWND hParent) = 0; + +}; + +#endif //NULLSOFT_AUTH_LOGIN_TEMPLATE_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginbox.cpp b/Src/auth/Loginbox/loginbox.cpp new file mode 100644 index 00000000..9161c734 --- /dev/null +++ b/Src/auth/Loginbox/loginbox.cpp @@ -0,0 +1,2804 @@ +#include "./common.h" +#include "./loginBox.h" + +#include "./loginProvider.h" +#include "./providerLoader.h" +#include "./providerEnumerator.h" +#include "./loginTemplate.h" +#include "./loginCommand.h" +#include "./loginPage.h" +#include "./loginData.h" +#include "./loginCredentials.h" +#include "./loginCurtain.h" +#include "./loginNotifier.h" +#include "./loginPopup.h" +#include "./popupAgreement.h" +#include "./popupPasscode.h" +#include "./popupMessage.h" +#include "./pageEmpty.h" +#include "./pageError.h" +#include "./animation.h" +#include "./loginStatus.h" +#include "./providerOperation.h" +#include "./loginConfig.h" +#include "./loginTab.h" +#include "./loginGui.h" + +#include "./download.h" +#include "./downloadResult.h" +#include "./imageCache.h" + +#include "../resource.h" + +#include "../api.h" +#include "../api_auth.h" +#include "../../nu/windowsTheme.h" +#include "../../winamp/commandLink.h" +#include "../../ombrowser/ifc_omcacherecord.h" + + +#include <windows.h> +#include <commctrl.h> +#include <vssym32.h> +#include <shlwapi.h> +#include <strsafe.h> + +#define PROVIDERLIST_URL L"http://client.winamp.com/data/loginproviders" +//#define PROVIDERLIST_URL L"http://dl.getdropbox.com/u/1994752/loginProviders.xml" + +// max mini in dpi +#define LOGINBOX_MINWIDTH 153 +#define LOGINBOX_MINHEIGHT 140 +#define LOGINBOX_MAXWIDTH 800 +#define LOGINBOX_MAXHEIGHT 420 + +#define NLBS_MODAL 0x00010000 + +typedef struct __LOGINBOXCREATEPARAM +{ + HWND hOwner; + api_auth *auth; + UINT style; + const GUID *pRealm; +} LOGINBOXCREATEPARAM; + + +#define PROVIDEROP_UPDATE 0 +#define PROVIDEROP_REMOVE 1 + +typedef struct __LOGINBOX +{ + api_auth *auth; + UINT style; + GUID realm; + RECT gripRect; + LoginDownloadResult *providerUpdate; + LoginResult *loginResult; + LoginStatus *loginStatus; + LoginProviderOperation *providerOp; + BOOL agreementOk; + RECT buttonMargins; + LONG buttonHeight; + LONG buttonSpace; + LONG buttonTop; + RECT minmaxInfo; + + LoginImageCache *imageCache; +} LOGINBOX; + +#define TIMER_ID_CHECKIMAGES 20 +#define TIMER_DELAY_CHECKIMAGES 25 + +typedef struct __LOGINBOXFIND +{ + HWND hwnd; + WCHAR buffer[32]; + GUID realm; +} LOGINBOXFIND; + + +#define LOGINBOX_PROP L"NullsoftAuthLoginBoxProp" +#define GetLoginBox(__hwnd) ((LOGINBOX*)GetProp(__hwnd, LOGINBOX_PROP)) + +#define IDC_TABFRAME 10000 +#define IDC_ACTIVEPAGE 10001 +#define IDC_NOTIFIER 10002 +#define IDC_CURTAIN 10003 +#define IDC_POPUPAGREEMENT 10004 +#define IDC_POPUPPASSCODE 10005 +#define IDC_POPUPPROVIDEROP 10006 + +static INT_PTR CALLBACK LoginBox_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +static BOOL LoginBox_InitCreateParam(LOGINBOXCREATEPARAM *param, api_auth *auth, const GUID *pRealm, HWND hOwner, UINT style) +{ + if (NULL == param) + return FALSE; + + ZeroMemory(param, sizeof(LOGINBOXCREATEPARAM)); + + param->style = style; + param->hOwner = hOwner; + param->pRealm = pRealm; + param->auth = auth; + + if (NULL == auth) + return FALSE; + + return TRUE; +} + +HWND LoginBox_CreateWindow(api_auth *auth, const GUID *pRealm, HWND hOwner, UINT fStyle) +{ + LOGINBOXCREATEPARAM param; + if (FALSE == LoginBox_InitCreateParam(¶m, auth, pRealm, hOwner, (0x0000FFFF & fStyle))) + return NULL; + + return WASABI_API_CREATEDIALOGPARAMW(IDD_LOGINBOX, hOwner, LoginBox_DialogProc, (LPARAM)¶m); +} + +INT_PTR LoginBox_Show(api_auth *auth, const GUID *pRealm, HWND hOwner, UINT fStyle) +{ + LOGINBOXCREATEPARAM param; + if (FALSE == LoginBox_InitCreateParam(¶m, auth, pRealm, hOwner, (0x0000FFFF & fStyle) | NLBS_MODAL)) + return -1; + + INT_PTR result = WASABI_API_DIALOGBOXPARAMW(IDD_LOGINBOX, hOwner, LoginBox_DialogProc, (LPARAM)¶m); + + return result; +} + +static BOOL CALLBACK LoginBox_WindowEnumerator(HWND hwnd, LPARAM lParam) +{ + LOGINBOXFIND *find = (LOGINBOXFIND*)lParam; + + if (0 != GetClassName(hwnd, find->buffer, ARRAYSIZE(find->buffer)) && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, 0, L"#32770", -1, find->buffer, -1)) + { + LOGINBOX *login = GetLoginBox(hwnd); + if (NULL != login && IsEqualGUID(find->realm, login->realm)) + { + find->hwnd = hwnd; + return FALSE; + } + } + return TRUE; +} + +HWND LoginBox_FindActive(const GUID *pRealm) +{ + LOGINBOXFIND find; + ZeroMemory(&find, sizeof(find)); + find.realm = (NULL == pRealm) ? GUID_NULL : *pRealm; + + EnumWindows(LoginBox_WindowEnumerator, (LPARAM)&find); + + return find.hwnd; +} + +static void LoginBox_ShowNotifier(HWND hwnd, INT durationMs) +{ + if (NULL == hwnd) return; + HWND hNotifier = GetDlgItem(hwnd, IDC_NOTIFIER); + if (NULL == hNotifier) return; + + LONG targetHeight = (LONG)LoginNotifier_GetIdealHeight(hNotifier); + RECT notifierRect; + GetWindowRect(hNotifier, ¬ifierRect); + LONG currentHeight = notifierRect.bottom - notifierRect.top; + LONG notifierWidth = notifierRect.right - notifierRect.left; + + if (currentHeight < targetHeight) + { + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)¬ifierRect, 2); + + HWND hPage = GetDlgItem(hwnd, IDC_ACTIVEPAGE); + RECT pageRect; + if (NULL != hPage) + { + GetWindowRect(hPage, &pageRect); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&pageRect, 2); + } + + LONG pageWidth = pageRect.right - pageRect.left; + LONG pageHeight = pageRect.bottom - pageRect.top; + + UINT pageStyle = (NULL != hPage) ? GetWindowStyle(hPage) : 0; + if (0 != (WS_VISIBLE & pageStyle)) + SetWindowLongPtr(hPage, GWL_STYLE, pageStyle & ~WS_VISIBLE); + + UINT notifierStyle = GetWindowStyle(hNotifier); + if (0 != (WS_VISIBLE & notifierStyle)) + SetWindowLongPtr(hNotifier, GWL_STYLE, notifierStyle & ~WS_VISIBLE); + + SetWindowPos(hNotifier, NULL, 0, 0, notifierWidth, targetHeight, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOCOPYBITS); + + + if (durationMs > 0) + { + HDC targetDc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != targetDc) + { + HDC composeDc = CreateCompatibleDC(targetDc); + if (NULL != composeDc) + { + HBITMAP composeBmp = CreateCompatibleBitmap(targetDc, + (notifierWidth > pageWidth) ? notifierWidth : pageWidth, + targetHeight + pageHeight); + if (NULL != composeBmp) + { + HBITMAP composeBmpOrig = (HBITMAP)SelectObject(composeDc, composeBmp); + + SendMessage(hNotifier, WM_PRINTCLIENT, (WPARAM)composeDc, (LPARAM)(PRF_CLIENT | PRF_ERASEBKGND)); + + + ANIMATIONDATA animation; + if (FALSE != Animation_Initialize(&animation, durationMs)) + { + while(currentHeight++ < targetHeight) + { + Animation_BeginStep(&animation); + Animation_SetWindowPos(hPage, pageRect.left, ++pageRect.top, pageWidth, --pageHeight, + 0, composeDc, 0, targetHeight); + + BitBlt(targetDc, notifierRect.left, notifierRect.top, notifierWidth, currentHeight, composeDc, 0, targetHeight - currentHeight, SRCCOPY); + if (NULL != hPage) + BitBlt(targetDc, pageRect.left, pageRect.top, pageWidth, pageHeight, composeDc, 0, targetHeight, SRCCOPY); + + Animation_EndStep(&animation, targetHeight - currentHeight); + } + } + + SelectObject(composeDc, composeBmpOrig); + DeleteObject(composeBmp); + } + DeleteDC(composeDc); + } + ReleaseDC(hwnd, targetDc); + } + } + + if (0 != (WS_VISIBLE & pageStyle)) + { + SetWindowLongPtr(hPage, GWL_STYLE, pageStyle); + INT delta = (targetHeight - currentHeight); + if (delta > 0) + { + SetWindowPos(hPage, NULL, pageRect.left, pageRect.top + delta, pageWidth, pageHeight - delta, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOCOPYBITS); + } + RedrawWindow(hPage, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_ERASENOW | RDW_ERASE); + } + + LoginNotifier_PlayBeep(hNotifier); + ShowWindow(hNotifier, SW_SHOWNA); + UpdateWindow(hNotifier); + } +} + +static void LoginBox_HideNotifier(HWND hwnd, INT durationMs) +{ + if (NULL == hwnd) return; + HWND hNotifier = GetDlgItem(hwnd, IDC_NOTIFIER); + if (NULL == hNotifier) return; + + UINT notifierStyle = GetWindowStyle(hNotifier); + if (0 == (WS_VISIBLE & notifierStyle)) return; + + SetWindowLongPtr(hNotifier, GWL_STYLE, notifierStyle & ~WS_VISIBLE); + + RECT notifierRect; + GetWindowRect(hNotifier, ¬ifierRect); + LONG currentHeight = notifierRect.bottom - notifierRect.top; + LONG startHeight = currentHeight; + LONG notifierWidth = notifierRect.right - notifierRect.left; + + if (currentHeight > 0) + { + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)¬ifierRect, 2); + + HWND hPage = GetDlgItem(hwnd, IDC_ACTIVEPAGE); + RECT pageRect; + if (NULL != hPage) + { + GetWindowRect(hPage, &pageRect); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&pageRect, 2); + } + + LONG pageWidth = pageRect.right - pageRect.left; + LONG pageHeight = pageRect.bottom - pageRect.top; + + UINT pageStyle = (NULL != hPage) ? GetWindowStyle(hPage) : 0; + if (0 != (WS_VISIBLE & pageStyle)) + SetWindowLongPtr(hPage, GWL_STYLE, pageStyle & ~WS_VISIBLE); + + if (durationMs > 0) + { + HDC targetDc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != targetDc) + { + HDC composeDc = CreateCompatibleDC(targetDc); + if (NULL != composeDc) + { + HBITMAP composeBmp = CreateCompatibleBitmap(targetDc, + (notifierWidth > pageWidth) ? notifierWidth : pageWidth, + 2*startHeight + pageHeight); + if (NULL != composeBmp) + { + HBITMAP composeBmpOrig = (HBITMAP)SelectObject(composeDc, composeBmp); + + SendMessage(hNotifier, WM_PRINTCLIENT, (WPARAM)composeDc, (LPARAM)(PRF_CLIENT | PRF_ERASEBKGND)); + + ANIMATIONDATA animation; + if (FALSE != Animation_Initialize(&animation, durationMs)) + { + while(currentHeight-- > 0) + { + Animation_BeginStep(&animation); + if (FALSE != Animation_SetWindowPos(hPage, pageRect.left, --pageRect.top, pageWidth, ++pageHeight, + 0, composeDc, 0, startHeight)) + { + BitBlt(targetDc, pageRect.left, pageRect.top, pageWidth, pageHeight, composeDc, 0, startHeight, SRCCOPY); + } + + BitBlt(targetDc, notifierRect.left, notifierRect.top, notifierWidth, currentHeight, composeDc, 0, startHeight - currentHeight, SRCCOPY); + + Animation_EndStep(&animation, currentHeight); + } + } + + SelectObject(composeDc, composeBmpOrig); + DeleteObject(composeBmp); + } + DeleteDC(composeDc); + } + ReleaseDC(hwnd, targetDc); + } + } + + if (0 != (WS_VISIBLE & pageStyle)) + { + SetWindowLongPtr(hPage, GWL_STYLE, pageStyle); + INT delta = currentHeight; + if (delta > 0) + { + SetWindowPos(hPage, NULL, pageRect.left, pageRect.top - delta, pageWidth, pageHeight + delta, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOCOPYBITS); + } + RedrawWindow(hPage, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_ERASENOW | RDW_ERASE); + } + + SetWindowPos(hNotifier, NULL, 0, 0, notifierWidth, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOCOPYBITS); + } +} +static HRESULT LoginBox_SaveUsername(const GUID *realm, LoginProvider *provider, LoginCredentials *credentials) +{ + if (NULL == realm || NULL == provider || NULL == credentials) + return E_INVALIDARG; + + HRESULT hr; + + LoginConfig *config; + hr = LoginConfig::CreateInstance(&config); + if (FAILED(hr)) return hr; + + RPC_CSTR pszRealm; + if (RPC_S_OK == UuidToStringA(realm, &pszRealm)) + { + GUID providerId; + RPC_CSTR pszProvider; + + if (SUCCEEDED(provider->GetId(&providerId)) && + RPC_S_OK == UuidToStringA(&providerId, &pszProvider)) + { + LPCWSTR pszUser = credentials->GetUsername(); + + LPSTR pszUserAnsi; + if (NULL != pszUser && L'\0' != *pszUser) + hr = LoginBox_WideCharToMultiByte(CP_UTF8, 0, pszUser, -1, NULL, NULL, &pszUserAnsi); + else + pszUserAnsi = NULL; + + if (SUCCEEDED(hr)) + { + hr = config->WriteAnsiStr((LPCSTR)pszRealm, (LPCSTR)pszProvider, pszUserAnsi); + LoginBox_FreeAnsiString(pszUserAnsi); + } + RpcStringFreeA(&pszProvider); + } + else + hr = E_FAIL; + + RpcStringFreeA(&pszRealm); + } + else + hr = E_FAIL; + + config->Release(); + return hr; +} + +static HRESULT LoginBox_LoadUsername(const GUID *realm, LoginProvider *provider, LPWSTR pszBuffer, UINT cchBufferMax) +{ + if (NULL == pszBuffer) + return E_POINTER; + + if (NULL == realm || NULL == provider) + return E_INVALIDARG; + + HRESULT hr; + + LoginConfig *config; + hr = LoginConfig::CreateInstance(&config); + if (FAILED(hr)) return hr; + + RPC_CSTR pszRealm; + if (RPC_S_OK == UuidToStringA(realm, &pszRealm)) + { + GUID providerId; + RPC_CSTR pszProvider; + + if (SUCCEEDED(provider->GetId(&providerId)) && + RPC_S_OK == UuidToStringA(&providerId, &pszProvider)) + { + CHAR szUserAnsi[1024] = {0}; + INT cchUser = config->ReadAnsiStr((LPCSTR)pszRealm, (LPCSTR)pszProvider, NULL, szUserAnsi, ARRAYSIZE(szUserAnsi)); + + if (0 == cchUser) + pszBuffer[0] = L'\0'; + else + { + cchUser = MultiByteToWideChar(CP_UTF8, 0, szUserAnsi, cchUser, pszBuffer, cchBufferMax); + if (0 == cchUser) + hr = HRESULT_FROM_WIN32(GetLastError()); + else + pszBuffer[cchUser] = L'\0'; + } + + RpcStringFreeA(&pszProvider); + } + else + hr = E_FAIL; + + RpcStringFreeA(&pszRealm); + } + else + hr = E_FAIL; + + config->Release(); + return hr; + + +} +static BOOL LoginBox_SelectActivePage(HWND hwnd) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return FALSE; + + loginbox->agreementOk = FALSE; + LoginBox_HideNotifier(hwnd, 0); + + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL == hFrame) return FALSE; + + HWND hPageOld = GetDlgItem(hwnd, IDC_ACTIVEPAGE); + if (NULL != hPageOld) + SetWindowLongPtr(hPageOld, GWLP_ID, 0); + + BOOL enableLogin = FALSE; + + HWND hPage = NULL; + LoginProvider *provider = NULL; + + INT iIndex = LoginTab_GetCurSel(hFrame); + if (-1 != iIndex) + { + NLTITEM tabItem; + tabItem.mask = NLTIF_PARAM; + provider = (TRUE == LoginTab_GetItem(hFrame, iIndex, &tabItem)) ? + (LoginProvider*)tabItem.param : NULL; + + if (NULL != provider) + { + LoginTemplate *pageTemplate; + if (SUCCEEDED(provider->GetTemplate(&pageTemplate)) && NULL != pageTemplate) + { + hPage = pageTemplate->CreatePage(hwnd, hwnd); + pageTemplate->Release(); + } + + LoginCommand *command; + if (SUCCEEDED(provider->GetCommand(&command)) && NULL != command) + { + enableLogin = (S_OK == command->IsValid()); + command->Release(); + } + } + } + + if (NULL == hPage) + { + hPage = (-1 == iIndex) ? + LoginPageEmpty::CreatePage(hwnd, hwnd) : + LoginPageError::CreatePage(hwnd, hwnd); + + } + + if (NULL != hPage) + { + //if (S_OK == UxTheme_IsThemeActive()) + // UxEnableThemeDialogTexture(hPage, ETDT_ENABLETAB); + + SetWindowLongPtr(hPage, GWLP_ID, IDC_ACTIVEPAGE); + + RECT rect; + if (NULL != hPageOld && FALSE != GetWindowRect(hPageOld, &rect)) + { + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rect, 2); + SetWindowPos(hPage, HWND_TOP, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOREDRAW); + } + else + { + SetWindowPos(hPage, HWND_TOP, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOMOVE); + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED | SWP_NOZORDER); + } + + WCHAR szUsername[512] = {0}; + if(SUCCEEDED(LoginBox_LoadUsername(&loginbox->realm, provider, szUsername, ARRAYSIZE(szUsername))) && + L'\0' != szUsername[0]) + { + LoginPage_SetUsername(hPage, szUsername); + } + + ShowWindow(hPage, SW_SHOWNORMAL); + + if (WS_VISIBLE == ((WS_VISIBLE | WS_DISABLED) & GetWindowStyle(hPage))) + { + HWND hFocus = GetFocus(); + if (hFrame != hFocus) + { + HWND hTarget = LoginPage_GetFirstItem(hPage); + if (NULL == hTarget || + ((WS_VISIBLE | WS_TABSTOP) != ((WS_VISIBLE | WS_TABSTOP | WS_DISABLED) & GetWindowStyle(hTarget)))) + { + hTarget = GetNextDlgTabItem(hwnd, hFrame, FALSE); + } + + if (NULL != hTarget && hTarget != hFocus) + PostMessage(hwnd, WM_NEXTDLGCTL, (WPARAM)hTarget, TRUE); + } + } + } + else + { + enableLogin = FALSE; + } + + HWND hButton = GetDlgItem(hwnd, IDOK); + if (NULL != hButton) + EnableWindow(hButton, enableLogin); + + if (NULL != hPageOld) + { + DestroyWindow(hPageOld); + } + + return TRUE; +} + +static BOOL LoginBox_RemoveImageHelper(HWND hFrame, HIMAGELIST imageList, UINT imageIndex) +{ + if (NLTM_IMAGE_NONE == imageIndex || NLTM_IMAGE_CALLBACK == imageIndex) + return FALSE; + + UINT imageListCount = ImageList_GetImageCount(imageList); + if (imageIndex > imageListCount) + return FALSE; + + INT iItem = LoginTab_GetItemCount(hFrame); + NLTITEM tab; + + while(iItem--) + { + tab.mask = NLTIF_IMAGE_MASK; + if (FALSE != LoginTab_GetItem(hFrame, iItem, &tab)) + { + tab.mask = 0; + if (tab.iImage >= imageIndex && tab.iImage < imageListCount) + { + tab.iImage = (tab.iImage != imageIndex) ? --tab.iImage : NLTM_IMAGE_NONE; + tab.mask |= NLTIF_IMAGE; + } + + if (tab.iImageActive >= imageIndex && tab.iImageActive < imageListCount) + { + tab.iImageActive = (tab.iImageActive != imageIndex) ? --tab.iImageActive : NLTM_IMAGE_NONE; + tab.mask |= NLTIF_IMAGE_ACTIVE; + } + + if (tab.iImageDisabled >= imageIndex && tab.iImageDisabled < imageListCount) + { + tab.iImageDisabled = (tab.iImageDisabled != imageIndex) ? --tab.iImageDisabled : NLTM_IMAGE_NONE; + tab.mask |= NLTIF_IMAGE_DISABLED; + } + + if (0 != tab.mask) + LoginTab_SetItem(hFrame, iItem, &tab); + } + } + return ImageList_Remove(imageList, imageIndex); +} + + +#define IS_IMAGEINDEX_VALID(__imageIndex) (((INT)(__imageIndex)) >= 0) +static BOOL LoginBox_RemoveImageHelper2(HWND hFrame, NLTITEM *tab) +{ + if (NULL == tab) return FALSE; + + HIMAGELIST imageList = LoginTab_GetImageList(hFrame); + if (NULL == imageList) return FALSE; + + if (IS_IMAGEINDEX_VALID(tab->iImageActive) && + FALSE != LoginBox_RemoveImageHelper(hFrame, imageList, tab->iImageActive)) + { + if (IS_IMAGEINDEX_VALID(tab->iImage)) + { + if (tab->iImage > tab->iImageActive) tab->iImage--; + else if (tab->iImage == tab->iImageActive) tab->iImage = NLTM_IMAGE_CALLBACK; + } + + if (IS_IMAGEINDEX_VALID(tab->iImageDisabled)) + { + if (tab->iImageDisabled > tab->iImageActive) tab->iImageDisabled--; + else if (tab->iImageDisabled == tab->iImageActive) tab->iImageDisabled = NLTM_IMAGE_CALLBACK; + } + + } + + if (IS_IMAGEINDEX_VALID(tab->iImage) && + FALSE != LoginBox_RemoveImageHelper(hFrame, imageList, tab->iImageActive)) + { + if (IS_IMAGEINDEX_VALID(tab->iImageActive)) + { + if (tab->iImageActive > tab->iImage) tab->iImageActive--; + else if (tab->iImageActive == tab->iImage) tab->iImageActive = NLTM_IMAGE_CALLBACK; + } + + if (IS_IMAGEINDEX_VALID(tab->iImageDisabled)) + { + if (tab->iImageDisabled > tab->iImage) tab->iImageDisabled--; + else if (tab->iImageDisabled == tab->iImage) tab->iImageDisabled = NLTM_IMAGE_CALLBACK; + } + + } + + if (IS_IMAGEINDEX_VALID(tab->iImageDisabled) && + FALSE != LoginBox_RemoveImageHelper(hFrame, imageList, tab->iImageDisabled)) + { + if (IS_IMAGEINDEX_VALID(tab->iImageActive)) + { + if (tab->iImageActive > tab->iImageDisabled) tab->iImageActive--; + else if (tab->iImageActive == tab->iImageDisabled) tab->iImageActive = NLTM_IMAGE_CALLBACK; + } + + if (IS_IMAGEINDEX_VALID(tab->iImage)) + { + if (tab->iImage > tab->iImageDisabled) tab->iImage--; + else if (tab->iImage == tab->iImageDisabled) tab->iImage = NLTM_IMAGE_CALLBACK; + } + + } + return TRUE; +} +static INT LoginBox_AppendMultipleTabs(HWND hwnd, LoginProviderEnumerator *enumerator, const GUID *filterOut, INT *filteredIndex) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return -1; + + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL == hFrame) return -1; + + if (NULL == enumerator) + return 0; + + GUID testId; + INT baseIndex = LoginTab_GetItemCount(hwnd); + INT count = 0; + NLTITEM tab; + WCHAR szTitle[256] = {0}; + + tab.mask = NLTIF_TEXT | NLTIF_PARAM | NLTIF_IMAGE_MASK; + + LoginProvider *provider; + while(S_OK == enumerator->Next(1, &provider, NULL)) + { + if (NULL != filterOut && SUCCEEDED(provider->GetId(&testId)) && FALSE != IsEqualGUID(testId, *filterOut)) + { + if (NULL != filteredIndex) + *filteredIndex = baseIndex + count; + } + else + { + if (FAILED(provider->GetName(szTitle, ARRAYSIZE(szTitle)))) + szTitle[0] = L'\0'; + + tab.param = (LPARAM)provider; + tab.pszText = (LPWSTR)szTitle; + tab.iImage = NLTM_IMAGE_CALLBACK; + tab.iImageActive = NLTM_IMAGE_CALLBACK; + tab.iImageDisabled = NLTM_IMAGE_CALLBACK; + + if (-1 != LoginTab_InsertItem(hFrame, baseIndex + count, &tab)) + { + provider->AddRef(); + count++; + } + } + + provider->Release(); + } + + return count; +} + +static INT LoginBox_InsertTab(HWND hwnd, INT index, LoginProvider *provider) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return -1; + + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL == hFrame) return -1; + + if (NULL == provider) + return -1; + + INT count = LoginTab_GetItemCount(hFrame); + if (index > count) index = count; + + NLTITEM tab; + WCHAR szTitle[256] = {0}; + + if (FAILED(provider->GetName(szTitle, ARRAYSIZE(szTitle)))) + szTitle[0] = L'\0'; + + tab.mask = NLTIF_TEXT | NLTIF_PARAM | NLTIF_IMAGE_MASK; + tab.param = (LPARAM)provider; + tab.pszText = (LPWSTR)szTitle; + tab.iImage = NLTM_IMAGE_CALLBACK; + tab.iImageActive = NLTM_IMAGE_CALLBACK; + tab.iImageDisabled = NLTM_IMAGE_CALLBACK; + + index = LoginTab_InsertItem(hFrame, index, &tab); + if (-1 != index) + provider->AddRef(); + + return index; +} + +static INT LoginBox_FindTabByProviderId(HWND hwnd, const GUID *providerId) +{ + if (NULL == providerId) + return -1; + + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL == hFrame) return -1; + + NLTITEM tab; + tab.mask = NLTIF_PARAM; + INT count = LoginTab_GetItemCount(hFrame); + GUID testId; + for (INT i =0; i < count; i++) + { + if (FALSE != LoginTab_GetItem(hFrame, i, &tab)) + { + if (NULL != tab.param) + { + LoginProvider *provider = (LoginProvider*)tab.param; + if (SUCCEEDED(provider->GetId(&testId)) && FALSE != IsEqualGUID(testId, *providerId)) + return i; + } + } + } + return -1; +} + +static void LoginBox_DeleteAllTabs(HWND hwnd) +{ + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL == hFrame) return; + + LoginTab_DeleteAllItems(hFrame); +} + +static BOOL LoginBox_ReplaceProvider(HWND hwnd, const GUID *sourceId, LoginProvider *target) +{ + if (NULL == sourceId || NULL == target) + return FALSE; + + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return FALSE; + + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL == hFrame) return FALSE; + + GUID testId; + + BOOL resultOk = FALSE; + + NLTITEM tab; + tab.mask = NLTIF_PARAM | NLTIF_IMAGE_MASK; + INT count = LoginTab_GetItemCount(hFrame); + for (INT i =0; i < count; i++) + { + if (FALSE != LoginTab_GetItem(hFrame, i, &tab)) + { + LoginProvider *provider = (LoginProvider*)tab.param; + if (NULL != provider && SUCCEEDED(provider->GetId(&testId)) && IsEqualGUID(*sourceId, testId)) + { + + tab.mask = NLTIF_TEXT | NLTIF_PARAM | NLTIF_IMAGE_MASK; + tab.param = (LPARAM)target; + + WCHAR szTitle[256] = {0}; + if (FAILED(target->GetName(szTitle, ARRAYSIZE(szTitle)))) + szTitle[0] = L'\0'; + + tab.pszText = szTitle; + + LoginBox_RemoveImageHelper2(hFrame, &tab); + + tab.iImage = NLTM_IMAGE_CALLBACK; + tab.iImageActive = NLTM_IMAGE_CALLBACK; + tab.iImageDisabled = NLTM_IMAGE_CALLBACK; + + if (TRUE == LoginTab_SetItem(hFrame,i, &tab)) + { + target->AddRef(); + resultOk = TRUE; + + if (i == LoginTab_GetCurSel(hFrame)) + LoginBox_SelectActivePage(hwnd); + + provider->Release(); + } + + break; + } + } + } + return resultOk; +} + +static BOOL LoginBox_DeleteProvider(HWND hwnd, const GUID *providerId) +{ + if (NULL == providerId) + return FALSE; + + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL == hFrame) return FALSE; + + GUID testId; + BOOL resultOk = FALSE; + + NLTITEM tab; + tab.mask = NLTIF_PARAM; + INT count = LoginTab_GetItemCount(hFrame); + for (INT i =0; i < count; i++) + { + if (FALSE != LoginTab_GetItem(hFrame, i, &tab)) + { + LoginProvider *provider = (LoginProvider*)tab.param; + if (NULL != provider && SUCCEEDED(provider->GetId(&testId)) && IsEqualGUID(*providerId, testId)) + { + BOOL updateSelection = (i == LoginTab_GetCurSel(hFrame)); + + if (TRUE == LoginTab_DeleteItem(hFrame, i)) + { + resultOk = TRUE; + if (FALSE != updateSelection) + { + LoginTab_SetCurSel(hFrame, 0); + LoginBox_SelectActivePage(hwnd); + } + } + break; + } + } + } + return resultOk; +} + +static BOOL LoginBox_CenterOver(HWND hwnd, HWND hOwner) +{ + RECT rect, rectOwner; + + if (NULL == hwnd || FALSE == GetWindowRect(hwnd, &rect)) + return FALSE; + + if(NULL == hOwner || FALSE == GetWindowRect(hOwner, &rectOwner)) + { + HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + if (NULL == hMonitor) return FALSE; + + MONITORINFO mi; + mi.cbSize = sizeof(mi); + if (FALSE == GetMonitorInfo(hMonitor, &mi)) return FALSE; + CopyRect(&rectOwner, &mi.rcWork); + } + + LONG x = rectOwner.left + ((rectOwner.right - rectOwner.left) - (rect.right - rect.left))/2; + LONG y = rectOwner.top + ((rectOwner.bottom - rectOwner.top) - (rect.bottom - rect.top))/2; + + return SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); +} + +static void LoginBox_UpdateMargins(HWND hwnd) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return; + + RECT rect; + SetRect(&rect, 4, 14, 0, 0); + MapDialogRect(hwnd, &rect); + + loginbox->buttonHeight = rect.top; + loginbox->buttonSpace = rect.left; + + HWND hControl = GetDlgItem(hwnd, IDOK); + if (NULL != hControl) + { + loginbox->buttonHeight = LoginBox_GetWindowTextHeight(hControl, 3); + } + + if (loginbox->buttonHeight < 23) + loginbox->buttonHeight = 23; + + SetRect(&rect, 2, 8, 8, 6); + MapDialogRect(hwnd, &rect); + CopyRect(&loginbox->buttonMargins, &rect); + + + SetRect(&loginbox->minmaxInfo, LOGINBOX_MINWIDTH, LOGINBOX_MINHEIGHT, LOGINBOX_MAXWIDTH, LOGINBOX_MAXHEIGHT); + MapDialogRect(hwnd, &loginbox->minmaxInfo); +} + +static BOOL LoginBox_GetGripSize(HWND hwnd, HDC hdc, SIZE *gripSize) +{ + BOOL gripSizeOk = FALSE; + if (SUCCEEDED(UxTheme_LoadLibrary()) && FALSE != UxIsAppThemed()) + { + UXTHEME hTheme = UxOpenThemeData(hwnd, L"Scrollbar"); + if (NULL != hTheme) + { + if (SUCCEEDED(UxGetThemePartSize(hTheme, hdc, SBP_SIZEBOX, SZB_RIGHTALIGN, NULL, TS_TRUE, gripSize))) + gripSizeOk = TRUE; + + UxCloseThemeData(hTheme); + } + } + + if (FALSE == gripSizeOk) + { + gripSize->cx = GetSystemMetrics(SM_CXVSCROLL); + gripSize->cy = GetSystemMetrics(SM_CYHSCROLL); + } + + return TRUE; +} + + +static void LoginBox_LayoutGrip(HWND hwnd) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return; + + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + SIZE gripSize; + if (FALSE != LoginBox_GetGripSize(hwnd, hdc, &gripSize)) + { + GetClientRect(hwnd, &loginbox->gripRect); + loginbox->gripRect.left = loginbox->gripRect.right - gripSize.cx; + loginbox->gripRect.top = loginbox->gripRect.bottom - gripSize.cy; + + LONG test; + test = gripSize.cx - MulDiv(gripSize.cx, 2, 3); + if (loginbox->buttonMargins.right < test) + loginbox->buttonMargins.right = test; + + test = gripSize.cy - MulDiv(gripSize.cy, 2, 3); + if (loginbox->buttonMargins.bottom < test) + loginbox->buttonMargins.bottom = test; + } + ReleaseDC(hwnd, hdc); + } +} + +static void LoginBox_UpdateLayout(HWND hwnd, BOOL fRedraw) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return; + + RECT clientRect; + if (NULL == hwnd || FALSE == GetClientRect(hwnd, &clientRect)) + return; + + UINT swpFlags = SWP_NOACTIVATE | SWP_NOZORDER; + if (FALSE == fRedraw) swpFlags |= SWP_NOREDRAW; + + const INT szButtons[] = { IDOK, IDCANCEL, }; + const INT szControls[] = { IDC_STATUS, IDC_TABFRAME, IDC_NOTIFIER, IDC_ACTIVEPAGE, IDC_CURTAIN, }; + + HDWP hdwp = BeginDeferWindowPos(ARRAYSIZE(szButtons) + ARRAYSIZE(szControls)); + if (NULL == hdwp) return; + + RECT rect; + + HRGN rgnUpdate(NULL), rgn(NULL); + if (FALSE != fRedraw) + { + rgnUpdate = CreateRectRgn(0, 0, 0, 0); + rgn = CreateRectRgn(0,0,0,0); + + if (FALSE == IsRectEmpty(&loginbox->gripRect)) + { + SetRectRgn(rgn, loginbox->gripRect.left, loginbox->gripRect.top, + loginbox->gripRect.right, loginbox->gripRect.bottom); + CombineRgn(rgnUpdate, rgnUpdate, rgn, RGN_OR); + } + + if (loginbox->buttonTop > clientRect.top) + { + SetRectRgn(rgn, clientRect.left, loginbox->buttonTop, clientRect.right, loginbox->buttonTop + 1); + CombineRgn(rgnUpdate, rgnUpdate, rgn, RGN_OR); + } + } + + LoginBox_LayoutGrip(hwnd); + + CopyRect(&rect, &clientRect); + rect.left += loginbox->buttonMargins.left; + rect.top += loginbox->buttonMargins.top; + rect.right -= loginbox->buttonMargins.right; + rect.bottom -= loginbox->buttonMargins.bottom; + + hdwp = LoginBox_LayoutButtonBar(hdwp, hwnd, szButtons, ARRAYSIZE(szButtons), &rect, + loginbox->buttonHeight, loginbox->buttonSpace, fRedraw, &rect); + + loginbox->buttonTop = rect.top - loginbox->buttonMargins.top; + + if (NULL != rgn && loginbox->buttonTop > clientRect.top) + { + SetRectRgn(rgn, clientRect.left, loginbox->buttonTop, clientRect.right, loginbox->buttonTop + 1); + CombineRgn(rgnUpdate, rgnUpdate, rgn, RGN_OR); + } + + LONG statusRight = rect.left - loginbox->buttonSpace; + LONG pageTop = clientRect.top; + LONG pageBottom = loginbox->buttonTop; + + UINT flags; + + if (NULL != rgn) + { + SetRectRgn(rgn, loginbox->gripRect.left, loginbox->gripRect.top, + loginbox->gripRect.right, loginbox->gripRect.bottom); + CombineRgn(rgnUpdate, rgnUpdate, rgn, RGN_OR); + } + + for (INT i = 0; i < ARRAYSIZE(szControls); i++) + { + HWND hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL == hControl || FALSE == GetWindowRect(hControl, &rect)) continue; + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rect, 2); + flags = swpFlags; + + switch(szControls[i]) + { + case IDC_STATUS: + if (NULL != rgn && 0 != (WS_VISIBLE & GetWindowStyle(hControl))) + { + SetRectRgn(rgn, rect.left, rect.top, rect.right, rect.bottom); + CombineRgn(rgnUpdate, rgnUpdate, rgn, RGN_OR); + } + rect.top = rect.bottom - rect.top; + rect.bottom = clientRect.bottom - loginbox->buttonMargins.bottom; + rect.top = rect.bottom - rect.top; + rect.left = clientRect.left + loginbox->buttonMargins.left; + rect.right = statusRight; + flags |= SWP_NOREDRAW | SWP_NOCOPYBITS; + if (NULL != rgn && 0 != (WS_VISIBLE & GetWindowStyle(hControl))) + { + SetRectRgn(rgn, rect.left, rect.top, rect.right, rect.bottom); + CombineRgn(rgnUpdate, rgnUpdate, rgn, RGN_OR); + } + + break; + + case IDC_TABFRAME: + rect.left = clientRect.left; + rect.top = clientRect.top; + rect.right = clientRect.right; + rect.bottom = rect.top + LoginTab_GetIdealHeight(hControl); + pageTop = rect.bottom; + break; + + case IDC_NOTIFIER: + rect.bottom = pageTop + (rect.bottom - rect.top); + rect.top = pageTop; + rect.left = clientRect.left; + rect.right = clientRect.right; + if (0 != (WS_VISIBLE & GetWindowStyle(hControl))) + pageTop = rect.bottom; + break; + + case IDC_ACTIVEPAGE: + case IDC_CURTAIN: + rect.left = clientRect.left; + rect.top = pageTop; + rect.right = clientRect.right; + rect.bottom = (pageBottom > pageTop) ? pageBottom : pageTop; + break; + } + + hdwp = DeferWindowPos(hdwp, hControl, NULL, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, flags); + + if (NULL == hdwp) break; + } + + if (NULL != hdwp) + { + EndDeferWindowPos(hdwp); + } + + if (NULL != rgnUpdate) + { + RedrawWindow(hwnd, NULL, rgnUpdate, RDW_INVALIDATE | RDW_ALLCHILDREN); + DeleteObject(rgnUpdate); + } + + if (NULL != rgn) + DeleteObject(rgn); +} + +static BOOL LoginBox_DrawResizeGrip(HWND hwnd, HDC hdc, const RECT *gripRect, HBRUSH brushBk) +{ + BOOL gripDrawOk = FALSE; + + if (SUCCEEDED(UxTheme_LoadLibrary()) && FALSE != UxIsAppThemed()) + { + UXTHEME hTheme = UxOpenThemeData(hwnd, L"Scrollbar"); + if (NULL != hTheme) + { + if (UxIsThemeBackgroundPartiallyTransparent(hTheme, SBP_SIZEBOX, SZB_RIGHTALIGN)) + { + UXTHEME windowTheme = UxOpenThemeData(hwnd, L"Window"); + if (NULL != windowTheme) + { + if (UxIsThemeBackgroundPartiallyTransparent(windowTheme, WP_DIALOG, 0)) + UxDrawThemeParentBackground(hwnd, hdc, gripRect); + + //UxDrawThemeBackground(windowTheme, hdc, WP_DIALOG, 0, &gripRect, &gripRect); + FillRect(hdc, gripRect, brushBk); + UxCloseThemeData(windowTheme); + } + } + + if (SUCCEEDED(UxDrawThemeBackground(hTheme, hdc, SBP_SIZEBOX, SZB_RIGHTALIGN, gripRect, gripRect))) + gripDrawOk = TRUE; + + UxCloseThemeData(hTheme); + } + } + + if (FALSE == gripDrawOk) + gripDrawOk = DrawFrameControl(hdc, (RECT*)gripRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); + + return gripDrawOk; +} + +static void LoginBox_Paint(HWND hwnd, HDC hdc, const RECT *prcPaint, BOOL fErase) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return; + + if (FALSE != fErase) + { + HBRUSH brushBk = (HBRUSH)SendMessage(hwnd, WM_CTLCOLORDLG, (WPARAM)hdc, (LPARAM)hwnd); + if (NULL == brushBk) + { + brushBk = GetSysColorBrush(COLOR_3DFACE); + } + + HRGN regionFill = CreateRectRgnIndirect(prcPaint); + HRGN regionClip = NULL; + + if (RectInRegion(regionFill, &loginbox->gripRect) && + FALSE != LoginBox_DrawResizeGrip(hwnd, hdc, &loginbox->gripRect, brushBk)) + { + regionClip = CreateRectRgnIndirect(&loginbox->gripRect); + if (NULL != regionClip) + CombineRgn(regionFill, regionFill, regionClip, RGN_DIFF); + } + + if (loginbox->buttonTop > 0) + { + RECT lineRect; + GetClientRect(hwnd, &lineRect); + lineRect.top = loginbox->buttonTop; + lineRect.bottom = loginbox->buttonTop + 1; + + if (RectInRegion(regionFill, &lineRect)) + { + COLORREF rgbLine = GetSysColor(COLOR_3DLIGHT); + if (rgbLine == GetSysColor(COLOR_3DFACE)) + rgbLine = ColorAdjustLuma(rgbLine, -50, TRUE); + + COLORREF rgbOrig = SetBkColor(hdc, rgbLine); + if (0 != ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &lineRect, NULL, 0, NULL)) + { + if (NULL == regionClip) regionClip = CreateRectRgnIndirect(&lineRect); + else SetRectRgn(regionClip, lineRect.left, lineRect.top, lineRect.right, lineRect.bottom); + + if (NULL != regionClip) + CombineRgn(regionFill, regionFill, regionClip, RGN_DIFF); + + } + if (rgbOrig != rgbLine) SetBkColor(hdc, rgbOrig); + } + } + + if (NULL != regionFill) + { + FillRgn(hdc, regionFill, brushBk); + DeleteObject(regionFill); + } + + if (NULL != regionClip) + DeleteObject(regionClip); + } +} + + +static LoginProvider *LoginBox_GetActiveProviderInternal(HWND hwnd) +{ + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL == hFrame) return NULL; + + INT iSelected = LoginTab_GetCurSel(hFrame); + if (-1 == iSelected) return NULL; + + NLTITEM tab; + tab.mask = NLTIF_PARAM; + return (TRUE == LoginTab_GetItem(hFrame, iSelected, &tab)) ? + (LoginProvider*)tab.param : NULL; +} + +static void CALLBACK LoginBox_LoginCompletedEvent(LoginResult *result) +{ + HWND hLoginbox; + if (SUCCEEDED(result->GetUser((void**)&hLoginbox)) && NULL != hLoginbox) + PostMessage(hLoginbox, NLBM_LOGINCOMPLETED, 0, (LPARAM)result); +} + +static void Loginbox_LockTabFrame(HWND hwnd, BOOL fLock) +{ + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL == hFrame) return; + + UINT frameStyle = GetWindowStyle(hFrame); + if (FALSE != fLock) + frameStyle &= ~WS_TABSTOP; + else + frameStyle |= WS_TABSTOP; + + SetWindowLongPtr(hFrame, GWL_STYLE, frameStyle); + + LoginTab_LockSelection(hFrame, fLock); +} + +static BOOL LoginBox_EnableLoginMode(HWND hwnd, BOOL fEnable, BOOL fValidateTos) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return FALSE; + + const static INT szControls[] = { IDC_ACTIVEPAGE, IDOK, }; + + BOOL resultCode = TRUE; + + HWND hPage = GetDlgItem(hwnd, IDC_ACTIVEPAGE); + HWND hCurtain = GetDlgItem(hwnd, IDC_CURTAIN); + + if (FALSE != LoginPopup::AnyPopup(hwnd)) + return FALSE; + + if (FALSE != fEnable) + { + HWND hPopup = NULL; + + if (NULL == hCurtain) + { + hCurtain = LoginCurtain_CreateWindow(hwnd, (NULL != hPage) ? hPage : hwnd); + if (NULL != hCurtain) + SetWindowLongPtr(hCurtain, GWLP_ID, IDC_CURTAIN); + } + + if (FALSE != fValidateTos && TRUE != loginbox->agreementOk) + { + + LoginProvider *provider = LoginBox_GetActiveProviderInternal(hwnd); + hPopup = LoginPopupAgreement::CreatePopup((NULL != hCurtain) ? hCurtain : hwnd, provider); + if (NULL != hPopup) + { + SetWindowLongPtr(hPopup, GWLP_ID, IDC_POPUPAGREEMENT); + + if (NULL != loginbox->loginStatus) + { + WCHAR szBuffer[128] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_STATUS_AGREEMENT_REQUIRED, szBuffer, ARRAYSIZE(szBuffer)); + if (L'\0' != szBuffer[0]) + { + UINT statusCookie = loginbox->loginStatus->Add(SysAllocString(szBuffer)); + if (-1 != statusCookie && FALSE == SetProp(hPopup, L"StatusCookie", (HANDLE)(UINT_PTR)(statusCookie + 1))) + loginbox->loginStatus->Remove(statusCookie); + } + } + + + SetWindowPos(hPopup, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE); + ShowWindow(hPopup, SW_SHOW); + if (NULL != hCurtain) + SetWindowPos(hCurtain, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED ); + + resultCode = FALSE; + + } + else + { + LoginBox_EnableLoginMode(hwnd, FALSE, FALSE); + return FALSE; + } + + + } + + if (NULL != hCurtain) + { + ShowWindow(hCurtain, SW_SHOW); + UpdateWindow(hCurtain); + } + + if (NULL != hPopup && IsWindowVisible(hPopup) && IsWindowEnabled(hPopup)) + { + HWND hRoot = GetAncestor(hPopup, GA_ROOT); + if (NULL != hRoot) + SendMessage(hRoot, WM_NEXTDLGCTL, (WPARAM)hPopup, TRUE); + LoginPopup_PlayBeep(hPopup); + } + + Loginbox_LockTabFrame(hwnd, TRUE); + for (INT i = 0; i < ARRAYSIZE(szControls); i++) + { + HWND hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL != hControl) + { + EnableWindow(hControl, FALSE); + } + } + + + } + else + { + Loginbox_LockTabFrame(hwnd, FALSE); + for (INT i = 0; i < ARRAYSIZE(szControls); i++) + { + HWND hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL != hControl) EnableWindow(hControl, TRUE); + } + + if (NULL != hPage && IsWindowVisible(hPage) && IsWindowEnabled(hPage)) + { + HWND hRoot = GetAncestor(hPage, GA_ROOT); + if (NULL != hRoot) + SendMessage(hRoot, WM_NEXTDLGCTL, (WPARAM)hPage, TRUE); + } + + if (NULL != hCurtain) + { + DestroyWindow(hCurtain); + } + } + + return resultCode; +} + +static void LoginBox_PerformLogin(HWND hwnd, LoginData *loginData) +{ + LoginBox_HideNotifier(hwnd, 150); + + if (FALSE == LoginBox_EnableLoginMode(hwnd, TRUE, TRUE)) + return; + + INT authError; + + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) authError = AUTH_UNEXPECTED; + else + { + HWND hPage = GetDlgItem(hwnd, IDC_ACTIVEPAGE); + if (NULL == hPage) authError = AUTH_UNEXPECTED; + else + { + LoginProvider *provider = LoginBox_GetActiveProviderInternal(hwnd); + if (NULL == provider) authError = AUTH_UNEXPECTED; + else + { + LoginCommand *command; + if (FAILED(provider->GetCommand(&command))) authError = AUTH_UNEXPECTED; + else + { + if (NULL == loginData) + { + if (FALSE == LoginPage_GetData(hPage, &loginData)) + loginData = NULL; + } + else + loginData->AddRef(); + + HRESULT hr = command->BeginLogin(loginData, LoginBox_LoginCompletedEvent, hwnd, &loginbox->loginResult); + authError = (SUCCEEDED(hr)) ? AUTH_SUCCESS : AUTH_UNEXPECTED; + command->Release(); + + if (NULL != loginData) + loginData->Release(); + } + } + } + } + + if (AUTH_SUCCESS != authError) + { + LoginBox_EnableLoginMode(hwnd, FALSE, FALSE); + if (AUTH_ABORT != authError) + { + HWND hNotifier = GetDlgItem(hwnd, IDC_NOTIFIER); + if (NULL != hNotifier) + { + LoginNotifier_Notify(hNotifier, NLNTYPE_ERROR, MAKEINTRESOURCE(authError)); + LoginBox_ShowNotifier(hwnd, 200); + } + } + } +} + +static INT LoginBox_SetCredentials(HWND hwnd, LoginCredentials *credentials) +{ + if (NULL == credentials) + return AUTH_UNEXPECTED; + + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox || NULL == loginbox->auth) + return AUTH_UNEXPECTED; + + INT result = loginbox->auth->SetCredentials(credentials->GetRealm(), credentials->GetSessionKey(), + credentials->GetToken(), credentials->GetUsername(), credentials->GetExpiration()); + + return result; +} + +static INT LoginBox_RequestPasscode(HWND hwnd, LoginResult *loginResult) +{ + LoginData *loginData; + if (NULL == loginResult || FAILED(loginResult->GetLoginData(&loginData)) || NULL == loginData) + return AUTH_UNEXPECTED; + + HWND hCurtain = GetDlgItem(hwnd, IDC_CURTAIN); + HWND hPopup = LoginPopupPasscode::CreatePopup((NULL != hCurtain) ? hCurtain : hwnd, loginData); + if (NULL != hPopup) + { + SetWindowLongPtr(hPopup, GWLP_ID, IDC_POPUPPASSCODE); + + SetWindowPos(hPopup, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE); + ShowWindow(hPopup, SW_SHOW); + + if (NULL != hCurtain) + SetWindowPos(hCurtain, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED ); + + if (IsWindowVisible(hPopup) && IsWindowEnabled(hPopup)) + { + HWND hRoot = GetAncestor(hPopup, GA_ROOT); + if (NULL != hRoot) + SendMessage(hRoot, WM_NEXTDLGCTL, (WPARAM)hPopup, TRUE); + LoginPopup_PlayBeep(hPopup); + } + + } + + loginData->Release(); + + return (NULL != hPopup) ? AUTH_SUCCESS : AUTH_UNEXPECTED; +} + +static BOOL LoginBox_EndDialog(HWND hwnd, INT authResult) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + + if (0 != (NLBS_MODAL & loginbox->style)) + { + EndDialog(hwnd, authResult); + } + else + { + DestroyWindow(hwnd); + } + return TRUE; +} + +static BOOL LoginBox_ReloadProviders(HWND hwnd, INT *loaded, INT *prefVisible) +{ + if (NULL != loaded) + *loaded = NULL; + + WCHAR szPath[MAX_PATH] = {0}; + if (FAILED(LoginBox_GetConfigPath(szPath, FALSE))) + return FALSE; + + if (FALSE == PathAppend(szPath, L"loginProviders.xml")) + return FALSE; + + LoginProviderEnumerator *enumerator; + LoginProviderLoader loader; + + if (FAILED(loader.ReadXml(szPath,&enumerator, prefVisible))) + return FALSE; + + + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL != hFrame) + { + UINT frameStyle = GetWindowStyle(hFrame); + if (0 != (WS_VISIBLE & frameStyle)) + SetWindowLongPtr(hFrame, GWL_STYLE, frameStyle & ~WS_VISIBLE); + + LoginBox_DeleteAllTabs(hwnd); + INT r = LoginBox_AppendMultipleTabs(hwnd, enumerator, NULL, NULL); + if (NULL != loaded) + *loaded = r; + + if (0 != (WS_VISIBLE & frameStyle)) + { + frameStyle = GetWindowStyle(hFrame); + if (0 == (WS_VISIBLE & frameStyle)) + SetWindowLongPtr(hFrame, GWL_STYLE, frameStyle | WS_VISIBLE); + } + } + + enumerator->Release(); + return TRUE; +} + + +static void LoginBox_PerformProviderOp(HWND hwnd) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox || NULL == loginbox->providerOp) + return; + + if (FALSE == LoginBox_EnableLoginMode(hwnd, TRUE, FALSE)) + return; + + HWND hCurtain = GetDlgItem(hwnd, IDC_CURTAIN); + + LPCWSTR pszMessage; + switch(loginbox->providerOp->GetCode()) + { + case LoginProviderOperation::operationReplace: + pszMessage = MAKEINTRESOURCE(IDS_PROVIDER_CHANGED); break; + case LoginProviderOperation::operationDelete: + pszMessage = MAKEINTRESOURCE(IDS_PROVIDER_REMOVED); break; + default: + pszMessage = NULL; + break; + } + HWND hPopup = LoginPopupMessage::CreatePopup((NULL != hCurtain) ? hCurtain : hwnd, + MAKEINTRESOURCE(IDS_PROVIDERUPDATE_TITLE), pszMessage, + LoginPopupMessage::typeContinue | LoginPopupMessage::iconWarning, NULL, NULL); + if (NULL != hPopup) + { + SetWindowLongPtr(hPopup, GWLP_ID, IDC_POPUPPROVIDEROP); + + SetWindowPos(hPopup, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE); + ShowWindow(hPopup, SW_SHOW); + + if (NULL != hCurtain) + SetWindowPos(hCurtain, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED ); + + if (IsWindowVisible(hPopup) && IsWindowEnabled(hPopup)) + { + HWND hRoot = GetAncestor(hPopup, GA_ROOT); + if (NULL != hRoot) + SendMessage(hRoot, WM_NEXTDLGCTL, (WPARAM)hPopup, TRUE); + LoginPopup_PlayBeep(hPopup); + } + } + else + { + LoginBox_EnableLoginMode(hwnd, FALSE, FALSE); + } +} +static INT_PTR LoginBox_OnInitDialog(HWND hwnd, HWND hFocus, LPARAM param) +{ + LOGINBOXCREATEPARAM *createParam = (LOGINBOXCREATEPARAM*)param; + LOGINBOX *login = (LOGINBOX*)calloc(1, sizeof(LOGINBOX)); + if (NULL == login) + { + return 0; + } + + SetProp(hwnd, LOGINBOX_PROP, login); + + if (NULL != createParam) + { + login->style = createParam->style; + login->realm = (NULL == createParam->pRealm) ? GUID_NULL : *createParam->pRealm; + login->auth = createParam->auth; + } + + if (NULL != login->auth) + login->auth->AddRef(); + + LoginGuiObject::InitializeThread(); + + HFONT hFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L); + + HWND hFrame = LoginTab_CreateWindow(0, L"Select provider:", + WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_VISIBLE, 0, 0, 0, 0, hwnd, IDC_TABFRAME); + + if (NULL != hFrame) + { + HIMAGELIST imageList = ImageList_Create(40, 40, ILC_COLOR32, 7*3, 3*3); + if (NULL != imageList) + LoginTab_SetImageList(hFrame, imageList); + } + + HWND hNotifier = LoginNotifier_CreateWindow(0, WS_CHILD | WS_CLIPSIBLINGS, 0, 0, 0, 0, hwnd, IDC_NOTIFIER); + if (NULL != hNotifier) + { + if (NULL != hFont) + SendMessage(hNotifier, WM_SETFONT, (WPARAM)hFont, 0L); + SetWindowPos(hNotifier, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE); + } + + HWND hStatus = GetDlgItem(hwnd, IDC_STATUS); + if (NULL != hStatus) + { + LoginStatus::CreateInstance(hStatus, &login->loginStatus); + } + + LoginGuiObject *loginGui; + if (SUCCEEDED(LoginGuiObject::QueryInstance(&loginGui))) + { + HFONT fontButton = loginGui->GetTextFont(); + if (NULL != fontButton) + { + INT szButtons[] = {IDOK, IDCANCEL, }; + for (INT i = 0; i < ARRAYSIZE(szButtons); i++) + { + HWND hButton = GetDlgItem(hwnd, szButtons[i]); + if(NULL != hButton) + SNDMSG(hButton, WM_SETFONT, (WPARAM)fontButton, 0L); + } + } + loginGui->Release(); + } + INT loaded, prefVisible, prefWidth; + + prefWidth = 0; + if (FALSE == LoginBox_ReloadProviders(hwnd, &loaded, &prefVisible)) + { + loaded = 0; + prefVisible = 0; + } + + if (NULL != hFrame && prefVisible > 0) + prefWidth = LoginTab_GetIdealWidth(hFrame, prefVisible); + + if (NULL != hFrame) + { + INT activeId = -1; + LoginConfig *config; + if (SUCCEEDED(LoginConfig::CreateInstance(&config))) + { + RPC_CSTR pszRealm; + if (RPC_S_OK == UuidToStringA(&login->realm, &pszRealm)) + { + CHAR szProvider[128] = {0}; + config->ReadAnsiStr((LPCSTR)pszRealm, "lastProvider", NULL, szProvider, ARRAYSIZE(szProvider)); + if (NULL != szProvider && L'\0' != *szProvider) + { + GUID providerId; + if (RPC_S_OK == UuidFromStringA((RPC_CSTR)szProvider, &providerId)) + activeId = LoginBox_FindTabByProviderId(hwnd, &providerId); + } + RpcStringFreeA(&pszRealm); + } + + config->Release(); + } + + if (-1 == activeId) + activeId = 0; + + LoginTab_SetCurSel(hFrame, activeId); + LoginBox_SelectActivePage(hwnd); + + } + + LoginBox_UpdateMargins(hwnd); + + if (0 != prefWidth) + { + RECT rect; + + GetClientRect(hwnd, &rect); + prefWidth -= rect.right - rect.left; + + GetWindowRect(hwnd, &rect); + prefWidth += rect.right - rect.left; + + + if (prefWidth < login->minmaxInfo.left) prefWidth = login->minmaxInfo.left; + else if (prefWidth > login->minmaxInfo.right) prefWidth = login->minmaxInfo.right; + + SetWindowPos(hwnd, NULL, 0, 0, prefWidth, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW); + + } + + LoginBox_UpdateLayout(hwnd, FALSE); + + if (NULL != createParam && 0 == (LBS_NOCENTEROWNER & createParam->style)) + LoginBox_CenterOver(hwnd, createParam->hOwner); + + SendMessage(hwnd, DM_REPOSITION, 0, 0L); + + PostMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0L); + PostMessage(hwnd, NLBM_UPDATEPROVIDERS, (0 == loaded), 0L); + + return 0; +} + +static void LoginBox_OnDestroy(HWND hwnd) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + RemoveProp(hwnd, LOGINBOX_PROP); + + if (NULL == loginbox) + return; + + LoginConfig *config; + if (SUCCEEDED(LoginConfig::CreateInstance(&config))) + { + RPC_CSTR pszRealm, pszProvider; + if (RPC_S_OK == UuidToStringA(&loginbox->realm, &pszRealm)) + { + GUID providerId; + LoginProvider *activeProvider = LoginBox_GetActiveProviderInternal(hwnd); + if (NULL == activeProvider || FAILED(activeProvider->GetId(&providerId)) || + RPC_S_OK != UuidToStringA(&providerId, &pszProvider)) + { + pszProvider = NULL; + } + + config->WriteAnsiStr((LPCSTR)pszRealm, "lastProvider", (LPCSTR)pszProvider); + if (NULL != pszProvider) + RpcStringFreeA(&pszProvider); + + RpcStringFreeA(&pszRealm); + } + + config->Release(); + } + + if (NULL != loginbox->imageCache) + { + loginbox->imageCache->Finish(); + loginbox->imageCache->Release(); + loginbox->imageCache = NULL; + } + + LoginBox_DeleteAllTabs(hwnd); + + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL != hFrame) + { + HIMAGELIST imageList = LoginTab_SetImageList(hFrame, NULL); + if (NULL != imageList) + { + ImageList_Destroy(imageList); + } + } + + if (NULL != loginbox->loginResult) + { + LoginResult *result = loginbox->loginResult; + result->RequestAbort(TRUE); + HANDLE resultAborted; + if (SUCCEEDED(result->GetWaitHandle(&resultAborted))) + { + WaitForSingleObjectEx(resultAborted, INFINITE, TRUE); + CloseHandle(resultAborted); + } + + loginbox->loginResult = NULL; + result->Release(); + } + + if (NULL != loginbox->auth) + loginbox->auth->Release(); + + if (NULL != loginbox->providerUpdate) + { + UINT state; + if (FAILED(loginbox->providerUpdate->GetState(&state)) || + LoginDownloadResult::stateCompleted != state) + { + loginbox->providerUpdate->RequestAbort(TRUE); + HANDLE completed; + if (SUCCEEDED(loginbox->providerUpdate->GetWaitHandle(&completed)) && NULL != completed) + { + WaitForSingleObject(completed, INFINITE); + CloseHandle(completed); + } + } + loginbox->providerUpdate->Release(); + } + + if (NULL != loginbox->providerOp) + loginbox->providerOp->Release(); + + if (NULL != loginbox->loginStatus) + { + loginbox->loginStatus->DetachWindow(); + loginbox->loginStatus->Release(); + } + + LoginGuiObject::UninitializeThread(); + + free(loginbox); +} + +static void LoginBox_OnGetMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) +{ + if (NULL == mmi) return; + + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL != loginbox) + { + mmi->ptMinTrackSize.x = loginbox->minmaxInfo.left; + mmi->ptMinTrackSize.y = loginbox->minmaxInfo.top; + mmi->ptMaxTrackSize.x = loginbox->minmaxInfo.right; + mmi->ptMaxTrackSize.y = loginbox->minmaxInfo.bottom; + } +} + +static void LoginBox_OnWindowPosChanged(HWND hwnd, WINDOWPOS *pwp) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return; + + if (SWP_NOSIZE != ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags)) + { + LoginBox_UpdateLayout(hwnd, 0 == (SWP_NOREDRAW & pwp->flags)); + } +} + +static void LoginBox_OnCommand(HWND hwnd, INT commandId, INT eventId, HWND hControl) +{ + switch(commandId) + { + case IDOK: + LoginBox_PerformLogin(hwnd, NULL); + break; + case IDCANCEL: + LoginBox_EndDialog(hwnd, AUTH_ABORT); + break; + case ID_LOGINBOX_UPDATEPROVIDERS: + LoginBox_UpdateProviders(hwnd, TRUE); + break; + case ID_LOGINTAB_RESETORDER: + LoginTab_ResetOrder(GetDlgItem(hwnd, IDC_TABFRAME)); + break; + } + +} + +static void LoginBox_OnTabSelected(HWND hwnd, HWND hFrame) +{ + LoginBox_SelectActivePage(hwnd); +} + +static void LoginBox_OnTabDeleted(HWND hwnd, HWND hFrame, INT iItem) +{ + if (NULL == hFrame || iItem < 0) return; + + NLTITEM tab; + tab.mask = NLTIF_PARAM | NLTIF_IMAGE_MASK; + if (FALSE != LoginTab_GetItem(hFrame, iItem, &tab)) + { + LoginBox_RemoveImageHelper2(hFrame, &tab); + + LoginProvider *provider = (LoginProvider*)tab.param; + if (NULL != provider) + provider->Release(); + + } +} + +static BOOL LoginBox_OnTabDeleteAll(HWND hwnd, HWND hFrame) +{ + if (NULL == hFrame) return FALSE; + + HIMAGELIST imageList = LoginTab_GetImageList(hFrame); + if (NULL != imageList) + ImageList_RemoveAll(imageList); + + NLTITEM tab; + tab.mask = NLTIF_PARAM; + + INT iItem = LoginTab_GetItemCount(hFrame); + while (iItem--) + { + if (FALSE != LoginTab_GetItem(hFrame, iItem, &tab)) + { + + LoginProvider *provider = (LoginProvider*)tab.param; + if (NULL != provider) + provider->Release(); + } + } + + return TRUE; +} +static void LoginBox_OnTabRClick(HWND hwnd, HWND hFrame, POINT pt) +{ + HMENU hMenu = WASABI_API_LOADMENUW(IDR_MENU_TABCONTEXT); + HMENU hContext = (NULL != hMenu) ? GetSubMenu(hMenu, 0) : NULL; + if (NULL == hContext) return; + + MapWindowPoints(hFrame, HWND_DESKTOP, &pt, 1); + + LOGINBOX *loginbox = GetLoginBox(hwnd); + UINT fEnable = (NULL != loginbox && NULL == loginbox->providerUpdate) ? MF_ENABLED : MF_DISABLED; + EnableMenuItem(hContext, ID_LOGINBOX_UPDATEPROVIDERS, fEnable | MF_BYCOMMAND); + + TrackPopupMenuEx(hContext, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_VERPOSANIMATION, pt.x, pt.y, hwnd, NULL); + +} +static void LoginBox_OnTabHelp(HWND hwnd, HWND hFrame, NMLOGINTABHELP *plth) +{ + if (NULL == plth || NULL == plth->param) + return; + LoginProvider *provider = (LoginProvider*)plth->param; + + WCHAR szBuffer[2048] = {0}; + if (SUCCEEDED(provider->GetDescription(szBuffer, ARRAYSIZE(szBuffer)))) + plth->bstrHelp = SysAllocString(szBuffer); +} +static void LoginBox_OnTabImage(HWND hwnd, HWND hFrame, NMLOGINTABIMAGE *plti) +{ + if (NULL == plti || NULL == plti->imageList) + return; + + if (NLTM_IMAGE_CALLBACK == plti->iImage) + { + plti->maskUpdate = NLTIF_IMAGE_MASK; + plti->iImage = NLTM_IMAGE_NONE; + plti->iImageActive = NLTM_IMAGE_NONE; + plti->iImageDisabled = NLTM_IMAGE_NONE; + + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL != loginbox) + { + if (NULL == loginbox->imageCache) + LoginImageCache::CreateInstance(hwnd, &loginbox->imageCache); + + LoginProvider *provider = (LoginProvider*)plti->param; + if (NULL != provider && NULL != loginbox->imageCache) + { + WCHAR szImage[4096] = {0}; + if (SUCCEEDED(provider->GetImagePath(szImage, ARRAYSIZE(szImage)))) + { + loginbox->imageCache->GetImageListIndex(szImage, plti->imageList, + &plti->iImage, &plti->iImageActive, &plti->iImageDisabled); + } + } + } + return; + } + + if (0 != (NLTIF_IMAGE_ACTIVE & plti->maskRequest)) + { + plti->iImageActive = plti->iImage; + plti->maskUpdate |= NLTIF_IMAGE_ACTIVE; + } + + if (0 != (NLTIF_IMAGE_DISABLED & plti->maskRequest)) + { + plti->iImageDisabled = plti->iImage; + plti->maskUpdate |= NLTIF_IMAGE_DISABLED; + + } +} +static void LoginBox_OnPopupAgreementResult(HWND hwnd, NLPNRESULT *pnr) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return; + + HWND hCurtain = GetDlgItem(hwnd, IDC_CURTAIN); + UINT curtainStyle = (NULL != hCurtain) ? GetWindowStyle(hCurtain) : 0; + if (0 != (WS_VISIBLE & curtainStyle)) + SetWindowLongPtr(hCurtain, GWL_STYLE, curtainStyle & ~WS_VISIBLE); + + UINT statusCookie = (UINT)(UINT_PTR)GetProp(pnr->hdr.hwndFrom, L"StatusCookie"); + RemoveProp(pnr->hdr.hwndFrom, L"StatusCookie"); + if (0 != statusCookie) + loginbox->loginStatus->Remove(statusCookie - 1); + + DestroyWindow(pnr->hdr.hwndFrom); + + + if (IDOK == pnr->exitCode) + { + loginbox->agreementOk = TRUE; + LoginBox_PerformLogin(hwnd, NULL); // this will restore curtain visibility + } + else + { + loginbox->agreementOk = FALSE; + + RECT invalidRect; + if (NULL != hCurtain) + { + GetWindowRect(hCurtain, &invalidRect); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&invalidRect, 2); + } + else + SetRectEmpty(&invalidRect); + + LoginBox_EnableLoginMode(hwnd, FALSE, FALSE); + if (FALSE == IsRectEmpty(&invalidRect)) + RedrawWindow(hwnd, &invalidRect, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_ERASENOW); + + LoginBox_PerformProviderOp(hwnd); + } +} + +static void LoginBox_OnPopupPasscodeResult(HWND hwnd, NPPNRESULT *pnr) +{ + HWND hCurtain = GetDlgItem(hwnd, IDC_CURTAIN); + UINT curtainStyle = (NULL != hCurtain) ? GetWindowStyle(hCurtain) : 0; + if (0 != (WS_VISIBLE & curtainStyle)) + SetWindowLongPtr(hCurtain, GWL_STYLE, curtainStyle & ~WS_VISIBLE); + + DestroyWindow(pnr->hdr.hwndFrom); + + if (IDOK == pnr->exitCode) + { + LoginBox_PerformLogin(hwnd, pnr->loginData); // this will restore curtain visibility + } + else + { + RECT invalidRect; + if (NULL != hCurtain) + { + GetWindowRect(hCurtain, &invalidRect); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&invalidRect, 2); + } + else + SetRectEmpty(&invalidRect); + + LoginBox_EnableLoginMode(hwnd, FALSE, FALSE); + if (FALSE == IsRectEmpty(&invalidRect)) + RedrawWindow(hwnd, &invalidRect, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_ERASENOW); + + LoginBox_PerformProviderOp(hwnd); + } +} + +static void LoginBox_OnPopupProviderOpResult(HWND hwnd, NLPNRESULT *pnr) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return; + + HWND hCurtain = GetDlgItem(hwnd, IDC_CURTAIN); + UINT curtainStyle = (NULL != hCurtain) ? GetWindowStyle(hCurtain) : 0; + if (0 != (WS_VISIBLE & curtainStyle)) + SetWindowLongPtr(hCurtain, GWL_STYLE, curtainStyle & ~WS_VISIBLE); + + DestroyWindow(pnr->hdr.hwndFrom); + + RECT invalidRect; + if (NULL != hCurtain) + { + GetWindowRect(hCurtain, &invalidRect); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&invalidRect, 2); + } + else + SetRectEmpty(&invalidRect); + + LoginBox_EnableLoginMode(hwnd, FALSE, FALSE); + if (FALSE == IsRectEmpty(&invalidRect)) + RedrawWindow(hwnd, &invalidRect, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_ERASENOW); + + if (NULL != loginbox->providerOp) + { + LoginProvider *source, *target; + GUID sourceId; + if(SUCCEEDED(loginbox->providerOp->GetSource(&source)) && NULL != source) + { + if (FAILED(source->GetId(&sourceId))) + sourceId = GUID_NULL; + + source->Release(); + + switch(loginbox->providerOp->GetCode()) + { + case LoginProviderOperation::operationReplace: + if(SUCCEEDED(loginbox->providerOp->GetTarget(&target))) + { + LoginBox_ReplaceProvider(hwnd, &sourceId, target); + target->Release(); + } + break; + case LoginProviderOperation::operationDelete: + LoginBox_DeleteProvider(hwnd, &sourceId); + break; + } + } + + loginbox->providerOp->Release(); + loginbox->providerOp = NULL; + } +} + +static LRESULT LoginBox_OnNotify(HWND hwnd, INT controlId, NMHDR *pnmh) +{ + switch(controlId) + { + case IDC_TABFRAME: + switch(pnmh->code) + { + case NLTN_SELCHANGE: + LoginBox_OnTabSelected(hwnd, pnmh->hwndFrom); + break; + case NLTN_DELETEITEM: + LoginBox_OnTabDeleted(hwnd, pnmh->hwndFrom, ((NMLOGINTAB*)pnmh)->iItem); + break; + case NLTN_DELETEALLITEMS: + return LoginBox_OnTabDeleteAll(hwnd, pnmh->hwndFrom); + case NM_RCLICK: + LoginBox_OnTabRClick(hwnd, pnmh->hwndFrom, ((NMLOGINTABCLICK*)pnmh)->pt); + break; + case NLTN_GETITEMHELP: + LoginBox_OnTabHelp(hwnd, pnmh->hwndFrom, (NMLOGINTABHELP*)pnmh); + break; + case NLTN_GETITEMIMAGE: + LoginBox_OnTabImage(hwnd, pnmh->hwndFrom, (NMLOGINTABIMAGE*)pnmh); + break; + } + break; + case IDC_POPUPAGREEMENT: + switch(pnmh->code) + { + case NLPN_RESULT: + LoginBox_OnPopupAgreementResult(hwnd, (NLPNRESULT*)pnmh); + break; + } + break; + case IDC_POPUPPASSCODE: + switch(pnmh->code) + { + case NPPN_RESULT: + LoginBox_OnPopupPasscodeResult(hwnd, (NPPNRESULT*)pnmh); + break; + } + break; + case IDC_POPUPPROVIDEROP: + switch(pnmh->code) + { + case NLPN_RESULT: + LoginBox_OnPopupProviderOpResult(hwnd, (NLPNRESULT*)pnmh); + break; + } + break; + + } + return 0; +} + +static INT_PTR LoginBox_OnNcHitTest(HWND hwnd, POINTS pts) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL != loginbox) + { + POINT pt; + POINTSTOPOINT(pt, pts); + MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1); + + LONG offsetX = (loginbox->gripRect.right - loginbox->gripRect.left)/3; + LONG offsetY = (loginbox->gripRect.bottom - loginbox->gripRect.top)/3; + if (pt.x < loginbox->gripRect.right && pt.y < loginbox->gripRect.bottom && + pt.x > (loginbox->gripRect.left + offsetX) && pt.y > (loginbox->gripRect.top + offsetY)) + { + SetWindowLongPtr(hwnd, DWLP_MSGRESULT, HTBOTTOMRIGHT); + return TRUE; + } + } + return FALSE; +} + + +static void LoginBox_OnSetFont(HWND hwnd, HFONT hFont, BOOL fRedraw) +{ + DefWindowProc(hwnd, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0)); + LoginBox_UpdateMargins(hwnd); +} +static void LoginBox_OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + if (BeginPaint(hwnd, &ps)) + { + if (ps.rcPaint.left != ps.rcPaint.right) + LoginBox_Paint(hwnd, ps.hdc, &ps.rcPaint, ps.fErase); + EndPaint(hwnd, &ps); + } +} + +static void LoginBox_OnPrintClient(HWND hwnd, HDC hdc, UINT options) +{ + if (0 != (PRF_CLIENT & options)) + { + RECT clientRect; + if (GetClientRect(hwnd, &clientRect)) + LoginBox_Paint(hwnd, hdc, &clientRect, TRUE); + } +} +static HBRUSH LoginBox_OnGetStaticColor(HWND hwnd, HDC hdc, HWND hControl) +{ + HBRUSH hb; + INT_PTR controlId = (INT_PTR)GetWindowLongPtr(hControl, GWLP_ID); + switch(controlId) + { + case IDC_STATUS: + hb = (HBRUSH)SendMessage(hwnd, WM_CTLCOLORDLG, (WPARAM)hdc, (LPARAM)hwnd); + SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); + return hb; + } + return 0; +} + +static BOOL LoginBox_OnGetAuthApi(HWND hwnd, api_auth **authApi) +{ + if (NULL == authApi) return FALSE; + + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox || NULL == loginbox->auth) + { + *authApi = NULL; + return FALSE; + } + + *authApi = loginbox->auth; + loginbox->auth->AddRef(); + + return TRUE; +} + +static BOOL LoginBox_OnGetRealm(HWND hwnd, GUID *realm) +{ + if (NULL == realm) return FALSE; + + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) + { + *realm = GUID_NULL; + return FALSE; + } + + *realm = loginbox->realm; + return TRUE; +} + +static BOOL LoginBox_OnGetActiveProvider(HWND hwnd, LoginProvider **provider) +{ + if (NULL == provider) + return FALSE; + + *provider = LoginBox_GetActiveProviderInternal(hwnd); + if (NULL == (*provider)) + return FALSE; + + (*provider)->AddRef(); + return TRUE; +} + +static void LoginBox_OnLoginCompleted(HWND hwnd, LoginResult *result) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox || NULL == result || loginbox->loginResult != result) + return; + + INT authError = AUTH_ABORT; + LoginProvider *provider = LoginBox_GetActiveProviderInternal(hwnd); + if (NULL != provider) + { + LoginCommand *command; + if (SUCCEEDED(provider->GetCommand(&command))) + { + LoginCredentials *credentials; + if (SUCCEEDED(command->EndLogin(result, &authError, &credentials))) + { + switch(authError) + { + case AUTH_SUCCESS: + authError = LoginBox_SetCredentials(hwnd, credentials); + LoginBox_SaveUsername(&loginbox->realm, provider, credentials); + break; + + case AUTH_SECURID: + authError = LoginBox_RequestPasscode(hwnd, result); + if (AUTH_SUCCESS == authError) + authError = AUTH_SECURID; + break; + } + + if (NULL != credentials) + credentials->Release(); + } + command->Release(); + } + } + + + loginbox->loginResult = NULL; + result->Release(); + + switch(authError) + { + case AUTH_SUCCESS: + LoginBox_EndDialog(hwnd, AUTH_SUCCESS); + break; + case AUTH_SECURID: + break; + default: + LoginBox_EnableLoginMode(hwnd, FALSE, FALSE); + if (AUTH_ABORT != authError) + { + HWND hNotifier = GetDlgItem(hwnd, IDC_NOTIFIER); + if (NULL != hNotifier) + { + LoginNotifier_Notify(hNotifier, NLNTYPE_ERROR, MAKEINTRESOURCE(authError)); + LoginBox_ShowNotifier(hwnd, 200); + } + } + + LoginBox_PerformProviderOp(hwnd); + break; + } + +} + +static BOOL LoginBox_OnGetStatus(HWND hwnd, LoginStatus **loginStatus) +{ + if (NULL == loginStatus) + return FALSE; + + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL != loginbox && NULL !=loginbox->loginStatus) + { + *loginStatus = loginbox->loginStatus; + (*loginStatus)->AddRef(); + return TRUE; + } + + return FALSE; +} +static UINT LoginBox_OnAddStatus(HWND hwnd, BSTR pszStatus) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL != loginbox && NULL !=loginbox->loginStatus) + return loginbox->loginStatus->Add(pszStatus); + + return 0; +} + +static BOOL LoginBox_OnSetStatus(HWND hwnd, UINT cookie, BSTR pszStatus) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL != loginbox && NULL !=loginbox->loginStatus) + return loginbox->loginStatus->Set(cookie, pszStatus); + + return TRUE; +} + +static void LoginBox_OnRemoveStatus(HWND hwnd, UINT cookie) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL != loginbox && NULL !=loginbox->loginStatus) + loginbox->loginStatus->Remove(cookie); +} + +static BOOL LoginBox_ApplyProvidersUpdate(HWND hwnd, LoginProviderEnumerator *providerEnum) +{ + if (NULL == providerEnum) + return FALSE; + + GUID activeId(GUID_NULL); + INT activeIndex(-1); + LoginProviderOperation *providerOp(NULL); + LoginProvider *providerActive = LoginBox_GetActiveProviderInternal(hwnd); + if (NULL != providerActive) + { + providerActive->GetId(&activeId); + + if (S_OK != LoginProviderOperation::CreateFromUpdate(providerActive, providerEnum, &providerOp)) + providerOp = NULL; + } + + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL != hFrame) + { + UINT frameStyle = GetWindowStyle(hFrame); + if (0 != (WS_VISIBLE & frameStyle)) + SetWindowLongPtr(hFrame, GWL_STYLE, frameStyle & ~WS_VISIBLE); + + LoginBox_DeleteAllTabs(hwnd); + providerEnum->Reset(); + + if(NULL != providerOp) + LoginBox_AppendMultipleTabs(hwnd, providerEnum, &activeId, &activeIndex); + else + LoginBox_AppendMultipleTabs(hwnd, providerEnum, NULL, NULL); + + LoginProvider *provider; + if (NULL != providerOp && SUCCEEDED(providerOp->GetSource(&provider))) + { + LoginBox_InsertTab(hwnd, activeIndex, provider); + provider->Release(); + } + + activeIndex = (FALSE == IsEqualGUID(activeId, GUID_NULL)) ? + LoginBox_FindTabByProviderId(hwnd, &activeId) : -1; + + if (-1 != activeIndex) + { + LoginTab_SetCurSel(hFrame, activeIndex); + } + else + { + LoginTab_SetCurSel(hFrame, 0); + LoginBox_SelectActivePage(hwnd); + } + + if (0 != (WS_VISIBLE & frameStyle)) + { + frameStyle = GetWindowStyle(hFrame); + if (0 == (WS_VISIBLE & frameStyle)) + SetWindowLongPtr(hFrame, GWL_STYLE, frameStyle | WS_VISIBLE); + + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN); + UpdateWindow(hFrame); + } + } + + if (NULL != providerOp) + { + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL != loginbox) + { + if (NULL != loginbox->providerOp) + loginbox->providerOp->Release(); + loginbox->providerOp = providerOp; + providerOp->AddRef(); + + } + providerOp->Release(); + } + LoginBox_PerformProviderOp(hwnd); + + return TRUE; +} + +static void LoginBox_OnProvidersUpdated(HWND hwnd, PROVIDERUPDATERESULT *result) +{ + if (NULL == result) + return; + + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox) return; + + + BOOL dataIdentical; + LoginProviderEnumerator *providerEnum; + HRESULT resultCode = result->errorCode; + + if (SUCCEEDED(resultCode)) + { + providerEnum = result->enumerator; + if(NULL != providerEnum) providerEnum->AddRef(); + + dataIdentical = result->dataIdentical; + } + else + { + providerEnum = NULL; + dataIdentical = FALSE; + } + + BOOL storedUpdate = (NULL != loginbox->providerUpdate && + loginbox->providerUpdate == result->downloader); + + ReplyMessage(0); + + if (SUCCEEDED(resultCode)) + { + BOOL updateOk; + + if(FALSE == dataIdentical) + updateOk = LoginBox_ApplyProvidersUpdate(hwnd, providerEnum); + else + updateOk = TRUE; + + if(FALSE != updateOk) + { + LoginConfig *config; + if (SUCCEEDED(LoginConfig::CreateInstance(&config))) + { + UINT ts = LoginBox_GetCurrentTime(); + config->WriteInt("LoginBox", "lastCheck", ts); + + LPSTR lang; + if (FAILED(LoginBox_GetCurrentLang(&lang))) + lang = NULL; + + config->WriteAnsiStr("LoginBox", "lastLang", lang); + LoginBox_FreeAnsiString(lang); + + config->Release(); + } + } + + } + + if (NULL != providerEnum) + providerEnum->Release(); + + if (FALSE != storedUpdate) + { + loginbox->providerUpdate->Release(); + loginbox->providerUpdate = NULL; + + HWND hPage = GetDlgItem(hwnd, IDC_ACTIVEPAGE); + if (NULL != hPage) + LoginPage_UpdateStateChange(hPage, FALSE); + } +} + +static void CALLBACK LoginBox_DownloadCompleted(LoginDownloadResult *result, void *data) +{ + HWND hLoginbox = (HWND)data; + if(NULL == result || FALSE == IsWindow(hLoginbox)) return; + + UINT type; + if (FAILED(result->GetType(&type)) || LoginDownloadResult::typeProviderList != type) + return; + + BSTR fileName = NULL; + LoginDownload download; + HRESULT hr = download.End(result, &fileName); + + LoginProviderEnumerator *enumerator = NULL; + + if (SUCCEEDED(hr)) + { + if (S_FALSE != hr) + { + LoginProviderLoader loader; + hr = loader.ReadXml(fileName, &enumerator, NULL); + if (FAILED(hr)) + enumerator = NULL; + } + } + + LoginBox_ProvidersUpdated(hLoginbox, result, hr, (S_FALSE == hr), enumerator); + + if (NULL != enumerator) + enumerator->Release(); + + SysFreeString(fileName); + +} + +static BOOL LoginBox_OnUpdateProviders(HWND hwnd, BOOL forceUpdate) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + if (NULL == loginbox || NULL != loginbox->providerUpdate) + return FALSE; + + if (FALSE == forceUpdate) + { + LoginConfig *config; + if (SUCCEEDED(LoginConfig::CreateInstance(&config))) + { + BOOL checkRequired = TRUE; + UINT ts = LoginBox_GetCurrentTime(); + UINT lastCheck = config->ReadInt("LoginBox", "lastCheck", 0); + + if ((ts - lastCheck) < (60*60*24)) // check once 24 hours + { + checkRequired = FALSE; + + LPSTR lang; + if (SUCCEEDED(LoginBox_GetCurrentLang(&lang))) + { + CHAR lastLang[32] = {0}; + config->ReadAnsiStr("LoginBox", "lastLang", NULL, lastLang, ARRAYSIZE(lastLang)); + checkRequired = (NULL == lang || NULL == lastLang) ? + ((NULL == lang) != (NULL == lastLang)) : + (CSTR_EQUAL != CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, lang, -1, lastLang, -1)); + + LoginBox_FreeAnsiString(lang); + } + } + + config->Release(); + + if (FALSE == checkRequired) + return FALSE; + } + } + + WCHAR szUrl[4096] = {0}; + LPWSTR cursor = szUrl; + size_t remaining = ARRAYSIZE(szUrl); + + HRESULT hr; + hr = StringCchCopyEx(cursor, remaining, PROVIDERLIST_URL, &cursor, &remaining, 0); + if (SUCCEEDED(hr)) + { + if (NULL != WASABI_API_LNG) + { + LPCWSTR lang = WASABI_API_LNG->GetLanguageIdentifier(LANG_LANG_CODE); + if (NULL != lang && L'\0' != *lang) + { + hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, 0, L"?lang=%s", lang); + } + } + } + + if (SUCCEEDED(hr)) + { + HWND hPage = GetDlgItem(hwnd, IDC_ACTIVEPAGE); + if (NULL != hPage) + LoginPage_UpdateStateChange(hPage, TRUE); + + LoginDownload download; + hr = download.Begin(szUrl, LoginDownloadResult::typeProviderList, LoginBox_DownloadCompleted, + hwnd, loginbox->loginStatus, &loginbox->providerUpdate); + if (FAILED(hr)) + { + loginbox->providerUpdate = NULL; + if (NULL != hPage) + LoginPage_UpdateStateChange(hPage, FALSE); + } + } + + return SUCCEEDED(hr); +} + +static BOOL LoginBox_OnGetUpdateState(HWND hwnd) +{ + LOGINBOX *loginbox = GetLoginBox(hwnd); + return (NULL != loginbox && NULL != loginbox->providerUpdate); +} + +static void LoginBox_OnImageCached(HWND hwnd, IMAGECACHERESULT *result) +{ + LoginImageCache *cache = NULL; + ifc_omcacherecord *record = NULL; + if (NULL != result) + { + cache = result->imageCache; + record = result->cacheRecord; + } + + if (NULL != cache) cache->AddRef(); + if (NULL != record) record->AddRef(); + + ReplyMessage(0); + + if (NULL != cache && NULL != record) + { + WCHAR szName[4096] = {0}; + if (SUCCEEDED(record->GetName(szName, ARRAYSIZE(szName)))) + { + HWND hFrame = GetDlgItem(hwnd, IDC_TABFRAME); + if (NULL != hFrame) + { + NLTITEM tab; + tab.mask = NLTIF_PARAM; + INT count = LoginTab_GetItemCount(hFrame); + for (INT i = 0; i < count; i++) + { + if (FALSE != LoginTab_GetItem(hFrame, i, &tab)) + { + WCHAR szImage[4096] = {0}; + LoginProvider *provider = (LoginProvider*)tab.param; + if (NULL != provider && + SUCCEEDED(provider->GetImagePath(szImage, ARRAYSIZE(szImage))) && + S_OK == LoginBox_IsStrEqInvI(szImage, szName)) + { + + + LoginBox_RemoveImageHelper2(hFrame, &tab); + + tab.mask = NLTIF_IMAGE_MASK; + tab.iImage = NLTM_IMAGE_CALLBACK; + tab.iImageActive = NLTM_IMAGE_CALLBACK; + tab.iImageDisabled = NLTM_IMAGE_CALLBACK; + LoginTab_SetItem(hFrame, i, &tab); + tab.mask = NLTIF_PARAM; + } + } + } + } + } + + } + + if (NULL != cache) cache->Release(); + if (NULL != record) record->Release(); +} + +static INT_PTR CALLBACK LoginBox_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: return LoginBox_OnInitDialog(hwnd, (HWND)wParam, lParam); + case WM_DESTROY: LoginBox_OnDestroy(hwnd); return 0; + case WM_WINDOWPOSCHANGED: LoginBox_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return TRUE; + case WM_COMMAND: LoginBox_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); return TRUE; + case WM_NOTIFY: MSGRESULT(hwnd, LoginBox_OnNotify(hwnd, (INT)wParam, (NMHDR*)lParam)); + case WM_GETMINMAXINFO: LoginBox_OnGetMinMaxInfo(hwnd, (MINMAXINFO*)lParam); return TRUE; + case WM_ERASEBKGND: MSGRESULT(hwnd, 0); + case WM_PAINT: LoginBox_OnPaint(hwnd); return TRUE; + case WM_PRINTCLIENT: LoginBox_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return TRUE; + case WM_CTLCOLORSTATIC: return (INT_PTR)LoginBox_OnGetStaticColor(hwnd, (HDC)wParam, (HWND)lParam); + case WM_NCHITTEST: return LoginBox_OnNcHitTest(hwnd, MAKEPOINTS(lParam)); + case WM_SETFONT: LoginBox_OnSetFont(hwnd, (HFONT)wParam, LOWORD(lParam)); return TRUE; + case WM_SIZE: MSGRESULT(hwnd, 0); + + case NLBM_GETAUTHAPI: MSGRESULT(hwnd, LoginBox_OnGetAuthApi(hwnd, (api_auth**)lParam)); + case NLBM_GETREALM: MSGRESULT(hwnd, LoginBox_OnGetRealm(hwnd, (GUID*)lParam)); + case NLBM_GETACTIVEPROVIDER:MSGRESULT(hwnd, LoginBox_OnGetActiveProvider(hwnd, (LoginProvider**)lParam)); + case NLBM_GETSTATUS: MSGRESULT(hwnd, LoginBox_OnGetStatus(hwnd, (LoginStatus**)lParam)); + case NLBM_ADDSTATUS: MSGRESULT(hwnd, LoginBox_OnAddStatus(hwnd, (BSTR)lParam)); + case NLBM_SETSTATUS: MSGRESULT(hwnd, LoginBox_OnSetStatus(hwnd, (UINT)wParam, (BSTR)lParam)); + case NLBM_REMOVESTATUS: LoginBox_OnRemoveStatus(hwnd, (UINT)wParam); + case NLBM_UPDATEPROVIDERS: MSGRESULT(hwnd, LoginBox_OnUpdateProviders(hwnd, (BOOL)wParam)); + case NLBM_GETUPDATESTATE: MSGRESULT(hwnd, LoginBox_OnGetUpdateState(hwnd)); + case NLBM_LOGINCOMPLETED: LoginBox_OnLoginCompleted(hwnd, (LoginResult*)lParam); return TRUE; + case NLBM_PROVIDERSUPDATED: LoginBox_OnProvidersUpdated(hwnd, (PROVIDERUPDATERESULT*)lParam); return TRUE; + case NLBM_IMAGECACHED: LoginBox_OnImageCached(hwnd, (IMAGECACHERESULT*)lParam); return TRUE; + } + + return FALSE; +} diff --git a/Src/auth/Loginbox/loginbox.h b/Src/auth/Loginbox/loginbox.h new file mode 100644 index 00000000..9729eace --- /dev/null +++ b/Src/auth/Loginbox/loginbox.h @@ -0,0 +1,90 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_HEADER +#define NULLSOFT_AUTH_LOGINBOX_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +class api_auth; +class LoginDownloadResult; +class LoginProviderEnumerator; +class ifc_omcacherecord; +class LoginImageCache; + +#define LBS_NOCENTEROWNER 0x00000001 + +HWND LoginBox_CreateWindow(api_auth *auth, const GUID *pRealm, HWND hOwner, UINT fStyle); +INT_PTR LoginBox_Show(api_auth *auth, const GUID *pRealm, HWND hOwner, UINT fStyle); +HWND LoginBox_FindActive(const GUID *pRealm); + + +// messages +#define NLBM_FIRST (WM_APP + 10) + +#define NLBM_GETAUTHAPI (NLBM_FIRST + 1) // wParam - not used, lParam = (LPARAM)(api_auth**)__authApi; Returns TRUE on success +#define LoginBox_GetAuthApi(/*HNWD*/ __hwnd, /*api_auth** */ __authApi)\ + ((BOOL)SendMessage((__hwnd), NLBM_GETAUTHAPI, 0, (LPARAM)(__authApi))) + +#define NLBM_GETREALM (NLBM_FIRST + 2) // wParam - not used, lParam = (LPARAM)(GUID*)__pGuidRealm; Returns TRUE on success +#define LoginBox_GetRealm(/*HNWD*/ __hwnd, /*GUID* */ __pGuidRealm)\ + ((BOOL)SendMessage((__hwnd), NLBM_GETREALM, 0, (LPARAM)(__pGuidRealm))) + +#define NLBM_GETACTIVEPROVIDER (NLBM_FIRST + 3) // wParam - not used, lParam = (LPARAM)(LoginProvider**)__ppProvider; Returns TRUE on success +#define LoginBox_GetActiveProvider(/*HNWD*/ __hwnd, /*LoginProvider** */ __ppProvider)\ + ((BOOL)SendMessage((__hwnd), NLBM_GETACTIVEPROVIDER, 0, (LPARAM)(__ppProvider))) + +#define NLBM_GETSTATUS (NLBM_FIRST + 4) // wParam - not used, lParam = (LPARAM)(LoginStatus**)ppStatus; Returns TRUE on success +#define LoginBox_GetStatus(/*HNWD*/ __hwnd, /*LoginStatus** */ __ppLoginStatus)\ + ((BOOL)SendMessage((__hwnd), NLBM_GETSTATUS, 0, (LPARAM)(__ppLoginStatus))) + +#define NLBM_ADDSTATUS (NLBM_FIRST + 5) // wParam - not used, lParam = (LPARAM)(BSTR)bstrStatusText; Return - (UINT)statusCookie or -1 +#define LoginBox_AddStatus(/*HNWD*/ __hwnd, /*BSTR*/ __bstrStatusText)\ + ((UINT)SendMessage((__hwnd), NLBM_ADDSTATUS, 0, (LPARAM)(__bstrStatusText))) + +#define NLBM_SETSTATUS (NLBM_FIRST + 6) // wParam = (WPARAM)(UINT)statusCookie, lParam = (LPARAM)(BSTR)bstrStatusText; Return TRUE on success. +#define LoginBox_SetStatus(/*HNWD*/ __hwnd, /*UINT*/__statusCookie, /*BSTR*/ __bstrStatusText)\ + ((BOOL)SendMessage((__hwnd), NLBM_SETSTATUS, (WPARAM)(__statusCookie), (LPARAM)(__bstrStatusText))) + +#define NLBM_REMOVESTATUS (NLBM_FIRST + 7) // wParam = (WPARAM)(UINT)statusCookie, lParam - not used. Return ingored. +#define LoginBox_RemoveStatus(/*HNWD*/ __hwnd, /*UINT*/__statusCookie)\ + (SendMessage((__hwnd), NLBM_REMOVESTATUS, (WPARAM)(__statusCookie), 0L)) + +#define NLBM_UPDATEPROVIDERS (NLBM_FIRST + 8) // wParam - (WPARAM)(BOOL)fForce, lParam - not used. +#define LoginBox_UpdateProviders(/*HNWD*/ __hwnd, /*BOOL*/ __forceUpdate)\ + ((BOOL)SendMessage((__hwnd), NLBM_UPDATEPROVIDERS, (WPARAM)(__forceUpdate), 0L)) + +#define NLBM_GETUPDATESTATE (NLBM_FIRST + 9) // wParam - not used, lParam - not used. Return TRUE if update active. +#define LoginBox_GetUpdateState(/*HNWD*/ __hwnd)\ + ((BOOL)SendMessage((__hwnd), NLBM_GETUPDATESTATE, 0, 0L)) + +#define NLBM_LOGINCOMPLETED (NLBM_FIRST + 20) // wParam - not used, lParam = (LPARAM)(LoginResult*)__pResult; + +typedef struct __PROVIDERUPDATERESULT +{ + LoginDownloadResult *downloader; + HRESULT errorCode; + BOOL dataIdentical; + LoginProviderEnumerator *enumerator; +} PROVIDERUPDATERESULT; + +#define NLBM_PROVIDERSUPDATED (NLBM_FIRST + 21) // wParam - not used, lParam = (LPARAM)(PROVIDERUPDATERESULT*)updateResult; +#define LoginBox_ProvidersUpdated(/*HNWD*/ __hwnd, /*LoginDownloadResult*/ __downloader, /*HRESULT*/ __errorCode, /*BOOL*/__dataIdentical, /*LoginProviderEnumerator**/ __enumerator)\ + {PROVIDERUPDATERESULT __updateResult;\ + __updateResult.downloader = (__downloader);\ + __updateResult.errorCode = (__errorCode);\ + __updateResult.dataIdentical = (__dataIdentical);\ + __updateResult.enumerator = (__enumerator);\ + (SendMessage((__hwnd), NLBM_PROVIDERSUPDATED, 0, (LPARAM)&(__updateResult)));} + + +typedef struct __IMAGECACHERESULT +{ + LoginImageCache *imageCache; + ifc_omcacherecord *cacheRecord; +} IMAGECACHERESULT; + +#define NLBM_IMAGECACHED (NLBM_FIRST + 22) // wParam - not used, lParam = (LPARAM)(IMAGECACHERESULT*)cacheResult; + + +#endif //NULLSOFT_AUTH_LOGINBOX_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginboxTosReminder.cpp b/Src/auth/Loginbox/loginboxTosReminder.cpp new file mode 100644 index 00000000..0d13f0d2 --- /dev/null +++ b/Src/auth/Loginbox/loginboxTosReminder.cpp @@ -0,0 +1,323 @@ +#include "./loginbox.h" +#include "./loginpage.h" +#include "../resource.h" + +#include "../api.h" + +#include <windows.h> + +typedef struct __TOSREMINDERCREATEPARAM +{ + HANDLE hModal; + BOOL fAnimate; + INT_PTR *pResult; +} TOSREMINDERCREATEPARAM; + +typedef struct __TOSREMINDER +{ + HANDLE hModal; + BOOL fAnimate; + SIZE idealSize; + INT_PTR *pResult; +} TOSREMINDER; + + + +#define TOSREMINDER_PROP L"LoginboxTosReminderProp" +#define GetTosReminder(__hwnd) ((TOSREMINDER*)GetProp(__hwnd, TOSREMINDER_PROP)) + +static INT_PTR CALLBACK TosReminder_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static INT_PTR TosReminder_EnterModalLoop(HANDLE hExit); + +HWND TosReminder_CreateWindow(HWND hParent) +{ + if (NULL == hParent) + return NULL; + + return WASABI_API_CREATEDIALOGPARAMW(IDD_TOSREMINDER, hParent, TosReminder_DialogProc, 0L); +} + +INT_PTR TosReminder_Show(HWND hParent, INT controlId, BOOL fAnimate) +{ + if (NULL == hParent) + return -1; + + INT_PTR result; + TOSREMINDERCREATEPARAM param; + param.fAnimate = fAnimate; + param.pResult = &result; + param.hModal = CreateEvent(NULL, FALSE, FALSE, NULL); + if (NULL == param.hModal) return -1; + + HWND hwnd = WASABI_API_CREATEDIALOGPARAMW(IDD_TOSREMINDER, + hParent, TosReminder_DialogProc, (LPARAM)¶m); + + if (NULL == hwnd) return -1; + + SetWindowLongPtr(hwnd, GWLP_ID, controlId); + SetWindowPos(hParent, NULL, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED); + + if (FALSE == fAnimate || + 0 == AnimateWindow(hwnd, 150, AW_ACTIVATE | AW_VER_POSITIVE | AW_SLIDE)) + { + ShowWindow(hwnd, SW_SHOWNORMAL); + } + + TosReminder_EnterModalLoop(param.hModal); + return result; +} + +static void TosReminder_EndDialog(HWND hwnd, INT_PTR code) +{ + TOSREMINDER *reminder = GetTosReminder(hwnd); + if (NULL != reminder) + { + if (NULL != reminder->pResult) + { + (*reminder->pResult) = code; + reminder->pResult = NULL; + } + + if (NULL != reminder->hModal) + { + SetEvent(reminder->hModal); + reminder->hModal = NULL; + } + + if (FALSE == reminder->fAnimate || + 0 == AnimateWindow(hwnd, 150, AW_HIDE | AW_VER_NEGATIVE | AW_SLIDE)) + { + ShowWindow(hwnd, SW_HIDE); + } + } + + DestroyWindow(hwnd); +} + +static void TosReminder_UpdateLayout(HWND hwnd, BOOL fRedraw) +{ + RECT clientRect; + if (FALSE == GetClientRect(hwnd, &clientRect)) return; + + LONG centerX = clientRect.left + (clientRect.right - clientRect.left)/2; + LONG buttonTop = clientRect.bottom; + const INT szControls[] = {IDOK, IDCANCEL, IDC_TEXT, }; + + RECT controlRect; + + UINT sharedFlags = SWP_NOZORDER | SWP_NOACTIVATE; + if (FALSE == fRedraw) sharedFlags |= SWP_NOREDRAW; + + HDWP hdwp = BeginDeferWindowPos(ARRAYSIZE(szControls)); + if (NULL == hdwp) return; + + UINT controlFlags; + LONG x, y, cx, cy; + + for (INT i = 0; i < ARRAYSIZE(szControls); i++) + { + HWND hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL == hControl || FALSE == GetWindowRect(hControl, &controlRect)) + continue; + + controlFlags = sharedFlags; + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&controlRect, 2); + x = controlRect.left; + y = controlRect.top; + cx = controlRect.right - controlRect.left; + cy = controlRect.bottom - controlRect.top; + + switch(szControls[i]) + { + case IDOK: + x = centerX - cx - 8; + if (x < clientRect.left) x = clientRect.left; + y = (clientRect.bottom - 8) - cy; + if (y < clientRect.top) y = clientRect.top; + if (y < buttonTop) buttonTop = y; + break; + + case IDCANCEL: + x = centerX + 8; + if ((x + cx) > clientRect.right) x = clientRect.right - cx; + y = (clientRect.bottom - 8) - cy; + if (y < clientRect.top) y = clientRect.top; + if (y < buttonTop) buttonTop = y; + break; + + case IDC_TEXT: + cx = (clientRect.right - clientRect.left) - 2*x; + cy = (buttonTop - y) - 16; + break; + } + + hdwp = DeferWindowPos(hdwp, hControl, NULL, x, y, cx, cy, controlFlags); + if (NULL == hdwp) return; + } + + EndDeferWindowPos(hdwp); + + if (FALSE != fRedraw) + { + HWND hControl = GetDlgItem(hwnd, IDC_TEXT); + if (NULL != hControl) InvalidateRect(hControl, NULL, FALSE); + } +} + +static INT_PTR TosReminder_OnInitDialog(HWND hwnd, HWND hFocus, LPARAM param) +{ + TOSREMINDER *reminder = (TOSREMINDER*)malloc(sizeof(TOSREMINDER)); + if (NULL == reminder) + { + DestroyWindow(hwnd); + return 0; + } + + ZeroMemory(reminder, sizeof(TOSREMINDER)); + SetProp(hwnd, TOSREMINDER_PROP, reminder); + + TOSREMINDERCREATEPARAM *createParam = (TOSREMINDERCREATEPARAM*)param; + if (NULL != createParam) + { + reminder->fAnimate = createParam->fAnimate; + reminder->hModal = createParam->hModal; + reminder->pResult = createParam->pResult; + } + + RECT windowRect; + if (FALSE != GetWindowRect(hwnd, &windowRect)) + { + reminder->idealSize.cx = windowRect.right - windowRect.left; + reminder->idealSize.cy = windowRect.bottom - windowRect.top; + } + + PostMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0L); + return 0; +} + +static void TosReminder_OnDestroy(HWND hwnd) +{ + TOSREMINDER *reminder = GetTosReminder(hwnd); + RemoveProp(hwnd, TOSREMINDER_PROP); + if (NULL == reminder) + return; + + if (NULL != reminder->pResult) + { + (*reminder->pResult) = -1; + } + + if (NULL != reminder->hModal) + { + SetEvent(reminder->hModal); + reminder->hModal = NULL; + } + + free(reminder); +} + + +static void TosReminder_OnWindowPosChanged(HWND hwnd, WINDOWPOS *pwp) +{ + if (SWP_NOSIZE != ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags)) + { + TosReminder_UpdateLayout(hwnd, 0 == (SWP_NOREDRAW & pwp->flags)); + } +} + +static void TosReminder_OnCommand(HWND hwnd, INT commandId, INT eventId, HWND hControl) +{ + + switch(commandId) + { + case IDOK: + TosReminder_EndDialog(hwnd, commandId); + break; + + case IDCANCEL: + TosReminder_EndDialog(hwnd, commandId); + break; + } + +} + +static BOOL TosReminder_OnGetIdealSize(HWND hwnd, SIZE *pSize) +{ + TOSREMINDER *reminder = GetTosReminder(hwnd); + if (NULL == reminder || NULL == pSize) return FALSE; + + CopyMemory(pSize, &reminder->idealSize, sizeof(SIZE)); + return TRUE; + +} + +static BOOL TosReminder_OnClose(HWND hwnd, BOOL *pfAbort) +{ + TOSREMINDER *reminder = GetTosReminder(hwnd); + if (NULL != reminder) reminder->fAnimate = FALSE; + + TosReminder_EndDialog(hwnd, IDCANCEL); + return TRUE; +} + +static INT_PTR CALLBACK TosReminder_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: return TosReminder_OnInitDialog(hwnd, (HWND)wParam, lParam); + case WM_DESTROY: TosReminder_OnDestroy(hwnd); return 0; + case WM_WINDOWPOSCHANGED: TosReminder_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return TRUE; + case WM_COMMAND: TosReminder_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); return TRUE; + + case NLPM_GETIDEALSIZE: MSGRESULT(hwnd, TosReminder_OnGetIdealSize(hwnd, (SIZE*)lParam)); + case NLPM_CLOSE: MSGRESULT(hwnd, TosReminder_OnClose(hwnd, (BOOL*)lParam)); + } + + return 0; +} + +static BOOL TosReminder_PumpMessages() +{ + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + if (WM_QUIT == msg.message) + { + PostQuitMessage((int)msg.wParam); + return TRUE; + } + + if (!CallMsgFilter(&msg, MSGF_DIALOGBOX)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + return FALSE; +} + +static INT_PTR TosReminder_EnterModalLoop(HANDLE hExit) +{ + if (NULL == hExit) + return FALSE; + + for(;;) + { + DWORD code = MsgWaitForMultipleObjectsEx(1, &hExit, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); + switch(code) + { + case WAIT_FAILED: + return FALSE; + + case WAIT_OBJECT_0: + return TRUE; + + case (WAIT_OBJECT_0 + 1): + if (FALSE != TosReminder_PumpMessages()) + return TRUE; + break; + } + } + return TRUE; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginboxTosReminder.h b/Src/auth/Loginbox/loginboxTosReminder.h new file mode 100644 index 00000000..c7ad7375 --- /dev/null +++ b/Src/auth/Loginbox/loginboxTosReminder.h @@ -0,0 +1,13 @@ +#ifndef NULLSOFT_AUTH_LOGINBOX_TOS_REMINDER_HEADER +#define NULLSOFT_AUTH_LOGINBOX_TOS_REMINDER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +HWND TosReminder_CreateWindow(HWND hParent); +INT_PTR TosReminder_Show(HWND hParent, INT controlId, BOOL fAnimate); + +#endif //NULLSOFT_AUTH_LOGINBOX_TOS_REMINDER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/loginpage.h b/Src/auth/Loginbox/loginpage.h new file mode 100644 index 00000000..e477965b --- /dev/null +++ b/Src/auth/Loginbox/loginpage.h @@ -0,0 +1,101 @@ +#ifndef NULLSOFT_AUTH_LOGINPAGE_HEADER +#define NULLSOFT_AUTH_LOGINPAGE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#define NLPM_FIRST (WM_APP + 10) +#define NLPM_PAGEFIRST (NLPM_FIRST + 40) + +#define NLPM_GETLOGINDATA (NLPM_FIRST + 0) // wParam - not used, lParam = (LPARAM)(LoginData**)__ppLoginData; Return: TRUE on success. +#define LoginPage_GetData(/*HWND*/ __hwnd, /*LoginData** */ __ppLoginData)\ + ((BOOL)(SNDMSG((__hwnd), NLPM_GETLOGINDATA, 0, (LPARAM)(__ppLoginData)))) + +#define NLPM_UPDATESTATECHANGE (NLPM_FIRST + 1) // wParam - not used, lParam = (BOOL)__updateActive; Return - ignored +#define LoginPage_UpdateStateChange(/*HWND*/ __hwnd, /*BOOL*/ __updateActive)\ + ((BOOL)(SNDMSG((__hwnd), NLPM_UPDATESTATECHANGE, 0, (LPARAM)(__updateActive)))) + +#define NLPM_SETUSERNAME (NLPM_FIRST + 2) // wParam - not used, lParam = (LPARAM)(LPCWSTR)__pszUsername; Return TRUE on success +#define LoginPage_SetUsername(/*HWND*/ __hwnd, /*LPCWSTR*/ __pszUsername)\ + ((BOOL)(SNDMSG((__hwnd), NLPM_SETUSERNAME, 0, (LPARAM)(__pszUsername)))) + +#define NLPM_SETPASSWORD (NLPM_FIRST + 3) // wParam - not used, lParam = (LPARAM)(LPCWSTR)__pszPassword; Return TRUE on success +#define LoginPage_SetPassword(/*HWND*/ __hwnd, /*LPCWSTR*/ __pszPassword)\ + ((BOOL)(SNDMSG((__hwnd), NLPM_SETPASSWORD, 0, (LPARAM)(__pszPassword)))) + +#define NLPM_GETFIRSTITEM (NLPM_FIRST + 4) // wParam - not used, lParam - not used; Return HWND where you want focus to be on page create or NULL. +#define LoginPage_GetFirstItem(/*HWND*/ __hwnd)\ + ((HWND)(SNDMSG((__hwnd), NLPM_GETFIRSTITEM, 0, 0L))) + +#define NLPM_SETTITLE (NLPM_FIRST + 5) // wParam - not used, lParam = (LPARAM)(LPCWSTR)__pszTitle; Return TRUE on success +#define LoginPage_SetTitle(/*HWND*/ __hwnd, /*LPCWSTR*/ __pszTitle)\ + ((BOOL)(SNDMSG((__hwnd), NLPM_SETTITLE, 0, (LPARAM)(__pszTitle)))) + +class LoginPage; +class LoginData; + +typedef HRESULT (CALLBACK *LOGINPAGECREATOR)(HWND /*hwnd*/, HWND /*hLoginbox*/, LoginPage** /*instance*/); + +class __declspec(novtable) LoginPage +{ + +protected: + LoginPage(HWND hwnd, HWND hLoginbox); + virtual ~LoginPage(); + +public: + static HWND CreatePage(HWND hLoginbox, LPCWSTR pszTemplate, HWND hParent, LPARAM param, LOGINPAGECREATOR fnCreator); + +protected: + virtual void UpdateMargins(); + virtual void UpdateColors(); + virtual void UpdateLayout(BOOL fRedraw); + + virtual BOOL GetPageRect(RECT *prc); + BOOL ShowHelp(); + BOOL IsHelpAvailable(); + INT GetTitleSpacing(); + BOOL SetLabelText(INT controlId, LPCWSTR pszText); + + +protected: + virtual BOOL OnInitDialog(HWND hFocus, LPARAM param); + virtual void OnDestroy(); + virtual void OnWindowPosChanged(const WINDOWPOS *pwp); + virtual void OnCommand(UINT commandId, UINT eventType, HWND hControl); + virtual BOOL OnNotify(UINT controlId, const NMHDR *pnmh); + virtual BOOL OnSetCursor(HWND hTarget, INT hitCode, INT uMsg); + virtual HBRUSH OnGetStaticColor(HDC hdc, HWND hControl); + virtual HBRUSH OnGetDialogColor(HDC hdc, HWND hControl); + virtual BOOL OnHelp(HELPINFO *phi); + virtual void OnThemeChanged(); + virtual void OnSysColorChanged(); + + virtual BOOL OnGetLoginData(LoginData **ppLoginData); + virtual void OnUpdateStateChange(BOOL updateActive); + virtual BOOL OnSetUsername(LPCWSTR pszUsername); + virtual BOOL OnSetPassword(LPCWSTR pszPassword); + virtual HWND OnGetFirstItem(); + virtual BOOL OnSetTitle(LPCWSTR pszTitle); + + virtual INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + +private: + friend static INT_PTR CALLBACK LoginPage_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +protected: + HWND hwnd; + HWND hLoginbox; + RECT margins; + COLORREF rgbTitle; + COLORREF rgbSecondaryText; + COLORREF rgbText; + COLORREF rgbBack; + HBRUSH hbrBack; + +}; + +#endif //NULLSOFT_AUTH_LOGINPAGE_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/pageAddress.cpp b/Src/auth/Loginbox/pageAddress.cpp new file mode 100644 index 00000000..6aaf9b79 --- /dev/null +++ b/Src/auth/Loginbox/pageAddress.cpp @@ -0,0 +1,342 @@ +#include "./pageAddress.h" +#include "./dataAddress.h" +#include "./common.h" +#include "./addressEncoder.h" +#include "./addressEditbox.h" +#include "./loginGui.h" + +#include "../resource.h" + +#include <shlwapi.h> +#include <wininet.h> + + + +static HRESULT CALLBACK LoginPageAddress_CreateInstance(HWND hwnd, HWND hLoginbox, LoginPage **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hwnd || NULL == hLoginbox) return E_INVALIDARG; + + *instance = new LoginPageAddress(hwnd, hLoginbox); + if (NULL == instance) return E_OUTOFMEMORY; + + return S_OK; +} + +LoginPageAddress::LoginPageAddress(HWND hwnd, HWND hLoginbox) + : LoginPage(hwnd, hLoginbox) +{ + HWND hEdit = GetDlgItem(hwnd, IDC_ADDRESS_EDIT); + if (NULL != hEdit) + AddressEditbox_AttachWindow(hEdit); + +} + +LoginPageAddress::~LoginPageAddress() +{ +} + +HWND LoginPageAddress::CreatePage(HWND hLoginbox, HWND hParent) +{ + return LoginPage::CreatePage(hLoginbox, MAKEINTRESOURCE(IDD_PAGE_ADDRESS), + hParent, NULL, LoginPageAddress_CreateInstance); +} + +BOOL LoginPageAddress::OnInitDialog(HWND hFocus, LPARAM param) +{ + HFONT fontEdit = NULL, fontLabel = NULL; + LoginGuiObject *loginGui; + if (SUCCEEDED(LoginGuiObject::QueryInstance(&loginGui))) + { + fontLabel = loginGui->GetTextFont(); + fontEdit = loginGui->GetEditorFont(); + loginGui->Release(); + } + + if (NULL != fontLabel) + { + HWND hLabel = GetDlgItem(hwnd, IDC_ADDRESS_LABEL); + if (NULL != hLabel) + SendMessage(hLabel, WM_SETFONT, (WPARAM)fontLabel, 0L); + + hLabel = GetDlgItem(hwnd, IDC_MESSAGE); + if (NULL != hLabel) + SendMessage(hLabel, WM_SETFONT, (WPARAM)fontLabel, 0L); + } + + if (NULL != fontEdit) + { + HWND hEdit = GetDlgItem(hwnd, IDC_ADDRESS_EDIT); + if (NULL != hEdit) + SendMessage(hEdit, WM_SETFONT, (WPARAM)fontEdit, 0L); + } + + LoginPage::OnInitDialog(hFocus, param); + return FALSE; +} + +void LoginPageAddress::OnDestroy() +{ + LoginPage::OnDestroy(); +} + +void LoginPageAddress::UpdateLayout(BOOL fRedraw) +{ + LoginPage::UpdateLayout(fRedraw); + + RECT pageRect; + GetPageRect(&pageRect); + + UINT flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW; + + const INT szControls[] = { IDC_ADDRESS_LABEL, IDC_ADDRESS_EDIT, IDC_MESSAGE, }; + HDWP hdwp = BeginDeferWindowPos(ARRAYSIZE(szControls)); + + INT baseunitX, baseunitY; + if (FALSE == LoginBox_GetWindowBaseUnits(hwnd, &baseunitX, &baseunitY)) + { + baseunitY = 13; + } + + HRGN invalidRegion = CreateRectRgn(0, 0, 0, 0); + HRGN tempRegion = CreateRectRgn(0, 0, 0, 0); + HWND hControl; + + INT nextTop = pageRect.top; + INT x, y, cx, cy; + + RECT rect; + + INT maxWidth = pageRect.right - pageRect.left; + hControl = GetDlgItem(hwnd, IDC_ADDRESS_EDIT); + if (NULL != hControl) + { + HDC hdc = GetDCEx(hControl, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT fontControl = (HFONT)SendMessage(hControl, WM_GETFONT, 0, 0L); + HFONT fontOrig = (HFONT)SelectObject(hdc, fontControl); + + maxWidth = LoginBox_GetAveStrWidth(hdc, 80); + if(maxWidth > (pageRect.right - pageRect.left)) + maxWidth = pageRect.right - pageRect.left; + + SelectObject(hdc, fontOrig); + ReleaseDC(hControl, hdc); + } + } + + for (INT i = 0; i < ARRAYSIZE(szControls); i++) + { + hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL == hControl || FALSE == GetWindowRect(hControl, &rect)) continue; + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rect, 2); + x = rect.left; + y = rect.top; + cx = rect.right - rect.left; + cy = rect.bottom - rect.top; + switch(szControls[i]) + { + case IDC_ADDRESS_LABEL: + case IDC_MESSAGE: + x = pageRect.left; + y = nextTop; + LoginBox_GetWindowTextSize(hControl, maxWidth, &cx, &cy); + nextTop += (cy + MulDiv(2, baseunitY, 8)); + break; + case IDC_ADDRESS_EDIT: + x = pageRect.left; + y = nextTop; + cy = LoginBox_GetWindowTextHeight(hControl, 0); + { + RECT r1, r2; + GetWindowRect(hControl, &r1); + SendMessage(hControl, EM_GETRECT, 0, (LPARAM)&r2); + INT t = (r1.bottom - r1.top) - (r2.bottom - r2.top); + cy += t; + } + cx = maxWidth; + nextTop += (cy + MulDiv(7, baseunitY, 8)); + break; + } + + if (rect.left != x || rect.top != y || + (rect.right - rect.left) != cx || (rect.bottom - rect.top) != cy) + { + hdwp = DeferWindowPos(hdwp, hControl, NULL, x, y, cx, cy, flags); + if (NULL == hdwp) break; + + if (FALSE != fRedraw) + { + SetRectRgn(tempRegion, rect.left, rect.top, rect.right, rect.bottom); + CombineRgn(invalidRegion, invalidRegion, tempRegion, RGN_OR); + + SetRectRgn(tempRegion, x, y , x + cx, y + cy); + CombineRgn(invalidRegion, invalidRegion, tempRegion, RGN_OR); + } + } + } + + if (NULL != hdwp) + EndDeferWindowPos(hdwp); + + if (FALSE != fRedraw) + { + RedrawWindow(hwnd, NULL, invalidRegion, + RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE | RDW_ERASENOW | RDW_ALLCHILDREN | RDW_VALIDATE); + } + + if (NULL != invalidRegion) + DeleteObject(invalidRegion); + if (NULL != tempRegion) + DeleteObject(tempRegion); +} + +BOOL LoginPageAddress::OnGetLoginData(LoginData **ppLoginData) +{ + if (NULL == ppLoginData) + return FALSE; + + HWND hEdit; + LPWSTR address; + + hEdit = GetDlgItem(hwnd, IDC_ADDRESS_EDIT); + if (NULL == hEdit || FAILED(LoginBox_GetWindowText(hEdit, &address, NULL))) + address = NULL; + else + { + size_t encodedMax = lstrlen(address)*2; + LPWSTR addressEncoded = LoginBox_MallocString(encodedMax); + + for(;;) + { + if (FALSE == InternetCanonicalizeUrl(address, addressEncoded, (DWORD*)&encodedMax, ICU_ENCODE_PERCENT | ICU_NO_META)) + { + if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) + { + LoginBox_FreeString(addressEncoded); + addressEncoded = LoginBox_MallocString(encodedMax); + if (NULL != addressEncoded) + continue; + } + } + else + { + LoginBox_FreeString(address); + address = LoginBox_CopyString(addressEncoded); + } + break; + } + + HRESULT hr; + for(;;) + { + hr = AddressEncoder_EncodeString(address, addressEncoded, &encodedMax, ICU_BROWSER_MODE); + if (ENC_E_INSUFFICIENT_BUFFER == hr) + { + LoginBox_FreeString(addressEncoded); + addressEncoded = LoginBox_MallocString(encodedMax); + if (NULL == addressEncoded) + hr = E_OUTOFMEMORY; + else + continue; + } + break; + } + + if (SUCCEEDED(hr)) + { + LoginBox_FreeString(address); + address = addressEncoded; + addressEncoded = NULL; + } + else + LoginBox_FreeString(addressEncoded); + } + + HRESULT hr = LoginDataAddress::CreateInstance(NULL, hwnd, hLoginbox, + address, (LoginDataAddress**)ppLoginData); + + LoginBox_FreeString(address); + return SUCCEEDED(hr); +} + +BOOL LoginPageAddress::OnSetUsername(LPCWSTR pszUsername) +{ + HWND hEdit = GetDlgItem(hwnd, IDC_ADDRESS_EDIT); + if (NULL == hEdit) return FALSE; + + if (NULL == pszUsername || L'\0' == *pszUsername) + return FALSE; + + INT cchLen = lstrlen(pszUsername); + + INT f, l; + SNDMSG(hEdit, EM_GETSEL, (WPARAM)&f, (LPARAM)&l); + if (f == l) return FALSE; + + SNDMSG(hEdit, EM_REPLACESEL, FALSE, (LPARAM)pszUsername); + SNDMSG(hEdit, EM_SETSEL, (WPARAM)f, (LPARAM)f + cchLen); + + return TRUE; + +} +HBRUSH LoginPageAddress::OnGetStaticColor(HDC hdc, HWND hControl) +{ + INT_PTR controlId = (INT_PTR)GetWindowLongPtr(hControl, GWLP_ID); + switch(controlId) + { + case IDC_MESSAGE: + SetTextColor(hdc, rgbSecondaryText); + SetBkColor(hdc, rgbBack); + return hbrBack; + } + + return LoginPage::OnGetStaticColor(hdc, hControl); +} + +BOOL LoginPageAddress::OnSetAddress(LPCWSTR pszAddress, BOOL replaceUsername) +{ + HWND hEdit = GetDlgItem(hwnd, IDC_ADDRESS_EDIT); + if (NULL == hEdit) return FALSE; + + BOOL succeeded; + LPWSTR addressDecoded; + + if (SUCCEEDED(AddressEncoder_DecodeString(pszAddress, &addressDecoded))) + { + succeeded = (BOOL)SNDMSG(hEdit, WM_SETTEXT, 0, (LPARAM)addressDecoded); + LoginBox_FreeString(addressDecoded); + } + else + succeeded = (BOOL)SNDMSG(hEdit, WM_SETTEXT, 0, (LPARAM)pszAddress); + + + return succeeded; +} + +BOOL LoginPageAddress::OnSetAddressTitle(LPCWSTR pszTitle) +{ + return SetLabelText(IDC_ADDRESS_LABEL, pszTitle); +} + +BOOL LoginPageAddress::OnSetMessage(LPCWSTR pszMessage) +{ + HWND hLabel = GetDlgItem(hwnd, IDC_MESSAGE); + if (NULL == hLabel) return FALSE; + + return SetWindowText(hLabel, pszMessage); +} + +INT_PTR LoginPageAddress::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case NLPAM_SETADDRESS: MSGRESULT(hwnd, OnSetAddress((LPCWSTR)lParam, (BOOL)wParam)); + case NLPAM_SETADDRESSTITLE: MSGRESULT(hwnd, OnSetAddressTitle((LPCWSTR)lParam)); + case NLPAM_SETMESSAGE: MSGRESULT(hwnd, OnSetMessage((LPCWSTR)lParam)); + + } + return __super::DialogProc(uMsg, wParam, lParam); +} + diff --git a/Src/auth/Loginbox/pageAddress.h b/Src/auth/Loginbox/pageAddress.h new file mode 100644 index 00000000..a527fc99 --- /dev/null +++ b/Src/auth/Loginbox/pageAddress.h @@ -0,0 +1,58 @@ +#ifndef NULLSOFT_AUTH_LOGIN_PAGE_ADDRESS_HEADER +#define NULLSOFT_AUTH_LOGIN_PAGE_ADDRESS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginPage.h" + +#define NLPAM_FIRST (NLPM_PAGEFIRST + 0) + +#define NLPAM_SETADDRESS (NLPAM_FIRST + 0) //wParam = (WPARAM)(BOOL)replaceUsername, lParam = (LPARAM)(LPCWSTR)pszAddress; Return - TRUE on succeess. +#define LoginPageAddress_SetAddress(/*HWND*/ __hwnd, /*LPCWSTR*/ __address, /*BOOL*/ __replaceUsername)\ + ((BOOL)SNDMSG((__hwnd), NLPAM_SETADDRESS, (WPARAM)(__replaceUsername), (LPARAM)(__address))) + +#define NLPAM_SETADDRESSTITLE (NLPAM_FIRST + 1) //wParam - not used, lParam = (LPARAM)(LPCWSTR)pszAddressTitle; Return - TRUE on succeess. +#define LoginPageAddress_SetAddressTitle(/*HWND*/ __hwnd, /*LPCWSTR*/ __addressTitle)\ + ((BOOL)SNDMSG((__hwnd), NLPAM_SETADDRESSTITLE, 0, (LPARAM)(__addressTitle))) + +#define NLPAM_SETMESSAGE (NLPAM_FIRST + 2) //wParam - not used, lParam = (LPARAM)(LPCWSTR)pszMessage; Return - TRUE on succeess. +#define LoginPageAddress_SetMessage(/*HWND*/ __hwnd, /*LPCWSTR*/ __pszMessage)\ + ((BOOL)SNDMSG((__hwnd), NLPAM_SETMESSAGE, 0, (LPARAM)(__pszMessage))) + + +class LoginPageAddress : public LoginPage +{ +protected: + LoginPageAddress(HWND hwnd, HWND hLoginbox); + ~LoginPageAddress(); + +public: + static HWND CreatePage(HWND hLoginbox, HWND hParent); + +protected: + void UpdateLayout(BOOL fRedraw); + + BOOL OnInitDialog(HWND hFocus, LPARAM param); + void OnDestroy(); + + BOOL OnGetLoginData(LoginData **ppLoginData); + BOOL OnSetUsername(LPCWSTR pszUsername); + + HBRUSH OnGetStaticColor(HDC hdc, HWND hControl); + + BOOL OnSetAddress(LPCWSTR pszAddress, BOOL replaceUsername); + BOOL OnSetAddressTitle(LPCWSTR pszTitle); + BOOL OnSetMessage(LPCWSTR pszMessage); + + INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + + +private: + friend static HRESULT CALLBACK LoginPageAddress_CreateInstance(HWND hwnd, HWND hLoginbox, LoginPage **instance); + + +}; + +#endif //NULLSOFT_AUTH_LOGIN_PAGE_ADDRESS_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/pageCredentials.cpp b/Src/auth/Loginbox/pageCredentials.cpp new file mode 100644 index 00000000..a78d0482 --- /dev/null +++ b/Src/auth/Loginbox/pageCredentials.cpp @@ -0,0 +1,416 @@ +#include "./pageCredentials.h" +#include "./dataCredentials.h" +#include "./common.h" +#include "./loginGui.h" +#include "../../winamp/commandLink.h" +#include "../resource.h" + +#include <commctrl.h> + + +static HRESULT CALLBACK LoginPageCredentials_CreateInstance(HWND hwnd, HWND hLoginbox, LoginPage **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hwnd || NULL == hLoginbox) return E_INVALIDARG; + + *instance = new LoginPageCredentials(hwnd, hLoginbox); + if (NULL == instance) return E_OUTOFMEMORY; + + return S_OK; +} + +LoginPageCredentials::LoginPageCredentials(HWND hwnd, HWND hLoginbox) + : LoginPage(hwnd, hLoginbox), accountRecoverUrl(NULL), accountCreateUrl(NULL) +{ + +} + +LoginPageCredentials::~LoginPageCredentials() +{ + LoginBox_FreeString(accountRecoverUrl); + LoginBox_FreeString(accountCreateUrl); +} + +HWND LoginPageCredentials::CreatePage(HWND hLoginbox, HWND hParent) +{ + return LoginPage::CreatePage(hLoginbox, MAKEINTRESOURCE(IDD_PAGE_CREDENTIALS), + hParent, NULL, LoginPageCredentials_CreateInstance); +} + + + +void LoginPageCredentials::UpdateLayout(BOOL fRedraw) +{ + LoginPage::UpdateLayout(fRedraw); + + RECT pageRect; + if (FALSE == GetPageRect(&pageRect)) + return; + + UINT flags = SWP_NOACTIVATE | SWP_NOZORDER; + if (FALSE == fRedraw) flags |= SWP_NOREDRAW; + + const INT szControls[] = { IDC_USERNAME_LABEL, IDC_USERNAME, IDC_PASSWORD_LABEL, IDC_PASSWORD}; + const INT szLinks[] = { IDC_CREATE_ACCOUNT, IDC_RECOVER_ACCOUNT }; + + INT baseunitX, baseunitY; + if (FALSE == LoginBox_GetWindowBaseUnits(hwnd, &baseunitX, &baseunitY)) + { + baseunitX = 6; + baseunitY = 13; + } + + HDWP hdwp = BeginDeferWindowPos(ARRAYSIZE(szControls) + ARRAYSIZE(szLinks)); + + HWND hControl; + INT cx, cy, x, y; + + INT editWidth = 0; + INT column1Width = 0; + INT column2Width = 0; + INT columnSpace = MulDiv(7, baseunitX, 4); + SIZE sizeLink; + RECT linkMargins; + + // column 1 width + for (INT i = 0; i < ARRAYSIZE(szControls); i++) + { + hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL == hControl) continue; + + switch(szControls[i]) + { + case IDC_USERNAME_LABEL: + case IDC_PASSWORD_LABEL: + if (FALSE != (LoginBox_GetWindowTextSize(hControl, pageRect.right - pageRect.left, &cx, &cy)) && + cx > column1Width) + { + column1Width = cx; + } + break; + case IDC_USERNAME: + case IDC_PASSWORD: + if (0 == editWidth) + { + HDC hdc = GetDCEx(hControl, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT fontControl = (HFONT)SendMessage(hControl, WM_GETFONT, 0, 0L); + HFONT fontOrig = (HFONT)SelectObject(hdc, fontControl); + + editWidth = LoginBox_GetAveStrWidth(hdc, 28); + + SelectObject(hdc, fontOrig); + ReleaseDC(hControl, hdc); + } + if (0 != editWidth) + { + LRESULT editMargins = SendMessage(hControl, EM_GETMARGINS, 0,0L); + editWidth += (LOWORD(editMargins) + HIWORD(editMargins)); + } + } + if (editWidth > column1Width) + column1Width = editWidth; + break; + } + } + // column 2 width + for (INT i = 0; i < ARRAYSIZE(szLinks); i++) + { + hControl = GetDlgItem(hwnd, szLinks[i]); + if (NULL != hControl && + 0 != (WS_VISIBLE & GetWindowStyle(hControl)) && + FALSE != CommandLink_GetIdealSize(hControl, &sizeLink)) + + { + if (FALSE != CommandLink_GetMargins(hControl, &linkMargins)) + sizeLink.cx -= linkMargins.right; + + if (column2Width < sizeLink.cx) + column2Width = sizeLink.cx; + } + } + + + BOOL multiColumnLayout; + if (column2Width > 0 && + (column1Width + columnSpace + column2Width) <= (pageRect.right - pageRect.left)) + { + multiColumnLayout = TRUE; + } + else + { + multiColumnLayout = FALSE; + } + + INT nextTop = 0; + + if (column2Width != 0) + { + if(FALSE == multiColumnLayout) + nextTop = pageRect.top - GetTitleSpacing()/2; + else + { + nextTop = pageRect.top; + HWND hLabel = GetDlgItem(hwnd, IDC_USERNAME_LABEL); + INT offsetY = (NULL != hLabel) ? LoginBox_GetWindowTextHeight(hLabel, 0) : 0; + if (0 == offsetY) offsetY = baseunitY; + nextTop += (offsetY - 1); + } + + for (INT i = 0; i < ARRAYSIZE(szLinks); i++) + { + hControl = GetDlgItem(hwnd, szLinks[i]); + if (NULL == hControl || 0 == (WS_VISIBLE & GetWindowStyle(hControl))) + continue; + + if (FALSE == CommandLink_GetIdealSize(hControl, &sizeLink)) + ZeroMemory(&sizeLink, sizeof(SIZE)); + + x = pageRect.right - sizeLink.cx; + if (x < pageRect.left) x = pageRect.left; + + if (FALSE != CommandLink_GetMargins(hControl, &linkMargins)) + x += linkMargins.right; + + if (i == 0) + nextTop -= linkMargins.top; + + hdwp = DeferWindowPos(hdwp, hControl, NULL, x, nextTop, sizeLink.cx, sizeLink.cy, flags); + if (NULL == hdwp) break; + + nextTop += sizeLink.cy; + + if (i != (ARRAYSIZE(szLinks) -1)) + nextTop += MulDiv(1, baseunitY, 8); + } + } + + if (FALSE != multiColumnLayout || 0 == column2Width) + nextTop = pageRect.top; + else + nextTop += MulDiv(2, baseunitY, 8); + + for (INT i = 0; i < ARRAYSIZE(szControls); i++) + { + hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL == hControl) continue; + + switch(szControls[i]) + { + case IDC_USERNAME_LABEL: + case IDC_PASSWORD_LABEL: + x = pageRect.left; + y = nextTop; + LoginBox_GetWindowTextSize(hControl, pageRect.right - pageRect.left, &cx, &cy); + nextTop += (cy + MulDiv(2, baseunitY, 8)); + if (cx > column1Width) + cx = column1Width; + break; + case IDC_USERNAME: + case IDC_PASSWORD: + x = pageRect.left; + y = nextTop; + cy = LoginBox_GetWindowTextHeight(hControl, 0); + { + RECT r1, r2; + GetWindowRect(hControl, &r1); + SendMessage(hControl, EM_GETRECT, 0, (LPARAM)&r2); + INT t = (r1.bottom - r1.top) - (r2.bottom - r2.top); + cy += t; + } + nextTop += (cy + MulDiv(4, baseunitY, 8)); + + cx = column1Width; + if ((x + cx) > pageRect.right) + cx = pageRect.right - x; + + break; + + } + + hdwp = DeferWindowPos(hdwp, hControl, NULL, x, y, cx, cy, flags); + if (NULL == hdwp) break; + } + + if (NULL != hdwp) + EndDeferWindowPos(hdwp); +} + + +BOOL LoginPageCredentials::OnInitDialog(HWND hFocus, LPARAM param) +{ + HWND hControl; + const INT szLinks[] = { IDC_CREATE_ACCOUNT, IDC_RECOVER_ACCOUNT }; + for (INT i = 0; i < ARRAYSIZE(szLinks); i++) + { + hControl = GetDlgItem(hwnd, szLinks[i]); + if (NULL == hControl) continue; + + RECT linkMargins; + SetRect(&linkMargins, 2, 0, 2, 2); + MapDialogRect(hwnd, &linkMargins); + CommandLink_SetMargins(hControl, &linkMargins); + } + + HFONT fontLabel = NULL, fontEdit = NULL; + LoginGuiObject *loginGui; + if (SUCCEEDED(LoginGuiObject::QueryInstance(&loginGui))) + { + fontLabel = loginGui->GetTextFont(); + fontEdit = loginGui->GetEditorFont(); + loginGui->Release(); + } + + if (NULL != fontLabel) + { + const INT szLabels[] = { IDC_USERNAME_LABEL, IDC_PASSWORD_LABEL}; + for (INT i = 0; i < ARRAYSIZE(szLabels); i++) + { + hControl = GetDlgItem(hwnd, szLabels[i]); + if (NULL != hControl) + SendMessage(hControl, WM_SETFONT, (WPARAM)fontLabel, 0L); + } + } + + if (NULL != fontEdit) + { + const INT szEdits[] = { IDC_USERNAME, IDC_PASSWORD}; + for (INT i = 0; i < ARRAYSIZE(szEdits); i++) + { + hControl = GetDlgItem(hwnd, szEdits[i]); + if (NULL != hControl) + SendMessage(hControl, WM_SETFONT, (WPARAM)fontEdit, 0L); + } + } + + + LoginPage::OnInitDialog(hFocus, param); + return FALSE; +} + +BOOL LoginPageCredentials::OnNotify(UINT controlId, const NMHDR *pnmh) +{ + switch(controlId) + { + case IDC_CREATE_ACCOUNT: + if (pnmh->code == NM_CLICK && NULL != accountCreateUrl && L'\0' != *accountCreateUrl) + LoginBox_OpenUrl(hwnd, accountCreateUrl, TRUE); + return TRUE; + + case IDC_RECOVER_ACCOUNT: + if (pnmh->code == NM_CLICK && NULL != accountRecoverUrl && L'\0' != *accountRecoverUrl) + LoginBox_OpenUrl(hwnd, accountRecoverUrl, TRUE); + return TRUE; + } + return LoginPage::OnNotify(controlId, pnmh); +} +BOOL LoginPageCredentials::OnGetLoginData(LoginData **ppLoginData) +{ + if (NULL == ppLoginData) + return FALSE; + + HWND hEdit; + LPWSTR username, password; + + hEdit = GetDlgItem(hwnd, IDC_USERNAME); + if (NULL == hEdit || FAILED(LoginBox_GetWindowText(hEdit, &username, NULL))) + username = NULL; + + hEdit = GetDlgItem(hwnd, IDC_PASSWORD); + if (NULL == hEdit || FAILED(LoginBox_GetWindowText(hEdit, &password, NULL))) + password = NULL; + + HRESULT hr = LoginDataCredentials::CreateInstance(NULL, hwnd, hLoginbox, + username, password, (LoginDataCredentials**)ppLoginData); + + LoginBox_FreeStringSecure(username); + LoginBox_FreeStringSecure(password); + + return SUCCEEDED(hr); +} + +void LoginPageCredentials::OnSetAccountRecoverUrl(LPCWSTR pszUrl) +{ + LoginBox_FreeString(accountRecoverUrl); + accountRecoverUrl = LoginBox_CopyString(pszUrl); + + HWND hLink = GetDlgItem(hwnd, IDC_RECOVER_ACCOUNT); + if (NULL != hLink) + { + INT showWindow = (NULL != accountRecoverUrl && S_OK == IsValidURL(NULL, accountRecoverUrl, 0)) ? SW_SHOWNA : SW_HIDE; + ShowWindow(hLink, showWindow); + } +} + +void LoginPageCredentials::OnSetAccountCreateUrl(LPCWSTR pszUrl) +{ + LoginBox_FreeString(accountCreateUrl); + accountCreateUrl = LoginBox_CopyString(pszUrl); + + HWND hLink = GetDlgItem(hwnd, IDC_CREATE_ACCOUNT); + if (NULL != hLink) + { + INT showWindow = (NULL != accountCreateUrl && S_OK == IsValidURL(NULL, accountCreateUrl, 0)) ? SW_SHOWNA : SW_HIDE; + ShowWindow(hLink, showWindow); + } +} + +void LoginPageCredentials::OnSetUsernameLabel(LPCWSTR pszLabel) +{ + SetLabelText(IDC_USERNAME_LABEL, pszLabel); +} + +void LoginPageCredentials::OnSetPasswordLabel(LPCWSTR pszLabel) +{ + SetLabelText(IDC_PASSWORD_LABEL, pszLabel); +} + +BOOL LoginPageCredentials::OnSetUsername(LPCWSTR pszUsername) +{ + HWND hEdit = GetDlgItem(hwnd, IDC_USERNAME); + if (NULL == hEdit) return FALSE; + + BOOL result = (BOOL)SNDMSG(hEdit, WM_SETTEXT, 0, (LPARAM)pszUsername); + if (FALSE != result) + { + SNDMSG(hEdit, EM_SETSEL, 0, -1); + } + return result; +} + +BOOL LoginPageCredentials::OnSetPassword(LPCWSTR pszPassword) +{ + HWND hEdit = GetDlgItem(hwnd, IDC_PASSWORD); + if (NULL == hEdit) return FALSE; + + BOOL result = (BOOL)SNDMSG(hEdit, WM_SETTEXT, 0, (LPARAM)pszPassword); + if (FALSE != result) + { + SNDMSG(hEdit, EM_SETSEL, 0, -1); + } + return result; +} + +HWND LoginPageCredentials::OnGetFirstItem() +{ + HWND hEdit = GetDlgItem(hwnd, IDC_USERNAME); + if (NULL != hEdit && (WS_VISIBLE == ((WS_VISIBLE | WS_DISABLED) & GetWindowStyle(hEdit)))) + return hEdit; + + return LoginPage::OnGetFirstItem(); +} + + +INT_PTR LoginPageCredentials::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case NLPCM_SETACCOUNTRECOVERURL: OnSetAccountRecoverUrl((LPCWSTR)lParam); return TRUE; + case NLPCM_SETACCOUNTCREATEURL: OnSetAccountCreateUrl((LPCWSTR)lParam); return TRUE; + case NLPCM_SETUSERNAMELABEL: OnSetUsernameLabel((LPCWSTR)lParam); return TRUE; + case NLPCM_SETPASSWORDLABEL: OnSetPasswordLabel((LPCWSTR)lParam); return TRUE; + } + return __super::DialogProc(uMsg, wParam, lParam); +} + diff --git a/Src/auth/Loginbox/pageCredentials.h b/Src/auth/Loginbox/pageCredentials.h new file mode 100644 index 00000000..ba233635 --- /dev/null +++ b/Src/auth/Loginbox/pageCredentials.h @@ -0,0 +1,66 @@ +#ifndef NULLSOFT_AUTH_LOGIN_PAGE_CREDENTIALS_HEADER +#define NULLSOFT_AUTH_LOGIN_PAGE_CREDENTIALS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginPage.h" + +#define NLPCM_FIRST (NLPM_PAGEFIRST + 0) + +#define NLPCM_SETACCOUNTRECOVERURL (NLPCM_FIRST + 1) //wParam - not used, lParam = (LPARAM)(LPCWSTR)pszAccountRecoverUrl; Return - no return value. +#define LoginPageCredentials_SetAccountRecoverUrl(/*HWND*/ __hwnd, /*LPCWSTR*/ __url)\ + (SNDMSG((__hwnd), NLPCM_SETACCOUNTRECOVERURL, 0, (LPARAM)(__url))) + +#define NLPCM_SETACCOUNTCREATEURL (NLPCM_FIRST + 2) //wParam - not used, lParam = (LPARAM)(LPCWSTR)pszAccountCreateUrl; Return - no return value. +#define LoginPageCredentials_SetAccountCreateUrl(/*HWND*/ __hwnd, /*LPCWSTR*/ __url)\ + (SNDMSG((__hwnd), NLPCM_SETACCOUNTCREATEURL, 0, (LPARAM)(__url))) + +#define NLPCM_SETUSERNAMELABEL (NLPCM_FIRST + 3) //wParam - not used, lParam = (LPARAM)(LPCWSTR)pszUsernameLabel; Return - no return value. +#define LoginPageCredentials_SetUsernameLabel(/*HWND*/ __hwnd, /*LPCWSTR*/ __label)\ + (SNDMSG((__hwnd), NLPCM_SETUSERNAMELABEL, 0, (LPARAM)(__label))) + +#define NLPCM_SETPASSWORDLABEL (NLPCM_FIRST + 4) //wParam - not used, lParam = (LPARAM)(LPCWSTR)pszPasswordLabel; Return - no return value. +#define LoginPageCredentials_SetPasswordLabel(/*HWND*/ __hwnd, /*LPCWSTR*/ __label)\ + (SNDMSG((__hwnd), NLPCM_SETPASSWORDLABEL, 0, (LPARAM)(__label))) + +class LoginPageCredentials : public LoginPage +{ +protected: + LoginPageCredentials(HWND hwnd, HWND hLoginbox); + ~LoginPageCredentials(); + +public: + static HWND CreatePage(HWND hLoginbox, HWND hParent); + +protected: + void UpdateLayout(BOOL fRedraw); + + BOOL OnInitDialog(HWND hFocus, LPARAM param); + BOOL OnNotify(UINT controlId, const NMHDR *pnmh); + + BOOL OnGetLoginData(LoginData **ppLoginData); + BOOL OnSetUsername(LPCWSTR pszUsername); + BOOL OnSetPassword(LPCWSTR pszPassword); + HWND OnGetFirstItem(); + + void OnSetAccountRecoverUrl(LPCWSTR pszUrl); + void OnSetAccountCreateUrl(LPCWSTR pszUrl); + void OnSetUsernameLabel(LPCWSTR pszLabel); + void OnSetPasswordLabel(LPCWSTR pszLabel); + + + INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + + +private: + friend static HRESULT CALLBACK LoginPageCredentials_CreateInstance(HWND hwnd, HWND hLoginbox, LoginPage **instance); + +protected: + LPWSTR accountRecoverUrl; + LPWSTR accountCreateUrl; + +}; + +#endif //NULLSOFT_AUTH_LOGIN_PAGE_CREDENTIALS_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/pageEmpty.cpp b/Src/auth/Loginbox/pageEmpty.cpp new file mode 100644 index 00000000..1455c79a --- /dev/null +++ b/Src/auth/Loginbox/pageEmpty.cpp @@ -0,0 +1,171 @@ +#include "./pageEmpty.h" +#include "./loginBox.h" +#include "./common.h" +#include "../resource.h" + + +static HRESULT CALLBACK LoginPageEmpty_CreateInstance(HWND hwnd, HWND hLoginbox, LoginPage **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hwnd || NULL == hLoginbox) return E_INVALIDARG; + + *instance = new LoginPageEmpty(hwnd, hLoginbox); + if (NULL == instance) return E_OUTOFMEMORY; + + return S_OK; +} + +LoginPageEmpty::LoginPageEmpty(HWND hwnd, HWND hLoginbox) + : LoginPage(hwnd, hLoginbox) +{ +} + +LoginPageEmpty::~LoginPageEmpty() +{ +} + +HWND LoginPageEmpty::CreatePage(HWND hLoginbox, HWND hParent) +{ + return LoginPage::CreatePage(hLoginbox, MAKEINTRESOURCE(IDD_PAGE_EMPTY), + hParent, NULL, LoginPageEmpty_CreateInstance); +} + +BOOL LoginPageEmpty::OnInitDialog(HWND hFocus, LPARAM param) +{ + if (NULL != hLoginbox) + { + BOOL updateActive = LoginBox_GetUpdateState(hLoginbox); + EnableCheckButton(FALSE == updateActive); + } + + LoginPage::OnInitDialog(hFocus, param); + return FALSE; +} + +void LoginPageEmpty::OnCommand(UINT commandId, UINT eventType, HWND hControl) +{ + switch(commandId) + { + case IDC_CHECKNOW: + if (BN_CLICKED == eventType && NULL != hLoginbox) + LoginBox_UpdateProviders(hLoginbox, TRUE); + break; + } + LoginPage::OnCommand(commandId, eventType, hControl); +} + +void LoginPageEmpty::UpdateLayout(BOOL fRedraw) +{ + LoginPage::UpdateLayout(fRedraw); + + RECT clientRect; + GetClientRect(hwnd, &clientRect); + + RECT offsetRect; + SetRect(&offsetRect, 2, 4, 4, 22); + MapDialogRect(hwnd, &offsetRect); + + clientRect.left += offsetRect.left; + clientRect.top += offsetRect.top; + clientRect.right -= offsetRect.right; + clientRect.bottom -= offsetRect.bottom; + + UINT flags = SWP_NOACTIVATE | SWP_NOZORDER; + if (FALSE == fRedraw) flags |= SWP_NOREDRAW; + + const INT szControls[] = { IDC_MESSAGE, IDC_CHECKNOW, IDC_CHECK_STATUS}; + HDWP hdwp = BeginDeferWindowPos(ARRAYSIZE(szControls)); + + RECT rect; + LONG clientTop = clientRect.top; + LONG left, top = clientRect.top; + for (INT i = 0; i < ARRAYSIZE(szControls); i++) + { + HWND hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL == hControl || FALSE == GetWindowRect(hControl, &rect)) continue; + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rect, 2); + + switch(szControls[i]) + { + case IDC_MESSAGE: + top = clientRect.top + ((clientRect.bottom - clientRect.top) - (rect.bottom - rect.top))/2; + left = clientRect.left + ((clientRect.right - clientRect.left) - (rect.right - rect.left))/2; + OffsetRect(&rect, left - rect.left, top - rect.top); + clientTop = rect.bottom; + break; + case IDC_CHECKNOW: + top = clientTop + 2*offsetRect.top; + left = clientRect.left + ((clientRect.right - clientRect.left) - (rect.right - rect.left))/2; + OffsetRect(&rect, left - rect.left, top - rect.top); + break; + case IDC_CHECK_STATUS: + top = clientTop + offsetRect.top; + left = clientRect.left + ((clientRect.right - clientRect.left) - (rect.right - rect.left))/2; + OffsetRect(&rect, left - rect.left, top - rect.top); + break; + } + + hdwp = DeferWindowPos(hdwp, hControl, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags); + if (NULL == hdwp) break; + } + + if (NULL != hdwp) + EndDeferWindowPos(hdwp); + +} + +HBRUSH LoginPageEmpty::OnGetStaticColor(HDC hdc, HWND hControl) +{ + INT_PTR controlId = (INT_PTR)GetWindowLongPtr(hControl, GWLP_ID); + switch(controlId) + { + case IDC_MESSAGE: + case IDC_CHECK_STATUS: + SetTextColor(hdc, rgbSecondaryText); + SetBkColor(hdc, rgbBack); + return hbrBack; + } + + return LoginPage::OnGetStaticColor(hdc, hControl); +} + +BOOL LoginPageEmpty::OnGetLoginData(LoginData **ppLoginData) +{ + return FALSE; +} + +void LoginPageEmpty::OnUpdateStateChange(BOOL updateActive) +{ + EnableCheckButton(FALSE == updateActive); +} + +void LoginPageEmpty::EnableCheckButton(BOOL fEnable) +{ + HWND hButton = GetDlgItem(hwnd, IDC_CHECKNOW); + HWND hStatus = GetDlgItem(hwnd, IDC_CHECK_STATUS); + + if (FALSE != fEnable) + { + if (NULL != hStatus) + ShowWindow(hStatus, SW_HIDE); + + if (NULL != hButton) + { + EnableWindow(hButton, TRUE); + ShowWindow(hButton, SW_SHOWNA); + } + + } + else + { + if (NULL != hButton) + { + if (NULL != hStatus) + ShowWindow(hButton, SW_HIDE); + EnableWindow(hButton, FALSE); + } + if (NULL != hStatus) + ShowWindow(hStatus, SW_SHOWNA); + } +} + diff --git a/Src/auth/Loginbox/pageEmpty.h b/Src/auth/Loginbox/pageEmpty.h new file mode 100644 index 00000000..53608898 --- /dev/null +++ b/Src/auth/Loginbox/pageEmpty.h @@ -0,0 +1,37 @@ +#ifndef NULLSOFT_AUTH_LOGIN_PAGE_EMPTY_HEADER +#define NULLSOFT_AUTH_LOGIN_PAGE_EMPTY_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginPage.h" + +class LoginPageEmpty: public LoginPage +{ +protected: + LoginPageEmpty(HWND hwnd, HWND hLoginbox); + ~LoginPageEmpty(); + +public: + static HWND CreatePage(HWND hLoginbox, HWND hParent); + +protected: + void UpdateLayout(BOOL fRedraw); + void EnableCheckButton(BOOL fEnable); + + BOOL OnInitDialog(HWND hFocus, LPARAM param); + void OnCommand(UINT commandId, UINT eventType, HWND hControl); + + BOOL OnGetLoginData(LoginData **ppLoginData); + void OnUpdateStateChange(BOOL updateActive); + + HBRUSH OnGetStaticColor(HDC hdc, HWND hControl); + +private: + friend static HRESULT CALLBACK LoginPageEmpty_CreateInstance(HWND hwnd, HWND hLoginbox, LoginPage **instance); + + +}; + +#endif //NULLSOFT_AUTH_LOGIN_PAGE_EMPTY_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/pageError.cpp b/Src/auth/Loginbox/pageError.cpp new file mode 100644 index 00000000..b4d0f749 --- /dev/null +++ b/Src/auth/Loginbox/pageError.cpp @@ -0,0 +1,98 @@ +#include "./pageError.h" +#include "./loginBox.h" +#include "./common.h" +#include "../resource.h" + + +static HRESULT CALLBACK LoginPageError_CreateInstance(HWND hwnd, HWND hLoginbox, LoginPage **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hwnd || NULL == hLoginbox) return E_INVALIDARG; + + *instance = new LoginPageError(hwnd, hLoginbox); + if (NULL == instance) return E_OUTOFMEMORY; + + return S_OK; +} + +LoginPageError::LoginPageError(HWND hwnd, HWND hLoginbox) + : LoginPage(hwnd, hLoginbox) +{ +} + +LoginPageError::~LoginPageError() +{ +} + +HWND LoginPageError::CreatePage(HWND hLoginbox, HWND hParent) +{ + return LoginPage::CreatePage(hLoginbox, MAKEINTRESOURCE(IDD_PAGE_ERROR), + hParent, NULL, LoginPageError_CreateInstance); +} + +void LoginPageError::UpdateLayout(BOOL fRedraw) +{ + LoginPage::UpdateLayout(fRedraw); + + RECT clientRect; + GetClientRect(hwnd, &clientRect); + + RECT offsetRect; + SetRect(&offsetRect, 2, 4, 4, 22); + MapDialogRect(hwnd, &offsetRect); + + clientRect.left += offsetRect.left; + clientRect.top += offsetRect.top; + clientRect.right -= offsetRect.right; + clientRect.bottom -= offsetRect.bottom; + + UINT flags = SWP_NOACTIVATE | SWP_NOZORDER; + if (FALSE == fRedraw) flags |= SWP_NOREDRAW; + + const INT szControls[] = { IDC_MESSAGE, }; + HDWP hdwp = BeginDeferWindowPos(ARRAYSIZE(szControls)); + + RECT rect; + LONG left, top = clientRect.top; + for (INT i = 0; i < ARRAYSIZE(szControls); i++) + { + HWND hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL == hControl || FALSE == GetWindowRect(hControl, &rect)) continue; + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rect, 2); + + switch(szControls[i]) + { + case IDC_MESSAGE: + top = clientRect.top + ((clientRect.bottom - clientRect.top) - (rect.bottom - rect.top))/2; + left = clientRect.left + ((clientRect.right - clientRect.left) - (rect.right - rect.left))/2; + OffsetRect(&rect, left - rect.left, top - rect.top); + break; + } + + hdwp = DeferWindowPos(hdwp, hControl, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags); + if (NULL == hdwp) break; + } + + if (NULL != hdwp) + EndDeferWindowPos(hdwp); + +} + +HBRUSH LoginPageError::OnGetStaticColor(HDC hdc, HWND hControl) +{ + INT_PTR controlId = (INT_PTR)GetWindowLongPtr(hControl, GWLP_ID); + switch(controlId) + { + case IDC_MESSAGE: + SetTextColor(hdc, rgbSecondaryText); + SetBkColor(hdc, rgbBack); + return hbrBack; + } + + return LoginPage::OnGetStaticColor(hdc, hControl); +} + +BOOL LoginPageError::OnGetLoginData(LoginData **ppLoginData) +{ + return FALSE; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/pageError.h b/Src/auth/Loginbox/pageError.h new file mode 100644 index 00000000..4e968a74 --- /dev/null +++ b/Src/auth/Loginbox/pageError.h @@ -0,0 +1,31 @@ +#ifndef NULLSOFT_AUTH_LOGIN_PAGE_ERROR_HEADER +#define NULLSOFT_AUTH_LOGIN_PAGE_ERROR_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginPage.h" + +class LoginPageError : public LoginPage +{ +protected: + LoginPageError(HWND hwnd, HWND hLoginbox); + ~LoginPageError(); + +public: + static HWND CreatePage(HWND hLoginbox, HWND hParent); + +protected: + void UpdateLayout(BOOL fRedraw); + + HBRUSH OnGetStaticColor(HDC hdc, HWND hControl); + BOOL OnGetLoginData(LoginData **ppLoginData); + +private: + friend static HRESULT CALLBACK LoginPageError_CreateInstance(HWND hwnd, HWND hLoginbox, LoginPage **instance); + + +}; + +#endif //NULLSOFT_AUTH_LOGIN_PAGE_ERROR_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/pageInfo.cpp b/Src/auth/Loginbox/pageInfo.cpp new file mode 100644 index 00000000..5b9ede55 --- /dev/null +++ b/Src/auth/Loginbox/pageInfo.cpp @@ -0,0 +1,131 @@ +#include "./pageInfo.h" +#include "./common.h" +#include "./loginGui.h" +#include "../resource.h" + + +static HRESULT CALLBACK LoginPageInfo_CreateInstance(HWND hwnd, HWND hLoginbox, LoginPage **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hwnd || NULL == hLoginbox) return E_INVALIDARG; + + *instance = new LoginPageInfo(hwnd, hLoginbox); + if (NULL == instance) return E_OUTOFMEMORY; + + return S_OK; +} + +LoginPageInfo::LoginPageInfo(HWND hwnd, HWND hLoginbox) + : LoginPage(hwnd, hLoginbox) +{ +} + +LoginPageInfo::~LoginPageInfo() +{ +} + +HWND LoginPageInfo::CreatePage(HWND hLoginbox, HWND hParent) +{ + return LoginPage::CreatePage(hLoginbox, MAKEINTRESOURCE(IDD_PAGE_INFO), + hParent, NULL, LoginPageInfo_CreateInstance); +} + +void LoginPageInfo::UpdateLayout(BOOL fRedraw) +{ + LoginPage::UpdateLayout(fRedraw); + + RECT pageRect; + if (FALSE == GetPageRect(&pageRect)) + return; + + + HWND hMessage = GetDlgItem(hwnd, IDC_MESSAGE); + if (NULL == hMessage) return; + + INT cx, cy; + cx = pageRect.right - pageRect.left; + + HDC hdc = GetDCEx(hMessage, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT fontControl = (HFONT)SendMessage(hMessage, WM_GETFONT, 0, 0L); + HFONT fontOrig = (HFONT)SelectObject(hdc, fontControl); + + INT maxWidth = LoginBox_GetAveStrWidth(hdc, 80); + if (cx > maxWidth) cx = maxWidth; + + SelectObject(hdc, fontOrig); + ReleaseDC(hMessage, hdc); + } + + LoginBox_GetWindowTextSize(hMessage, cx, &cx, &cy); + if(cy > (pageRect.bottom - pageRect.top)) + cy = pageRect.bottom - pageRect.top; + + RECT messageRect; + GetWindowRect(hMessage, &messageRect); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&messageRect, 2); + if (messageRect.left != pageRect.left || + messageRect.top != pageRect.top || + (messageRect.right - messageRect.left) != cx || + (messageRect.bottom - messageRect.top) != cy) + { + + SetWindowPos(hMessage, NULL, pageRect.left, pageRect.top, cx, cy, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW); + + if (FALSE != fRedraw) + { + HRGN rgn1 = CreateRectRgnIndirect(&messageRect); + HRGN rgn2 = CreateRectRgn(pageRect.left, pageRect.top, pageRect.left + cx, pageRect.top + cy); + CombineRgn(rgn1, rgn1, rgn2, RGN_OR); + RedrawWindow(hwnd, NULL, rgn1, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE | RDW_ERASENOW | RDW_VALIDATE | RDW_ALLCHILDREN); + DeleteObject(rgn1); + DeleteObject(rgn2); + } + } + +} + +BOOL LoginPageInfo::OnInitDialog(HWND hFocus, LPARAM param) +{ + HFONT fontText = NULL; + LoginGuiObject *loginGui; + if (SUCCEEDED(LoginGuiObject::QueryInstance(&loginGui))) + { + fontText = loginGui->GetTextFont(); + loginGui->Release(); + } + + if (NULL != fontText) + { + HWND hMessage = GetDlgItem(hwnd, IDC_MESSAGE); + if (NULL != hMessage) + SendMessage(hMessage, WM_SETFONT, (WPARAM)fontText, 0L); + } + + LoginPage::OnInitDialog(hFocus, param); + return FALSE; +} + + +void LoginPageInfo::OnSetMessage(LPCWSTR pszMessage) +{ + HWND hMessage = GetDlgItem(hwnd, IDC_MESSAGE); + if (NULL != hMessage) + { + SetWindowText(hMessage, pszMessage); + INT showWindow = (NULL != pszMessage && L'\0' != *pszMessage) ? SW_SHOWNA : SW_HIDE; + ShowWindow(hMessage, showWindow); + } +} + +INT_PTR LoginPageInfo::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case NLPIM_SETMESSAGE: OnSetMessage((LPCWSTR)lParam); return TRUE; + } + return __super::DialogProc(uMsg, wParam, lParam); +} + diff --git a/Src/auth/Loginbox/pageInfo.h b/Src/auth/Loginbox/pageInfo.h new file mode 100644 index 00000000..867b497d --- /dev/null +++ b/Src/auth/Loginbox/pageInfo.h @@ -0,0 +1,41 @@ +#ifndef NULLSOFT_AUTH_LOGIN_PAGE_INFO_HEADER +#define NULLSOFT_AUTH_LOGIN_PAGE_INFO_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginPage.h" + +#define NLPIM_FIRST (NLPM_PAGEFIRST + 0) + +#define NLPIM_SETMESSAGE (NLPIM_FIRST + 0) //wParam - not used, lParam = (LPARAM)(LPCWSTR)pszMessage; Return - no return value. +#define LoginPageInfo_SetMessage(/*HWND*/ __hwnd, /*LPCWSTR*/ __pszMessage)\ + (SNDMSG((__hwnd), NLPIM_SETMESSAGE, 0, (LPARAM)(__pszMessage))) + +class LoginPageInfo: public LoginPage +{ +protected: + LoginPageInfo(HWND hwnd, HWND hLoginbox); + ~LoginPageInfo(); + +public: + static HWND CreatePage(HWND hLoginbox, HWND hParent); + +protected: + void UpdateLayout(BOOL fRedraw); + + BOOL OnInitDialog(HWND hFocus, LPARAM param); + + void OnSetMessage(LPCWSTR pszMessage); + + INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + + +private: + friend static HRESULT CALLBACK LoginPageInfo_CreateInstance(HWND hwnd, HWND hLoginbox, LoginPage **instance); + + +}; + +#endif //NULLSOFT_AUTH_LOGIN_PAGE_INFO_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/png.rc b/Src/auth/Loginbox/png.rc new file mode 100644 index 00000000..27646883 --- /dev/null +++ b/Src/auth/Loginbox/png.rc @@ -0,0 +1,16 @@ +#include "../resource.h" +///////////////////////////////////////////////////////////////////////////// +// +// Data +// +IDR_POPUPBORDER_IMAGE RCDATA +".\\resources\\popupBorder.png" + +IDR_NOTIFIERICONS_IMAGE RCDATA +".\\resources\\notifierIcons.png" + +IDR_SELECTIONFRAME_IMAGE RCDATA +".\\resources\\selection.png" + +IDR_ARROW_IMAGE RCDATA +".\\resources\\arrow.png"
\ No newline at end of file diff --git a/Src/auth/Loginbox/popupAgreement.cpp b/Src/auth/Loginbox/popupAgreement.cpp new file mode 100644 index 00000000..08682f9b --- /dev/null +++ b/Src/auth/Loginbox/popupAgreement.cpp @@ -0,0 +1,567 @@ +#include "./popupAgreement.h" +#include "./loginNotifier.h" +#include "./loginProvider.h" + +#include "./common.h" +#include "../resource.h" +#include "../api.h" + +#include "../../winamp/commandLink.h" + +#include <windows.h> +#include <commctrl.h> +#include <strsafe.h> + +#define LINK_TOS 0 +#define STATIC_AND 1 +#define LINK_PRIVACY 2 + +#define IDC_AOL 11000 +#define IDC_AOL_TOS (IDC_AOL + LINK_TOS) +#define IDC_AOL_AND (IDC_AOL + STATIC_AND) +#define IDC_AOL_PRIVACY (IDC_AOL + LINK_PRIVACY) + +#define IDC_3DPARTY 11010 +#define IDC_3DPARTY_TOS (IDC_3DPARTY + LINK_TOS) +#define IDC_3DPARTY_AND (IDC_3DPARTY + STATIC_AND) +#define IDC_3DPARTY_PRIVACY (IDC_3DPARTY + LINK_PRIVACY) + + +#define LINK_TARGET L"LinkTargetProp" + +typedef struct __PARTSIZE +{ + HWND hwnd; + LONG cx; + LONG cy; +} PARTSIZE; + +typedef struct __LINKSIZEINFO +{ + PARTSIZE tos; + PARTSIZE and; + PARTSIZE privacy; + LONG spaceWidth; + RECT linkMargins; +} LINKSIZEINFO; + +static HRESULT CALLBACK LoginPopupAgreement_CreateInstance(HWND hwnd, LPARAM param, LoginPopup **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hwnd) return E_INVALIDARG; + + *instance = new LoginPopupAgreement(hwnd); + if (NULL == instance) return E_OUTOFMEMORY; + + return S_OK; +} + +LoginPopupAgreement::LoginPopupAgreement(HWND hwnd) + : LoginPopup(hwnd, -1, MAKEINTRESOURCE(IDS_POPUP_AGREEMENT_TITLE)) +{ +} + +LoginPopupAgreement::~LoginPopupAgreement() +{ + +} + +HWND LoginPopupAgreement::CreatePopup(HWND hParent, LoginProvider *provider) +{ + if (NULL == provider) + return NULL; + + return LoginPopup::CreatePopup(MAKEINTRESOURCE(IDD_POPUP_AGREEMENT), hParent, (LPARAM)provider, LoginPopupAgreement_CreateInstance); +} + +void LoginPopupAgreement::UpdateLayout(BOOL fRedraw) +{ + LoginPopup::UpdateLayout(fRedraw); + + RECT rect; + if (FALSE == GetInfoRect(&rect)) return; + + const INT szButtons[] = { IDOK, IDCANCEL, }; + + + UINT flags = SWP_NOZORDER | SWP_NOACTIVATE; + if (FALSE == fRedraw) flags |= SWP_NOREDRAW; + + HDWP hdwp = BeginDeferWindowPos(ARRAYSIZE(szButtons) + 1 + 2*3); + if (NULL == hdwp) return; + + hdwp = LayoutButtons(hdwp, szButtons, ARRAYSIZE(szButtons), fRedraw, NULL); + + LONG top = rect.top; + SIZE partSize; + HWND hText = GetDlgItem(hwnd, IDC_TEXT); + if (NULL != hText && FALSE != GetTextSize(hText, rect.right - rect.left, &partSize)) + { + hdwp = DeferWindowPos(hdwp, hText, NULL, rect.left, top, partSize.cx, partSize.cy, flags); + if (NULL == hdwp) return; + top += partSize.cy; + } + + top += marginLinkFirst; + hdwp = LayoutProviderLinks(hdwp, IDC_AOL, NULL, rect.left + marginLinkLeft, top, flags, &partSize); + if (NULL == hdwp) return; + + if (0 != partSize.cy) + top += partSize.cy + marginLinkNext; + hdwp = LayoutProviderLinks(hdwp, IDC_3DPARTY, NULL, rect.left + marginLinkLeft, top, flags, &partSize); + if (NULL == hdwp) return; + + EndDeferWindowPos(hdwp); + + if (FALSE != fRedraw) + { + HWND hControl = GetDlgItem(hwnd, IDC_TEXT); + if (NULL != hControl) InvalidateRect(hControl, NULL, FALSE); + } +} + +void LoginPopupAgreement::EndDialog(INT_PTR code) +{ + NLPNRESULT result; + result.exitCode = code; + SendNotification(NLPN_RESULT, (NMHDR*)&result); + + LoginPopup::EndDialog(code); +} + +void LoginPopupAgreement::UpdateMargins() +{ + RECT rect; + SetRect(&rect, 8, 8, 0, 2); + MapDialogRect(hwnd, &rect); + + marginLinkLeft = rect.left; + marginLinkFirst = rect.top; + marginLinkNext = rect.bottom; + + LoginPopup::UpdateMargins(); +} + +static BOOL ProviderLinks_GetSizeInfo(HWND hwnd, INT groupId, LINKSIZEINFO *sizeInfo) +{ + if(NULL == sizeInfo) + return FALSE; + + HWND hControl; + SIZE partSize; + + SetRectEmpty(&sizeInfo->linkMargins); + + hControl = GetDlgItem(hwnd, groupId + LINK_TOS); + if (NULL == hControl || + 0 == (WS_VISIBLE & GetWindowStyle(hControl)) || + FALSE == CommandLink_GetIdealSize(hControl, &partSize)) + { + ZeroMemory(&sizeInfo->tos, sizeof(PARTSIZE)); + } + else + { + sizeInfo->tos.hwnd = hControl; + sizeInfo->tos.cx = partSize.cx; + sizeInfo->tos.cy = partSize.cy; + + CommandLink_GetMargins(hControl, &sizeInfo->linkMargins); + } + + hControl = GetDlgItem(hwnd, groupId + LINK_PRIVACY); + if (NULL == hControl || + 0 == (WS_VISIBLE & GetWindowStyle(hControl)) || + FALSE == CommandLink_GetIdealSize(hControl, &partSize)) + { + ZeroMemory(&sizeInfo->privacy, sizeof(PARTSIZE)); + } + else + { + sizeInfo->privacy.hwnd = hControl; + sizeInfo->privacy.cx = partSize.cx; + sizeInfo->privacy.cy = partSize.cy; + + if (IsRectEmpty(&sizeInfo->linkMargins)) + CommandLink_GetMargins(hControl, &sizeInfo->linkMargins); + } + + if (NULL == sizeInfo->tos.hwnd && NULL == sizeInfo->privacy.hwnd) + return FALSE; + + ZeroMemory(&sizeInfo->and, sizeof(PARTSIZE)); + sizeInfo->spaceWidth = 0; + + if (NULL != sizeInfo->tos.hwnd && NULL != sizeInfo->privacy.hwnd) + { + hControl = GetDlgItem(hwnd, groupId + STATIC_AND); + if (NULL != hControl) + { + WCHAR szBuffer[64] = {0}; + INT cchLen = (INT)SendMessage(hControl, WM_GETTEXT, ARRAYSIZE(szBuffer), (LPARAM)szBuffer); + if (cchLen > 0) + { + HDC hdc = GetDCEx(hControl, NULL, DCX_CACHE | DCX_WINDOW | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT font = (HFONT)SendMessage(hControl, WM_GETFONT, 0, 0L); + HFONT fontOrig = (HFONT)SelectObject(hdc, font); + + if (FALSE != GetTextExtentPoint32W(hdc, szBuffer, cchLen, &partSize)) + { + sizeInfo->and.hwnd = hControl; + sizeInfo->and.cx = partSize.cx; + sizeInfo->and.cy = partSize.cy; + } + + if (FALSE != GetTextExtentPoint32W(hdc, L" ", 1, &partSize)) + { + sizeInfo->spaceWidth = partSize.cx; + } + + SelectObject(hdc, fontOrig); + ReleaseDC(hControl, hdc); + } + } + } + } + return TRUE; +} + +HDWP LoginPopupAgreement::LayoutProviderLinks(HDWP hdwp, INT groupId, HWND hwndInsertAfter, INT x, INT y, UINT flags, SIZE *size) +{ + if (NULL == hdwp) + { + if (NULL == size) + return NULL; + x = 0; + y = 0; + } + + LONG ox = x, cy = 0; + + LINKSIZEINFO sizeInfo; + if (FALSE == ProviderLinks_GetSizeInfo(hwnd, groupId, &sizeInfo)) + return hdwp; + + if (NULL != sizeInfo.tos.hwnd) + { + if (NULL != hdwp) + { + hdwp = DeferWindowPos(hdwp, sizeInfo.tos.hwnd, hwndInsertAfter, x, y, sizeInfo.tos.cx, sizeInfo.tos.cy, flags); + if (NULL == hdwp) return NULL; + } + + x += sizeInfo.tos.cx; + if (cy < sizeInfo.tos.cy) cy = sizeInfo.tos.cy; + } + + if (NULL != sizeInfo.and.hwnd) + { + LONG top = y + ((sizeInfo.tos.cy - (sizeInfo.linkMargins.bottom + sizeInfo.linkMargins.top)) - sizeInfo.and.cy)/2; + LONG space = (sizeInfo.spaceWidth - sizeInfo.linkMargins.right); + if (space < 1) space = 1; + x += space; + if (NULL != hdwp) + { + hdwp = DeferWindowPos(hdwp, sizeInfo.and.hwnd, hwndInsertAfter, x, top, sizeInfo.and.cx, sizeInfo.and.cy, flags); + if (NULL == hdwp) return NULL; + } + + x += sizeInfo.and.cx; + if (cy < sizeInfo.and.cy) cy = sizeInfo.and.cy; + } + + if (NULL != sizeInfo.privacy.hwnd) + { + if (NULL != sizeInfo.and.hwnd) + { + LONG space = (sizeInfo.spaceWidth - sizeInfo.linkMargins.left); + if (space < 1) space = 1; + x += space; + } + + if (NULL != hdwp) + { + hdwp = DeferWindowPos(hdwp, sizeInfo.privacy.hwnd, hwndInsertAfter, x, y, sizeInfo.privacy.cx, sizeInfo.privacy.cy, flags); + if (NULL == hdwp) return NULL; + } + + x += sizeInfo.privacy.cx; + if (cy < sizeInfo.privacy.cy) cy = sizeInfo.privacy.cy; + + } + + if (NULL != size) + { + size->cx = (x - ox); + size->cy = cy; + } + + return (NULL == hdwp) ? (HDWP)(TRUE) : hdwp; +} + +BOOL LoginPopupAgreement::CreateProviderLinks(LPCWSTR pszProvider, LPCWSTR pszTos, LPCWSTR pszPrivacy, INT groupId, HWND hwndInsertAfter) +{ + WCHAR szTemplate[256] = {0}, szBuffer[256] = {0}; + UINT linkStyle = WS_CHILD | WS_TABSTOP | WS_GROUP | WS_VISIBLE | + CLS_DEFAULTCOLORS | CLS_HOTTRACK /*| CLS_ALWAYSUNDERLINE*/; + + HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L); + + LPWSTR pszUrl; + HWND hControl; + INT createdCount = 0; + INT failedCount = 0; + if (NULL != pszTos && L'\0' != *pszTos) + { + WASABI_API_LNGSTRINGW_BUF(IDS_TOSLINK_TEMPLATE, szTemplate, ARRAYSIZE(szTemplate)); + StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szTemplate, pszProvider); + hControl = CommandLink_CreateWindow(0, szBuffer, linkStyle, 0, 0, 0, 0, hwnd, groupId + LINK_TOS); + if (NULL != hControl) + { + pszUrl = LoginBox_CopyString(pszTos); + if (NULL == pszUrl || FALSE == SetProp(hControl, LINK_TARGET, pszUrl)) + { + LoginBox_FreeString(pszUrl); + DestroyWindow(hControl); + hControl = NULL; + } + } + + if (NULL != hControl) + { + SendMessage(hControl, WM_SETFONT, (WPARAM)font, 0L); + SetWindowPos(hControl, hwndInsertAfter, 0, 0,0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW); + hwndInsertAfter = hControl; + createdCount++; + } + else failedCount++; + } + + if (0 == failedCount && NULL != pszPrivacy && L'\0' != *pszPrivacy) + { + WASABI_API_LNGSTRINGW_BUF(IDS_PRIVACYLINK_TEMPLATE, szTemplate, ARRAYSIZE(szTemplate)); + StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szTemplate, pszProvider); + hControl = CommandLink_CreateWindow(0, szBuffer, linkStyle, 0, 0, 0, 0, hwnd, groupId + LINK_PRIVACY); + if (NULL != hControl) + { + pszUrl = LoginBox_CopyString(pszPrivacy); + if (NULL == pszUrl || FALSE == SetProp(hControl, LINK_TARGET, pszUrl)) + { + LoginBox_FreeString(pszUrl); + DestroyWindow(hControl); + hControl = NULL; + } + } + if (NULL != hControl) + { + SendMessage(hControl, WM_SETFONT, (WPARAM)font, 0L); + SetWindowPos(hControl, hwndInsertAfter, 0, 0,0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW); + hwndInsertAfter = hControl; + createdCount++; + } + else failedCount++; + } + + if (0 == failedCount && createdCount > 1) + { + WASABI_API_LNGSTRINGW_BUF(IDS_AND, szBuffer, ARRAYSIZE(szBuffer)); + hControl = CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Static", szBuffer, WS_CHILD | WS_VISIBLE | SS_LEFT, + 0, 0, 0, 0, hwnd, (HMENU)(INT_PTR)(groupId + STATIC_AND), NULL, 0L); + if (NULL != hControl) + { + SendMessage(hControl, WM_SETFONT, (WPARAM)font, 0L); + SetWindowPos(hControl, hwndInsertAfter, 0, 0,0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW); + hwndInsertAfter = hControl; + createdCount++; + } + else + failedCount++; + } + + if (0 != failedCount) + { + hControl = GetDlgItem(hwnd, (groupId + LINK_TOS)); + if (NULL != hControl) DestroyWindow(hControl); + hControl = GetDlgItem(hwnd, (groupId + LINK_PRIVACY)); + if (NULL != hControl) DestroyWindow(hControl); + hControl = GetDlgItem(hwnd, (groupId + STATIC_AND)); + if (NULL != hControl) DestroyWindow(hControl); + } + + return (0 == failedCount); +} + +BOOL LoginPopupAgreement::OnInitDialog(HWND hFocus, LPARAM param) +{ + LoginProvider *provider = (LoginProvider*)param; + if (NULL != provider) + { + WCHAR szName[128] = {0}, szTos[4096] = {0}, szPrivacy[4096] = {0}; + if (FAILED(provider->GetTosLink(szTos, ARRAYSIZE(szTos)))) + szTos[0] = L'\0'; + if (FAILED(provider->GetPrivacyLink(szPrivacy, ARRAYSIZE(szPrivacy)))) + szPrivacy[0] = L'\0'; + + if((L'\0' != szTos[0] || L'\0' != szPrivacy[0]) && + SUCCEEDED(provider->GetName(szName, ARRAYSIZE(szName)))) + { + CreateProviderLinks(szName, szTos, szPrivacy, IDC_3DPARTY, NULL); + } + } + + CreateProviderLinks(L"AOL", L"https://new.aol.com/freeaolweb/resources/jsp/mem_tos.jsp", + L"http://about.aol.com/aolnetwork/mem_policy", IDC_AOL, NULL); + + LoginPopup::OnInitDialog(hFocus, param); + + HWND hAgree = GetDlgItem(hwnd, IDOK); + if (NULL != hAgree && (WS_VISIBLE == ((WS_VISIBLE | WS_DISABLED) & GetWindowStyle(hAgree)))) + { + PostMessage(hwnd, WM_NEXTDLGCTL, (WPARAM)hAgree, TRUE); + return TRUE; + } + + return FALSE; +} +void LoginPopupAgreement::OnDestroy() +{ + INT szLinks[] = {IDC_AOL_TOS, IDC_AOL_PRIVACY, IDC_3DPARTY_TOS, IDC_3DPARTY_PRIVACY, }; + for (INT i = 0; i < ARRAYSIZE(szLinks); i++) + { + HWND hLink = GetDlgItem(hwnd, szLinks[i]); + if (NULL != hLink) DestroyWindow(hLink); + } +} + +BOOL LoginPopupAgreement::OnUpdateWindowPos(const RECT* clientRect, RECT *rectOut) +{ + if (NULL == clientRect || NULL == rectOut) + return FALSE; + + SIZE maxSize; + SIZE partSize; + + maxSize.cx = 0; + maxSize.cy = 0; + + if (((HDWP)TRUE) == LayoutProviderLinks(NULL, IDC_AOL, NULL, 0, 0, 0, &partSize)) + { + if (maxSize.cx < partSize.cx) maxSize.cx = partSize.cx; + maxSize.cy += (partSize.cy + marginLinkFirst); + } + + if (((HDWP)TRUE) == LayoutProviderLinks(NULL, IDC_3DPARTY, NULL, 0, 0, 0, &partSize)) + { + if (maxSize.cx < partSize.cx) maxSize.cx = partSize.cx; + maxSize.cy += (partSize.cy + ((0 == maxSize.cy) ? marginLinkFirst : marginLinkNext)); + } + + if (0 != maxSize.cx) + maxSize.cx += marginLinkLeft; + + if (0 != maxSize.cy) + maxSize.cy += marginLinkFirst; + + LONG maxWidth = clientRect->right - clientRect->left - + (clientMargins.right + clientMargins.left) - + (infoMargins.right + infoMargins.left); + + if (maxSize.cx > maxWidth) + maxSize.cx = maxWidth; + + if (FALSE != GetTextSize(GetDlgItem(hwnd, IDC_TEXT), maxWidth, &partSize)) + { + if (maxSize.cx < partSize.cx) maxSize.cx = partSize.cx; + maxSize.cy += partSize.cy; + } + + if (FALSE == CalculateWindowRect(maxSize.cx, maxSize.cy, NULL, 0, TRUE, rectOut)) + return FALSE; + + + LONG ox = clientRect->left + ((clientRect->right - clientRect->left) - (rectOut->right - rectOut->left))/2; + LONG oy = clientRect->top + ((clientRect->bottom - clientRect->top) - (rectOut->bottom - rectOut->top))/2; + + if (ox < clientRect->left) ox = clientRect->left; + if (oy < clientRect->top) oy = clientRect->top; + + OffsetRect(rectOut, ox, oy); + return TRUE; +} +HBRUSH LoginPopupAgreement::OnGetStaticColor(HDC hdc, HWND hControl) +{ + HBRUSH hb = LoginPopup::OnGetStaticColor(hdc, hControl); + INT controlId = (NULL != hControl) ? (INT)GetWindowLongPtr(hControl, GWLP_ID) : 0; + switch(controlId) + { + case IDC_AOL_AND: + case IDC_3DPARTY_AND: + { + HWND hLink = GetDlgItem(hwnd, (controlId - STATIC_AND)); + if (NULL != hLink) + { + // SetTextColor(hdc, CommandLink_GetTextColor(hLink)); + } + } + break; + } + + return hb; +} + +void LoginPopupAgreement::OnParentNotify(UINT eventId, UINT wParam, LPARAM lParam) +{ + switch(eventId) + { + case WM_DESTROY: + switch(wParam) + { + case IDC_AOL_TOS: + case IDC_AOL_PRIVACY: + case IDC_3DPARTY_TOS: + case IDC_3DPARTY_PRIVACY: + { + LPWSTR url = (LPWSTR)GetProp((HWND)lParam, LINK_TARGET); + RemoveProp((HWND)lParam, LINK_TARGET); + LoginBox_FreeString(url); + } + break; + } + break; + + } + LoginPopup::OnParentNotify(eventId, wParam, lParam); +} + + +void LoginPopupAgreement::OnLinkClicked(HWND hLink) +{ + if (NULL == hLink) + return; + + LPCWSTR pszTarget = (LPCWSTR)GetProp(hLink, LINK_TARGET); + if (NULL != pszTarget && L'\0' != *pszTarget) + { + LoginBox_OpenUrl(hwnd, pszTarget, TRUE); + } +} + +LRESULT LoginPopupAgreement::OnNotify(UINT controlId, const NMHDR *pnmh) +{ + switch(controlId) + { + case IDC_AOL_TOS: + case IDC_AOL_PRIVACY: + case IDC_3DPARTY_TOS: + case IDC_3DPARTY_PRIVACY: + switch(pnmh->code) + { + case NM_CLICK: + OnLinkClicked(pnmh->hwndFrom); + return 0; + } + break; + } + return LoginPopup::OnNotify(controlId, pnmh); +} diff --git a/Src/auth/Loginbox/popupAgreement.h b/Src/auth/Loginbox/popupAgreement.h new file mode 100644 index 00000000..39f0c964 --- /dev/null +++ b/Src/auth/Loginbox/popupAgreement.h @@ -0,0 +1,48 @@ +#ifndef NULLSOFT_AUTH_LOGINPOPUP_AGREEMENT_HEADER +#define NULLSOFT_AUTH_LOGINPOPUP_AGREEMENT_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginPopup.h" + +class LoginProvider; + +class LoginPopupAgreement : public LoginPopup +{ +protected: + LoginPopupAgreement(HWND hwnd); + ~LoginPopupAgreement(); + +public: + static HWND CreatePopup(HWND hParent, LoginProvider *provider); + +protected: + void UpdateLayout(BOOL fRedraw); + void EndDialog(INT_PTR code); + void UpdateMargins(); + + BOOL CreateProviderLinks(LPCWSTR pszProvider, LPCWSTR pszTos, LPCWSTR pszPrivacy, INT groupId, HWND hwndInsertAfter); + HDWP LayoutProviderLinks(HDWP hdwp, INT groupId, HWND hwndInsertAfter, INT x, INT y, UINT flags, SIZE *size); // pass hdwp = NULL to get ideal size + + + void OnLinkClicked(HWND hLink); + HBRUSH OnGetStaticColor(HDC hdc, HWND hControl); + LRESULT OnNotify(UINT controlId, const NMHDR *pnmh); + void OnParentNotify(UINT eventId, UINT wParam, LPARAM lParam); + BOOL OnUpdateWindowPos(const RECT* clientRect, RECT *rectOut); + BOOL OnInitDialog(HWND hFocus, LPARAM param); + void OnDestroy(); + +protected: + LONG marginLinkLeft; + LONG marginLinkFirst; + LONG marginLinkNext; + +private: + friend static HRESULT CALLBACK LoginPopupAgreement_CreateInstance(HWND hwnd, LPARAM param, LoginPopup **instance); +}; + + +#endif //NULLSOFT_AUTH_LOGINPOPUP_AGREEMENT_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/popupMessage.cpp b/Src/auth/Loginbox/popupMessage.cpp new file mode 100644 index 00000000..ca3311a1 --- /dev/null +++ b/Src/auth/Loginbox/popupMessage.cpp @@ -0,0 +1,253 @@ +#include "./popupMessage.h" +#include "./loginNotifier.h" +#include "./common.h" + +#include "../resource.h" +#include "../api.h" + +#include <windows.h> +#include <strsafe.h> + +typedef struct __MESSAGECREATEPARAM +{ + UINT type; + LPCWSTR title; + LPCWSTR message; + LoginPopupMessage::ResultCallback callback; + LPARAM param; +} MESSAGECREATEPARAM; + +typedef struct __MESSAGEBUTTON +{ + INT id; + LPCWSTR pTitle; + BOOL fGroup; + BOOL fDisabled; + BOOL fDefault; +} MESSAGEBUTTON; + +const static MESSAGEBUTTON szTypeContinue[] = +{ + { IDOK, MAKEINTRESOURCE(IDS_BUTTON_CONTINUE), TRUE, FALSE, TRUE, }, +}; + +const static MESSAGEBUTTON szTypeYesNo[] = +{ + { IDYES, MAKEINTRESOURCE(IDS_BUTTON_YES), TRUE, FALSE, TRUE, }, + { IDNO, MAKEINTRESOURCE(IDS_BUTTON_NO), FALSE, FALSE, FALSE, }, +}; + +static HRESULT CALLBACK LoginPopupMessage_CreateInstance(HWND hwnd, LPARAM param, LoginPopup **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hwnd) return E_INVALIDARG; + + *instance = new LoginPopupMessage(hwnd); + + if (NULL == instance) return E_OUTOFMEMORY; + + return S_OK; +} + +LoginPopupMessage::LoginPopupMessage(HWND hwnd) + : LoginPopup(hwnd, NLNTYPE_INFORMATION, NULL), callback(NULL), param(0L), + buttonsCount(0) +{ + memset(szButtons, 0, sizeof(szButtons)); +} + +LoginPopupMessage::~LoginPopupMessage() +{ +} + +HWND LoginPopupMessage::CreatePopup(HWND hParent, LPCWSTR pszTitle, LPCWSTR pszMessage, UINT uType, ResultCallback callback, LPARAM param) +{ + switch(typeMask & uType) + { + case typeContinue: + case typeYesNo: + break; + default: + return NULL; + } + + MESSAGECREATEPARAM createParam; + createParam.type = uType; + createParam.title = pszTitle; + createParam.message = pszMessage; + createParam.callback = callback; + createParam.param = param; + + return LoginPopup::CreatePopup(MAKEINTRESOURCE(IDD_POPUP_MESSAGE), hParent, + (LPARAM)&createParam, LoginPopupMessage_CreateInstance); +} + +void LoginPopupMessage::UpdateLayout(BOOL fRedraw) +{ + LoginPopup::UpdateLayout(fRedraw); + + RECT rect; + if (FALSE == GetInfoRect(&rect)) return; + + HDWP hdwp = BeginDeferWindowPos(1 + buttonsCount); + if (NULL == hdwp) return; + + UINT flags = SWP_NOZORDER | SWP_NOACTIVATE; + if (FALSE == fRedraw) flags |= SWP_NOREDRAW; + + HWND hMessage = GetDlgItem(hwnd, IDC_MESSAGE); + if (NULL != hMessage) + { + hdwp = DeferWindowPos(hdwp, hMessage, NULL, + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags); + + if (NULL == hdwp) return; + } + + if (NULL != buttonsCount) + hdwp = LayoutButtons(hdwp, szButtons, buttonsCount, fRedraw, NULL); + + EndDeferWindowPos(hdwp); +} + + +void LoginPopupMessage::EndDialog(INT_PTR code) +{ + ResultCallback callbackCopy = callback; + LPARAM paramCopy = param; + + + NLPNRESULT result; + result.exitCode = code; + SendNotification(NLPN_RESULT, (NMHDR*)&result); + + if (NULL != callbackCopy) + callbackCopy(hwnd, code, paramCopy); + + LoginPopup::EndDialog(code); +} + +static BOOL LoginPopupMessage_CreateButtons(HWND hwnd, const MESSAGEBUTTON *buttonList, UINT *buttonsCount, INT *buttonIdList) +{ + if (NULL == hwnd || NULL == buttonList || NULL == buttonsCount || 0 == *buttonsCount) + return FALSE; + + UINT count = *buttonsCount; + *buttonsCount = 0; + + WCHAR szBuffer[256] = {0}; + RECT rect; + SetRect(&rect, 50, 15, 0, 0); + MapDialogRect(hwnd, &rect); + + LONG width = rect.left; + LONG height = rect.top; + + HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L); + + for (UINT i = 0; i < count; i++) + { + LPCWSTR title = buttonList[i].pTitle; + if (NULL != title && FALSE != IS_INTRESOURCE(title)) + { + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)title, szBuffer, ARRAYSIZE(szBuffer)); + title = szBuffer; + } + + UINT style = BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP; + if (FALSE != buttonList[i].fGroup) style |= WS_GROUP; + if (FALSE != buttonList[i].fDisabled) style |= WS_DISABLED; + if (FALSE != buttonList[i].fDefault) style |= BS_DEFPUSHBUTTON; + + HWND hButton = CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Button", title, style, + 0, 0, width, height, hwnd, (HMENU)(INT_PTR)buttonList[i].id, NULL, 0L); + + if (NULL != hButton) + { + if (NULL != font) + SendMessage(hButton, WM_SETFONT, (WPARAM)font, 0L); + + if (NULL != buttonIdList) + buttonIdList[*buttonsCount] = buttonList[i].id; + + (*buttonsCount)++; + } + + } + return TRUE; +} + +BOOL LoginPopupMessage::OnInitDialog(HWND hFocus, LPARAM param) +{ + MESSAGECREATEPARAM *createParam = (MESSAGECREATEPARAM*)param; + + if (NULL != createParam) + { + callback = createParam->callback; + param = createParam->param; + + switch(iconMask & createParam->type) + { + case iconInfo: popupType = NLNTYPE_INFORMATION; break; + case iconWarning: popupType = NLNTYPE_WARNING; break; + case iconError: popupType = NLNTYPE_ERROR; break; + } + SetTitle(popupType, createParam->title); + + switch(typeMask & createParam->type) + { + case typeContinue: + buttonsCount = ARRAYSIZE(szTypeContinue); + LoginPopupMessage_CreateButtons(hwnd, szTypeContinue, &buttonsCount, szButtons); + break; + case typeYesNo: + buttonsCount = ARRAYSIZE(szTypeYesNo); + LoginPopupMessage_CreateButtons(hwnd, szTypeYesNo, &buttonsCount, szButtons); + break; + } + + HWND hMessage = GetDlgItem(hwnd, IDC_MESSAGE); + if (NULL != hMessage) + { + if (NULL != createParam->message && FALSE != IS_INTRESOURCE(createParam->message)) + { + WCHAR szBuffer[4096] = {0}; + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)createParam->message, szBuffer, ARRAYSIZE(szBuffer)); + SendMessage(hMessage, WM_SETTEXT, 0, (LPARAM)szBuffer); + } + else + SendMessage(hMessage, WM_SETTEXT, 0, (LPARAM)createParam->message); + } + } + return LoginPopup::OnInitDialog(hFocus, param); +} + +BOOL LoginPopupMessage::OnUpdateWindowPos(const RECT* clientRect, RECT *rectOut) +{ + if (NULL == clientRect || NULL == rectOut) + return FALSE; + + SIZE size; + + LONG maxWidth = clientRect->right - clientRect->left - + (clientMargins.right + clientMargins.left) - + (infoMargins.right + infoMargins.left); + + if (FALSE == GetTextSize(GetDlgItem(hwnd, IDC_MESSAGE), maxWidth, &size)) + { + size.cx = 0; + size.cy = 0; + } + + if (FALSE == CalculateWindowRect(size.cx, size.cy, szButtons, buttonsCount, TRUE, rectOut)) + return FALSE; + + LONG ox = clientRect->left + ((clientRect->right - clientRect->left) - (rectOut->right - rectOut->left))/2; + LONG oy = clientRect->top + ((clientRect->bottom - clientRect->top) - (rectOut->bottom - rectOut->top))/2; + + if (ox < clientRect->left) ox = clientRect->left; + if (oy < clientRect->top) oy = clientRect->top; + + OffsetRect(rectOut, ox, oy); + return TRUE; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/popupMessage.h b/Src/auth/Loginbox/popupMessage.h new file mode 100644 index 00000000..bed5dc5c --- /dev/null +++ b/Src/auth/Loginbox/popupMessage.h @@ -0,0 +1,52 @@ +#ifndef NULLSOFT_AUTH_LOGINPOPUP_MESSAGE_HEADER +#define NULLSOFT_AUTH_LOGINPOPUP_MESSAGE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginPopup.h" + +class LoginPopupMessage : public LoginPopup +{ +public: + typedef enum + { + typeMask = 0x0000FFFF, + typeContinue = 0x00000000, + typeYesNo = 0x00000001, + iconMask = 0xFFFF0000, + iconInfo = 0x00000000, + iconWarning = 0x00010000, + iconError = 0x00020000, + } Type; + + typedef void (CALLBACK *ResultCallback)(HWND /*hPopup*/, INT_PTR /*resultCode*/, LPARAM param); + +protected: + LoginPopupMessage(HWND hwnd); + ~LoginPopupMessage(); + +public: + static HWND CreatePopup(HWND hParent, LPCWSTR pszTitle, LPCWSTR pszMessage, UINT uType, ResultCallback callback, LPARAM param); + +protected: + void UpdateLayout(BOOL fRedraw); + void EndDialog(INT_PTR code); + + BOOL OnInitDialog(HWND hFocus, LPARAM param); + BOOL OnUpdateWindowPos(const RECT* clientRect, RECT *rectOut); + +protected: + ResultCallback callback; + LPARAM param; + INT szButtons[8]; + UINT buttonsCount; + +private: + friend static HRESULT CALLBACK LoginPopupMessage_CreateInstance(HWND hwnd, LPARAM param, LoginPopup **instance); +}; + + + +#endif //NULLSOFT_AUTH_LOGINPOPUP_MESSAGE_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/popupPasscode.cpp b/Src/auth/Loginbox/popupPasscode.cpp new file mode 100644 index 00000000..583a817d --- /dev/null +++ b/Src/auth/Loginbox/popupPasscode.cpp @@ -0,0 +1,405 @@ +#include "./popupPasscode.h" +#include "./common.h" +#include "./loginNotifier.h" +#include "./dataCredentials.h" +#include "./editboxExtender.h" + +#include "../resource.h" +#include "../api.h" +#include "../api_auth.h" + +#include <windows.h> +#include <commctrl.h> +#include <strsafe.h> + +#define MAX_PASSCODE_LENGTH 6 +#define MIN_PASSCODE_LENGTH 6 + +#define TIMER_UPDATETITLE_ID 14 +#define TIMER_UPDATETITLE_DELAY 50 + +static HRESULT CALLBACK LoginPopupPasscode_CreateInstance(HWND hwnd, LPARAM param, LoginPopup **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == hwnd) return E_INVALIDARG; + + *instance = new LoginPopupPasscode(hwnd); + if (NULL == instance) return E_OUTOFMEMORY; + + return S_OK; +} + +LoginPopupPasscode::LoginPopupPasscode(HWND hwnd) + : LoginPopup(hwnd, -1, MAKEINTRESOURCE(IDS_POPUP_PASSCODE_TITLE)), + loginData(NULL), message(NULL), messageType(-1) +{ +} + +LoginPopupPasscode::~LoginPopupPasscode() +{ + if (NULL != loginData) + loginData->Release(); + + LoginBox_FreeString(message); +} + +HWND LoginPopupPasscode::CreatePopup(HWND hParent, LoginData *loginData) +{ + LoginDataCredentials *credentials; + if (NULL == loginData || + FAILED(loginData->QueryInterface(IID_LoginDataCredentials, (void**)&credentials))) + { + return NULL; + } + + HWND hwnd = LoginPopup::CreatePopup(MAKEINTRESOURCE(IDD_POPUP_PASSCODE), hParent, (LPARAM)credentials, LoginPopupPasscode_CreateInstance); + credentials->Release(); + return hwnd; +} + +void LoginPopupPasscode::UpdateLayout(BOOL fRedraw) +{ + LoginPopup::UpdateLayout(fRedraw); + + RECT rect; + if (FALSE == GetInfoRect(&rect)) return; + + const INT szButtons[] = { IDOK, }; + const INT szControls[] = { IDC_PASSCODE_TITLE, IDC_PASSCODE_EDIT, IDC_PASSCODE_HINT, }; + + HDWP hdwp = BeginDeferWindowPos(ARRAYSIZE(szControls) + ARRAYSIZE(szButtons)); + if (NULL == hdwp) return; + + hdwp = LayoutButtons(hdwp, szButtons, ARRAYSIZE(szButtons), fRedraw, NULL); + + LONG top = rect.top; + RECT controlRect; + + for(INT i = 0; i < ARRAYSIZE(szControls); i++) + { + HWND hControl = GetDlgItem(hwnd, szControls[i]); + if (NULL == hControl || FALSE == GetWindowRect(hControl, &controlRect)) + continue; + + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&controlRect, 2); + + hdwp = DeferWindowPos(hdwp, hControl, NULL, + rect.left, top, rect.right - rect.left, controlRect.bottom - controlRect.top, + SWP_NOACTIVATE | SWP_NOZORDER); + + if (NULL == hdwp) + return; + + top += controlRect.bottom - controlRect.top; + + switch(szControls[i]) + { + case IDC_PASSCODE_TITLE: top += 2; break; + } + + } + + + + EndDeferWindowPos(hdwp); +} + + +void LoginPopupPasscode::EndDialog(INT_PTR code) +{ + if (IDOK == code) + { + code = -1; + if (NULL != loginData) + { + HWND hEdit = GetDlgItem(hwnd, IDC_PASSCODE_EDIT); + if (NULL != hEdit) + { + WCHAR szBuffer[64] = {0}; + GetWindowText(hEdit, szBuffer, ARRAYSIZE(szBuffer)); + if(SUCCEEDED(loginData->SetPasscode(szBuffer))) + code = IDOK; + } + } + } + + NPPNRESULT result; + result.exitCode = code; + result.loginData = loginData; + if (NULL != result.loginData) + result.loginData->AddRef(); + + SendNotification(NPPN_RESULT, (NMHDR*)&result); + + if (NULL != result.loginData) + result.loginData->Release(); + + LoginPopup::EndDialog(code); +} + +BOOL LoginPopupPasscode::Validate() +{ + BOOL validatedOk = TRUE; + + HWND hEdit = GetDlgItem(hwnd, IDC_PASSCODE_EDIT); + if (NULL == hEdit) + { + SetAlert(NLNTYPE_ERROR, MAKEINTRESOURCE(IDS_ERR_UNEXPECTED)); + validatedOk = FALSE; + } + else + { + INT cchText = (INT)SendMessage(hEdit, WM_GETTEXTLENGTH, 0, 0L); + if (cchText < MIN_PASSCODE_LENGTH || cchText > MAX_PASSCODE_LENGTH) + { + WCHAR szBuffer[256] = {0}, szTemplate[256] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_ERR_PASSCODE_BADLENGTH_TEMPLATE, szTemplate, ARRAYSIZE(szTemplate)); + StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szTemplate, MAX_PASSCODE_LENGTH); + SetAlert(NLNTYPE_ERROR, szBuffer); + validatedOk = FALSE; + } + else + { + WCHAR szPasscode[MAX_PASSCODE_LENGTH + 1] = {0}; + WORD szType[MAX_PASSCODE_LENGTH] = {0}; + SendMessage(hEdit, WM_GETTEXT, (WPARAM)ARRAYSIZE(szPasscode), (LPARAM)szPasscode); + if (FALSE != GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, szPasscode, cchText, szType)) + { + for(INT i = 0; i < cchText; i++) + { + if (0 == (C1_DIGIT & szType[i])) + { + SetAlert(NLNTYPE_ERROR, MAKEINTRESOURCE(IDS_ERR_PASSCODE_ONLYDIGITS)); + validatedOk = FALSE; + break; + } + } + } + else + { + SetAlert(NLNTYPE_ERROR, MAKEINTRESOURCE(IDS_ERR_UNEXPECTED)); + validatedOk = FALSE; + } + } + } + + UpdateTitle(TRUE); + return validatedOk; +} + +BOOL LoginPopupPasscode::OnInitDialog(HWND hFocus, LPARAM param) +{ + loginData = (LoginDataCredentials*)param; + if (NULL != loginData) + loginData->AddRef(); + + if (NULL != loginData) + { + LPCWSTR pcode = loginData->GetPasscode(); + if (NULL != pcode && L'\0' != *pcode) + { + SetAlert(NLNTYPE_ERROR, MAKEINTRESOURCE(IDS_ERR_PASSCODE_INVALID)); + UpdateTitle(FALSE); + } + } + + HWND hEdit = GetDlgItem(hwnd, IDC_PASSCODE_EDIT); + if (NULL != hEdit) + { + EditboxExtender_AttachWindow(hEdit); + } + + HWND hHint = GetDlgItem(hwnd, IDC_PASSCODE_HINT); + if (NULL != hHint) + { + WCHAR szBuffer[128], szTemplate[128] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_PASSCODE_EDIT_HINT, szTemplate, ARRAYSIZE(szTemplate)); + StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szTemplate, MAX_PASSCODE_LENGTH); + SendMessage(hHint, WM_SETTEXT, 0, (LPARAM)szBuffer); + } + + return LoginPopup::OnInitDialog(hFocus, param); +} + +void LoginPopupPasscode::OnCommand(UINT commandId, UINT eventType, HWND hControl) +{ + if (IDOK == commandId && FALSE == Validate()) + return; + + LoginPopup::OnCommand(commandId, eventType, hControl); +} + +LRESULT LoginPopupPasscode::OnNotify(UINT controlId, const NMHDR *pnmh) +{ + switch(controlId) + { + case IDC_PASSCODE_EDIT: + switch(pnmh->code) + { + case NM_CHAR: + return OnEditboxChar(pnmh->hwndFrom, ((NMCHAR*)pnmh)->ch); + case NM_KEYDOWN: + return OnEditboxKey(pnmh->hwndFrom, ((NMKEY*)pnmh)->nVKey, ((NMKEY*)pnmh)->uFlags); + case EENM_PASTE: + return OnEditboxPaste(pnmh->hwndFrom, ((EENMPASTE*)pnmh)->text); + } + return 0; + } + return LoginPopup::OnNotify(controlId, pnmh); +} + +HBRUSH LoginPopupPasscode::OnGetStaticColor(HDC hdc, HWND hControl) +{ + HBRUSH hb = LoginPopup::OnGetStaticColor(hdc, hControl); + + INT controlId = (NULL != hControl) ? (INT)GetWindowLongPtr(hControl, GWLP_ID) : 0; + switch(controlId) + { + case IDC_PASSCODE_HINT: + SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); + break; + } + + return hb; +} + + +LRESULT LoginPopupPasscode::OnEditboxKey(HWND hEdit, UINT vKey, UINT flags) +{ + if (-1 != alertType) + { + RemoveAlert(); + SetTimer(hwnd, TIMER_UPDATETITLE_ID, TIMER_UPDATETITLE_DELAY, NULL); + } + return 0L; +} + +LRESULT LoginPopupPasscode::OnEditboxChar(HWND hEdit, UINT ch) +{ + if (VK_BACK == ch) + return FALSE; + + BOOL filtered = FALSE; + + INT selBegin, selEnd; + SendMessage(hEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd); + + INT cchText = (INT)SendMessage(hEdit, WM_GETTEXTLENGTH, 0, 0L); + if (selBegin == selEnd && cchText >= MAX_PASSCODE_LENGTH) + { + WCHAR szBuffer[256] = {0}, szTemplate[256] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_ERR_PASSCODE_BADLENGTH_TEMPLATE, szTemplate, ARRAYSIZE(szTemplate)); + StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szTemplate, MAX_PASSCODE_LENGTH); + SetAlert(NLNTYPE_WARNING, szBuffer); + KillTimer(hwnd, TIMER_UPDATETITLE_ID); + UpdateTitle(TRUE); + filtered = TRUE; + } + else + { + WORD charType; + if (FALSE != GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, (WCHAR*)&ch, 1, &charType)) + { + if (0 == (C1_DIGIT & charType)) + { + SetAlert(NLNTYPE_WARNING, MAKEINTRESOURCE(IDS_ERR_PASSCODE_ONLYDIGITS)); + KillTimer(hwnd, TIMER_UPDATETITLE_ID); + UpdateTitle(TRUE); + filtered = TRUE; + } + } + } + + return filtered; +} + +LRESULT LoginPopupPasscode::OnEditboxPaste(HWND hEdit, LPCWSTR pszText) +{ + if (NULL == pszText) + return TRUE; + + INT cchText = lstrlen(pszText); + if (0 == cchText) return TRUE; + + WCHAR szBuffer[MAX_PASSCODE_LENGTH + 1] = {0}; + INT iBuffer = 0; + + WORD charType; + + BOOL validatedOk = TRUE; + + + INT maxSize = MAX_PASSCODE_LENGTH - (INT)SendMessage(hEdit, WM_GETTEXTLENGTH, 0, 0L); + if (maxSize < MAX_PASSCODE_LENGTH) + { + INT selBegin, selEnd; + SendMessage(hEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd); + selEnd -= selBegin; + if (selEnd < 0) selEnd = -selBegin; + maxSize += selEnd; + } + for (INT i =0; i <cchText; i++) + { + if (FALSE == GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, &pszText[i], 1, &charType)) + { + SetAlert(NLNTYPE_WARNING, MAKEINTRESOURCE(IDS_ERR_UNEXPECTED)); + validatedOk = FALSE; + break; + } + else if (0 == (C1_DIGIT & charType)) + { + if (0 == ((C1_SPACE | C1_CNTRL | C1_BLANK) & charType)) + { + SetAlert(NLNTYPE_WARNING, MAKEINTRESOURCE(IDS_ERR_PASSCODE_ONLYDIGITS)); + validatedOk = FALSE; + break; + } + } + else + { + if (iBuffer > maxSize) + { + WCHAR szBuffer[256], szTemplate[256] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_ERR_PASSCODE_BADLENGTH_TEMPLATE, szTemplate, ARRAYSIZE(szTemplate)); + StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szTemplate, MAX_PASSCODE_LENGTH); + SetAlert(NLNTYPE_WARNING, szBuffer); + validatedOk = FALSE; + break; + } + else + { + szBuffer[iBuffer] = pszText[i]; + iBuffer++; + } + } + } + + if(FALSE != validatedOk) + { + szBuffer[iBuffer] = L'\0'; + SendMessage(hEdit, EM_REPLACESEL, (WPARAM)TRUE, (LPARAM)szBuffer); + } + else + { + UpdateTitle(TRUE); + } + + return TRUE; +} + +INT_PTR LoginPopupPasscode::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_TIMER: + switch(wParam) + { + case TIMER_UPDATETITLE_ID: + KillTimer(hwnd, wParam); + UpdateTitle((-1 != alertType)); + return TRUE; + } + break; + } + return LoginPopup::DialogProc(uMsg, wParam, lParam); +} diff --git a/Src/auth/Loginbox/popupPasscode.h b/Src/auth/Loginbox/popupPasscode.h new file mode 100644 index 00000000..7a0b72c5 --- /dev/null +++ b/Src/auth/Loginbox/popupPasscode.h @@ -0,0 +1,65 @@ +#ifndef NULLSOFT_AUTH_LOGINPOPUP_PASSCODE_HEADER +#define NULLSOFT_AUTH_LOGINPOPUP_PASSCODE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginPopup.h" + +class LoginData; +class LoginDataCredentials; + + +// notifications +#define NPPN_FIRST (120) + +typedef struct __NPPNRESULT +{ + NMHDR hdr; + INT_PTR exitCode; + LoginData *loginData; +} NPPNRESULT; + +#define NPPN_RESULT (NPPN_FIRST + 0) + + +class LoginPopupPasscode : public LoginPopup +{ +protected: + LoginPopupPasscode(HWND hwnd); + ~LoginPopupPasscode(); + +public: + static HWND CreatePopup(HWND hParent, LoginData *loginData); + +protected: + void UpdateLayout(BOOL fRedraw); + void EndDialog(INT_PTR code); + + BOOL Validate(); + + BOOL OnInitDialog(HWND hFocus, LPARAM param); + void OnCommand(UINT commandId, UINT eventType, HWND hControl); + LRESULT OnNotify(UINT controlId, const NMHDR *pnmh); + HBRUSH OnGetStaticColor(HDC hdc, HWND hControl); + + LRESULT OnEditboxChar(HWND hEdit, UINT ch); + LRESULT OnEditboxKey(HWND hEdit, UINT vKey, UINT flags); + LRESULT OnEditboxPaste(HWND hEdit, LPCWSTR pszText); + + INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + +protected: + LoginDataCredentials *loginData; + LPWSTR message; + UINT messageType; + +private: + friend static HRESULT CALLBACK LoginPopupPasscode_CreateInstance(HWND hwnd, LPARAM param, LoginPopup **instance); +}; + + + + +#endif //NULLSOFT_AUTH_LOGINPOPUP_PASSCODE_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/providerEnumerator.cpp b/Src/auth/Loginbox/providerEnumerator.cpp new file mode 100644 index 00000000..e3dda5b9 --- /dev/null +++ b/Src/auth/Loginbox/providerEnumerator.cpp @@ -0,0 +1,112 @@ +#include "./common.h" +#include "./providerEnumerator.h" +#include "./loginProvider.h" + +LoginProviderEnumerator::LoginProviderEnumerator() +: ref(1), cursor(0), list(NULL), size(0) +{ +} + +LoginProviderEnumerator::~LoginProviderEnumerator() +{ + if (NULL != list) + { + size_t index = size; + while(index--) + { + LoginProvider *provider = list[index]; + if (NULL != provider) provider->Release(); + } + + free(list); + } +} + +ULONG LoginProviderEnumerator::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginProviderEnumerator::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginProviderEnumerator::CreateInstance(LoginProvider **list, size_t size, LoginProviderEnumerator **instance) +{ + if (NULL == instance) return E_POINTER; + if (NULL == list) return E_INVALIDARG; + + LoginProviderEnumerator *enumerator = new LoginProviderEnumerator(); + if(NULL == enumerator) + return E_OUTOFMEMORY; + + if (0 != size) + { + enumerator->list = (LoginProvider**)calloc(size, sizeof(LoginProvider*)); + if (NULL == enumerator->list) + { + enumerator->Release(); + return E_OUTOFMEMORY; + } + + enumerator->size = size; + CopyMemory(enumerator->list, list, size * sizeof(LoginProvider*)); + } + + *instance = enumerator; + return S_OK; +} + +HRESULT LoginProviderEnumerator::Next(ULONG listSize, LoginProvider **elementList, ULONG *elementCount) +{ + if (NULL == elementList || 0 == listSize) return E_INVALIDARG; + if (cursor >= size) + { + if (NULL != elementCount) *elementCount = 0; + return S_FALSE; + } + + ULONG count = 0; + + for (;cursor < size && count < listSize; cursor++) + { + LoginProvider *provider = list[cursor]; + if (NULL != provider) + { + provider->AddRef(); + elementList[count] = provider; + count++; + } + } + + if (NULL != elementCount) *elementCount = count; + + return (count == listSize) ? S_OK : S_FALSE; +} + + +HRESULT LoginProviderEnumerator::Reset(void) +{ + cursor = 0; + return S_OK; +} + +HRESULT LoginProviderEnumerator::Skip(ULONG elementCount) +{ + cursor += elementCount; + if (cursor >= size) + { + cursor = (size - 1); + return S_FALSE; + } + return S_OK; + +} diff --git a/Src/auth/Loginbox/providerEnumerator.h b/Src/auth/Loginbox/providerEnumerator.h new file mode 100644 index 00000000..a51bf6c8 --- /dev/null +++ b/Src/auth/Loginbox/providerEnumerator.h @@ -0,0 +1,38 @@ +#ifndef NULLSOFT_AUTH_LOGIN_PROVIDER_ENUMERATOR_HEADER +#define NULLSOFT_AUTH_LOGIN_PROVIDER_ENUMERATOR_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class LoginProvider; + +class LoginProviderEnumerator +{ + +protected: + LoginProviderEnumerator(); + ~LoginProviderEnumerator(); + +public: + static HRESULT CreateInstance(LoginProvider **list, size_t size, LoginProviderEnumerator **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + HRESULT Next(ULONG listSize, LoginProvider **elementList, ULONG *elementCount); + HRESULT Reset(void); + HRESULT Skip(ULONG elementCount); + +protected: + ULONG ref; + size_t cursor; + size_t size; + LoginProvider **list; + +}; + +#endif //NULLSOFT_AUTH_LOGIN_PROVIDER_ENUMERATOR_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/providerLoader.cpp b/Src/auth/Loginbox/providerLoader.cpp new file mode 100644 index 00000000..ca2d4d87 --- /dev/null +++ b/Src/auth/Loginbox/providerLoader.cpp @@ -0,0 +1,142 @@ +#include "./providerLoader.h" +#include "./providerEnumerator.h" +#include "./xmlInt32Parser.h" + +#include "../api.h" +#include "../../xml/obj_xml.h" +#include <api/service/waservicefactory.h> + + +LoginProviderLoader::LoginProviderLoader() +{ +} + +LoginProviderLoader::~LoginProviderLoader() +{ +} + +HRESULT LoginProviderLoader::ReadXml(LPCWSTR pszPath, LoginProviderEnumerator **enumerator, INT *prefVisible) +{ + if (NULL == enumerator) return E_POINTER; + *enumerator = NULL; + + if (NULL == pszPath || L'\0' == *pszPath) + return E_INVALIDARG; + + HANDLE hFile = CreateFile(pszPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + DWORD error = GetLastError(); + return HRESULT_FROM_WIN32(error); + } + + HRESULT hr; + + if (NULL != WASABI_API_SVC) + { + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(obj_xmlGUID); + obj_xml *reader = (NULL != sf) ? (obj_xml*)sf->getInterface() : NULL; + if (NULL != reader) + { + if (OBJ_XML_SUCCESS == reader->xmlreader_open()) + { + providerList.clear(); + parser.SetReader(reader); + + XmlInt32Parser visibleParser; + + reader->xmlreader_registerCallback(L"loginProviders\fprovider", this); + if (NULL != prefVisible) + reader->xmlreader_registerCallback(L"loginProviders\fvisibleProviders", &visibleParser); + + hr = FeedFile(reader, hFile, 8192); + reader->xmlreader_close(); + + parser.SetReader(NULL); + if (SUCCEEDED(hr)) + hr = LoginProviderEnumerator::CreateInstance(providerList.begin(), providerList.size(), enumerator); + + if (NULL != prefVisible && FAILED(visibleParser.GetValue(prefVisible))) + *prefVisible = 0; + + } + else + hr = E_FAIL; + + sf->releaseInterface(reader); + } + else + hr = E_FAIL; + } + else + hr = E_UNEXPECTED; + + + CloseHandle(hFile); + return hr; +} + + +HRESULT LoginProviderLoader::FeedFile(obj_xml *reader, HANDLE hFile, DWORD bufferSize) +{ + if (NULL == reader || INVALID_HANDLE_VALUE == hFile || 0 == bufferSize) + return E_INVALIDARG; + + BYTE *buffer = (BYTE*)calloc(bufferSize, sizeof(BYTE)); + if (NULL == buffer) return E_OUTOFMEMORY; + + HRESULT hr; + INT readerCode = OBJ_XML_SUCCESS; + + for(;;) + { + DWORD read = 0; + if (FALSE == ReadFile(hFile, buffer, bufferSize, &read, NULL) || 0 == read) + { + DWORD errorCode = GetLastError(); + hr = HRESULT_FROM_WIN32(errorCode); + + if (0 == read && OBJ_XML_SUCCESS == readerCode) + reader->xmlreader_feed(0, 0); + + break; + } + + readerCode = reader->xmlreader_feed(buffer, read); + if (OBJ_XML_SUCCESS != readerCode) + { + hr = E_FAIL; + break; + } + } + + free(buffer); + return hr; +} + +void LoginProviderLoader::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + parser.Begin(params); +} + +void LoginProviderLoader::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + LoginProvider *provider; + if (SUCCEEDED(parser.End(&provider))) + { + providerList.push_back(provider); + } +} + +void LoginProviderLoader::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ + parser.End(NULL); +} + +#define CBCLASS LoginProviderLoader +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/Loginbox/providerLoader.h b/Src/auth/Loginbox/providerLoader.h new file mode 100644 index 00000000..32f35b75 --- /dev/null +++ b/Src/auth/Loginbox/providerLoader.h @@ -0,0 +1,44 @@ +#ifndef NULLSOFT_AUTH_LOGIN_PROVIDER_LOADER_HEADER +#define NULLSOFT_AUTH_LOGIN_PROVIDER_LOADER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "../../nu/ptrlist.h" +#include "./providerParser.h" + +class obj_xml; +class LoginProviderEnumerator; + +class LoginProviderLoader : public ifc_xmlreadercallback +{ + +public: + LoginProviderLoader(); + ~LoginProviderLoader(); + +public: + HRESULT ReadXml(LPCWSTR pszPath, LoginProviderEnumerator **enumerator, INT *prefVisible); + +private: + HRESULT FeedFile(obj_xml *reader, HANDLE hFile, DWORD bufferSize); + + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +private: + typedef nu::PtrList<LoginProvider> ProviderList; + +private: + ProviderList providerList; + LoginProviderParser parser; + +protected: + RECVS_DISPATCH; +}; + +#endif //NULLSOFT_AUTH_LOGIN_PROVIDER_LOADER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/providerOperation.cpp b/Src/auth/Loginbox/providerOperation.cpp new file mode 100644 index 00000000..43bf663f --- /dev/null +++ b/Src/auth/Loginbox/providerOperation.cpp @@ -0,0 +1,136 @@ +#include "./providerOperation.h" +#include "./loginProvider.h" +#include "./providerEnumerator.h" + +LoginProviderOperation::LoginProviderOperation(LoginProvider *pSource, LoginProvider *pTarget, UINT uCode) + : ref(1), source(pSource), target(pTarget), code(uCode) +{ + if (NULL != source) source->AddRef(); + if (NULL != target) target->AddRef(); +} + +LoginProviderOperation::~LoginProviderOperation() +{ + if (NULL != source) source->Release(); + if (NULL != target) target->Release(); +} + +HRESULT LoginProviderOperation::CreateDeleteOperation(LoginProvider *pRemove, LoginProviderOperation **instance) +{ + if (NULL == instance) + return E_POINTER; + + if (NULL == pRemove) + { + *instance = NULL; + return E_INVALIDARG; + } + + *instance = new LoginProviderOperation(pRemove, NULL, operationDelete); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +HRESULT LoginProviderOperation::CreateReplaceOperation(LoginProvider *pSource, LoginProvider *pTarget, LoginProviderOperation **instance) +{ + if (NULL == instance) + return E_POINTER; + + if (NULL == pSource || NULL == pTarget) + { + *instance = NULL; + return E_INVALIDARG; + } + + *instance = new LoginProviderOperation(pSource, pTarget, operationReplace); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +HRESULT LoginProviderOperation::CreateFromUpdate(LoginProvider *active, LoginProviderEnumerator *enumerator, LoginProviderOperation **instance) +{ + if (NULL == instance) + return E_POINTER; + + *instance = NULL; + + if (NULL == active || NULL == enumerator) + return E_INVALIDARG; + + GUID testId, activeId(GUID_NULL); + HRESULT hr = active->GetId(&activeId); + if (FAILED(hr)) return hr; + + LoginProvider *test; + BOOL providerFound = FALSE; + enumerator->Reset(); + + BOOL loop = TRUE; + while(TRUE == loop && S_OK == enumerator->Next(1, &test, NULL)) + { + if (FAILED(test->GetId(&testId))) + { + hr = E_FAIL; + loop = FALSE; + } + else if(FALSE != IsEqualGUID(activeId, testId)) + { + providerFound = TRUE; + if (S_OK == test->IsIdentical(active)) + hr = S_FALSE; + else + hr = CreateReplaceOperation(active, test, instance); + loop = FALSE; + } + + test->Release(); + } + + if (SUCCEEDED(hr) && FALSE == providerFound) + hr = CreateDeleteOperation(active, instance); + + return hr; +} + +ULONG LoginProviderOperation::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginProviderOperation::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +UINT LoginProviderOperation::GetCode() +{ + return code; +} +HRESULT LoginProviderOperation::GetSource(LoginProvider **provider) +{ + if (NULL == provider) return E_POINTER; + *provider = source; + + if (NULL != source) + source->AddRef(); + + return S_OK; +} + +HRESULT LoginProviderOperation::GetTarget(LoginProvider **provider) +{ + if (NULL == provider) return E_POINTER; + *provider = target; + + if (NULL != target) + target->AddRef(); + + return S_OK; +} diff --git a/Src/auth/Loginbox/providerOperation.h b/Src/auth/Loginbox/providerOperation.h new file mode 100644 index 00000000..d000dbeb --- /dev/null +++ b/Src/auth/Loginbox/providerOperation.h @@ -0,0 +1,47 @@ +#ifndef NULLSOFT_AUTH_LOGIN_PROVIDER_OPERATION_HEADER +#define NULLSOFT_AUTH_LOGIN_PROVIDER_OPERATION_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class LoginProvider; +class LoginProviderEnumerator; + +class LoginProviderOperation +{ +public: + typedef enum + { + operationDelete = 0, + operationReplace = 1, + } OperationCode; + +protected: + LoginProviderOperation(LoginProvider *pSource, LoginProvider *pTarget, UINT uCode); + ~LoginProviderOperation(); + +public: + static HRESULT CreateDeleteOperation(LoginProvider *pRemove, LoginProviderOperation **instance); + static HRESULT CreateReplaceOperation(LoginProvider *pSource, LoginProvider *pTarget, LoginProviderOperation **instance); + static HRESULT CreateFromUpdate(LoginProvider *active, LoginProviderEnumerator *enumerator, LoginProviderOperation **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + UINT GetCode(); + HRESULT GetSource(LoginProvider **provider); + HRESULT GetTarget(LoginProvider **provider); + +protected: + ULONG ref; + LoginProvider *source; + LoginProvider *target; + UINT code; + +}; + +#endif //NULLSOFT_AUTH_LOGIN_PROVIDER_OPERATION_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/providerParser.cpp b/Src/auth/Loginbox/providerParser.cpp new file mode 100644 index 00000000..dcf0660b --- /dev/null +++ b/Src/auth/Loginbox/providerParser.cpp @@ -0,0 +1,194 @@ +#include "./providerParser.h" +#include "./loginProvider.h" +#include "./loginTemplate.h" +#include "./loginCommand.h" +#include "./common.h" + +//#include "../api.h" +#include "../../xml/obj_xml.h" + + +typedef void (CALLBACK *PROVIDERTAGCALLBACK)(LoginProvider* /*provider*/, LPCWSTR /*value*/); + +typedef struct __PROVIDERTAG +{ + LPCWSTR name; + PROVIDERTAGCALLBACK callback; +} PROVIDERTAG; + +static void CALLBACK ProviderTag_Name(LoginProvider *provider, LPCWSTR value) +{ + provider->SetName(value); +} + +static void CALLBACK ProviderTag_ImagePath(LoginProvider *provider, LPCWSTR value) +{ + provider->SetImagePath(value); +} + +static void CALLBACK ProviderTag_Description(LoginProvider *provider, LPCWSTR value) +{ + provider->SetDescription(value); +} + +static void CALLBACK ProviderTag_TosLink(LoginProvider *provider, LPCWSTR value) +{ + provider->SetTosLink(value); +} + +static void CALLBACK ProviderTag_PrivacyLink(LoginProvider *provider, LPCWSTR value) +{ + provider->SetPrivacyLink(value); +} + +static void CALLBACK ProviderTag_HelpLink(LoginProvider *provider, LPCWSTR value) +{ + provider->SetHelpLink(value); +} + +static const PROVIDERTAG szProviderTags[PROVIDER_TAG_MAX] = +{ + {L"name", ProviderTag_Name}, + {L"icon", ProviderTag_ImagePath}, + {L"description", ProviderTag_Description}, + {L"tos", ProviderTag_TosLink}, + {L"privacy", ProviderTag_PrivacyLink}, + {L"help", ProviderTag_HelpLink}, +}; + +LoginProviderParser::LoginProviderParser() + : reader(NULL), provider(NULL) +{ + ZeroMemory(hitList, sizeof(hitList)); +} + +LoginProviderParser::~LoginProviderParser() +{ + if (NULL != reader) + reader->Release(); + + if (NULL != provider) + provider->Release(); +} + + +HRESULT LoginProviderParser::SetReader(obj_xml *pReader) +{ + if (NULL != reader) + { + reader->Release(); + } + + reader = pReader; + if (NULL != reader) + reader->AddRef(); + + return S_OK; +} + +HRESULT LoginProviderParser::Begin(ifc_xmlreaderparams *params) +{ + if (NULL != provider) + return E_PENDING; + + if (NULL == reader) return E_UNEXPECTED; + if (NULL == params) return E_INVALIDARG; + + GUID providerId; + LPCWSTR pszId = params->getItemValue(L"id"); + if (NULL != pszId && RPC_S_OK == UuidFromString((RPC_WSTR)pszId, &providerId)) + { + if (FAILED(LoginProvider::CreateInstance(&providerId, &provider))) + provider = NULL; + } + + if (NULL == provider) + return E_FAIL; + + reader->xmlreader_registerCallback(L"loginProviders\fprovider\fname", this); + reader->xmlreader_registerCallback(L"loginProviders\fprovider\ficon", this); + reader->xmlreader_registerCallback(L"loginProviders\fprovider\fdescription", this); + reader->xmlreader_registerCallback(L"loginProviders\fprovider\ftos", this); + reader->xmlreader_registerCallback(L"loginProviders\fprovider\fprivacy", this); + reader->xmlreader_registerCallback(L"loginProviders\fprovider\fhelp", this); + ZeroMemory(hitList, sizeof(hitList)); + + templateNodeParser.Begin(reader, provider); + commandNodeParser.Begin(reader, provider); + + return S_OK; +} + +HRESULT LoginProviderParser::End(LoginProvider **ppProvider) +{ + templateNodeParser.End(); + commandNodeParser.End(); + reader->xmlreader_unregisterCallback(this); + + + + if (NULL == provider || S_OK != provider->IsValid()) + { + if (NULL != provider) + { + provider->Release(); + provider = NULL; + } + + if (NULL != ppProvider) + *ppProvider = NULL; + + return E_FAIL; + } + + if (NULL != ppProvider) + { + *ppProvider = provider; + if (NULL != provider) + provider->AddRef(); + } + + provider->Release(); + provider = NULL; + + return S_OK; +} + + +void LoginProviderParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementString.Clear(); +} + +void LoginProviderParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + for (INT i = 0; i < PROVIDER_TAG_MAX; i++) + { + if (FALSE == hitList[i] && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, szProviderTags[i].name, -1, xmltag, -1)) + { + szProviderTags[i].callback(provider, elementString.Get()); + hitList[i] = TRUE; + break; + } + } +} + +void LoginProviderParser::Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value) +{ + elementString.Append(value); +} + +void LoginProviderParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ + elementString.Clear(); +} + +#define CBCLASS LoginProviderParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONCHARDATA, Event_XmlCharData) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/Loginbox/providerParser.h b/Src/auth/Loginbox/providerParser.h new file mode 100644 index 00000000..c6af9120 --- /dev/null +++ b/Src/auth/Loginbox/providerParser.h @@ -0,0 +1,48 @@ +#ifndef NULLSOFT_AUTH_LOGIN_PROVIDER_PARSER_HEADER +#define NULLSOFT_AUTH_LOGIN_PROVIDER_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "./stringBuilder.h" +#include "./templateNodeParser.h" +#include "./commandNodeParser.h" + +class obj_xml; +class LoginProvider; + +#define PROVIDER_TAG_MAX 6 + +class LoginProviderParser : public ifc_xmlreadercallback +{ + +public: + LoginProviderParser(); + ~LoginProviderParser(); +public: + HRESULT SetReader(obj_xml *pReader); + HRESULT Begin(ifc_xmlreaderparams *params); + HRESULT End(LoginProvider **ppProvider); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + obj_xml *reader; + StringBuilder elementString; + LoginProvider *provider; + LoginTemplateNodeParser templateNodeParser; + LoginCommandNodeParser commandNodeParser; + BOOL hitList[PROVIDER_TAG_MAX]; + +protected: + RECVS_DISPATCH; +}; + +#endif //NULLSOFT_AUTH_LOGIN_PROVIDER_PARSER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/resources/arrow.png b/Src/auth/Loginbox/resources/arrow.png Binary files differnew file mode 100644 index 00000000..4cc9d964 --- /dev/null +++ b/Src/auth/Loginbox/resources/arrow.png diff --git a/Src/auth/Loginbox/resources/notifierIcons.png b/Src/auth/Loginbox/resources/notifierIcons.png Binary files differnew file mode 100644 index 00000000..791cea9d --- /dev/null +++ b/Src/auth/Loginbox/resources/notifierIcons.png diff --git a/Src/auth/Loginbox/resources/popupBorder.png b/Src/auth/Loginbox/resources/popupBorder.png Binary files differnew file mode 100644 index 00000000..59078913 --- /dev/null +++ b/Src/auth/Loginbox/resources/popupBorder.png diff --git a/Src/auth/Loginbox/resources/selection.png b/Src/auth/Loginbox/resources/selection.png Binary files differnew file mode 100644 index 00000000..2e1a1415 --- /dev/null +++ b/Src/auth/Loginbox/resources/selection.png diff --git a/Src/auth/Loginbox/resultWebAuth.cpp b/Src/auth/Loginbox/resultWebAuth.cpp new file mode 100644 index 00000000..3ba4a961 --- /dev/null +++ b/Src/auth/Loginbox/resultWebAuth.cpp @@ -0,0 +1,842 @@ +#include "./resultWebAuth.h" +#include "./common.h" +#include "./commandWebAuth.h" +#include "./loginData.h" +#include "./dataAddress.h" +#include "./loginCredentials.h" +#include "./loginBox.h" +#include "./loginProvider.h" +#include "./loginStatus.h" +#include "./externalMngr.h" +#include "./browserWindow.h" + +#include "../resource.h" +#include "../api.h" +#include "../api_auth.h" + +#include "../../omBrowser/obj_ombrowser.h" +#include "../../omBrowser/ifc_ombrowserwndenum.h" +#include "../../omBrowser/ifc_ombrowserwndmngr.h" +#include "../../omBrowser/browserUiCommon.h" + +#include "../../winamp/jsapi.h" +#include "../../winamp/jsapi_CallbackParameters.h" + +#include <api/service/waservicefactory.h> + +#include <strsafe.h> + +#define SERVICE_ID 369 + +#define DISPTABLE_CLASS LoginResultWebAuth +DISPTABLE_BEGIN() + DISPENTRY_ADD(DISPID_LOGINCOMPLETE, L"loginComplete", OnLoginComplete) + DISPENTRY_ADD(DISPID_GETPAGERECT, L"getPageRect", OnGetPageRect) + DISPENTRY_ADD(DISPID_GETBOXRECT, L"getBoxRect", OnGetBoxRect) + DISPENTRY_ADD(DISPID_SETSTATUS, L"setStatus", OnSetStatus) + DISPENTRY_ADD(DISPID_NAVIGATE, L"navigate", OnNavigate) + DISPENTRY_ADD(DISPID_GETSTRING, L"getString", OnGetString) +DISPTABLE_END +#undef DISPTABLE_CLASS + +LoginResultWebAuth::LoginResultWebAuth(obj_ombrowser *pManager, LPCWSTR pszTargetUrl, LoginData *pInput, Callback fnCallback, void *pUser) + : ref(1), browserMngr(pManager), external(NULL), targetUrl(NULL), input(pInput), callback(fnCallback), user(pUser), + credentials(NULL), authCode(AUTH_NOT_AUTHORIZED), hView(NULL), completed(NULL), + dispId(DISPID_UNKNOWN), connectionVerified(FALSE), readyUrl(NULL) +{ + InitializeCriticalSection(&lock); + + targetUrl = LoginBox_CopyString(pszTargetUrl); + input->AddRef(); + browserMngr->AddRef(); + + if(SUCCEEDED(ExternalManager::CreateInstance(&external))) + external->AddDispatch(L"loginBox", this, &dispId); +} + +LoginResultWebAuth::~LoginResultWebAuth() +{ + hView = NULL; + + Finish(); + + if (NULL != completed) + { + CloseHandle(completed); + } + + LoginBox_FreeString(targetUrl); + input->Release(); + + if (NULL != credentials) + credentials->Release(); + + LoginBox_FreeString(readyUrl); + + + + DeleteCriticalSection(&lock); + +} + +HRESULT LoginResultWebAuth::InitBrowserManager(obj_ombrowser **browserMngr) +{ + HRESULT hr; + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(OBJ_OmBrowser); + if (NULL == sf) return E_UNEXPECTED; + + *browserMngr = (obj_ombrowser*)sf->getInterface(); + if (NULL == browserMngr) + hr = E_UNEXPECTED; + else + { + HWND hWinamp = (NULL != WASABI_API_WINAMP) ? WASABI_API_WINAMP->GetMainWindow() : NULL; + if (NULL == hWinamp || FAILED((*browserMngr)->Initialize(NULL, hWinamp))) + { + (*browserMngr)->Release(); + hr = E_FAIL; + } + else + { + hr = S_OK; + } + } + + sf->Release(); + return hr; +} + +HRESULT LoginResultWebAuth::CreateInstance(LPCWSTR targetUrl, LoginData *input, Callback callback, void *user, LoginResultWebAuth **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = NULL; + + if (NULL == targetUrl || NULL == input) + return E_INVALIDARG; + + obj_ombrowser *browserMngr; + if (FAILED(InitBrowserManager(&browserMngr))) + return E_FAIL; + + HRESULT hr; + + LoginResultWebAuth *result = new LoginResultWebAuth(browserMngr, targetUrl, input, callback, user); + if (NULL == result) + hr = E_OUTOFMEMORY; + else + { + if (DISPID_UNKNOWN == result->dispId) + hr = E_FAIL; + else + hr = result->Start(); + + if (FAILED(hr)) + { + result->Release(); + result = NULL; + } + else + { + *instance = result; + } + } + + browserMngr->Release(); + return hr; +} + +STDMETHODIMP_(ULONG) LoginResultWebAuth::AddRef(void) +{ + return InterlockedIncrement((LONG*)&ref); +} + +STDMETHODIMP_(ULONG) LoginResultWebAuth::Release(void) +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +STDMETHODIMP LoginResultWebAuth::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (NULL == ppvObject) + return E_POINTER; + + if (IsEqualIID(riid, IFC_OmService)) + *ppvObject = static_cast<ifc_omservice*>(this); + else if (IsEqualIID(riid, IFC_OmServiceCommand)) + *ppvObject = static_cast<ifc_omservicecommand*>(this); + else if (IsEqualIID(riid, LCUID_WEBAUTH)) + *ppvObject = static_cast<LoginResultWebAuth*>(this); + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = static_cast<IDispatch*>(this); + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = static_cast<IUnknown*>(reinterpret_cast<LoginResult*>(this)); + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + if (NULL == *ppvObject) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +size_t LoginResultWebAuth::Wasabi_AddRef() +{ + return AddRef(); +} + +size_t LoginResultWebAuth::Wasabi_Release() +{ + return Release(); +} + +int LoginResultWebAuth::Wasabi_QueryInterface(GUID iid, void **object) +{ + return QueryInterface(iid, object); +} + +HRESULT LoginResultWebAuth::Start() +{ + input->SetStatus(MAKEINTRESOURCE(IDS_STATUS_INITIALIZING)); + + HWND hParent = input->GetPage(); + UINT viewStyle = NBCS_DISABLECONTEXTMENU | NBCS_DIALOGMODE | NBCS_POPUPOWNER | + NBCS_DISABLEFULLSCREEN | NBCS_DISABLEHOSTCSS | NBCS_NOTOOLBAR | NBCS_NOSTATUSBAR; + + HRESULT hr = browserMngr->CreateView(this, hParent, NULL, viewStyle, &hView); + if (FAILED(hr)) + return hr; + + if (FALSE == BrowserWindow_Attach(hView, this)) + { + DestroyWindow(hView); + hView = NULL; + hr = E_FAIL; + } + else + { + SetWindowPos(hView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE); + } + return hr; +} + +HRESULT LoginResultWebAuth::Finish() +{ + EnterCriticalSection(&lock); + + if (NULL != external) + { + if (DISPID_UNKNOWN != dispId) + { + external->DeleteMemberByDispID(dispId); + dispId = DISPID_UNKNOWN; + } + external->Release(); + external = NULL; + } + + // try to destroy windows before browserMngr->Finish() (this will let them to lock less) + + if (NULL != hView) + { + DestroyWindow(hView); + hView = NULL; + } + + if (NULL != browserMngr) + { + ifc_ombrowserwndmngr *windowMngr; + if (SUCCEEDED(browserMngr->QueryInterface(IFC_OmBrowserWindowManager, (void**)&windowMngr))) + { + ifc_ombrowserwndenum *windowEnum; + if (SUCCEEDED(windowMngr->Enumerate(NULL, NULL, &windowEnum))) + { + HWND hBrowser; + while(S_OK == windowEnum->Next(1, &hBrowser, NULL)) + { + if (FALSE != DestroyWindow(hBrowser) && hBrowser == hView) + hView = NULL; + } + windowEnum->Release(); + } + windowMngr->Release(); + } + } + + if (NULL != browserMngr) + { + browserMngr->Finish(); + browserMngr->Release(); + browserMngr = NULL; + } + + LeaveCriticalSection(&lock); + + return S_OK; +} +void LoginResultWebAuth::NotifyComplete() +{ + Callback callbackCopy(NULL); + HANDLE completedCopy(NULL); + + INT statusId; + switch(authCode) + { + case AUTH_SUCCESS: statusId = IDS_STATUS_SUCCEEDED; break; + case AUTH_SECURID: statusId = IDS_STATUS_PASSCODE_REQUIRED; break; + case AUTH_ABORT: statusId = IDS_STATUS_ABORTED; break; + default: statusId = IDS_STATUS_FAILED; break; + } + input->SetStatus(MAKEINTRESOURCE(statusId)); + + EnterCriticalSection(&lock); + + callbackCopy = callback; + if (NULL == completed || FALSE == DuplicateHandle(GetCurrentProcess(), completed, + GetCurrentProcess(), &completedCopy, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + completedCopy = NULL; + } + + LeaveCriticalSection(&lock); + + if (NULL != completedCopy) + { + SetEvent(completedCopy); + CloseHandle(completedCopy); + } + + if (NULL != callbackCopy) + callbackCopy(this); + +} + +HRESULT LoginResultWebAuth::GetWaitHandle(HANDLE *handle) +{ + if (NULL == handle) + return E_POINTER; + + HRESULT hr = S_OK; + + EnterCriticalSection(&lock); + + if (NULL == completed) + { + completed = CreateEvent(NULL, TRUE, (S_OK == IsCompleted()), NULL); + if (NULL == completed) + { + *handle = NULL; + DWORD error = GetLastError(); + hr = HRESULT_FROM_WIN32(error); + } + } + + if (SUCCEEDED(hr) && + FALSE == DuplicateHandle(GetCurrentProcess(), completed, + GetCurrentProcess(), handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + *handle = NULL; + DWORD error = GetLastError(); + hr = HRESULT_FROM_WIN32(error); + } + + LeaveCriticalSection(&lock); + return hr; +} + +HRESULT LoginResultWebAuth::GetUser(void **pUser) +{ + if (NULL == pUser) return E_POINTER; + *pUser = user; + return S_OK; +} + +HRESULT LoginResultWebAuth::RequestAbort(BOOL fDrop) +{ + HRESULT hr; + EnterCriticalSection(&lock); + + authCode = AUTH_ABORT; + + if (NULL == browserMngr) + hr = S_FALSE; + else + { + input->SetStatus(MAKEINTRESOURCE(IDS_STATUS_ABORTING)); + Finish(); + hr = S_OK; + } + + if (NULL != credentials) + { + credentials->Release(); + credentials = NULL; + } + + LeaveCriticalSection(&lock); + + if (S_OK == hr) + NotifyComplete(); + + return hr; +} + +HRESULT LoginResultWebAuth::IsCompleted() +{ + return (NULL == browserMngr) ? S_OK : S_FALSE; +} + +HRESULT LoginResultWebAuth::IsAborting() +{ + return E_NOTIMPL; +} + +HRESULT LoginResultWebAuth::GetLoginData(LoginData **loginData) +{ + if (NULL == loginData) return E_POINTER; + *loginData = input; + if (NULL != input) + input->AddRef(); + return S_OK; +} + +STDMETHODIMP_(void) LoginResultWebAuth::Event_BrowserReady(HWND hBrowser) +{ + input->SetStatus(MAKEINTRESOURCE(IDS_STATUS_CONNECTING)); +} + +STDMETHODIMP_(void) LoginResultWebAuth::Event_DocumentReady(HWND hBrowser) +{ + BOOL validateDocument = TRUE; + + EnterCriticalSection(&lock); + if (NULL != readyUrl) + { + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, readyUrl, -1, L"loginbox:finish", -1)) + { + if (FAILED(BrowserWindow_QueueApc(hView, 0L))) + { + Finish(); + NotifyComplete(); + } + return; + } + + connectionVerified = FALSE; + BrowserControl_Navigate(hBrowser, NAVIGATE_STOP, FALSE); + if (FALSE != BrowserControl_Navigate(hBrowser, readyUrl, TRUE)) + validateDocument = FALSE; + + LoginBox_FreeString(readyUrl); + readyUrl = NULL; + + } + LeaveCriticalSection(&lock); + + if (FALSE != validateDocument && FALSE == connectionVerified) + { + // time to kill it + authCode = AUTH_404; + Finish(); + NotifyComplete(); + } +} + +STDMETHODIMP_(void) LoginResultWebAuth::Event_BrowserClosing(HWND hBrowser) +{ +} + +STDMETHODIMP_(void) LoginResultWebAuth::Event_InvokeApc(HWND hBrowser, LPARAM param) +{ + Finish(); + NotifyComplete(); +} +unsigned int LoginResultWebAuth::GetId() +{ + return SERVICE_ID; +} + +HRESULT LoginResultWebAuth::GetName(wchar_t *pszBuffer, int cchBufferMax) +{ + if (NULL == pszBuffer) + return E_POINTER; + + WCHAR szName[128] = {0}, szTemplate[256] = {0}; + + WASABI_API_LNGSTRINGW_BUF(IDS_WEBAUTH_CAPTION_TEMPLATE, szTemplate, ARRAYSIZE(szTemplate)); + + LoginProvider *loginProvider; + if (SUCCEEDED(input->GetProvider(&loginProvider)) && NULL != loginProvider) + { + loginProvider->GetName(szName, ARRAYSIZE(szName)); + loginProvider->Release(); + } + + return StringCchPrintf(pszBuffer, cchBufferMax, szTemplate, szName); +} + +HRESULT LoginResultWebAuth::GetUrl(wchar_t *pszBuffer, int cchBufferMax) +{ + + input->SetStatus(MAKEINTRESOURCE(IDS_STATUS_CONNECTING)); + + LPWSTR cursor; + size_t remaining; + HRESULT hr = StringCchCopyEx(pszBuffer, cchBufferMax, targetUrl, &cursor, &remaining, STRSAFE_IGNORE_NULLS); + if (FAILED(hr)) return hr; + + LoginDataAddress *inputAddress; + if (SUCCEEDED(input->QueryInterface(IID_LoginDataAddress, (void**)&inputAddress))) + { + LPCWSTR pszAddress = inputAddress->GetAddress(); + if (NULL != pszAddress && L'\0' != *pszAddress) + { + LPWSTR paramBegin = cursor; + while (L'?' != *paramBegin && paramBegin != pszBuffer) + paramBegin = CharPrev(pszBuffer, paramBegin); + + WCHAR separator = (L'?' == *paramBegin) ? L'&' : L'?'; + hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, 0, L"%cuserUrl=%s", separator, pszAddress); + } + inputAddress->Release(); + } + return hr; +} + +HRESULT LoginResultWebAuth::GetExternal(IDispatch **ppDispatch) +{ + if (NULL == ppDispatch) + return E_POINTER; + + *ppDispatch = external; + if (NULL != external) + external->AddRef(); + + return S_OK; +} + +HRESULT LoginResultWebAuth::QueryState(HWND hBrowser, const GUID *commandGroup, UINT commandId) +{ + if (NULL == commandGroup) + return E_NOTIMPL; + + if (IsEqualGUID(*commandGroup, CMDGROUP_NAVIGATION)) + { + switch(commandId) + { + case NAVCOMMAND_HOME: + return CMDSTATE_DISABLED; + } + } + + return E_NOTIMPL; +} + +HRESULT LoginResultWebAuth::Exec(HWND hBrowser, const GUID *commandGroup, UINT commandId, ULONG_PTR commandArg) +{ + return E_NOTIMPL; +} + +HRESULT LoginResultWebAuth::DispParamsToCredentials(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, UINT FAR *puArgErr, HRESULT *errorEx, LPWSTR *finishUrl) +{ + if (NULL != finishUrl) + *finishUrl = NULL; + + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT_OPTIONAL(pdispparams, 5, 6); + + BSTR username; + BSTR token; + BSTR sessionKey; + __time64_t expire; + + JSAPI_GETUNSIGNED_AS_NUMBER(authCode, pdispparams, 1, puArgErr); + JSAPI_GETSTRING(token, pdispparams, 2, puArgErr); + JSAPI_GETSTRING(sessionKey, pdispparams, 3, puArgErr); + JSAPI_GETUNSIGNED_AS_NUMBER(expire, pdispparams, 4, puArgErr); + JSAPI_GETSTRING(username, pdispparams, 5, puArgErr); + expire += _time64(0); + + if (NULL != finishUrl && pdispparams->cArgs > 5) + { + VARIANTARG *arg = &pdispparams->rgvarg[pdispparams->cArgs - 6]; + if (NULL != arg && VT_BSTR == arg->vt) + *finishUrl = LoginBox_CopyString(arg->bstrVal); + } + + if (NULL != credentials) + { + credentials->Release(); + credentials = NULL; + } + + if (AUTH_SUCCESS == authCode) + { + GUID realm; + HRESULT hr = input->GetRealm(&realm); + if (SUCCEEDED(hr)) + { + LPSTR sessionAnsi(NULL), tokenAnsi(NULL); + if (FAILED(LoginBox_WideCharToMultiByte(CP_UTF8, 0, sessionKey, -1, NULL, NULL, &sessionAnsi)) || + FAILED(LoginBox_WideCharToMultiByte(CP_UTF8, 0, token, -1, NULL, NULL, &tokenAnsi))) + { + hr = E_FAIL; + } + if (SUCCEEDED(hr)) + hr = LoginCredentials::CreateInstance(&realm, username, sessionAnsi, tokenAnsi, expire, &credentials); + + LoginBox_FreeAnsiString(sessionAnsi); + LoginBox_FreeAnsiString(tokenAnsi); + } + + if (FAILED(hr)) + { + if (NULL != errorEx) + *errorEx = hr; + } + } + + return S_OK; + +} + +HRESULT LoginResultWebAuth::OnLoginComplete(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + + HRESULT hr, errorEx; + BOOL finishLogin = TRUE; + + EnterCriticalSection(&lock); + + LPWSTR finishUrl; + hr = DispParamsToCredentials(wFlags, pdispparams, pvarResult, puArgErr, &errorEx, &finishUrl); + JSAPI_SET_RESULT(pvarResult, boolVal, (SUCCEEDED(hr) && SUCCEEDED(errorEx)) ? VARIANT_TRUE : VARIANT_FALSE); + + if (NULL != finishUrl && L'\0' != *finishUrl) + { + LoginBox_FreeString(readyUrl); + readyUrl = LoginBox_CopyString(L"loginbox:finish"); + if (NULL != readyUrl) + { + if (FALSE == hView || FALSE == BrowserControl_Navigate(hView, finishUrl, FALSE)) + { + LoginBox_FreeString(readyUrl); + readyUrl = NULL; + } + else + finishLogin = FALSE; + } + } + + LoginBox_FreeString(finishUrl); + + LeaveCriticalSection(&lock); + + if (FALSE != finishLogin) + { + if (FAILED(BrowserWindow_QueueApc(hView, 0L))) + { + Finish(); + NotifyComplete(); + } + } + + return hr; +} + +HRESULT LoginResultWebAuth::OnGetPageRect(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_INIT_RESULT(pvarResult, VT_DISPATCH); + + RECT rect; + EnterCriticalSection(&lock); + + HWND hPage = input->GetPage(); + if (NULL == hPage || FALSE == GetWindowRect(hPage, &rect)) + SetRectEmpty(&rect); + + LeaveCriticalSection(&lock); + + JSAPI::CallbackParameters *params = new JSAPI::CallbackParameters; + params->AddLong(L"x", rect.left); + params->AddLong(L"y", rect.top); + params->AddLong(L"cx", rect.right - rect.left); + params->AddLong(L"cy", rect.bottom - rect.top); + V_DISPATCH(pvarResult) = params; + return S_OK; +} + +HRESULT LoginResultWebAuth::OnGetBoxRect(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_INIT_RESULT(pvarResult, VT_DISPATCH); + + RECT rect; + EnterCriticalSection(&lock); + + HWND hBox = input->GetLoginbox(); + if (NULL == hBox || FALSE == GetWindowRect(hBox, &rect)) + SetRectEmpty(&rect); + + LeaveCriticalSection(&lock); + + if (NULL != pvarResult) + { + JSAPI::CallbackParameters *params = new JSAPI::CallbackParameters; + params->AddLong(L"x", rect.left); + params->AddLong(L"y", rect.top); + params->AddLong(L"cx", rect.right - rect.left); + params->AddLong(L"cy", rect.bottom - rect.top); + V_DISPATCH(pvarResult) = params; + } + return S_OK; +} + +HRESULT LoginResultWebAuth::OnSetStatus(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + connectionVerified = TRUE; + + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + + LPCWSTR pszStatus = NULL; + INT paramIndex = pdispparams->cArgs - 1; + VARIANTARG *param = &pdispparams->rgvarg[paramIndex]; + switch(param->vt) + { + case VT_I4: + pszStatus = (MAKEINTRESOURCE(param->lVal)); + break; + case VT_BSTR: + pszStatus = param->bstrVal; + break; + default: + if (puArgErr) *puArgErr = paramIndex; + return DISP_E_TYPEMISMATCH; + } + + HRESULT hr = input->SetStatus(pszStatus); + JSAPI_SET_RESULT(pvarResult, boolVal, (SUCCEEDED(hr) ? VARIANT_TRUE : VARIANT_FALSE)); + + return S_OK; +} + +HRESULT LoginResultWebAuth::OnNavigate(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT_OPTIONAL(pdispparams, 1, 3); + + JSAPI_INIT_RESULT(pvarResult, VT_BOOL); + + BSTR targetUrl = JSAPI_PARAM_OPTIONAL(pdispparams, 1, bstrVal, NULL); + BSTR callbackUrl = JSAPI_PARAM_OPTIONAL(pdispparams, 2, bstrVal, NULL); + BSTR message = JSAPI_PARAM_OPTIONAL(pdispparams, 3, bstrVal, NULL); + + HRESULT hr; + + if (NULL == targetUrl || L'\0' == *targetUrl || + NULL == callbackUrl || L'\0' == *callbackUrl) + { + hr = E_INVALIDARG; + } + else + { + EnterCriticalSection(&lock); + LoginBox_FreeString(readyUrl); + readyUrl = LoginBox_CopyString(callbackUrl); + if (NULL == readyUrl) + hr = E_OUTOFMEMORY; + else + { + connectionVerified = FALSE; + if (NULL == hView || FALSE == BrowserControl_Navigate(hView, targetUrl, FALSE)) + { + LoginBox_FreeString(readyUrl); + readyUrl = NULL; + hr = E_FAIL; + } + else + hr = S_OK; + } + LeaveCriticalSection(&lock); + } + + JSAPI_SET_RESULT(pvarResult, boolVal, (SUCCEEDED(hr) ? VARIANT_TRUE : VARIANT_FALSE)); + + return S_OK; +} + +HRESULT LoginResultWebAuth::OnGetString(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + if (0 == ((DISPATCH_METHOD |DISPATCH_PROPERTYGET) &wFlags)) + return DISP_E_MEMBERNOTFOUND; + + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1); + JSAPI_INIT_RESULT(pvarResult, VT_BSTR); + + UINT stringId; + JSAPI_GETUNSIGNED_AS_NUMBER(stringId, pdispparams, 1, puArgErr); + + WCHAR szBuffer[4096] = {0}; + BSTR string = SysAllocString(WASABI_API_LNGSTRINGW_BUF(stringId, szBuffer, ARRAYSIZE(szBuffer))); + JSAPI_SET_RESULT(pvarResult, bstrVal, string); + + return S_OK; +} +HRESULT LoginResultWebAuth::GetResult(INT *pAuthCode, LoginCredentials **ppCredentials) +{ + if (S_OK != IsCompleted()) + return E_PENDING; + + EnterCriticalSection(&lock); + + if (NULL != pAuthCode) + *pAuthCode = authCode; + + if (NULL != ppCredentials) + { + *ppCredentials = credentials; + if (NULL != credentials) + credentials->AddRef(); + } + + LeaveCriticalSection(&lock); + + return S_OK; +} + + +#define CBCLASS LoginResultWebAuth +START_MULTIPATCH; + START_PATCH(MPIID_OMSVC) + M_CB(MPIID_OMSVC, ifc_omservice, ADDREF, Wasabi_AddRef); + M_CB(MPIID_OMSVC, ifc_omservice, RELEASE, Wasabi_Release); + M_CB(MPIID_OMSVC, ifc_omservice, QUERYINTERFACE, Wasabi_QueryInterface); + M_CB(MPIID_OMSVC, ifc_omservice, API_GETID, GetId); + M_CB(MPIID_OMSVC, ifc_omservice, API_GETNAME, GetName); + M_CB(MPIID_OMSVC, ifc_omservice, API_GETURL, GetUrl); + M_CB(MPIID_OMSVC, ifc_omservice, API_GETEXTERNAL, GetExternal); + NEXT_PATCH(MPIID_OMSVCCOMMAND) + M_CB(MPIID_OMSVCCOMMAND, ifc_omservicecommand, ADDREF, Wasabi_AddRef); + M_CB(MPIID_OMSVCCOMMAND, ifc_omservicecommand, RELEASE, Wasabi_Release); + M_CB(MPIID_OMSVCCOMMAND, ifc_omservicecommand, QUERYINTERFACE, Wasabi_QueryInterface); + M_CB(MPIID_OMSVCCOMMAND, ifc_omservicecommand, API_QUERYSTATE, QueryState); + M_CB(MPIID_OMSVCCOMMAND, ifc_omservicecommand, API_EXEC, Exec); + END_PATCH +END_MULTIPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/Loginbox/resultWebAuth.h b/Src/auth/Loginbox/resultWebAuth.h new file mode 100644 index 00000000..ac4cdc34 --- /dev/null +++ b/Src/auth/Loginbox/resultWebAuth.h @@ -0,0 +1,129 @@ +#ifndef NULLSOFT_AUTH_LOGINRESULT_WEBAUTH_HEADER +#define NULLSOFT_AUTH_LOGINRESULT_WEBAUTH_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginResult.h" +#include "./browserEvent.h" +#include "../omBrowser/ifc_omservice.h" +#include "../omBrowser/ifc_omservicecommand.h" +#include "../../nu/dispatchTable.h" + +#include <bfc/multipatch.h> + +class LoginData; +class LoginCredentials; +class ExternalManager; +class obj_ombrowser; + + +#define MPIID_OMSVC 10 +#define MPIID_OMSVCCOMMAND 20 + +class LoginResultWebAuth : public LoginResult, + public IDispatch, + public BrowserEvent, + public MultiPatch<MPIID_OMSVC, ifc_omservice>, + public MultiPatch<MPIID_OMSVCCOMMAND, ifc_omservicecommand> + +{ + +public: + typedef enum + { + DISPID_LOGINCOMPLETE = 700, + DISPID_GETPAGERECT = 701, + DISPID_GETBOXRECT = 702, + DISPID_SETSTATUS = 703, + DISPID_NAVIGATE = 704, + DISPID_GETSTRING = 705, + } DispatchCodes; + +protected: + LoginResultWebAuth(obj_ombrowser *pManager, LPCWSTR pszTargetUrl, LoginData *pInput, Callback fnCallback, void *pUser); + ~LoginResultWebAuth(); + +public: + static HRESULT CreateInstance(LPCWSTR targetUrl, LoginData *input, Callback callback, void *user, LoginResultWebAuth **instance); + +public: + /* IUnknown */ + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + + /* IDispatchable */ + DISPTABLE_INCLUDE(); + DISPHANDLER_REGISTER(OnLoginComplete); + DISPHANDLER_REGISTER(OnGetPageRect); + DISPHANDLER_REGISTER(OnGetBoxRect); + DISPHANDLER_REGISTER(OnSetStatus); + DISPHANDLER_REGISTER(OnNavigate); + DISPHANDLER_REGISTER(OnGetString); + + /* LoignResult */ + HRESULT GetWaitHandle(HANDLE *handle); + HRESULT GetUser(void **pUser); + HRESULT RequestAbort(BOOL fDrop); + HRESULT IsCompleted(); + HRESULT IsAborting(); + HRESULT GetLoginData(LoginData **loginData); + + /* BrowserEvent */ + STDMETHOD_(void, Event_BrowserReady)(HWND hBrowser); + STDMETHOD_(void, Event_DocumentReady)(HWND hBrowser); + STDMETHOD_(void, Event_BrowserClosing)(HWND hBrowser); + STDMETHOD_(void, Event_InvokeApc)(HWND hBrowser, LPARAM param); + +protected: + /* Dispatchable */ + size_t Wasabi_AddRef(); + size_t Wasabi_Release(); + int Wasabi_QueryInterface(GUID iid, void **object); + + /* ifc_omservice */ + unsigned int GetId(); + HRESULT GetName(wchar_t *pszBuffer, int cchBufferMax); + HRESULT GetUrl(wchar_t *pszBuffer, int cchBufferMax); + HRESULT GetExternal(IDispatch **ppDispatch); + + /* ifc_omservicecommand */ + HRESULT QueryState(HWND hBrowser, const GUID *commandGroup, UINT commandId); + HRESULT Exec(HWND hBrowser, const GUID *commandGroup, UINT commandId, ULONG_PTR commandArg); + +public: + HRESULT GetResult(INT *authCode, LoginCredentials **credentials); + +private: + static HRESULT InitBrowserManager(obj_ombrowser **browserMngr); + HRESULT Start(); + HRESULT Finish(); + void NotifyComplete(); + HRESULT DispParamsToCredentials(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, UINT FAR *puArgErr, HRESULT *errorEx, LPWSTR *finishUrl); + + +protected: + ULONG ref; + LoginData *input; + Callback callback; + void *user; + LoginCredentials *credentials; + INT authCode; + obj_ombrowser *browserMngr; + LPWSTR targetUrl; + HWND hView; + CRITICAL_SECTION lock; + HANDLE completed; + ExternalManager *external; + DISPID dispId; + BOOL connectionVerified; + LPWSTR readyUrl; + + +protected: + RECVS_MULTIPATCH; +}; + +#endif //NULLSOFT_AUTH_LOGINRESULT_WEBAUTH_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/resultWinampAuth.cpp b/Src/auth/Loginbox/resultWinampAuth.cpp new file mode 100644 index 00000000..4c04e0e4 --- /dev/null +++ b/Src/auth/Loginbox/resultWinampAuth.cpp @@ -0,0 +1,574 @@ +#include "./resultWinampAuth.h" +#include "./common.h" +#include "./commandWinampAuth.h" +#include "./loginData.h" +#include "./dataCredentials.h" +#include "./loginCredentials.h" +#include "./loginbox.h" + +#include "../resource.h" + +LoginResultWinampAuth::LoginResultWinampAuth(api_auth *auth, LoginDataCredentials *pInput, Callback fnCallback, void *pUser) + : ref(1), authApi(auth), input(pInput), callback(fnCallback), user(pUser), + thread(NULL), abort(NULL), completed(NULL), credentials(NULL), authCode(AUTH_NOT_AUTHORIZED), statusCookie(0) +{ + InitializeCriticalSection(&lock); + + authApi->AddRef(); + input->AddRef(); +} + +LoginResultWinampAuth::~LoginResultWinampAuth() +{ + if (NULL != abort) + CloseHandle(abort); + + if (NULL != completed) + CloseHandle(completed); + + if (NULL != thread) + CloseHandle(thread); + + authApi->Release(); + input->Release(); + + if (NULL != credentials) + credentials->Release(); + + DeleteCriticalSection(&lock); +} + +HRESULT LoginResultWinampAuth::CreateInstance(LoginData *input, Callback callback, void *user, LoginResultWinampAuth **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = NULL; + + LoginDataCredentials *credentialsInput; + if (NULL == input || FAILED(input->QueryInterface(IID_LoginDataCredentials, (void**)&credentialsInput))) + return E_INVALIDARG; + + HRESULT hr; + api_auth *authApi; + + HWND hLoginbox = credentialsInput->GetLoginbox(); + if (NULL == hLoginbox || FALSE == LoginBox_GetAuthApi(hLoginbox, &authApi)) + hr = E_FAIL; + else + { + LoginResultWinampAuth *result = new LoginResultWinampAuth(authApi, credentialsInput, callback, user); + if (NULL == result) + hr = E_OUTOFMEMORY; + else + { + hr = result->Start(); + if (FAILED(hr)) + { + result->Release(); + result = NULL; + } + else + { + *instance = result; + } + } + + authApi->Release(); + } + credentialsInput->Release(); + return hr; +} + +STDMETHODIMP_(ULONG) LoginResultWinampAuth::AddRef(void) +{ + return InterlockedIncrement((LONG*)&ref); +} + +STDMETHODIMP_(ULONG) LoginResultWinampAuth::Release(void) +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +STDMETHODIMP LoginResultWinampAuth::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (NULL == ppvObject) + return E_POINTER; + + if (IsEqualIID(riid, LCUID_WINAMPAUTH)) + *ppvObject = static_cast<LoginResultWinampAuth*>(this); + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = static_cast<IUnknown*>(this); + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + if (NULL == *ppvObject) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +size_t LoginResultWinampAuth::Wasabi_AddRef() +{ + return AddRef(); +} + +size_t LoginResultWinampAuth::Wasabi_Release() +{ + return Release(); +} + +int LoginResultWinampAuth::Wasabi_QueryInterface(GUID iid, void **object) +{ + return QueryInterface(iid, object); +} + +HRESULT LoginResultWinampAuth::Start() +{ + //HRESULT hr; + HANDLE threadCopy = NULL; + + input->SetStatus(MAKEINTRESOURCE(IDS_STATUS_INITIALIZING)); + EnterCriticalSection(&lock); + if (NULL != thread) + { + //hr = E_PENDING; + } + else + { + AddRef(); + thread = CreateThread(NULL, 0, WinampAuth_ThreadProcCallback, this, CREATE_SUSPENDED, NULL); + if (NULL == thread) + { + Release(); + } + else + { + if (0 == DuplicateHandle(GetCurrentProcess(), thread, GetCurrentProcess(), &threadCopy, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ResumeThread(thread); // grr... + threadCopy = NULL; + } + } + } + LeaveCriticalSection(&lock); + + if (NULL != threadCopy) + { + ResumeThread(threadCopy); + CloseHandle(threadCopy); + } + + return S_OK; +} +DWORD LoginResultWinampAuth::ThreadProc() +{ + Callback callbackCopy(NULL); + HANDLE completedCopy(NULL); + INT resultCode = AUTH_SUCCESS; + LPWSTR username(NULL), password(NULL); + + api_auth::AuthResults authResults; + SecureZeroMemory(&authResults, sizeof(authResults)); + + EnterCriticalSection(&lock); + authCode = AUTH_NOT_AUTHORIZED; + if (NULL != credentials) + { + credentials->Release(); + credentials = NULL; + } + LeaveCriticalSection(&lock); + + username = GetUsername(input->GetUsername(), &resultCode); + + if (AUTH_SUCCESS == resultCode) + password = GetPassword(input->GetPassword(), &resultCode); + + if (AUTH_SUCCESS == resultCode) + { + if (NULL == input->GetContext()) + { + resultCode = authApi->Login(username, password, &authResults, this); + } + else + { + LPWSTR passcode = GetPasscode(input->GetPasscode(), &resultCode); + if (AUTH_SUCCESS == resultCode) + { + resultCode = authApi->LoginSecurID(username, password, input->GetContext(), passcode, &authResults, this); + LoginBox_FreeStringSecure(passcode); + } + } + } + + EnterCriticalSection(&lock); + + authCode = resultCode; + switch(authCode) + { + case AUTH_SUCCESS: + { + GUID realm; + if (FAILED(input->GetRealm(&realm)) || + FAILED(LoginCredentials::CreateInstance(&realm, username, authResults.session_key, + authResults.token, authResults.expire, &credentials))) + { + authCode = AUTH_UNEXPECTED; + } + } + break; + case AUTH_SECURID: + if(FAILED(input->SetContext(authResults.context))) + authCode = AUTH_NOT_AUTHORIZED; + break; + } + + INT statusId; + switch(authCode) + { + case AUTH_SUCCESS: statusId = IDS_STATUS_SUCCEEDED; break; + case AUTH_SECURID: statusId = IDS_STATUS_PASSCODE_REQUIRED; break; + case AUTH_ABORT: statusId = IDS_STATUS_ABORTED; break; + default: statusId = IDS_STATUS_FAILED; break; + } + input->SetStatus(MAKEINTRESOURCE(statusId)); + + CloseHandle(thread); + thread = NULL; + callbackCopy = callback; + + if (NULL == completed || FALSE == DuplicateHandle(GetCurrentProcess(), completed, + GetCurrentProcess(), &completedCopy, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + completedCopy = NULL; + } + + LeaveCriticalSection(&lock); + + SecureZeroMemory(&authResults, sizeof(authResults)); + LoginBox_FreeStringSecure(username); + LoginBox_FreeStringSecure(password); + + if (NULL != completedCopy) + { + SetEvent(completedCopy); + CloseHandle(completedCopy); + } + + if (NULL != callbackCopy) + callbackCopy(this); + + return 0; +} + +HRESULT LoginResultWinampAuth::GetWaitHandle(HANDLE *handle) +{ + if (NULL == handle) + return E_POINTER; + + HRESULT hr = S_OK; + + EnterCriticalSection(&lock); + + if (NULL == completed) + { + completed = CreateEvent(NULL, TRUE, (S_OK == IsCompleted()), NULL); + if (NULL == completed) + { + *handle = NULL; + DWORD error = GetLastError(); + hr = HRESULT_FROM_WIN32(error); + } + } + + if (SUCCEEDED(hr) && + FALSE == DuplicateHandle(GetCurrentProcess(), completed, + GetCurrentProcess(), handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + *handle = NULL; + DWORD error = GetLastError(); + hr = HRESULT_FROM_WIN32(error); + } + + LeaveCriticalSection(&lock); + return hr; +} + +HRESULT LoginResultWinampAuth::GetUser(void **pUser) +{ + if (NULL == pUser) return E_POINTER; + EnterCriticalSection(&lock); + *pUser = user; + LeaveCriticalSection(&lock); + return S_OK; +} + +HRESULT LoginResultWinampAuth::RequestAbort(BOOL fDrop) +{ + HRESULT hr; + EnterCriticalSection(&lock); + if (NULL == abort) + { + abort = CreateEvent(NULL, TRUE, FALSE, NULL); + if (NULL == abort) + { + DWORD error = GetLastError(); + hr = HRESULT_FROM_WIN32(error); + } + } + + if (NULL != abort && FALSE == SetEvent(abort)) + { + DWORD error = GetLastError(); + hr = HRESULT_FROM_WIN32(error); + } + else + { + input->SetStatus(MAKEINTRESOURCE(IDS_STATUS_ABORTING)); + } + + if (FALSE != fDrop) + { + callback = NULL; + user = NULL; + } + + LeaveCriticalSection(&lock); + return hr; +} + +HRESULT LoginResultWinampAuth::IsCompleted() +{ + return (NULL == thread) ? S_OK : S_FALSE; +} + +HRESULT LoginResultWinampAuth::IsAborting() +{ + return IsAbortingEx(0); +} + +HRESULT LoginResultWinampAuth::IsAbortingEx(UINT waitMs) +{ + return (NULL != abort && WAIT_OBJECT_0 == WaitForSingleObjectEx(abort, waitMs, TRUE))? + S_OK : S_FALSE; +} + +HRESULT LoginResultWinampAuth::GetLoginData(LoginData **loginData) +{ + if (NULL == loginData) return E_POINTER; + EnterCriticalSection(&lock); + + *loginData = input; + if (NULL != input) + input->AddRef(); + + LeaveCriticalSection(&lock); + return S_OK; +} + +HRESULT LoginResultWinampAuth::GetResult(INT *pAuthCode, LoginCredentials **ppCredentials) +{ + if (S_OK != IsCompleted()) + return E_PENDING; + + EnterCriticalSection(&lock); + + if (NULL != pAuthCode) + *pAuthCode = authCode; + + if (NULL != ppCredentials) + { + *ppCredentials = credentials; + if (NULL != credentials) + credentials->AddRef(); + } + + LeaveCriticalSection(&lock); + + return S_OK; +} + +int LoginResultWinampAuth::Event_AuthConnecting() +{ + if (S_OK == IsAbortingEx(0)) + return 1; + + input->SetStatus(MAKEINTRESOURCE(IDS_STATUS_CONNECTING)); + return 0; +} + +int LoginResultWinampAuth::Event_AuthSending() +{ + if (S_OK == IsAbortingEx(0)) + return 1; + + input->SetStatus(MAKEINTRESOURCE(IDS_STATUS_SENDING)); + return 0; +} + +int LoginResultWinampAuth::Event_AuthReceiving() +{ + if (S_OK == IsAbortingEx(0)) + return 1; + + input->SetStatus(MAKEINTRESOURCE(IDS_STATUS_RECEIVING)); + return 0; +} + +int LoginResultWinampAuth::Event_AuthIdle() +{ + if (S_OK == IsAbortingEx(50)) + return 1; + + return 0; +} + +LPWSTR LoginResultWinampAuth::GetUsername(LPCWSTR pszInput, INT *authError) +{ + return MakeAuthParam(pszInput, -1, 3, 48, TRUE, TRUE, (0xFFFF & ~C1_CNTRL), AUTH_USERNAME, authError); +} + +LPWSTR LoginResultWinampAuth::GetPassword(LPCWSTR pszInput, INT *authError) +{ + return MakeAuthParam(pszInput, -1, 6, 48, TRUE, FALSE, (0xFFFF & ~C1_CNTRL), AUTH_PASSWORD, authError); +} + +LPWSTR LoginResultWinampAuth::GetPasscode(LPCWSTR pszInput, INT *authError) +{ + return MakeAuthParam(pszInput, -1, 6, 6, FALSE, FALSE, C1_DIGIT, AUTH_PASSCODE, authError); +} + +LPWSTR LoginResultWinampAuth::MakeAuthParam(LPCWSTR pszInput, INT cchInput, INT min, INT max, BOOL removeSpaces, BOOL firstLetter, WORD typeMask, INT errorBase, INT *authError) +{ + if (cchInput < 0 || NULL == pszInput) + cchInput = (NULL != pszInput) ? lstrlen(pszInput) : 0; + + if (cchInput < min || (FALSE == removeSpaces && cchInput > max)) + { + if (NULL != authError) + *authError = errorBase + ((cchInput < min) ? AUTHPARAM_TOOSHORT : AUTHPARAM_TOOLONG); + return NULL; + } + + WORD *info = (WORD*)calloc(cchInput, sizeof(WORD)); + if (NULL == info) + { + if (NULL != authError) *authError = AUTH_UNEXPECTED; + return NULL; + } + + if (FALSE == GetStringTypeW(CT_CTYPE1, pszInput, cchInput, info)) + { + free(info); + if (NULL != authError) *authError = AUTH_UNEXPECTED; + return NULL; + } + + INT error = AUTH_SUCCESS; + LPWSTR dest = NULL; + + BOOL firstChecked = FALSE; + INT cchSpaces = 0; + for (INT i = 0; i < cchInput; i++) + { + if (FALSE != removeSpaces && 0 != (C1_SPACE & info[i])) + cchSpaces++; + else if (0 == (typeMask & info[i])) + { + error = errorBase + AUTHPARAM_BADFORMAT; + break; + } + else + { + if (FALSE != firstLetter && FALSE == firstChecked) + { + if (0 == (C1_ALPHA & info[i])) + { + error = errorBase + AUTHPARAM_BADFORMAT; + break; + } + firstChecked = TRUE; + } + } + } + + if (AUTH_SUCCESS == error) + { + INT cchTotal = cchInput - cchSpaces; + if (cchTotal < min) + error = errorBase + AUTHPARAM_TOOSHORT; + else if (cchTotal > max) + error = errorBase + AUTHPARAM_TOOLONG; + else + { + dest = LoginBox_MallocString(cchTotal + 1); + if (NULL == dest) + error = AUTH_UNEXPECTED; + else + { + if (FALSE != removeSpaces) + { + LPCWSTR s = pszInput; + LPWSTR d = dest; + for (INT i = 0; i < cchInput; i++, s++) + { + if (0 == (C1_SPACE & info[i])) + { + *d = *s; + d++; + } + } + *d = L'\0'; + } + else + { + CopyMemory(dest, pszInput, (cchInput * sizeof(WCHAR))); + dest[cchInput] = L'\0'; + } + } + } + } + + + free(info); + + if (NULL != authError) + *authError = error; + + return dest; +} + + +static DWORD WINAPI WinampAuth_ThreadProcCallback(void *param) +{ + LoginResultWinampAuth *result =(LoginResultWinampAuth*)param; + if (NULL == result) return 1; + + INT exitCode = result->ThreadProc(); + result->Release(); + return exitCode; +} + + +#define CBCLASS LoginResultWinampAuth +START_DISPATCH; +CB(ADDREF, Wasabi_AddRef) +CB(RELEASE, Wasabi_Release) +CB(QUERYINTERFACE, Wasabi_QueryInterface) +CB(ONCONNECTING, Event_AuthConnecting) +CB(ONSENDING, Event_AuthSending) +CB(ONRECEIVING, Event_AuthReceiving) +CB(ONIDLE, Event_AuthIdle) +END_DISPATCH; +#undef CBCLASS + diff --git a/Src/auth/Loginbox/resultWinampAuth.h b/Src/auth/Loginbox/resultWinampAuth.h new file mode 100644 index 00000000..c20b300b --- /dev/null +++ b/Src/auth/Loginbox/resultWinampAuth.h @@ -0,0 +1,85 @@ +#ifndef NULLSOFT_AUTH_LOGINRESULT_WINAMPAUTH_HEADER +#define NULLSOFT_AUTH_LOGINRESULT_WINAMPAUTH_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginResult.h" +#include "../auth/ifc_authcallback.h" +#include "../auth/api_auth.h" + +class LoginData; +class LoginCredentials; +class LoginDataCredentials; + +class LoginResultWinampAuth : public LoginResult, + public ifc_authcallback +{ +protected: + LoginResultWinampAuth(api_auth *auth, LoginDataCredentials *pInput, Callback callback, void *user); + ~LoginResultWinampAuth(); + +public: + static HRESULT CreateInstance(LoginData *input, Callback callback, void *user, LoginResultWinampAuth **instance); + +public: + /* IUnknown */ + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + + /* LoginResult */ + HRESULT GetWaitHandle(HANDLE *handle); + HRESULT GetUser(void **pUser); + HRESULT RequestAbort(BOOL fDrop); + HRESULT IsCompleted(); + HRESULT IsAborting(); + HRESULT GetLoginData(LoginData **loginData); + +public: + HRESULT GetResult(INT *authCode, LoginCredentials **credentials); + +protected: + /* Dispatchable */ + size_t Wasabi_AddRef(); + size_t Wasabi_Release(); + int Wasabi_QueryInterface(GUID iid, void **object); + + /*ifc_authcallback*/ + int Event_AuthConnecting(); + int Event_AuthSending(); + int Event_AuthReceiving(); + int Event_AuthIdle(); + +private: + HRESULT Start(); + DWORD ThreadProc(); + HRESULT IsAbortingEx(UINT waitMs); + LPWSTR MakeAuthParam(LPCWSTR pszInput, INT cchInput, INT min, INT max, BOOL removeSpaces, BOOL firstLetter, WORD typeMask, INT errorBase, INT *authError); + LPWSTR GetUsername(LPCWSTR pszInput, INT *authError); + LPWSTR GetPassword(LPCWSTR pszInput, INT *authError); + LPWSTR GetPasscode(LPCWSTR pszInput, INT *authError); + + friend static DWORD WINAPI WinampAuth_ThreadProcCallback(void *param); + + +protected: + ULONG ref; + LoginDataCredentials *input; + Callback callback; + void *user; + api_auth *authApi; + HANDLE thread; + HANDLE abort; + HANDLE completed; + LoginCredentials *credentials; + INT authCode; + CRITICAL_SECTION lock; + UINT statusCookie; + +protected: + RECVS_DISPATCH; +}; + +#endif //NULLSOFT_AUTH_LOGINRESULT_WINAMPAUTH_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/stringBuilder.cpp b/Src/auth/Loginbox/stringBuilder.cpp new file mode 100644 index 00000000..d20ef954 --- /dev/null +++ b/Src/auth/Loginbox/stringBuilder.cpp @@ -0,0 +1,85 @@ +#include "./common.h" +#include "./stringBuilder.h" +#include <strsafe.h> + +StringBuilder::StringBuilder() + : buffer(NULL), cursor(NULL), allocated(0), remaining(0) +{ +} + +StringBuilder::~StringBuilder() +{ + LoginBox_FreeString(buffer); +} + +HRESULT StringBuilder::Allocate(size_t newSize) +{ + if (newSize <= allocated) + return S_FALSE; + + LPWSTR t = LoginBox_ReAllocString(buffer, newSize); + if (NULL == t) return E_OUTOFMEMORY; + + cursor = t + (cursor - buffer); + buffer = t; + + remaining += newSize - allocated; + allocated = newSize; + + return S_OK; +} + +void StringBuilder::Clear(void) +{ + if (NULL != buffer) + { + buffer[0] = L'\0'; + } + cursor = buffer; + remaining = allocated; +} + +LPCWSTR StringBuilder::Get(void) +{ + return buffer; +} + +HRESULT StringBuilder::Set(size_t index, WCHAR value) +{ + if (NULL == buffer) + return E_POINTER; + + if (index >= allocated) + return E_INVALIDARG; + + buffer[index] = value; + return S_OK; +} + +HRESULT StringBuilder::Append(LPCWSTR pszString) +{ + HRESULT hr; + if (NULL == buffer) + { + hr = Allocate(1024); + if (FAILED(hr)) return hr; + } + + size_t cchCursor = remaining; + hr = StringCchCopyEx(cursor, cchCursor, pszString, &cursor, &remaining, STRSAFE_IGNORE_NULLS); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + size_t offset = cchCursor - remaining; + size_t requested = lstrlen(pszString) + (allocated - remaining) + 1; + size_t newsize = allocated * 2; + while (newsize < requested) newsize = newsize * 2; + + hr = Allocate(newsize); + if (FAILED(hr)) return hr; + + hr = StringCchCopyEx(cursor, remaining, pszString + offset, &cursor, &remaining, STRSAFE_IGNORE_NULLS); + } + + return hr; +} +
\ No newline at end of file diff --git a/Src/auth/Loginbox/stringBuilder.h b/Src/auth/Loginbox/stringBuilder.h new file mode 100644 index 00000000..cc2170b7 --- /dev/null +++ b/Src/auth/Loginbox/stringBuilder.h @@ -0,0 +1,31 @@ +#ifndef NULLSOFT_WINAMP_STRING_BUILDER_HEADER +#define NULLSOFT_WINAMP_STRING_BUILDER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class StringBuilder +{ +public: + StringBuilder(); + ~StringBuilder(); + +public: + HRESULT Allocate(size_t newSize); + void Clear(void); + LPCWSTR Get(void); + HRESULT Set(size_t index, WCHAR value); + HRESULT Append(LPCWSTR pszString); + + +protected: + LPWSTR buffer; + LPWSTR cursor; + size_t allocated; + size_t remaining; +}; + +#endif //NULLSOFT_WINAMP_STRING_BUILDER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/templateAddress.cpp b/Src/auth/Loginbox/templateAddress.cpp new file mode 100644 index 00000000..81b51f80 --- /dev/null +++ b/Src/auth/Loginbox/templateAddress.cpp @@ -0,0 +1,148 @@ +#include "./templateAddress.h" +#include "./pageAddress.h" +#include "./common.h" + +#include <shlwapi.h> +#include <strsafe.h> + +LoginTemplateAddress::LoginTemplateAddress() + : ref(1), title(NULL), message(NULL), address(NULL), addressTitle(NULL), + replaceUsername(FALSE) +{ +} + +LoginTemplateAddress::~LoginTemplateAddress() +{ + LoginBox_FreeString(title); + LoginBox_FreeString(message); + LoginBox_FreeString(address); + LoginBox_FreeString(addressTitle); +} + +HRESULT LoginTemplateAddress::CreateInstance(LoginTemplateAddress **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = new LoginTemplateAddress(); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +ULONG LoginTemplateAddress::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginTemplateAddress::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginTemplateAddress::GetType(GUID *templateUid) +{ + if (NULL == templateUid) return E_INVALIDARG; + *templateUid = LTUID_ADDRESS; + return S_OK; +} + +HRESULT LoginTemplateAddress::SetParameter(LPCWSTR pszKey, LPCWSTR pszValue) +{ + + if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"title")) + { + LoginBox_FreeString(title); + title = LoginBox_CopyString(pszValue); + } + else if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"message")) + { + LoginBox_FreeString(message); + message = LoginBox_CopyString(pszValue); + } + else if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"address")) + { + LoginBox_FreeString(address); + address = LoginBox_CopyString(pszValue); + } + else if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"addressTitle")) + { + LoginBox_FreeString(addressTitle); + addressTitle = LoginBox_CopyString(pszValue); + } + else if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"replaceUsername")) + { + if (NULL != pszValue) + { + WORD charType; + LPCWSTR p = pszValue; + while(L'\0' != *p) + { + if (FALSE == GetStringTypeW(CT_CTYPE1, p, 1, &charType) || + 0 != ((C1_DIGIT | C1_XDIGIT) & charType)) + { + break; + } + p = CharNext(p); + } + INT r; + if (FALSE != StrToIntEx(p, STIF_SUPPORT_HEX, &r) && 0 != r) + replaceUsername = TRUE; + + } + } + return S_OK; +} + +HRESULT LoginTemplateAddress::IsValid() +{ + return S_OK; +} + +HRESULT LoginTemplateAddress::IsIdentical(LoginTemplate *test) +{ + if (NULL == test) + return E_INVALIDARG; + + GUID typeId; + if (FAILED(test->GetType(&typeId)) || FALSE == IsEqualGUID(LTUID_ADDRESS, typeId)) + return S_FALSE; + + LoginTemplateAddress *testAddr = (LoginTemplateAddress*)test; + + if(S_OK != LoginBox_IsStrEq(title, testAddr->title) || + S_OK != LoginBox_IsStrEq(message, testAddr->message) || + S_OK != LoginBox_IsStrEqInvI(address, testAddr->address) || + S_OK != LoginBox_IsStrEq(addressTitle, testAddr->addressTitle) || + replaceUsername != testAddr->replaceUsername) + { + return S_FALSE; + } + + return S_OK; +} + + +HWND LoginTemplateAddress::CreatePage(HWND hLoginbox, HWND hParent) +{ + HWND hPage = LoginPageAddress::CreatePage(hLoginbox, hParent); + if (NULL == hPage) return NULL; + + if (NULL != title) + LoginPage_SetTitle(hPage, title); + + if (NULL != message) + LoginPageAddress_SetMessage(hPage, message); + + if (NULL != address) + LoginPageAddress_SetAddress(hPage, address, replaceUsername); + + if (NULL != addressTitle) + LoginPageAddress_SetAddressTitle(hPage, addressTitle); + + return hPage; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/templateAddress.h b/Src/auth/Loginbox/templateAddress.h new file mode 100644 index 00000000..e09a9f25 --- /dev/null +++ b/Src/auth/Loginbox/templateAddress.h @@ -0,0 +1,44 @@ +#ifndef NULLSOFT_AUTH_LOGIN_TEMPLATE_ADDRESS_HEADER +#define NULLSOFT_AUTH_LOGIN_TEMPLATE_ADDRESS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginTemplate.h" + +// {F244385A-178A-4e8c-95DC-BF43058BAA9B} +static const GUID LTUID_ADDRESS = +{ 0xf244385a, 0x178a, 0x4e8c, { 0x95, 0xdc, 0xbf, 0x43, 0x5, 0x8b, 0xaa, 0x9b } }; + +class LoginTemplateAddress : public LoginTemplate +{ +protected: + LoginTemplateAddress(); + ~LoginTemplateAddress(); + +public: + static HRESULT CreateInstance(LoginTemplateAddress **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + HRESULT GetType(GUID *templateUid); + + HRESULT SetParameter(LPCWSTR pszKey, LPCWSTR pszValue); + HRESULT IsValid(); + HRESULT IsIdentical(LoginTemplate *test); + + HWND CreatePage(HWND hLoginbox, HWND hParent); + +protected: + ULONG ref; + LPWSTR title; + LPWSTR message; + LPWSTR address; + LPWSTR addressTitle; + BOOL replaceUsername; +}; + +#endif //NULLSOFT_AUTH_LOGIN_TEMPLATE_ADDRESS_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/templateCredentials.cpp b/Src/auth/Loginbox/templateCredentials.cpp new file mode 100644 index 00000000..ae905216 --- /dev/null +++ b/Src/auth/Loginbox/templateCredentials.cpp @@ -0,0 +1,129 @@ +#include "./templateCredentials.h" +#include "./pageCredentials.h" +#include "./common.h" + +LoginTemplateCredentials::LoginTemplateCredentials() + : ref(1), title(NULL), accountRecoverUrl(NULL), accountCreateUrl(NULL), usernameLabel(NULL), passwordLabel(NULL) +{ +} + +LoginTemplateCredentials::~LoginTemplateCredentials() +{ + LoginBox_FreeString(title); + LoginBox_FreeString(accountRecoverUrl); + LoginBox_FreeString(accountCreateUrl); + LoginBox_FreeString(usernameLabel); + LoginBox_FreeString(passwordLabel); +} + +HRESULT LoginTemplateCredentials::CreateInstance(LoginTemplateCredentials **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = new LoginTemplateCredentials(); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +ULONG LoginTemplateCredentials::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginTemplateCredentials::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginTemplateCredentials::GetType(GUID *templateUid) +{ + if (NULL == templateUid) return E_INVALIDARG; + *templateUid = LTUID_CREDENTIALS; + return S_OK; +} + +HRESULT LoginTemplateCredentials::SetParameter(LPCWSTR pszKey, LPCWSTR pszValue) +{ + if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"title")) + { + LoginBox_FreeString(title); + title = LoginBox_CopyString(pszValue); + } + else if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"accountRecoverUrl")) + { + LoginBox_FreeString(accountRecoverUrl); + accountRecoverUrl = LoginBox_CopyString(pszValue); + } + else if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"accountCreateUrl")) + { + LoginBox_FreeString(accountCreateUrl); + accountCreateUrl = LoginBox_CopyString(pszValue); + } + else if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"usernameLabel")) + { + LoginBox_FreeString(usernameLabel); + usernameLabel = LoginBox_CopyString(pszValue); + } + else if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"passwordLabel")) + { + LoginBox_FreeString(passwordLabel); + passwordLabel = LoginBox_CopyString(pszValue); + } + + return S_OK; +} + +HRESULT LoginTemplateCredentials::IsValid() +{ + return S_OK; +} + + +HRESULT LoginTemplateCredentials::IsIdentical(LoginTemplate *test) +{ + if (NULL == test) + return E_INVALIDARG; + + GUID typeId; + if (FAILED(test->GetType(&typeId)) || FALSE == IsEqualGUID(LTUID_CREDENTIALS, typeId)) + return S_FALSE; + + LoginTemplateCredentials *testCred = (LoginTemplateCredentials*)test; + + if(S_OK != LoginBox_IsStrEq(title, testCred->title) || + S_OK != LoginBox_IsStrEqInvI(accountRecoverUrl, testCred->accountRecoverUrl) || + S_OK != LoginBox_IsStrEqInvI(accountCreateUrl, testCred->accountCreateUrl) || + S_OK != LoginBox_IsStrEq(usernameLabel, testCred->usernameLabel) || + S_OK != LoginBox_IsStrEq(passwordLabel, testCred->passwordLabel)) + { + return S_FALSE; + } + + return S_OK; +} + + +HWND LoginTemplateCredentials::CreatePage(HWND hLoginbox, HWND hParent) +{ + HWND hPage = LoginPageCredentials::CreatePage(hLoginbox, hParent); + if (NULL == hPage) return NULL; + + if (NULL != title) + LoginPage_SetTitle(hPage, title); + if (NULL != accountRecoverUrl) + LoginPageCredentials_SetAccountRecoverUrl(hPage, accountRecoverUrl); + if (NULL != accountCreateUrl) + LoginPageCredentials_SetAccountCreateUrl(hPage, accountCreateUrl); + if (NULL != usernameLabel) + LoginPageCredentials_SetUsernameLabel(hPage, usernameLabel); + if (NULL != passwordLabel) + LoginPageCredentials_SetPasswordLabel(hPage, passwordLabel); + + return hPage; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/templateCredentials.h b/Src/auth/Loginbox/templateCredentials.h new file mode 100644 index 00000000..d5aa9dc2 --- /dev/null +++ b/Src/auth/Loginbox/templateCredentials.h @@ -0,0 +1,46 @@ +#ifndef NULLSOFT_AUTH_LOGIN_TEMPLATE_CREDENTIALS_HEADER +#define NULLSOFT_AUTH_LOGIN_TEMPLATE_CREDENTIALS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginTemplate.h" + +// {13B3CEEB-A751-4864-8C69-A8E0566B169C} +static const GUID LTUID_CREDENTIALS = +{ 0x13b3ceeb, 0xa751, 0x4864, { 0x8c, 0x69, 0xa8, 0xe0, 0x56, 0x6b, 0x16, 0x9c } }; + + +class LoginTemplateCredentials : public LoginTemplate +{ +protected: + LoginTemplateCredentials(); + ~LoginTemplateCredentials(); + +public: + static HRESULT CreateInstance(LoginTemplateCredentials **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + HRESULT GetType(GUID *templateUid); + + HRESULT SetParameter(LPCWSTR pszKey, LPCWSTR pszValue); + HRESULT IsValid(); + HRESULT IsIdentical(LoginTemplate *test); + + HWND CreatePage(HWND hLoginbox, HWND hParent); + +protected: + ULONG ref; + LPWSTR title; + LPWSTR accountRecoverUrl; + LPWSTR accountCreateUrl; + LPWSTR usernameLabel; + LPWSTR passwordLabel; + +}; + +#endif //NULLSOFT_AUTH_LOGIN_TEMPLATE_CREDENTIALS_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/templateInfo.cpp b/Src/auth/Loginbox/templateInfo.cpp new file mode 100644 index 00000000..cf36621c --- /dev/null +++ b/Src/auth/Loginbox/templateInfo.cpp @@ -0,0 +1,100 @@ +#include "./templateInfo.h" +#include "./pageInfo.h" +#include "./common.h" + +LoginTemplateInfo::LoginTemplateInfo() + : ref(1), title(NULL), message(NULL) +{ +} + +LoginTemplateInfo::~LoginTemplateInfo() +{ + LoginBox_FreeString(title); + LoginBox_FreeString(message); +} + +HRESULT LoginTemplateInfo::CreateInstance(LoginTemplateInfo **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = new LoginTemplateInfo(); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +ULONG LoginTemplateInfo::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginTemplateInfo::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginTemplateInfo::GetType(GUID *templateUid) +{ + if (NULL == templateUid) return E_INVALIDARG; + *templateUid = LTUID_INFO; + return S_OK; +} + +HRESULT LoginTemplateInfo::SetParameter(LPCWSTR pszKey, LPCWSTR pszValue) +{ + if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"title")) + { + LoginBox_FreeString(title); + title = LoginBox_CopyString(pszValue); + } + else if (S_OK == LoginBox_IsStrEqInvI(pszKey, L"message")) + { + LoginBox_FreeString(message); + message = LoginBox_CopyString(pszValue); + } + return S_OK; +} + +HRESULT LoginTemplateInfo::IsValid() +{ + return S_OK; +} + +HRESULT LoginTemplateInfo::IsIdentical(LoginTemplate *test) +{ + if (NULL == test) + return E_INVALIDARG; + + GUID typeId; + if (FAILED(test->GetType(&typeId)) || FALSE == IsEqualGUID(LTUID_INFO, typeId)) + return S_FALSE; + + LoginTemplateInfo *testInfo = (LoginTemplateInfo*)test; + + if(S_OK != LoginBox_IsStrEq(title, testInfo->title) || + S_OK != LoginBox_IsStrEq(message, testInfo->message)) + { + return S_FALSE; + } + + return S_OK; +} + +HWND LoginTemplateInfo::CreatePage(HWND hLoginbox, HWND hParent) +{ + HWND hPage = LoginPageInfo::CreatePage(hLoginbox, hParent); + if (NULL == hPage) return NULL; + + if (NULL != title) + LoginPage_SetTitle(hPage, title); + + if (NULL != message) + LoginPageInfo_SetMessage(hPage, message); + + return hPage; +}
\ No newline at end of file diff --git a/Src/auth/Loginbox/templateInfo.h b/Src/auth/Loginbox/templateInfo.h new file mode 100644 index 00000000..ca4094be --- /dev/null +++ b/Src/auth/Loginbox/templateInfo.h @@ -0,0 +1,42 @@ +#ifndef NULLSOFT_AUTH_LOGIN_TEMPLATE_INFO_HEADER +#define NULLSOFT_AUTH_LOGIN_TEMPLATE_INFO_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./loginTemplate.h" + +// {AF0F69A5-61B5-4e22-BC20-C075AEAB6C0A} +static const GUID LTUID_INFO = +{ 0xaf0f69a5, 0x61b5, 0x4e22, { 0xbc, 0x20, 0xc0, 0x75, 0xae, 0xab, 0x6c, 0xa } }; + + +class LoginTemplateInfo : public LoginTemplate +{ +protected: + LoginTemplateInfo(); + ~LoginTemplateInfo(); + +public: + static HRESULT CreateInstance(LoginTemplateInfo **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + HRESULT GetType(GUID *templateUid); + + HRESULT SetParameter(LPCWSTR pszKey, LPCWSTR pszValue); + HRESULT IsValid(); + HRESULT IsIdentical(LoginTemplate *test); + + HWND CreatePage(HWND hLoginbox, HWND hParent); + +protected: + ULONG ref; + LPWSTR title; + LPWSTR message; +}; + +#endif //NULLSOFT_AUTH_LOGIN_TEMPLATE_INFO_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/templateNodeParser.cpp b/Src/auth/Loginbox/templateNodeParser.cpp new file mode 100644 index 00000000..101a71ec --- /dev/null +++ b/Src/auth/Loginbox/templateNodeParser.cpp @@ -0,0 +1,82 @@ +#include "./templateNodeParser.h" +#include "./loginTemplate.h" +#include "./loginProvider.h" + +#include "../../xml/obj_xml.h" + +LoginTemplateNodeParser::LoginTemplateNodeParser() + : reader(NULL), provider(NULL) +{ +} + +LoginTemplateNodeParser::~LoginTemplateNodeParser() +{ + End(); +} + + +HRESULT LoginTemplateNodeParser::Begin(obj_xml *pReader, LoginProvider *pProvider) +{ + if (NULL != reader || NULL != provider) + return E_PENDING; + + if (NULL == pReader || NULL == pProvider) + return E_INVALIDARG; + + reader = pReader; + reader->AddRef(); + + provider = pProvider; + provider->AddRef(); + + reader->xmlreader_registerCallback(L"loginProviders\fprovider\ftemplate", this); + + return S_OK; +} + +HRESULT LoginTemplateNodeParser::End() +{ + if (NULL != reader) + { + reader->xmlreader_unregisterCallback(this); + reader->Release(); + reader = NULL; + } + + if (NULL != provider) + { + provider->Release(); + provider = NULL; + } + return S_OK; +} + + +void LoginTemplateNodeParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementParser.Begin(reader, params); +} + +void LoginTemplateNodeParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + LoginTemplate *result; + if (SUCCEEDED(elementParser.End(reader, &result))) + { + if (NULL != provider) + provider->SetTemplate(result); + + result->Release(); + } +} + +void LoginTemplateNodeParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ +} + +#define CBCLASS LoginTemplateNodeParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/Loginbox/templateNodeParser.h b/Src/auth/Loginbox/templateNodeParser.h new file mode 100644 index 00000000..36fc0d2a --- /dev/null +++ b/Src/auth/Loginbox/templateNodeParser.h @@ -0,0 +1,41 @@ +#ifndef NULLSOFT_AUTH_LOGIN_TEMPLATE_NODE_PARSER_HEADER +#define NULLSOFT_AUTH_LOGIN_TEMPLATE_NODE_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "./templateParser.h" + +class obj_xml; +class LoginTemplate; +class LoginProvider; + +class LoginTemplateNodeParser : public ifc_xmlreadercallback +{ + +public: + LoginTemplateNodeParser(); + ~LoginTemplateNodeParser(); + +public: + HRESULT Begin(obj_xml *reader, LoginProvider *provider); + HRESULT End(); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + obj_xml *reader; + LoginTemplateParser elementParser; + LoginProvider *provider; + +protected: + RECVS_DISPATCH; +}; + +#endif //NULLSOFT_AUTH_LOGIN_TEMPLATE_NODE_PARSER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/templateParser.cpp b/Src/auth/Loginbox/templateParser.cpp new file mode 100644 index 00000000..4bcb884f --- /dev/null +++ b/Src/auth/Loginbox/templateParser.cpp @@ -0,0 +1,111 @@ +#include "./templateParser.h" +#include "./loginTemplate.h" +#include "./templateCredentials.h" +#include "./templateInfo.h" +#include "./templateAddress.h" + +#include "./common.h" + +#include "../../xml/obj_xml.h" + + +LoginTemplateParser::LoginTemplateParser() + : object(NULL) +{ +} + +LoginTemplateParser::~LoginTemplateParser() +{ + if (NULL != object) + object->Release(); + +} + + +HRESULT LoginTemplateParser::Begin(obj_xml *reader, ifc_xmlreaderparams *params) +{ + if (NULL != object) + return E_PENDING; + + if (NULL == reader || NULL == params) + return E_INVALIDARG; + + GUID templateId; + LPCWSTR pszId = params->getItemValue(L"id"); + if (NULL == pszId || RPC_S_OK != UuidFromString((RPC_WSTR)pszId, &templateId)) + return E_INVALIDARG; + + HRESULT hr; + if (IsEqualGUID(LTUID_CREDENTIALS, templateId)) + hr = LoginTemplateCredentials::CreateInstance((LoginTemplateCredentials**)&object); + else if (IsEqualGUID(LTUID_INFO, templateId)) + hr = LoginTemplateInfo::CreateInstance((LoginTemplateInfo**)&object); + else if (IsEqualGUID(LTUID_ADDRESS, templateId)) + hr = LoginTemplateAddress::CreateInstance((LoginTemplateAddress**)&object); + else + hr = E_INVALIDARG; + + if (SUCCEEDED(hr)) + reader->xmlreader_registerCallback(L"loginProviders\fprovider\ftemplate\f*", this); + + return hr; +} + +HRESULT LoginTemplateParser::End(obj_xml *reader, LoginTemplate **instance) +{ + if (NULL == object) + return E_UNEXPECTED; + + HRESULT hr; + + if (SUCCEEDED(object->IsValid())) + { + if (NULL != instance) + { + *instance = object; + object->AddRef(); + } + hr = S_OK; + } + else + hr = E_FAIL; + + object->Release(); + object = NULL; + + if (NULL != reader) + reader->xmlreader_unregisterCallback(this); + + return hr; +} + + +void LoginTemplateParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementString.Clear(); +} + +void LoginTemplateParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + if (NULL != object) + object->SetParameter(xmltag, elementString.Get()); +} + +void LoginTemplateParser::Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value) +{ + elementString.Append(value); +} + +void LoginTemplateParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ + elementString.Clear(); +} + +#define CBCLASS LoginTemplateParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONCHARDATA, Event_XmlCharData) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/Loginbox/templateParser.h b/Src/auth/Loginbox/templateParser.h new file mode 100644 index 00000000..fa4ba7f5 --- /dev/null +++ b/Src/auth/Loginbox/templateParser.h @@ -0,0 +1,40 @@ +#ifndef NULLSOFT_AUTH_LOGIN_TEMPLATE_PARSER_HEADER +#define NULLSOFT_AUTH_LOGIN_TEMPLATE_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "./stringBuilder.h" + +class obj_xml; +class LoginTemplate; + +class LoginTemplateParser : public ifc_xmlreadercallback +{ + +public: + LoginTemplateParser(); + ~LoginTemplateParser(); + +public: + HRESULT Begin(obj_xml *reader, ifc_xmlreaderparams *params); + HRESULT End(obj_xml *reader, LoginTemplate **instance); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + LoginTemplate *object; + StringBuilder elementString; + +protected: + RECVS_DISPATCH; +}; + +#endif //NULLSOFT_AUTH_LOGIN_TEMPLATE_PARSER_HEADER
\ No newline at end of file diff --git a/Src/auth/Loginbox/update.cpp b/Src/auth/Loginbox/update.cpp new file mode 100644 index 00000000..6e549e96 --- /dev/null +++ b/Src/auth/Loginbox/update.cpp @@ -0,0 +1,179 @@ +#include "./update.h" +#include "./download.h" +#include "./downloadResult.h" +#include "./loginBox.h" +#include "./loginStatus.h" +#include "./providerLoader.h" +#include "./providerEnumerator.h" + +#include "../resource.h" +#include "../api.h" + + +LoginUpdate::LoginUpdate(HWND hLoginbox) +: ref(1), hwnd(hLoginbox) +{ + InitializeCriticalSection(&lock); +} + +LoginUpdate::~LoginUpdate() +{ + EnterCriticalSection(&lock); + + size_t index = downloads.size(); + while(index--) + { + LoginDownloadResult *result = downloads[index]; + if (NULL != result) + { + result->RequestAbort(TRUE); + result->Release(); + } + } + downloads.clear(); + + LeaveCriticalSection(&lock); + DeleteCriticalSection(&lock); +} + +HRESULT LoginUpdate::CreateInstance(HWND hLoginbox, LoginUpdate **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = new LoginUpdate(hLoginbox); + if (NULL == *instance) return E_OUTOFMEMORY; + return S_OK; +} + +ULONG LoginUpdate::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +ULONG LoginUpdate::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +HRESULT LoginUpdate::Start() +{ + EnterCriticalSection(&lock); + + HRESULT hr; + if (0 != downloads.size()) + hr = E_PENDING; + else + { + LoginDownload download; + + downloads.push_back(NULL); + LoginDownloadResult **ppResult = (downloads.end() - 1); + + hr = download.Begin(L"http://dl.getdropbox.com/u/1994752/loginProviders.xml", + LoginDownloadResult::typeProviderList, LoginUpdate_DownloadCompleted, this, NULL, ppResult); + if (FAILED(hr)) + { + downloads.pop_back(); + } + } + + LeaveCriticalSection(&lock); + return hr; +} + +HRESULT LoginUpdate::Abort() +{ + EnterCriticalSection(&lock); + size_t index = downloads.size(); + while(index--) + { + LoginDownloadResult *result = downloads[index]; + if (NULL != result) + { + result->RequestAbort(TRUE); + result->Release(); + } + } + downloads.clear(); + + LeaveCriticalSection(&lock); + return S_OK; +} + +void LoginUpdate::DownloadCompleted(LoginDownloadResult *result) +{ + +// BSTR fileName = NULL; +// LoginDownload download; +// HRESULT hr = download.End(result, &fileName); +// +// SleepEx(10000, FALSE); +// +// UINT type; +// if (FAILED(result->GetType(&type))) +// type = LoginDownloadResult::typeUnknown; +// +// switch(type) +// { +// case LoginDownloadResult::typeProviderList: +// { +// LoginProviderEnumerator *enumerator; +// if (S_FALSE != hr) +// { +// LoginProviderLoader loader; +// hr = loader.ReadXml(fileName, &enumerator); +// if (FAILED(hr)) +// enumerator = NULL; +// } +// else +// enumerator = NULL; +// +// if (SUCCEEDED(hr)) +// { +// LoginBox_ProvidersUpdated(hwnd, this, (S_FALSE == hr), enumerator); +// } +// +// if (NULL != enumerator) +// enumerator->Release(); +// } +// break; +// case LoginDownloadResult::typeImage: +// break; +// } +// +// EnterCriticalSection(&lock); +// +// size_t index = downloads.size(); +// while(index--) +// { +// if (downloads[index] == result) +// { +// downloads.eraseindex(index); +// result->Release(); +// break; +// } +// } +// +// LeaveCriticalSection(&lock); +// +// SysFreeString(fileName); +} + +static void CALLBACK LoginUpdate_DownloadCompleted(LoginDownloadResult *result, void *data) +{ + if (NULL == result) return; + + LoginUpdate *update = (LoginUpdate*)data; + if (NULL != update) + { + update->AddRef(); + update->DownloadCompleted(result); + update->Release(); + } +} diff --git a/Src/auth/Loginbox/update.h b/Src/auth/Loginbox/update.h new file mode 100644 index 00000000..4ac3bec4 --- /dev/null +++ b/Src/auth/Loginbox/update.h @@ -0,0 +1,48 @@ +#ifndef NULLSOFT_AUTH_LOGIN_UPDATE_HEADER +#define NULLSOFT_AUTH_LOGIN_UPDATE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../nu/ptrlist.h" + +class LoginDownloadResult; +class LoginStatus; + +class LoginUpdate +{ + +protected: + LoginUpdate(HWND hLoginbox); + ~LoginUpdate(); + +public: + static HRESULT CreateInstance(HWND hLoginbox, LoginUpdate **instance); + +public: + ULONG AddRef(); + ULONG Release(); + + HRESULT Start(); + HRESULT Abort(); + +protected: + void DownloadCompleted(LoginDownloadResult *result); + + +protected: + typedef nu::PtrList<LoginDownloadResult> DownloadList; + friend static void CALLBACK LoginUpdate_DownloadCompleted(LoginDownloadResult *result, void *data); + +protected: + ULONG ref; + HWND hwnd; + DownloadList downloads; + CRITICAL_SECTION lock; + +}; + +#endif //NULLSOFT_AUTH_LOGIN_UPDATE_HEADER + diff --git a/Src/auth/Loginbox/xmlInt32Parser.cpp b/Src/auth/Loginbox/xmlInt32Parser.cpp new file mode 100644 index 00000000..1f4ab48b --- /dev/null +++ b/Src/auth/Loginbox/xmlInt32Parser.cpp @@ -0,0 +1,65 @@ +#include "./xmlInt32Parser.h" + +#include <shlwapi.h> +#include <strsafe.h> + + +XmlInt32Parser::XmlInt32Parser() + : value(0), result(E_PENDING) +{ + memset(szBuffer, 0, sizeof(szBuffer)); +} + +XmlInt32Parser::~XmlInt32Parser() +{ + +} + +HRESULT XmlInt32Parser::GetValue(INT *pValue) +{ + if (NULL == pValue) return E_POINTER; + *pValue = value; + return result; +} + +void XmlInt32Parser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + szBuffer[0] = L'\0'; + result = S_FALSE; +} + +void XmlInt32Parser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + if (SUCCEEDED(result)) + { + if (FALSE == StrToIntEx(szBuffer, STIF_SUPPORT_HEX, &value)) + result = E_FAIL; + else + result = S_OK; + } + szBuffer[0] = L'\0'; +} + +void XmlInt32Parser::Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value) +{ + if (SUCCEEDED(result)) + { + if (FAILED(StringCchCat(szBuffer, ARRAYSIZE(szBuffer), value))) + result = E_FAIL; + } +} + +void XmlInt32Parser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ + szBuffer[0] = L'\0'; + result = E_FAIL; +} + +#define CBCLASS XmlInt32Parser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONCHARDATA, Event_XmlCharData) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/Loginbox/xmlInt32Parser.h b/Src/auth/Loginbox/xmlInt32Parser.h new file mode 100644 index 00000000..5795acdc --- /dev/null +++ b/Src/auth/Loginbox/xmlInt32Parser.h @@ -0,0 +1,38 @@ +#ifndef NULLSOFT_AUTH_LOGIN_XML_INT32_PARSER_HEADER +#define NULLSOFT_AUTH_LOGIN_XML_INT32_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" + +class obj_xml; + +class XmlInt32Parser: public ifc_xmlreadercallback +{ + +public: + XmlInt32Parser(); + ~XmlInt32Parser(); + +public: + HRESULT GetValue(INT *pValue); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + INT value; + HRESULT result; + WCHAR szBuffer[33]; + +protected: + RECVS_DISPATCH; +}; + +#endif //NULLSOFT_AUTH_LOGIN_XML_INT32_PARSER_HEADER
\ No newline at end of file diff --git a/Src/auth/OAuthKey.cpp b/Src/auth/OAuthKey.cpp new file mode 100644 index 00000000..7fd3fdeb --- /dev/null +++ b/Src/auth/OAuthKey.cpp @@ -0,0 +1,31 @@ +#include "OAuthKey.h" +#include "b64.h" + +OAuthKey::OAuthKey(const void *key, size_t key_len) +{ + HMAC_SHA256_Init(&ctx); + HMAC_SHA256_UpdateKey(&ctx, (unsigned char *)key, (int)key_len); + HMAC_SHA256_EndKey(&ctx); + HMAC_SHA256_StartMessage(&ctx); +} + +OAuthKey::~OAuthKey() +{ + HMAC_SHA256_Done(&ctx); +} + +void OAuthKey::FeedMessage(const void *data, size_t data_len) +{ + HMAC_SHA256_UpdateMessage(&ctx, (unsigned char *)data, (unsigned int)data_len); +} + +void OAuthKey::EndMessage() +{ + HMAC_SHA256_EndMessage(buf, &ctx); +} + +void OAuthKey::GetBase64(char *output, size_t cch) +{ + size_t len = b64::b64_encode(buf, HMAC_SHA256_DIGEST_LENGTH, output, cch); + output[len] = '\0'; +}
\ No newline at end of file diff --git a/Src/auth/OAuthKey.h b/Src/auth/OAuthKey.h new file mode 100644 index 00000000..f52668ed --- /dev/null +++ b/Src/auth/OAuthKey.h @@ -0,0 +1,15 @@ +#include "hmac_sha256.h" +#include <bfc/platform/types.h> + +class OAuthKey +{ +public: + OAuthKey(const void *key, size_t key_len); + ~OAuthKey(); + void FeedMessage(const void *data, size_t data_len); + void EndMessage(); + void GetBase64(char *output, size_t len); + HMAC_SHA256_CTX ctx; + uint8_t buf[HMAC_SHA256_DIGEST_LENGTH]; +}; + diff --git a/Src/auth/XMLString.cpp b/Src/auth/XMLString.cpp new file mode 100644 index 00000000..2987de4e --- /dev/null +++ b/Src/auth/XMLString.cpp @@ -0,0 +1,57 @@ +/** (c) Nullsoft, Inc. C O N F I D E N T I A L + ** Filename: + ** Project: + ** Description: + ** Author: Ben Allison benski@nullsoft.com + ** Created: + **/ + +#include "XMLString.h" +#include "../nu/strsafe.h" + +XMLString::XMLString() +{ + data[0]=0; +} + +void XMLString::Reset() +{ + data[0]=0; +} + +const wchar_t *XMLString::GetString() +{ + return data; +} + +void XMLString::StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + data[0]=0; +} + + +void XMLString::TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str) +{ + StringCchCatW(data, XMLSTRING_SIZE, str); +} + + +void XMLString::ManualSet(const wchar_t *string) +{ +StringCchCatW(data, XMLSTRING_SIZE, string); +} + +uint32_t XMLString::GetUInt32() +{ + return wcstoul(data, 0, 10); +} + +#ifdef CBCLASS +#undef CBCLASS +#endif + +#define CBCLASS XMLString +START_DISPATCH; +VCB(ONSTARTELEMENT, StartTag) +VCB(ONCHARDATA, TextHandler) +END_DISPATCH; diff --git a/Src/auth/XMLString.h b/Src/auth/XMLString.h new file mode 100644 index 00000000..4c36c0b8 --- /dev/null +++ b/Src/auth/XMLString.h @@ -0,0 +1,30 @@ +#ifndef NULLSOFT_WINAMP_XMLSTRING_H +#define NULLSOFT_WINAMP_XMLSTRING_H + + +#include "../xml/ifc_xmlreadercallback.h" +/* +this one is an xml callback that just saves the last encountered string +*/ + +#define XMLSTRING_SIZE 2048 +class XMLString : public ifc_xmlreadercallback +{ +public: + XMLString(); + void Reset(); + const wchar_t *GetString(); + uint32_t GetUInt32(); + void ManualSet(const wchar_t *string); +private: + /* XML callbacks */ + void StartTag(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void EndTag(const wchar_t *xmlpath, const wchar_t *xmltag); + void TextHandler(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *str); + + wchar_t data[XMLSTRING_SIZE]; // for now, we'll make it dynamic later + + RECVS_DISPATCH; +}; + +#endif
\ No newline at end of file diff --git a/Src/auth/api.h b/Src/auth/api.h new file mode 100644 index 00000000..e587c5a0 --- /dev/null +++ b/Src/auth/api.h @@ -0,0 +1,31 @@ +#pragma once +#include <api/service/api_service.h> +extern api_service *serviceManager; +#define WASABI_API_SVC serviceManager + +#include <api/application/api_application.h> +extern api_application *applicationApi; +#define WASABI_API_APP applicationApi + +#include "../Agave/Config/api_config.h" +extern api_config *config; +#define AGAVE_API_CONFIG config + +#include <api/syscb/api_syscb.h> +#define WASABI_API_SYSCB sysCallbackApi + +#include "../Agave/Language/api_language.h" + +#include "../winamp/api_winamp.h" +extern api_winamp *winampApi; +#define WASABI_API_WINAMP winampApi + +#include <api/memmgr/api_memmgr.h> +extern api_memmgr *memManagerApi; +#define WASABI_API_MEMMNGR memManagerApi + +#include <api/service/svcs/svc_imgload.h> +extern svc_imageLoader *pngLoaderApi; +#define WASABI_API_PNGLOADER pngLoaderApi +EXTERN_C const GUID pngLoaderGUID; + diff --git a/Src/auth/api_auth.h b/Src/auth/api_auth.h new file mode 100644 index 00000000..d4f3f02b --- /dev/null +++ b/Src/auth/api_auth.h @@ -0,0 +1,144 @@ +#pragma once + +#include <bfc/dispatch.h> +#include <time.h> + +class ifc_authcallback; + +enum +{ + AUTHPARAM_EMPTY = 0, + AUTHPARAM_TOOSHORT = 1, + AUTHPARAM_TOOLONG = 2, + AUTHPARAM_BADFORMAT = 3, +}; + +enum +{ + AUTH_SUCCESS = 0, + AUTH_404 = 1, + AUTH_TIMEOUT = 2, + AUTH_NOHTTP = 3, + AUTH_NOPARSER = 4, + AUTH_CONNECTIONRESET = 5, + AUTH_ERROR_PARSING_XML = 6, + AUTH_NOT_AUTHORIZED = 7, + AUTH_SECURID = 8, + AUTH_ABORT = 10, + AUTH_INVALIDCRED = 11, + AUTH_UNCONFIRMED = 12, + AUTH_UNEXPECTED = 13, // unexpected catastrophic failure + AUTH_INVALIDPASSCODE = 14, + + AUTH_USERNAME = 20, + AUTH_USERNAME_EMPTY = (AUTH_USERNAME + AUTHPARAM_EMPTY), + AUTH_USERNAME_TOOSHORT = (AUTH_USERNAME + AUTHPARAM_TOOSHORT), + AUTH_USERNAME_TOOLONG = (AUTH_USERNAME + AUTHPARAM_TOOLONG), + AUTH_USERNAME_BADFORMAT = (AUTH_USERNAME + AUTHPARAM_BADFORMAT), + + AUTH_PASSWORD = 30, + AUTH_PASSWORD_EMPTY = (AUTH_PASSWORD + AUTHPARAM_EMPTY), + AUTH_PASSWORD_TOOSHORT = (AUTH_PASSWORD + AUTHPARAM_TOOSHORT), + AUTH_PASSWORD_TOOLONG = (AUTH_PASSWORD + AUTHPARAM_TOOLONG), + AUTH_PASSWORD_BADFORMAT = (AUTH_PASSWORD + AUTHPARAM_BADFORMAT), + + AUTH_PASSCODE = 40, + AUTH_PASSCODE_EMPTY = (AUTH_PASSCODE + AUTHPARAM_EMPTY), + AUTH_PASSCODE_TOOSHORT = (AUTH_PASSCODE + AUTHPARAM_TOOSHORT), + AUTH_PASSCODE_TOOLONG = (AUTH_PASSCODE + AUTHPARAM_TOOLONG), + AUTH_PASSCODE_BADFORMAT = (AUTH_PASSCODE + AUTHPARAM_BADFORMAT), + + +}; +// {392839D2-640D-4961-8E2A-1B5315C2F970} +static const GUID AuthApiGUID = +{ 0x392839d2, 0x640d, 0x4961, { 0x8e, 0x2a, 0x1b, 0x53, 0x15, 0xc2, 0xf9, 0x70 } }; + +class api_auth : public Dispatchable +{ +protected: + api_auth() {} + ~api_auth() {} +public: + + struct AuthResults + { + // plx2b using SecureZeroMemory when you are done :) + char session_key[256]; // seems to be strlen = ~200, but let's just be safe + char token[256]; // always seems to be strlen=44, but let's just be safe + __time64_t expire; // expiration time in absolute time [AOL's Auth API returns duration, but api_auth adds time()] + char context[512]; // dunno exact length yet + int statusCode; + int statusDetailCode; + }; + static const GUID getServiceGuid() { return AuthApiGUID; } + int Login(const wchar_t *username, const wchar_t *password, AuthResults *results, ifc_authcallback *callback); + int LoginSecurID(const wchar_t *username, const wchar_t *password, const char *context, const wchar_t *securid, AuthResults *results, ifc_authcallback *callback); + // realm is just a "named account" kinda thing. pass realm = GUID_NULL for "default" account, or your own unique name if you need to login with a separate ID from the rest of Winamp + int SetCredentials(GUID realm, const char *session_key, const char *token, const wchar_t *username, __time64_t expire); + int GetCredentials(GUID realm, char *session_key, size_t session_key_len, char *token, size_t token_len, wchar_t *username, size_t username_len, __time64_t *expire); + const char *GetDevID(); + int ClientToWeb(GUID realm, const wchar_t *destination_url, wchar_t *url, size_t urlcch); + + HWND CreateLoginWindow(GUID realm, HWND owner, UINT style); + INT_PTR LoginBox(GUID realm, HWND owner, UINT style); + int GetUserName(GUID realm, wchar_t *username, size_t username_len); + + enum + { + API_AUTH_LOGIN = 0, + API_AUTH_SETCREDENTIALS = 1, + API_AUTH_GETCREDENTIALS = 2, + API_AUTH_GETDEVID = 3, + API_AUTH_LOGIN_SECURID = 4, + API_AUTH_CLIENT_TO_WEB = 5, + API_AUTH_CREATELOGINWINDOW = 6, + API_AUTH_LOGINBOX = 7, + API_AUTH_GETUSERNAME=8, + }; +}; + +inline int api_auth::Login(const wchar_t *username, const wchar_t *password, AuthResults *results, ifc_authcallback *callback) +{ + return _call(API_AUTH_LOGIN, (int)AUTH_NOHTTP, username, password, results, callback); +} + +inline int api_auth::LoginSecurID(const wchar_t *username, const wchar_t *password, const char *context, const wchar_t *securid, AuthResults *results, ifc_authcallback *callback) +{ + return _call(API_AUTH_LOGIN_SECURID, (int)AUTH_NOHTTP, username, password, context, securid, results, callback); +} + +inline int api_auth::SetCredentials(GUID realm, const char *session_key, const char *token, const wchar_t *username, __time64_t expire) +{ + return _call(API_AUTH_SETCREDENTIALS, (int)AUTH_NOT_AUTHORIZED, realm, session_key, token, username, expire); +} + +inline int api_auth::GetCredentials(GUID realm, char *session_key, size_t session_key_len, char *token, size_t token_len, wchar_t *username, size_t username_len, __time64_t *expire) +{ + return _call(API_AUTH_GETCREDENTIALS, (int)AUTH_NOT_AUTHORIZED, realm, session_key, session_key_len, token, token_len, username, username_len, expire); +} + +inline const char *api_auth::GetDevID() +{ + return _call(API_AUTH_GETDEVID, (const char *)0); +} + +inline int api_auth::ClientToWeb(GUID realm, const wchar_t *destination_url, wchar_t *url, size_t urlcch) +{ + return _call(API_AUTH_CLIENT_TO_WEB, (int)1, realm, destination_url, url, urlcch); +} + +inline HWND api_auth::CreateLoginWindow(GUID realm, HWND owner, UINT style) +{ + return _call(API_AUTH_CREATELOGINWINDOW, (HWND)NULL, realm, owner, style); +} + +inline INT_PTR api_auth::LoginBox(GUID realm, HWND owner, UINT style) +{ + return _call(API_AUTH_LOGINBOX, (INT_PTR)-1, realm, owner, style); +} + +inline int api_auth::GetUserName(GUID realm, wchar_t *username, size_t username_len) +{ + return _call(API_AUTH_GETUSERNAME, (int)AUTH_UNEXPECTED, realm, username, username_len); +}
\ No newline at end of file diff --git a/Src/auth/auth.cpp b/Src/auth/auth.cpp new file mode 100644 index 00000000..83a2e235 --- /dev/null +++ b/Src/auth/auth.cpp @@ -0,0 +1,479 @@ +#include "api.h" + +#include "../nu/ns_wc.h" +#include <api/service/waservicefactory.h> +#include "OAuthKey.h" +#include "../nu/AutoCharFn.h" +#include "../nu/AutoChar.h" +#include "auth.h" +#include "./loginbox/loginbox.h" + +#include "ifc_authcallback.h" +#include "main.h" +#include "../nu/AutoUrl.h" +#include <api/syscb/callbacks/authcb.h> + +#include "../Winamp/buildType.h" +#include <strsafe.h> + +//#ifdef INTERNAL +//// QA +////const char *openauth_url ="https://authapi.qa.aol.com:8439/auth/clientLogin"; +//// QH +//const char *openauth_url ="https://authapi.qh.aol.com:6443/auth/clientLogin"; +//#else +const char *openauth_url ="https://api.screenname.aol.com:443/auth/clientLogin"; +//#endif + +static void CleanPassword(char *password) +{ + char *src = password; + char *dest = password; + + while (src && *src) + { + char c = *src++; + if (c >= 'a' && c <= 'z') + *dest++ = c; + else if (c >= 'A' && c <= 'Z') + *dest++ = c; + else if (c >= '0' && c <= '9') + *dest++ = c; + } + *dest=0; +} + +static void ParsePassword(const wchar_t *password, char **password_url, char **securid=0) +{ + const wchar_t *find_slash = wcschr(password, L'/'); + if (find_slash) + { + *password_url = AutoUrlDupN(password, find_slash-password); + if (securid) + *securid = AutoUrlDup(find_slash+1); + } + else + { + *password_url = AutoUrlDup(password); + if (securid) + *securid=0; + } +} + +// TODO: benski> use &forceRateLimit=true to force captcha request +int PostXML(const char *url, const char *post_data, obj_xml *parser, ifc_authcallback *callback); +static int Authorize(obj_xml *parser, const wchar_t *username, const wchar_t *password, ifc_authcallback *callback) +{ + char *password_url, *securid; + ParsePassword(password, &password_url, &securid); + + char post_data[2048] = {0}; + StringCbPrintfA(post_data, sizeof(post_data), + "devId=%s" + "&f=xml" + "&pwd=%s" + "&s=%s" + "%s%s" + "&tokenType=longterm", + OPENAUTH_DEVID, + password_url, + AutoUrl(username), + (securid?"&securid=":""), + (securid?securid:"") + ); + + free(password_url); + free(securid); + return PostXML(openauth_url, post_data, parser, callback); +} + +static int AuthorizeSecurID(obj_xml *parser, const wchar_t *username, const char *context, const wchar_t *securid, ifc_authcallback *callback) +{ + char post_data[2048] = {0}; + StringCbPrintfA(post_data, sizeof(post_data), + "devId=%s" + "&f=xml" + "&s=%s" + "&context=%s" + "&securid=%s" + "&tokenType=longterm", + OPENAUTH_DEVID, + AutoUrl(username), + AutoUrl(context), + AutoUrl(securid) + ); + + return PostXML(openauth_url, post_data, parser, callback); +} + + +void OpenAuthParser::RegisterCallbacks(obj_xml *parser) +{ + parser->xmlreader_registerCallback(L"response\fstatusCode", &statusCode); + parser->xmlreader_registerCallback(L"response\fstatusText", &statusText); + parser->xmlreader_registerCallback(L"response\fstatusDetailCode", &statusDetailCode); + parser->xmlreader_registerCallback(L"response\fdata\ftoken\fa", &token); + parser->xmlreader_registerCallback(L"response\fdata\ftoken\fexpiresIn", &expire); + parser->xmlreader_registerCallback(L"response\fdata\fsessionSecret", &session_secret); + parser->xmlreader_registerCallback(L"response\fdata\fchallenge\fcontext", &context); + +} +void OpenAuthParser::UnregisterCallbacks(obj_xml *parser) +{ + parser->xmlreader_unregisterCallback(&statusCode); + parser->xmlreader_unregisterCallback(&statusText); + parser->xmlreader_unregisterCallback(&statusDetailCode); + parser->xmlreader_unregisterCallback(&token); + parser->xmlreader_unregisterCallback(&expire); + parser->xmlreader_unregisterCallback(&session_secret); + parser->xmlreader_unregisterCallback(&context); +} + + +int Auth::SetupLogin(OpenAuthParser &authParser, waServiceFactory *&parserFactory, obj_xml *&parser) +{ + parserFactory = WASABI_API_SVC->service_getServiceByGuid(obj_xmlGUID); + if (parserFactory) + parser = (obj_xml *)parserFactory->getInterface(); + + if (parser) + { + parser->xmlreader_setCaseSensitive(); + authParser.RegisterCallbacks(parser); + parser->xmlreader_open(); + return AUTH_SUCCESS; + } + else + return AUTH_NOPARSER; +} + +int Auth::ParseAuth(const wchar_t *password, OpenAuthParser &authParser, AuthResults *results) +{ + switch(authParser.statusCode.GetUInt32()) + { + case 200: + { + char *password_url; + ParsePassword(password, &password_url); + UrlDecode(password_url); + OAuthKey key(password_url, strlen(password_url)); + AutoChar session_utf8(authParser.session_secret.GetString(), CP_UTF8); + key.FeedMessage((char *)session_utf8, strlen(session_utf8)); + key.EndMessage(); + key.GetBase64(results->session_key, sizeof(results->session_key)); + WideCharToMultiByteSZ(CP_UTF8, 0, authParser.token.GetString(), -1, results->token, sizeof(results->token), 0, 0); + results->expire = authParser.expire.GetUInt32() + _time64(0); + free(password_url); + return AUTH_SUCCESS; + } + case 330: + switch(authParser.statusDetailCode.GetUInt32()) + { + case 3011: // Password-LoginId Required/Invalid + return AUTH_INVALIDCRED; + case 3012: // SecurId Required/Invalid + case 3013: // SecurId Next Token Required + WideCharToMultiByteSZ(CP_UTF8, 0, authParser.context.GetString(), -1, results->context, sizeof(results->context), 0, 0); + return AUTH_SECURID; + } + break; + case 401: + switch(authParser.statusDetailCode.GetUInt32()) + { + case 3020: + return AUTH_UNCONFIRMED; + } + break; + } + + return AUTH_NOT_AUTHORIZED; +} + +int Auth::Login(const wchar_t *username, const wchar_t *password, AuthResults *results, ifc_authcallback *callback) +{ + SecureZeroMemory(results, sizeof(AuthResults)); + OpenAuthParser authParser; + obj_xml *parser = 0; + waServiceFactory *parserFactory = 0; + + int err = SetupLogin(authParser, parserFactory, parser); + if (err == AUTH_SUCCESS) + { + err = Authorize(parser, username, password, callback); + authParser.UnregisterCallbacks(parser); + parser->xmlreader_close(); + parserFactory->releaseInterface(parser); + if (err != AUTH_SUCCESS) + return err; + + return ParseAuth(password, authParser, results); + } + else + return err; + + return AUTH_NOT_AUTHORIZED; +} + +int Auth::LoginSecurID(const wchar_t *username, const wchar_t *password, const char *context, const wchar_t *securid, AuthResults *results, ifc_authcallback *callback) +{ + SecureZeroMemory(results, sizeof(AuthResults)); + OpenAuthParser authParser; + obj_xml *parser = 0; + waServiceFactory *parserFactory = 0; + + int err = SetupLogin(authParser, parserFactory, parser); + if (err == AUTH_SUCCESS) + { + err = AuthorizeSecurID(parser, username, context, securid, callback); + authParser.UnregisterCallbacks(parser); + parser->xmlreader_close(); + parserFactory->releaseInterface(parser); + if (err != AUTH_SUCCESS) + return err; + + return ParseAuth(password, authParser, results); + } + else + return err; + + return AUTH_NOT_AUTHORIZED; +} + +const char *Auth::GetDevID() +{ + return OPENAUTH_DEVID; +} + +int Auth::SetCredentials(GUID realm, const char *session_key, const char *token, const wchar_t *username, __time64_t expire) +{ + if (NULL != WASABI_API_SYSCB) + WASABI_API_SYSCB->syscb_issueCallback(SysCallback::AUTH, AuthCallback::CREDENTIALS_ABOUTTOCHANGE, (intptr_t)this, (intptr_t)&realm); + + char guid_str[40] = {0}; + if (realm != GUID_NULL) + { + StringCbPrintfA( guid_str, sizeof(guid_str), "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", + (int)realm.Data1, (int)realm.Data2, (int)realm.Data3, + (int)realm.Data4[0], (int)realm.Data4[1], + (int)realm.Data4[2], (int)realm.Data4[3], + (int)realm.Data4[4], (int)realm.Data4[5], + (int)realm.Data4[6], (int)realm.Data4[7] ); + } + else + { + StringCbCopyA(guid_str, sizeof(guid_str), "default"); + } + + if (session_key && session_key[0]) + { + WritePrivateProfileStringA(guid_str, "session_key", session_key, inifile); + WritePrivateProfileStringA(guid_str, "token", token, inifile); + WritePrivateProfileStringA(guid_str, "username", AutoChar(username, CP_UTF8), inifile); + char temp[128] = {0}; + StringCbPrintfA(temp, sizeof(temp), "%I64d", expire); + WritePrivateProfileStringA(guid_str, "expiration", temp, inifile); + } + else + { + char empty[2] = {0,0}; + WritePrivateProfileSectionA(guid_str, empty, inifile); + if (username && username[0]) // they might want to save their username tho + WritePrivateProfileStringA(guid_str, "username", AutoChar(username, CP_UTF8), inifile); + } + + if (NULL != WASABI_API_SYSCB) + WASABI_API_SYSCB->syscb_issueCallback(SysCallback::AUTH, AuthCallback::CREDENTIALS_CHANGED, (intptr_t)this, (intptr_t)&realm); + + return AUTH_SUCCESS; +} + +int Auth::GetCredentials(GUID realm, char *session_key, size_t session_key_len, char *token, size_t token_len, wchar_t *username, size_t username_len, __time64_t *expire) +{ + char guid_str[40] = {0}; + if (realm != GUID_NULL) + { + StringCbPrintfA( guid_str, sizeof(guid_str), "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", + (int)realm.Data1, (int)realm.Data2, (int)realm.Data3, + (int)realm.Data4[0], (int)realm.Data4[1], + (int)realm.Data4[2], (int)realm.Data4[3], + (int)realm.Data4[4], (int)realm.Data4[5], + (int)realm.Data4[6], (int)realm.Data4[7] ); + } + else + { + StringCbCopyA(guid_str, sizeof(guid_str), "default"); + } + + GetPrivateProfileStringA(guid_str, "session_key", "", session_key, (DWORD)session_key_len, inifile); + GetPrivateProfileStringA(guid_str, "token", "", token, (DWORD)token_len, inifile); + char temp[1024] = {0}; + GetPrivateProfileStringA(guid_str, "username", "", temp, sizeof(temp), inifile); + MultiByteToWideCharSZ(CP_UTF8, 0, temp, -1, username, (DWORD)username_len); + + GetPrivateProfileStringA(guid_str, "expiration", "", temp, sizeof(temp), inifile); + *expire = _atoi64(temp); + return AUTH_SUCCESS; +} + +int Auth::GetUserName(GUID realm, wchar_t *username, size_t username_len) +{ + char guid_str[40] = {0}; + if (realm != GUID_NULL) + { + StringCbPrintfA( guid_str, sizeof(guid_str), "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", + (int)realm.Data1, (int)realm.Data2, (int)realm.Data3, + (int)realm.Data4[0], (int)realm.Data4[1], + (int)realm.Data4[2], (int)realm.Data4[3], + (int)realm.Data4[4], (int)realm.Data4[5], + (int)realm.Data4[6], (int)realm.Data4[7] ); + } + else + { + StringCbCopyA(guid_str, sizeof(guid_str), "default"); + } + + char temp[1024] = {0}; + GetPrivateProfileStringA(guid_str, "username", "", temp, sizeof(temp), inifile); + MultiByteToWideCharSZ(CP_UTF8, 0, temp, -1, username, (DWORD)username_len); + return AUTH_SUCCESS; +} + + +Auth::Auth() +{ + inifile=0; +} +void Auth::Init() +{ + wchar_t inifileW[MAX_PATH] = {0}; + const wchar_t *settings_path = WASABI_API_APP->path_getUserSettingsPath(); + PathCombineW(inifileW, settings_path, L"auth.ini"); + inifile = _strdup(AutoCharFn(inifileW)); +} + +void Auth::Quit() +{ + free(inifile); +} + +static void AddParameter(char *&position, size_t &len, const char *param, const wchar_t *val) +{ + AutoUrl encoded_val(val); + StringCchPrintfExA(position, len, &position, &len, 0, "&%s=%s", param, encoded_val); +} + +static void AddParameter(char *&position, size_t &len, const char *param, const char *val) +{ + AutoUrl encoded_val(val); + StringCchPrintfExA(position, len, &position, &len, 0, "&%s=%s", param, encoded_val); +} + +static void AddParameter(char *&position, size_t &len, const char *param, int64_t val) +{ + char temp[64] = {0}; + StringCchPrintfA(temp, 64, "%I64d", val); + StringCchPrintfExA(position, len, &position, &len, 0, "&%s=%s", param, temp); +} + +//#ifdef INTERNAL +// QA +//static const char *c2w_server="my.screenname.qa.aol.com"; +//static const char *c2w_path="/_cqr/login/login.psp"; +//static const char *c2w_path_encoded="%2F_cqr%2Flogin%2Flogin.psp"; +// QH +//static const char *c2w_server="my.screenname.qh.aol.com"; +//static const char *c2w_path="/_cqr/login/login.psp"; +//static const char *c2w_path_encoded="%2F_cqr%2Flogin%2Flogin.psp"; +//#else +static const char *c2w_server="my.screenname.aol.com"; +static const char *c2w_path="/_cqr/login/login.psp"; +static const char *c2w_path_encoded="%2F_cqr%2Flogin%2Flogin.psp"; +//#endif + + +int Auth::ClientToWeb(GUID realm, const wchar_t *destination_url, wchar_t *url, size_t urlcch) +{ + char session_key[1024], token[1024] = {0}; + wchar_t username[1024] = {0}; + __time64_t expire; + int ret = GetCredentials(realm, session_key, 1024, token, 1024, username, 1024, &expire); + if (ret) + return ret; + + if (!session_key[0] || !token[0] || !username[0]) + return 1; + + char post_data[2048]=""; + char *post_itr=post_data; + size_t post_cch=sizeof(post_data)/sizeof(*post_data); + + OAuthKey key(session_key, strlen(session_key)); + + key.FeedMessage("GET&", 4); + key.FeedMessage("http%3A%2F%2F", 13); + key.FeedMessage(c2w_server, strlen(c2w_server)); + key.FeedMessage(c2w_path_encoded, strlen(c2w_path_encoded)); + key.FeedMessage("&", 1); + + // parameters + StringCbPrintfExA(post_itr, post_cch, &post_itr, &post_cch, 0, "a=%s", AutoUrl(token)); + char *start = post_itr; + key.FeedMessage("a%3D", 4); + AutoUrl token_a_url1(token); + AutoUrl token_a_url((char *)token_a_url1); + key.FeedMessage(token_a_url, strlen((char *)token_a_url)); + + AddParameter(post_itr, post_cch, "destUrl", destination_url); + AddParameter(post_itr, post_cch, "devId", GetDevID()); + AddParameter(post_itr, post_cch, "entryType", L"client2Web"); + __time64_t t = _time64(0); + AddParameter(post_itr, post_cch, "ts", t); + + AutoUrl encoded_post(start); + key.FeedMessage((char *)encoded_post, strlen(encoded_post)); + + key.EndMessage(); + char hash[512] = {0}; + key.GetBase64(hash, 512); + + StringCchPrintfA(post_itr, post_cch, "&sig_sha256=%s", AutoUrl(hash)); + + char urla[2048] = {0}; + StringCbPrintfA(urla, sizeof(urla), "http://%s%s?%s", c2w_server, c2w_path, post_data); + + MultiByteToWideCharSZ(CP_UTF8, 0, urla, -1, url, (int)urlcch); + + return 0; +} + +HWND Auth::CreateLoginWindow(GUID realm, HWND owner, UINT style) +{ +#ifndef USE_LOGINBOX + return NULL; +#else + return LoginBox_CreateWindow(this, &realm, owner, style); +#endif +} + +INT_PTR Auth::LoginBox(GUID realm, HWND owner, UINT style) +{ +#ifndef USE_LOGINBOX + return -1; +#else + return LoginBox_Show(this, &realm, owner, style); +#endif +} + +#define CBCLASS Auth +START_DISPATCH; +CB(API_AUTH_LOGIN, Login) +CB(API_AUTH_GETDEVID, GetDevID) +CB(API_AUTH_LOGIN_SECURID, LoginSecurID) +CB(API_AUTH_SETCREDENTIALS, SetCredentials) +CB(API_AUTH_GETCREDENTIALS, GetCredentials) +CB(API_AUTH_CLIENT_TO_WEB, ClientToWeb) +CB(API_AUTH_CREATELOGINWINDOW, CreateLoginWindow) +CB(API_AUTH_LOGINBOX, LoginBox) +CB(API_AUTH_GETUSERNAME, GetUserName) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/auth/auth.h b/Src/auth/auth.h new file mode 100644 index 00000000..801f7542 --- /dev/null +++ b/Src/auth/auth.h @@ -0,0 +1,46 @@ +#include "api_auth.h" +#include <api/service/services.h> +#include "../nu/PtrMap.h" +#include "XMLString.h" +#include "../xml/obj_xml.h" + +class OpenAuthParser +{ +public: + void RegisterCallbacks(obj_xml *parser); + void UnregisterCallbacks(obj_xml *parser); + XMLString statusCode, statusText, statusDetailCode; + XMLString token; + XMLString expire; + XMLString session_secret; + XMLString context; +}; + + +class Auth : public api_auth +{ +public: + static const char *getServiceName() { return "Authorization API"; } + static FOURCC getServiceType() { return WaSvc::UNIQUE; } + void Init(); + void Quit(); + Auth(); + int Login(const wchar_t *username, const wchar_t *password, AuthResults *results, ifc_authcallback *callback); + int LoginSecurID(const wchar_t *username, const wchar_t *password, const char *context, const wchar_t *securid, AuthResults *results, ifc_authcallback *callback); + const char *GetDevID(); + int SetCredentials(GUID realm, const char *session_key, const char *token, const wchar_t *username, __time64_t expire); + int GetCredentials(GUID realm, char *session_key, size_t session_key_len, char *token, size_t token_len, wchar_t *username, size_t username_len, __time64_t *expire); + int ClientToWeb(GUID realm, const wchar_t *destination_url, wchar_t *url, size_t urlcch); + + HWND CreateLoginWindow(GUID realm, HWND owner, UINT style); + INT_PTR LoginBox(GUID realm, HWND owner, UINT style); + int GetUserName(GUID realm, wchar_t *username, size_t username_len); + +private: + static int SetupLogin(OpenAuthParser &authParser, waServiceFactory *&parserFactory, obj_xml *&parser); + static int ParseAuth(const wchar_t *password, OpenAuthParser &authParser, AuthResults *results); + + char *inifile; +protected: + RECVS_DISPATCH; +}; diff --git a/Src/auth/auth.rc b/Src/auth/auth.rc new file mode 100644 index 00000000..dbe38a18 --- /dev/null +++ b/Src/auth/auth.rc @@ -0,0 +1,333 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_LOGINBOX DIALOGEX 0, 0, 245, 199 +STYLE DS_SETFONT | DS_SETFOREGROUND | DS_FIXEDSYS | DS_NOFAILCREATE | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Winamp Sign In" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "&Sign In",IDOK,131,179,50,14 + PUSHBUTTON "&Cancel",IDCANCEL,191,179,50,14 + CONTROL "",IDC_STATUS,"Static",SS_LEFTNOWORDWRAP | SS_ENDELLIPSIS | WS_GROUP,4,185,104,8 +END + +IDD_POPUP_AGREEMENT DIALOGEX 0, 0, 200, 88 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "&Agree",IDOK,86,67,50,15 + PUSHBUTTON "&Decline",IDCANCEL,142,67,50,15 + LTEXT "In order to continue you need to read and agree with the following terms of services and privacy policies:",IDC_TEXT,8,20,184,21,SS_NOPREFIX +END + +IDD_PAGE_CREDENTIALS DIALOGEX 0, 0, 213, 118 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Create Account",IDC_CREATE_ACCOUNT,"NullsoftCommandLink",NOT WS_VISIBLE | WS_TABSTOP | 0x18,119,19,94,9 + CONTROL "Forgot Password",IDC_RECOVER_ACCOUNT, + "NullsoftCommandLink",NOT WS_VISIBLE | WS_TABSTOP | 0x18,119,30,94,9 + EDITTEXT IDC_USERNAME,0,59,106,14,ES_AUTOHSCROLL | NOT WS_BORDER,WS_EX_CLIENTEDGE + EDITTEXT IDC_PASSWORD,2,86,106,14,ES_PASSWORD | ES_AUTOHSCROLL | NOT WS_BORDER,WS_EX_CLIENTEDGE + LTEXT "Username:",IDC_USERNAME_LABEL,0,49,106,8 + LTEXT "Password:",IDC_PASSWORD_LABEL,1,76,106,9 +END + +IDD_PAGE_ADDRESS DIALOGEX 0, 0, 212, 124 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Enter address:",IDC_ADDRESS_LABEL,2,21,208,8 + EDITTEXT IDC_ADDRESS_EDIT,2,30,208,14,ES_AUTOHSCROLL | ES_NOHIDESEL + LTEXT "Type in your info and when you done click on the Login button. You will be taken to the provider page. Your provider will verify who you are, and send you back here to finish login process.",IDC_MESSAGE,2,52,208,64 +END + +IDD_PAGE_INFO DIALOGEX 0, 0, 189, 125 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_MESSAGE,6,46,177,73 +END + +IDD_POPUP_PASSCODE DIALOGEX 0, 0, 185, 90 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_PASSCODE_EDIT,8,32,169,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "&Continue",IDOK,121,69,56,15 + LTEXT "Enter passcode:",IDC_PASSCODE_TITLE,8,20,144,8 + CONTROL "",IDC_PASSCODE_HINT,"Static",SS_LEFTNOWORDWRAP | SS_NOPREFIX,8,46,169,8 +END + +IDD_POPUP_MESSAGE DIALOGEX 0, 0, 200, 63 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_MESSAGE,7,20,186,18,SS_NOPREFIX +END + +IDD_PAGE_EMPTY DIALOGEX 0, 0, 214, 115 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "There are no login providers installed",IDC_MESSAGE, + "Static",SS_LEFTNOWORDWRAP | SS_NOPREFIX | WS_GROUP,46,48,120,8 + PUSHBUTTON "Check Online",IDC_CHECKNOW,66,70,70,15,NOT WS_VISIBLE + CONTROL "checking online...",IDC_CHECK_STATUS,"Static",SS_LEFTNOWORDWRAP | SS_NOPREFIX | WS_GROUP,78,57,57,9 +END + +IDD_PAGE_ERROR DIALOGEX 0, 0, 214, 115 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Unable to initialize provider data",IDC_MESSAGE,"Static",SS_LEFTNOWORDWRAP | SS_NOPREFIX | WS_GROUP,60,53,106,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_LOGINBOX, DIALOG + BEGIN + RIGHTMARGIN, 241 + VERTGUIDE, 4 + BOTTOMMARGIN, 193 + END + + IDD_POPUP_AGREEMENT, DIALOG + BEGIN + LEFTMARGIN, 8 + RIGHTMARGIN, 192 + TOPMARGIN, 6 + BOTTOMMARGIN, 82 + HORZGUIDE, 20 + END + + IDD_PAGE_ADDRESS, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 210 + TOPMARGIN, 1 + BOTTOMMARGIN, 90 + END + + IDD_PAGE_INFO, DIALOG + BEGIN + LEFTMARGIN, 6 + TOPMARGIN, 6 + END + + IDD_POPUP_PASSCODE, DIALOG + BEGIN + LEFTMARGIN, 8 + RIGHTMARGIN, 177 + TOPMARGIN, 6 + BOTTOMMARGIN, 84 + END + + IDD_POPUP_MESSAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 193 + TOPMARGIN, 7 + BOTTOMMARGIN, 56 + END + + IDD_PAGE_EMPTY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 207 + TOPMARGIN, 7 + BOTTOMMARGIN, 108 + END + + IDD_PAGE_ERROR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 207 + TOPMARGIN, 7 + BOTTOMMARGIN, 108 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU_TABCONTEXT MENU +BEGIN + POPUP "TabContext" + BEGIN + MENUITEM "&Update providers", ID_LOGINBOX_UPDATEPROVIDERS + MENUITEM SEPARATOR + MENUITEM "&Reset providers order", ID_LOGINTAB_RESETORDER + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ERR_SUCCESS "Success" + IDS_ERR_404 "Unable to connect" + IDS_ERR_TIMEOUT "Connection timeout" + IDS_ERR_NOHTTP "Unable to establish connection" + IDS_ERR_NOPARSER "Unable to initialize parser" + IDS_ERR_CONNECTIONRESET "Connection reset" + IDS_ERR_PARSING_XML "Unable to parse data" + IDS_ERR_NOT_AUTHORIZED "Account not authorized" + IDS_ERR_SECURID "Passcode required" +END + +STRINGTABLE +BEGIN + IDS_ERR_ABORT "Operation aborted" + IDS_ERR_INVALIDCRED "Unknown user name or bad password" + IDS_ERR_UNCONFIRMED "Account unconfirmed" + IDS_ERR_UNEXPECTED "Unexpected error" + IDS_ERR_USERNAME_EMPTY "Username cannot be blank" + IDS_ERR_USERNAME_TOOSHORT "Username too short" + IDS_ERR_USERNAME_TOOLONG "Username too long" + IDS_ERR_USERNAME_BADFORMAT "Invalid username format" + IDS_ERR_PASSWORD_EMPTY "Password cannot be blank" + IDS_ERR_PASSWORD_TOOSHORT "Password too short" + IDS_ERR_PASSWORD_TOOLONG "Password too long" + IDS_ERR_PASSWORD_BADFORMAT "Password contains invalid characters" + IDS_ERR_PASSCODE_EMPTY "Passcode cannot be blank" + IDS_ERR_PASSCODE_TOOSHORT "Passcode too short" + IDS_ERR_PASSCODE_TOOLONG "Passcode too long" + IDS_ERR_PASSCODE_BADFORMAT "Passcode contains invalid characters" +END + +STRINGTABLE +BEGIN + IDS_ERR_UNKNOWN "Unknown error" + IDS_LOGIN_FAILURE "Logon failure" + IDS_ERR_PASSCODE_INVALID "Invalid passcode" + IDS_WEBAUTH_CAPTION_TEMPLATE "%s Authentication" + IDS_ERR_PASSCODE_BADLENGTH_TEMPLATE "Passcode must be %d digits long" + IDS_ERR_PASSCODE_ONLYDIGITS "Passcode can contain only digits" + IDS_PASSCODE_EDIT_HINT "passcode must be %d-digit number" + IDS_STATUS_INITIALIZING "Initializing..." + IDS_STATUS_CONNECTING "Connecting..." + IDS_STATUS_PASSCODE_REQUIRED "Passcode required" + IDS_STATUS_RECEIVING "Receiving..." + IDS_STATUS_SENDING "Sending..." + IDS_STATUS_ABORTING "Aborting..." + IDS_STATUS_USERACTION_REQUIRED "User action required" + IDS_STATUS_SUCCEEDED "Success" + IDS_STATUS_FAILED "Failed" +END + +STRINGTABLE +BEGIN + IDS_STATUS_ABORTED "Aborted" + IDS_STATUS_UPDATEBEGIN "Updating providers..." + IDS_POPUP_AGREEMENT_TITLE "Agreement Required" + IDS_POPUP_PASSCODE_TITLE "Passcode Required" + IDS_TOSLINK_TEMPLATE "%s Terms of Service" + IDS_PRIVACYLINK_TEMPLATE "Privacy Policy" + IDS_AND "and" + IDS_STATUS_AGREEMENT_REQUIRED "User agreement required" + IDS_BUTTON_CONTINUE "&Continue" + IDS_BUTTON_YES "&Yes" + IDS_BUTTON_NO "&No" + IDS_PROVIDERUPDATE_TITLE "Provider Updated" + IDS_PROVIDER_CHANGED "Provider information has changed and needs to be updated" + IDS_PROVIDER_REMOVED "This provider is not available anymore and will be removed" + IDS_LOGINTAB_MOREPROVIDERS "show more providers..." + IDS_ERR_OPENID_INVALIDURL "Invalid OpenId Url" +END + +STRINGTABLE +BEGIN + 65535 "{95C65BA3-3C34-40ec-AE74-8D2C60AAE3C8}" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/auth/auth.sln b/Src/auth/auth.sln new file mode 100644 index 00000000..ade06f3f --- /dev/null +++ b/Src/auth/auth.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "auth", "auth.vcxproj", "{92037EB1-5BEB-42A4-AAC8-E2CAE0DEED1E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {92037EB1-5BEB-42A4-AAC8-E2CAE0DEED1E}.Debug|Win32.ActiveCfg = Debug|Win32 + {92037EB1-5BEB-42A4-AAC8-E2CAE0DEED1E}.Debug|Win32.Build.0 = Debug|Win32 + {92037EB1-5BEB-42A4-AAC8-E2CAE0DEED1E}.Debug|x64.ActiveCfg = Debug|x64 + {92037EB1-5BEB-42A4-AAC8-E2CAE0DEED1E}.Debug|x64.Build.0 = Debug|x64 + {92037EB1-5BEB-42A4-AAC8-E2CAE0DEED1E}.Release|Win32.ActiveCfg = Release|Win32 + {92037EB1-5BEB-42A4-AAC8-E2CAE0DEED1E}.Release|Win32.Build.0 = Release|Win32 + {92037EB1-5BEB-42A4-AAC8-E2CAE0DEED1E}.Release|x64.ActiveCfg = Release|x64 + {92037EB1-5BEB-42A4-AAC8-E2CAE0DEED1E}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {896DC24A-5EED-47E8-9948-7EFE0F3CE497} + EndGlobalSection +EndGlobal diff --git a/Src/auth/auth.vcxproj b/Src/auth/auth.vcxproj new file mode 100644 index 00000000..354cdc60 --- /dev/null +++ b/Src/auth/auth.vcxproj @@ -0,0 +1,397 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{92037EB1-5BEB-42A4-AAC8-E2CAE0DEED1E}</ProjectGuid> + <RootNamespace>auth</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>true</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir>..\external_dependencies\vcpkg</VcpkgInstalledDir> + <VcpkgUseStatic>true</VcpkgUseStatic> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir>..\external_dependencies\vcpkg</VcpkgInstalledDir> + <VcpkgUseStatic>true</VcpkgUseStatic> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir>..\external_dependencies\vcpkg</VcpkgInstalledDir> + <VcpkgUseStatic>true</VcpkgUseStatic> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir>..\external_dependencies\vcpkg</VcpkgInstalledDir> + <VcpkgUseStatic>true</VcpkgUseStatic> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;AUTH_EXPORTS;USE_LOGINBOX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>EditAndContinue</DebugInformationFormat> + <DisableSpecificWarnings>4091;4838;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;rpcrt4.lib;urlmon.lib;wininet.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(ProjectName).w5s</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(ProjectDir)x86_Debug\$(ProjectName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(ProjectDir)x86_Debug\$(ProjectName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)*.w5s ..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)*.w5s ..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;AUTH_EXPORTS;USE_LOGINBOX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>EditAndContinue</DebugInformationFormat> + <DisableSpecificWarnings>4091;4838;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;rpcrt4.lib;urlmon.lib;wininet.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(ProjectName).w5s</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(ProjectDir)x64_Debug\$(ProjectName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(ProjectDir)x64_Debug\$(ProjectName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)*.w5s ..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)*.w5s ..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;AUTH_EXPORTS;USE_LOGINBOX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <BufferSecurityCheck>false</BufferSecurityCheck> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4091;4838;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;rpcrt4.lib;urlmon.lib;wininet.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(ProjectName).w5s</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(ProjectDir)x86_Release\$(ProjectName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(ProjectDir)x86_Release\$(ProjectName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)*.w5s ..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)*.w5s ..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;AUTH_EXPORTS;USE_LOGINBOX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <BufferSecurityCheck>false</BufferSecurityCheck> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4091;4838;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;rpcrt4.lib;urlmon.lib;wininet.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(ProjectName).w5s</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(ProjectDir)x64_Release\$(ProjectName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(ProjectDir)x64_Release\$(ProjectName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)*.w5s ..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)*.w5s ..\..\..\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\nu\PtrList.cpp" /> + <ClCompile Include="..\nu\windowsTheme.cpp" /> + <ClCompile Include="..\Winamp\JSAPI_CallbackParameters.cpp" /> + <ClCompile Include="auth.cpp" /> + <ClCompile Include="b64.c" /> + <ClCompile Include="decode.cpp" /> + <ClCompile Include="hmac_sha256.c" /> + <ClCompile Include="log.cpp" /> + <ClCompile Include="Loginbox\addressEditbox.cpp" /> + <ClCompile Include="Loginbox\addressEncoder.cpp" /> + <ClCompile Include="Loginbox\animation.cpp" /> + <ClCompile Include="Loginbox\browserWindow.cpp" /> + <ClCompile Include="Loginbox\commandNodeParser.cpp" /> + <ClCompile Include="Loginbox\commandParser.cpp" /> + <ClCompile Include="Loginbox\commandWebAuth.cpp" /> + <ClCompile Include="Loginbox\commandWinampAuth.cpp" /> + <ClCompile Include="Loginbox\common.cpp" /> + <ClCompile Include="Loginbox\dataAddress.cpp" /> + <ClCompile Include="Loginbox\dataCredentials.cpp" /> + <ClCompile Include="Loginbox\download.cpp" /> + <ClCompile Include="Loginbox\downloadResult.cpp" /> + <ClCompile Include="Loginbox\editboxExtender.cpp" /> + <ClCompile Include="Loginbox\externalMngr.cpp" /> + <ClCompile Include="Loginbox\graphics.cpp" /> + <ClCompile Include="Loginbox\imageCache.cpp" /> + <ClCompile Include="Loginbox\imageLoader.cpp" /> + <ClCompile Include="Loginbox\loginBox.cpp" /> + <ClCompile Include="Loginbox\loginConfig.cpp" /> + <ClCompile Include="Loginbox\loginCredentials.cpp" /> + <ClCompile Include="Loginbox\loginCurtain.cpp" /> + <ClCompile Include="Loginbox\loginData.cpp" /> + <ClCompile Include="Loginbox\loginGui.cpp" /> + <ClCompile Include="Loginbox\loginNotifier.cpp" /> + <ClCompile Include="Loginbox\loginPage.cpp" /> + <ClCompile Include="Loginbox\loginPopup.cpp" /> + <ClCompile Include="Loginbox\loginProvider.cpp" /> + <ClCompile Include="Loginbox\loginStatus.cpp" /> + <ClCompile Include="Loginbox\loginTab.cpp" /> + <ClCompile Include="Loginbox\pageAddress.cpp" /> + <ClCompile Include="Loginbox\pageCredentials.cpp" /> + <ClCompile Include="Loginbox\pageEmpty.cpp" /> + <ClCompile Include="Loginbox\pageError.cpp" /> + <ClCompile Include="Loginbox\pageInfo.cpp" /> + <ClCompile Include="Loginbox\popupAgreement.cpp" /> + <ClCompile Include="Loginbox\popupMessage.cpp" /> + <ClCompile Include="Loginbox\popupPasscode.cpp" /> + <ClCompile Include="Loginbox\providerEnumerator.cpp" /> + <ClCompile Include="Loginbox\providerLoader.cpp" /> + <ClCompile Include="Loginbox\providerOperation.cpp" /> + <ClCompile Include="Loginbox\providerParser.cpp" /> + <ClCompile Include="Loginbox\resultWebAuth.cpp" /> + <ClCompile Include="Loginbox\resultWinampAuth.cpp" /> + <ClCompile Include="Loginbox\stringBuilder.cpp" /> + <ClCompile Include="Loginbox\templateAddress.cpp" /> + <ClCompile Include="Loginbox\templateCredentials.cpp" /> + <ClCompile Include="Loginbox\templateInfo.cpp" /> + <ClCompile Include="Loginbox\templateNodeParser.cpp" /> + <ClCompile Include="Loginbox\templateParser.cpp" /> + <ClCompile Include="Loginbox\update.cpp" /> + <ClCompile Include="Loginbox\xmlInt32Parser.cpp" /> + <ClCompile Include="main.cpp" /> + <ClCompile Include="OAuthKey.cpp" /> + <ClCompile Include="post.cpp" /> + <ClCompile Include="sha2.c" /> + <ClCompile Include="XMLString.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\nu\windowsTheme.h" /> + <ClInclude Include="..\Wasabi\api\syscb\callbacks\authcb.h" /> + <ClInclude Include="..\Wasabi\api\syscb\callbacks\authcbi.h" /> + <ClInclude Include="api.h" /> + <ClInclude Include="api_auth.h" /> + <ClInclude Include="auth.h" /> + <ClInclude Include="b64.h" /> + <ClInclude Include="hmac_sha256.h" /> + <ClInclude Include="ifc_authcallback.h" /> + <ClInclude Include="Loginbox\addressEditbox.h" /> + <ClInclude Include="Loginbox\addressEncoder.h" /> + <ClInclude Include="Loginbox\animation.h" /> + <ClInclude Include="Loginbox\browserEvent.h" /> + <ClInclude Include="Loginbox\browserWindow.h" /> + <ClInclude Include="Loginbox\commandNodeParser.h" /> + <ClInclude Include="Loginbox\commandParser.h" /> + <ClInclude Include="Loginbox\commandWebAuth.h" /> + <ClInclude Include="Loginbox\commandWinampAuth.h" /> + <ClInclude Include="Loginbox\common.h" /> + <ClInclude Include="Loginbox\dataAddress.h" /> + <ClInclude Include="Loginbox\dataCredentials.h" /> + <ClInclude Include="Loginbox\download.h" /> + <ClInclude Include="Loginbox\downloadResult.h" /> + <ClInclude Include="Loginbox\editboxExtender.h" /> + <ClInclude Include="Loginbox\externalMngr.h" /> + <ClInclude Include="Loginbox\graphics.h" /> + <ClInclude Include="Loginbox\imageCache.h" /> + <ClInclude Include="Loginbox\imageLoader.h" /> + <ClInclude Include="Loginbox\loginBox.h" /> + <ClInclude Include="Loginbox\loginCommand.h" /> + <ClInclude Include="Loginbox\loginConfig.h" /> + <ClInclude Include="Loginbox\loginCredentials.h" /> + <ClInclude Include="Loginbox\loginCurtain.h" /> + <ClInclude Include="Loginbox\loginData.h" /> + <ClInclude Include="Loginbox\loginGui.h" /> + <ClInclude Include="Loginbox\loginNotifier.h" /> + <ClInclude Include="Loginbox\loginPage.h" /> + <ClInclude Include="Loginbox\loginPopup.h" /> + <ClInclude Include="Loginbox\loginProvider.h" /> + <ClInclude Include="Loginbox\loginResult.h" /> + <ClInclude Include="Loginbox\loginStatus.h" /> + <ClInclude Include="Loginbox\loginTab.h" /> + <ClInclude Include="Loginbox\loginTemplate.h" /> + <ClInclude Include="Loginbox\pageAddress.h" /> + <ClInclude Include="Loginbox\pageCredentials.h" /> + <ClInclude Include="Loginbox\pageEmpty.h" /> + <ClInclude Include="Loginbox\pageError.h" /> + <ClInclude Include="Loginbox\pageInfo.h" /> + <ClInclude Include="Loginbox\popupAgreement.h" /> + <ClInclude Include="Loginbox\popupMessage.h" /> + <ClInclude Include="Loginbox\popupPasscode.h" /> + <ClInclude Include="Loginbox\providerEnumerator.h" /> + <ClInclude Include="Loginbox\providerLoader.h" /> + <ClInclude Include="Loginbox\providerOperation.h" /> + <ClInclude Include="Loginbox\providerParser.h" /> + <ClInclude Include="Loginbox\resultWebAuth.h" /> + <ClInclude Include="Loginbox\resultWinampAuth.h" /> + <ClInclude Include="Loginbox\stringBuilder.h" /> + <ClInclude Include="Loginbox\templateAddress.h" /> + <ClInclude Include="Loginbox\templateCredentials.h" /> + <ClInclude Include="Loginbox\templateInfo.h" /> + <ClInclude Include="Loginbox\templateNodeParser.h" /> + <ClInclude Include="Loginbox\templateParser.h" /> + <ClInclude Include="Loginbox\update.h" /> + <ClInclude Include="Loginbox\xmlInt32Parser.h" /> + <ClInclude Include="main.h" /> + <ClInclude Include="OAuthKey.h" /> + <ClInclude Include="resource.h" /> + <ClInclude Include="sha2.h" /> + <ClInclude Include="XMLString.h" /> + </ItemGroup> + <ItemGroup> + <Image Include="Loginbox\resources\arrow.png" /> + <Image Include="Loginbox\resources\notifierIcons.png" /> + <Image Include="Loginbox\resources\popupBorder.png" /> + <Image Include="Loginbox\resources\selection.png" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="auth.rc" /> + <ResourceCompile Include="Loginbox\png.rc" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/auth/auth.vcxproj.filters b/Src/auth/auth.vcxproj.filters new file mode 100644 index 00000000..2c6e0598 --- /dev/null +++ b/Src/auth/auth.vcxproj.filters @@ -0,0 +1,448 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="Loginbox\addressEditbox.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\addressEncoder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="auth.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\animation.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="b64.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\browserWindow.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\commandNodeParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\commandParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\commandWinampAuth.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\commandWebAuth.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\common.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\dataAddress.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\dataCredentials.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="decode.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\download.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\editboxExtender.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\downloadResult.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\externalMngr.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\graphics.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="hmac_sha256.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\imageCache.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Winamp\JSAPI_CallbackParameters.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\imageLoader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="log.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginBox.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginConfig.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginCredentials.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginCurtain.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginData.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginGui.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginNotifier.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginPage.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginPopup.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginProvider.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginStatus.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\loginTab.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="OAuthKey.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\pageAddress.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\pageCredentials.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\pageEmpty.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\pageError.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\pageInfo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\popupAgreement.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\popupMessage.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\popupPasscode.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="post.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\providerEnumerator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\providerLoader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\providerOperation.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\providerParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\nu\PtrList.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\resultWebAuth.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="sha2.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\resultWinampAuth.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\stringBuilder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\templateAddress.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\templateCredentials.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\templateInfo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\templateNodeParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\templateParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\update.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\nu\windowsTheme.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Loginbox\xmlInt32Parser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="XMLString.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="Loginbox\addressEditbox.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\addressEncoder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\animation.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="api.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="api_auth.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="auth.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\Wasabi\api\syscb\callbacks\authcb.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\Wasabi\api\syscb\callbacks\authcbi.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="b64.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\browserEvent.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\browserWindow.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\commandNodeParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\commandParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\commandWebAuth.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\commandWinampAuth.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\common.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\dataAddress.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\dataCredentials.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\download.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\downloadResult.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\editboxExtender.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\externalMngr.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\graphics.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="hmac_sha256.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="ifc_authcallback.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\imageCache.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\imageLoader.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginBox.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginCommand.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginConfig.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginCredentials.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginCurtain.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginData.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginGui.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginNotifier.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginPage.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginPopup.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginProvider.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginResult.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginStatus.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginTab.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\loginTemplate.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="main.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="OAuthKey.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\pageAddress.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\pageCredentials.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\pageEmpty.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\pageError.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\pageInfo.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\popupAgreement.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\popupMessage.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\popupPasscode.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\providerEnumerator.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\providerLoader.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\providerOperation.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\providerParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\resultWebAuth.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\resultWinampAuth.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="sha2.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\stringBuilder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\templateAddress.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\templateCredentials.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\templateInfo.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\templateNodeParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\templateParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\update.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\nu\windowsTheme.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Loginbox\xmlInt32Parser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="XMLString.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="auth.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + <ResourceCompile Include="Loginbox\png.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> + <ItemGroup> + <Image Include="Loginbox\resources\arrow.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="Loginbox\resources\notifierIcons.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="Loginbox\resources\popupBorder.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="Loginbox\resources\selection.png"> + <Filter>Image Files</Filter> + </Image> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{435eeb3b-4f82-47a7-856a-ac293054e2dc}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{915a28cb-9ee6-45ab-ae50-df8adee69775}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{d2313219-3085-4862-8aa6-1c5a74b4a7dd}</UniqueIdentifier> + </Filter> + <Filter Include="Image Files"> + <UniqueIdentifier>{a1b669f5-0ae3-4b80-9225-caca5c8a4890}</UniqueIdentifier> + </Filter> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/auth/b64.c b/Src/auth/b64.c new file mode 100644 index 00000000..b4960306 --- /dev/null +++ b/Src/auth/b64.c @@ -0,0 +1,606 @@ +/* ///////////////////////////////////////////////////////////////////////////// + * File: b64.c + * + * Purpose: Implementation file for the b64 library + * + * Created: 18th October 2004 + * Updated: 9th February 2008 + * + * Home: http://synesis.com.au/software/ + * + * Copyright (c) 2004-2008, Matthew Wilson and Synesis Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of + * any contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * ////////////////////////////////////////////////////////////////////////// */ + + +/** \file b64.c Implementation file for the b64 library + */ + +/* ///////////////////////////////////////////////////////////////////////////// + * Version information + */ + +#ifndef B64_DOCUMENTATION_SKIP_SECTION +# define B64_VER_C_B64_MAJOR 1 +# define B64_VER_C_B64_MINOR 2 +# define B64_VER_C_B64_REVISION 2 +# define B64_VER_C_B64_EDIT 16 +#endif /* !B64_DOCUMENTATION_SKIP_SECTION */ + +/* ///////////////////////////////////////////////////////////////////////////// + * Includes + */ + +#include "b64.h" + +#include <assert.h> +#include <string.h> + +/* ///////////////////////////////////////////////////////////////////////////// + * Constants and definitions + */ + +#ifndef B64_DOCUMENTATION_SKIP_SECTION +# define NUM_PLAIN_DATA_BYTES (3) +# define NUM_ENCODED_DATA_BYTES (4) +#endif /* !B64_DOCUMENTATION_SKIP_SECTION */ + +/* ///////////////////////////////////////////////////////////////////////////// + * Macros + */ + +#ifndef NUM_ELEMENTS +# define NUM_ELEMENTS(x) (sizeof(x) / sizeof(x[0])) +#endif /* !NUM_ELEMENTS */ + +/* ///////////////////////////////////////////////////////////////////////////// + * Warnings + */ + +#if defined(_MSC_VER) && \ + _MSC_VER < 1000 +# pragma warning(disable : 4705) +#endif /* _MSC_VER < 1000 */ + +/* ///////////////////////////////////////////////////////////////////////////// + * Data + */ + +static const char b64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const signed char b64_indexes[] = +{ + /* 0 - 31 / 0x00 - 0x1f */ + -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + /* 32 - 63 / 0x20 - 0x3f */ + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, 62, -1, -1, -1, 63 /* ... , '+', ... '/' */ + , 52, 53, 54, 55, 56, 57, 58, 59 /* '0' - '7' */ + , 60, 61, -1, -1, -1, -1, -1, -1 /* '8', '9', ... */ + /* 64 - 95 / 0x40 - 0x5f */ + , -1, 0, 1, 2, 3, 4, 5, 6 /* ..., 'A' - 'G' */ + , 7, 8, 9, 10, 11, 12, 13, 14 /* 'H' - 'O' */ + , 15, 16, 17, 18, 19, 20, 21, 22 /* 'P' - 'W' */ + , 23, 24, 25, -1, -1, -1, -1, -1 /* 'X', 'Y', 'Z', ... */ + /* 96 - 127 / 0x60 - 0x7f */ + , -1, 26, 27, 28, 29, 30, 31, 32 /* ..., 'a' - 'g' */ + , 33, 34, 35, 36, 37, 38, 39, 40 /* 'h' - 'o' */ + , 41, 42, 43, 44, 45, 46, 47, 48 /* 'p' - 'w' */ + , 49, 50, 51, -1, -1, -1, -1, -1 /* 'x', 'y', 'z', ... */ + + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 + , -1, -1, -1, -1, -1, -1, -1, -1 +}; + +/* ///////////////////////////////////////////////////////////////////////////// + * Helper functions + */ + +/** This function reads in 3 bytes at a time, and translates them into 4 + * characters. + */ +static size_t b64_encode_( unsigned char const *src + , size_t srcSize + , char *const dest + , size_t destLen + , unsigned lineLen + , B64_RC *rc) +{ + size_t total = ((srcSize + (NUM_PLAIN_DATA_BYTES - 1)) / NUM_PLAIN_DATA_BYTES) * NUM_ENCODED_DATA_BYTES; + + assert(NULL != rc); + *rc = B64_RC_OK; + + if(lineLen > 0) + { + unsigned numLines = (unsigned int)(total + (lineLen - 1)) / lineLen; + + total += 2 * (numLines - 1); + } + + if(NULL == dest) + { + return total; + } + else if(destLen < total) + { + *rc = B64_RC_INSUFFICIENT_BUFFER; + + return 0; + } + else + { + char *p = dest; + char *end = dest + destLen; + size_t len = 0; + + for(; NUM_PLAIN_DATA_BYTES <= srcSize; srcSize -= NUM_PLAIN_DATA_BYTES) + { + char characters[NUM_ENCODED_DATA_BYTES] = {0}; + + /* + * + * | 0 | 1 | 2 | + * + * | | | | + * | | | | | | | + * | | | | | | | | | | | | | + * | | | | | | | | | | | | | | | | | | | | | | | | | + * + * | 0 | 1 | 2 | 3 | + * + */ + + /* characters[0] is the 6 left-most bits of src[0] */ + characters[0] = (char)((src[0] & 0xfc) >> 2); + /* characters[0] is the right-most 2 bits of src[0] and the left-most 4 bits of src[1] */ + characters[1] = (char)(((src[0] & 0x03) << 4) + ((src[1] & 0xf0) >> 4)); + /* characters[0] is the right-most 4 bits of src[1] and the 2 left-most bits of src[2] */ + characters[2] = (char)(((src[1] & 0x0f) << 2) + ((src[2] & 0xc0) >> 6)); + /* characters[3] is the right-most 6 bits of src[2] */ + characters[3] = (char)(src[2] & 0x3f); + +#ifndef __WATCOMC__ + assert(characters[0] >= 0 && characters[0] < 64); + assert(characters[1] >= 0 && characters[1] < 64); + assert(characters[2] >= 0 && characters[2] < 64); + assert(characters[3] >= 0 && characters[3] < 64); +#endif /* __WATCOMC__ */ + + src += NUM_PLAIN_DATA_BYTES; + *p++ = b64_chars[(unsigned char)characters[0]]; + assert(NULL != strchr(b64_chars, *(p-1))); + ++len; + assert(len != lineLen); + + *p++ = b64_chars[(unsigned char)characters[1]]; + assert(NULL != strchr(b64_chars, *(p-1))); + ++len; + assert(len != lineLen); + + *p++ = b64_chars[(unsigned char)characters[2]]; + assert(NULL != strchr(b64_chars, *(p-1))); + ++len; + assert(len != lineLen); + + *p++ = b64_chars[(unsigned char)characters[3]]; + assert(NULL != strchr(b64_chars, *(p-1))); + + if( ++len == lineLen && + p != end) + { + *p++ = '\r'; + *p++ = '\n'; + len = 0; + } + } + + if(0 != srcSize) + { + /* Deal with the overspill, by boosting it up to three bytes (using 0s) + * and then appending '=' for any missing characters. + * + * This is done into a temporary buffer, so we can call ourselves and + * have the output continue to be written direct to the destination. + */ + + unsigned char dummy[NUM_PLAIN_DATA_BYTES] = {0}; + size_t i; + + for(i = 0; i < srcSize; ++i) + { + dummy[i] = *src++; + } + + for(; i < NUM_PLAIN_DATA_BYTES; ++i) + { + dummy[i] = '\0'; + } + + b64_encode_(&dummy[0], NUM_PLAIN_DATA_BYTES, p, NUM_ENCODED_DATA_BYTES * (1 + 2), 0, rc); + + for(p += 1 + srcSize; srcSize++ < NUM_PLAIN_DATA_BYTES; ) + { + *p++ = '='; + } + } + + return total; + } +} + +/** This function reads in a character string in 4-character chunks, and writes + * out the converted form in 3-byte chunks to the destination. + */ +static size_t b64_decode_( char const *src + , size_t srcLen + , unsigned char *dest + , size_t destSize + , unsigned flags + , char const **badChar + , B64_RC *rc) +{ + const size_t wholeChunks = (srcLen / NUM_ENCODED_DATA_BYTES); + const size_t remainderBytes = (srcLen % NUM_ENCODED_DATA_BYTES); + size_t maxTotal = (wholeChunks + (0 != remainderBytes)) * NUM_PLAIN_DATA_BYTES; + unsigned char *dest_ = dest; + + ((void)remainderBytes); + + assert(NULL != badChar); + assert(NULL != rc); + + *badChar = NULL; + *rc = B64_RC_OK; + + if(NULL == dest) + { + return maxTotal; + } + else if(destSize < maxTotal) + { + *rc = B64_RC_INSUFFICIENT_BUFFER; + + return 0; + } + else + { + /* Now we iterate through the src, collecting together four characters + * at a time from the Base-64 alphabet, until the end-point is reached. + * + * + */ + + char const *begin = src; + char const *const end = begin + srcLen; + size_t currIndex = 0; + size_t numPads = 0; + signed char indexes[NUM_ENCODED_DATA_BYTES] = {0}; /* 4 */ + + for(; begin != end; ++begin) + { + const char ch = *begin; + + if('=' == ch) + { + assert(currIndex < NUM_ENCODED_DATA_BYTES); + + indexes[currIndex++] = '\0'; + + ++numPads; + } + else + { + /* NOTE: Had to rename 'index' to 'ix', due to name clash with GCC on 64-bit Linux. */ + signed char ix = b64_indexes[(unsigned char)ch]; + + if(-1 == ix) + { + switch(ch) + { + case ' ': + case '\t': + case '\b': + case '\v': + if(B64_F_STOP_ON_UNEXPECTED_WS & flags) + { + *rc = B64_RC_DATA_ERROR; + *badChar = begin; + return 0; + } + else + { + /* Fall through */ + } + case '\r': + case '\n': + continue; + default: + if(B64_F_STOP_ON_UNKNOWN_CHAR & flags) + { + *rc = B64_RC_DATA_ERROR; + *badChar = begin; + return 0; + } + else + { + continue; + } + } + } + else + { + numPads = 0; + + assert(currIndex < NUM_ENCODED_DATA_BYTES); + + indexes[currIndex++] = ix; + } + } + + if(NUM_ENCODED_DATA_BYTES == currIndex) + { + unsigned char bytes[NUM_PLAIN_DATA_BYTES] = {0}; /* 3 */ + + bytes[0] = (unsigned char)((indexes[0] << 2) + ((indexes[1] & 0x30) >> 4)); + + currIndex = 0; + + *dest++ = bytes[0]; + if(2 != numPads) + { + bytes[1] = (unsigned char)(((indexes[1] & 0xf) << 4) + ((indexes[2] & 0x3c) >> 2)); + + *dest++ = bytes[1]; + + if(1 != numPads) + { + bytes[2] = (unsigned char)(((indexes[2] & 0x3) << 6) + indexes[3]); + + *dest++ = bytes[2]; + } + } + if(0 != numPads) + { + break; + } + } + } + + return (size_t)(dest - dest_); + } +} + +/* ///////////////////////////////////////////////////////////////////////////// + * API functions + */ + +size_t b64_encode(void const *src, size_t srcSize, char *dest, size_t destLen) +{ + /* Use Null Object (Variable) here for rc, so do not need to check + * elsewhere. + */ + B64_RC rc_; + + return b64_encode_((unsigned char const*)src, srcSize, dest, destLen, 0, &rc_); +} + +size_t b64_encode2( void const *src + , size_t srcSize + , char *dest + , size_t destLen + , unsigned flags + , int lineLen /* = -1 */ + , B64_RC *rc /* = NULL */) +{ + /* Use Null Object (Variable) here for rc, so do not need to check + * elsewhere + */ + B64_RC rc_; + if(NULL == rc) + { + rc = &rc_; + } + + switch(B64_F_LINE_LEN_MASK & flags) + { + case B64_F_LINE_LEN_USE_PARAM: + if(lineLen >= 0) + { + break; + } + /* Fall through to 64 */ + case B64_F_LINE_LEN_64: + lineLen = 64; + break; + case B64_F_LINE_LEN_76: + lineLen = 76; + break; + default: + assert(!"Bad line length flag specified to b64_encode2()"); + case B64_F_LINE_LEN_INFINITE: + lineLen = 0; + break; + } + + assert(0 == (lineLen % 4)); + + return b64_encode_((unsigned char const*)src, srcSize, dest, destLen, (unsigned)lineLen, rc); +} + +size_t b64_decode(char const *src, size_t srcLen, void *dest, size_t destSize) +{ + /* Use Null Object (Variable) here for rc and badChar, so do not need to + * check elsewhere. + */ + char const *badChar_; + B64_RC rc_; + + return b64_decode_(src, srcLen, (unsigned char*)dest, destSize, B64_F_STOP_ON_NOTHING, &badChar_, &rc_); +} + +size_t b64_decode2( char const *src + , size_t srcLen + , void *dest + , size_t destSize + , unsigned flags + , char const **badChar /* = NULL */ + , B64_RC *rc /* = NULL */) +{ + char const *badChar_; + B64_RC rc_; + + /* Use Null Object (Variable) here for rc and badChar, so do not need to + * check elsewhere. + */ + if(NULL == badChar) + { + badChar = &badChar_; + } + if(NULL == rc) + { + rc = &rc_; + } + + return b64_decode_(src, srcLen, (unsigned char*)dest, destSize, flags, badChar, rc); +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef B64_DOCUMENTATION_SKIP_SECTION +struct b64ErrorString_t_ +#else /* !B64_DOCUMENTATION_SKIP_SECTION */ +typedef struct b64ErrorString_t_ b64ErrorString_t_; +struct b64ErrorString_t_ +#endif /* !B64_DOCUMENTATION_SKIP_SECTION */ +{ + int code; /*!< The error code. */ + char const *str; /*!< The string. */ + size_t len; /*!< The string length. */ +}; + + + +#define SEVERITY_STR_DECL(rc, desc) \ + \ + static const char s_str##rc[] = desc; \ + static const b64ErrorString_t_ s_rct##rc = { rc, s_str##rc, NUM_ELEMENTS(s_str##rc) - 1 } + + +#define SEVERITY_STR_ENTRY(rc) \ + \ + &s_rct##rc + + +static char const *b64_LookupCodeA_(int code, b64ErrorString_t_ const **mappings, size_t cMappings, size_t *len) +{ + /* Use Null Object (Variable) here for len, so do not need to check + * elsewhere. + */ + size_t len_; + + if(NULL == len) + { + len = &len_; + } + + /* Checked, indexed search. */ + if( code >= 0 && + code < B64_max_RC_value) + { + if(code == mappings[code]->code) + { + return (*len = mappings[code]->len, mappings[code]->str); + } + } + + /* Linear search. Should only be needed if order in + * b64_LookupErrorStringA_() messed up. + */ + { size_t i; for(i = 0; i < cMappings; ++i) + { + if(code == mappings[i]->code) + { + return (*len = mappings[i]->len, mappings[i]->str); + } + }} + + return (*len = 0, ""); +} + +static char const *b64_LookupErrorStringA_(int error, size_t *len) +{ + SEVERITY_STR_DECL(B64_RC_OK , "Operation was successful" ); + SEVERITY_STR_DECL(B64_RC_INSUFFICIENT_BUFFER , "The given translation buffer was not of sufficient size" ); + SEVERITY_STR_DECL(B64_RC_TRUNCATED_INPUT , "The input did not represent a fully formed stream of octet couplings" ); + SEVERITY_STR_DECL(B64_RC_DATA_ERROR , "Invalid data" ); + + static const b64ErrorString_t_ *s_strings[] = + { + SEVERITY_STR_ENTRY(B64_RC_OK), + SEVERITY_STR_ENTRY(B64_RC_INSUFFICIENT_BUFFER), + SEVERITY_STR_ENTRY(B64_RC_TRUNCATED_INPUT), + SEVERITY_STR_ENTRY(B64_RC_DATA_ERROR), + }; + + return b64_LookupCodeA_(error, s_strings, NUM_ELEMENTS(s_strings), len); +} + +char const *b64_getErrorString(B64_RC code) +{ + return b64_LookupErrorStringA_((int)code, NULL); +} + +size_t b64_getErrorStringLength(B64_RC code) +{ + size_t len; + + return (b64_LookupErrorStringA_((int)code, &len), len); +} + +/* ////////////////////////////////////////////////////////////////////////// */ diff --git a/Src/auth/b64.h b/Src/auth/b64.h new file mode 100644 index 00000000..3da01d17 --- /dev/null +++ b/Src/auth/b64.h @@ -0,0 +1,417 @@ +/* ///////////////////////////////////////////////////////////////////////////// + * File: b64/b64.h + * + * Purpose: Header file for the b64 library + * + * Created: 18th October 2004 + * Updated: 15th March 2008 + * + * Thanks: To Adam McLaurin, for ideas regarding the b64_decode2() and + * b64_encode2(). + * + * Home: http://synesis.com.au/software/ + * + * Copyright (c) 2004-2008, Matthew Wilson and Synesis Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of + * any contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * ////////////////////////////////////////////////////////////////////////// */ + + +/** \file b64/b64.h + * + * \brief [C/C++] Header file for the b64 library. + */ + +#ifndef B64_INCL_B64_H_B64 +#define B64_INCL_B64_H_B64 + +/* ///////////////////////////////////////////////////////////////////////////// + * Version information + */ + +#ifndef B64_DOCUMENTATION_SKIP_SECTION +# define B64_VER_B64_H_B64_MAJOR 1 +# define B64_VER_B64_H_B64_MINOR 5 +# define B64_VER_B64_H_B64_REVISION 1 +# define B64_VER_B64_H_B64_EDIT 25 +#endif /* !B64_DOCUMENTATION_SKIP_SECTION */ + +/** \def B64_VER_MAJOR + * The major version number of b64 + */ + +/** \def B64_VER_MINOR + * The minor version number of b64 + */ + +/** \def B64_VER_REVISION + * The revision version number of b64 + */ + +/** \def B64_VER + * The current composite version number of b64 + */ + +#ifndef B64_DOCUMENTATION_SKIP_SECTION +# define B64_VER_1_0_1 0x01000100 +# define B64_VER_1_0_2 0x01000200 +# define B64_VER_1_0_3 0x01000300 +# define B64_VER_1_1_1 0x01010100 +# define B64_VER_1_1_2 0x01010200 +# define B64_VER_1_1_3 0x01010300 +# define B64_VER_1_2_1 0x01020100 +# define B64_VER_1_2_2 0x01020200 +# define B64_VER_1_2_3 0x01020300 +# define B64_VER_1_2_4 0x01020400 +# define B64_VER_1_2_5 0x01020500 +# define B64_VER_1_2_6 0x01020600 +# define B64_VER_1_2_7 0x01020700 +# define B64_VER_1_3_1 0x010301ff + +# define B64_VER B64_VER_1_3_1 +#else /* ? B64_DOCUMENTATION_SKIP_SECTION */ +# define B64_VER 0x010301ff +#endif /* !B64_DOCUMENTATION_SKIP_SECTION */ + +#define B64_VER_MAJOR 1 +#define B64_VER_MINOR 3 +#define B64_VER_REVISION 1 + +/* ///////////////////////////////////////////////////////////////////////////// + * Includes + */ + +#include <stddef.h> + +/* ///////////////////////////////////////////////////////////////////////////// + * Namespace + */ + +#if !defined(B64_NO_NAMESPACE) && \ + !defined(__cplusplus) +# define B64_NO_NAMESPACE +#endif /* !B64_NO_NAMESPACE && !__cplusplus */ + +#ifdef B64_NAMESPACE +# undef B64_NAMESPACE +#endif /* B64_NAMESPACE */ + +#ifdef B64_NAMESPACE_QUALIFIER +# undef B64_NAMESPACE_QUALIFIER +#endif /* B64_NAMESPACE_QUALIFIER */ + + +#ifndef B64_NO_NAMESPACE + +# ifdef B64_CUSTOM_NAMESPACE +# define B64_NAMESPACE B64_CUSTOM_NAMESPACE +# else /* ? B64_CUSTOM_NAMESPACE */ +# define B64_NAMESPACE b64 +# endif /* B64_CUSTOM_NAMESPACE */ + +# if defined(B64_CUSTOM_NAMESPACE) && \ + defined(B64_CUSTOM_NAMESPACE_QUALIFIER) +# define B64_NAMESPACE_QUALIFIER B64_CUSTOM_NAMESPACE_QUALIFIER +# else /* B64_CUSTOM_NAMESPACE && B64_CUSTOM_NAMESPACE_QUALIFIER */ +# define B64_NAMESPACE_QUALIFIER ::B64_NAMESPACE +# endif /* B64_CUSTOM_NAMESPACE && B64_CUSTOM_NAMESPACE_QUALIFIER */ + + +/** \brief [C/C++] The b64 namespace, within which the core library types and functions + * reside in C++ compilation. In C compilation, they all reside in the global + * namespace. + * + * \htmlonly + * <hr> + * \endhtmlonly + */ +namespace B64_NAMESPACE +{ +#endif /* !B64_NO_NAMESPACE */ + +/* ///////////////////////////////////////////////////////////////////////////// + * Enumerations + */ + +/** \brief Return codes (from b64_encode2() / b64_decode2()) + */ +enum B64_RC +{ + B64_RC_OK = 0 /*!< Operation was successful. */ + , B64_RC_INSUFFICIENT_BUFFER = 1 /*!< The given translation buffer was not of sufficient size. */ + , B64_RC_TRUNCATED_INPUT = 2 /*!< The input did not represent a fully formed stream of octet couplings. */ + , B64_RC_DATA_ERROR = 3 /*!< Invalid data. */ +#ifndef B64_DOCUMENTATION_SKIP_SECTION + , B64_max_RC_value +#endif /* !B64_DOCUMENTATION_SKIP_SECTION */ +}; + +#ifndef __cplusplus +typedef enum B64_RC B64_RC; +#endif /* !__cplusplus */ + +/** \brief Coding behaviour modification flags (for b64_encode2() / b64_decode2()) + */ +enum B64_FLAGS +{ + B64_F_LINE_LEN_USE_PARAM = 0x0000 /*!< Uses the lineLen parameter to b64_encode2(). Ignored by b64_decode2(). */ + , B64_F_LINE_LEN_INFINITE = 0x0001 /*!< Ignores the lineLen parameter to b64_encode2(). Line length is infinite. Ignored by b64_decode2(). */ + , B64_F_LINE_LEN_64 = 0x0002 /*!< Ignores the lineLen parameter to b64_encode2(). Line length is 64. Ignored by b64_decode2(). */ + , B64_F_LINE_LEN_76 = 0x0003 /*!< Ignores the lineLen parameter to b64_encode2(). Line length is 76. Ignored by b64_decode2(). */ + , B64_F_LINE_LEN_MASK = 0x000f /*!< Mask for testing line length flags to b64_encode2(). Ignored by b64_encode2(). */ + , B64_F_STOP_ON_NOTHING = 0x0000 /*!< Decoding ignores all invalid characters in the input data. Ignored by b64_encode2(). */ + , B64_F_STOP_ON_UNKNOWN_CHAR = 0x0100 /*!< Causes decoding to break if any non-Base-64 [a-zA-Z0-9=+/], non-whitespace character is encountered. Ignored by b64_encode2(). */ + , B64_F_STOP_ON_UNEXPECTED_WS = 0x0200 /*!< Causes decoding to break if any unexpected whitespace is encountered. Ignored by b64_encode2(). */ + , B64_F_STOP_ON_BAD_CHAR = 0x0300 /*!< Causes decoding to break if any non-Base-64 [a-zA-Z0-9=+/] character is encountered. Ignored by b64_encode2(). */ +}; + +#ifndef __cplusplus +typedef enum B64_FLAGS B64_FLAGS; +#endif /* !__cplusplus */ + +/* ///////////////////////////////////////////////////////////////////////////// + * Functions + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** \brief Encodes a block of binary data into Base-64 + * + * \param src Pointer to the block to be encoded. May not be NULL, except when + * \c dest is NULL, in which case it is ignored. + * \param srcSize Length of block to be encoded + * \param dest Pointer to the buffer into which the result is to be written. May + * be NULL, in which case the function returns the required length + * \param destLen Length of the buffer into which the result is to be written. Must + * be at least as large as that indicated by the return value from + * \link b64::b64_encode b64_encode(NULL, srcSize, NULL, 0)\endlink. + * + * \return 0 if the size of the buffer was insufficient, or the length of the + * converted buffer was longer than \c destLen + * + * \note The function returns the required length if \c dest is NULL + * + * \note The function returns the required length if \c dest is NULL. The returned size + * might be larger than the actual required size, but will never be smaller. + * + * \note Threading: The function is fully re-entrant. + * + * \see b64::encode() + */ +size_t b64_encode(void const *src, size_t srcSize, char *dest, size_t destLen); + +/** \brief Encodes a block of binary data into Base-64 + * + * \param src Pointer to the block to be encoded. May not be NULL, except when + * \c dest is NULL, in which case it is ignored. + * \param srcSize Length of block to be encoded + * \param dest Pointer to the buffer into which the result is to be written. May + * be NULL, in which case the function returns the required length + * \param destLen Length of the buffer into which the result is to be written. Must + * be at least as large as that indicated by the return value from + * \link b64::b64_encode2 b64_encode2(NULL, srcSize, NULL, 0, flags, lineLen, rc)\endlink. + * \param flags A combination of the B64_FLAGS enumeration, that moderate the + * behaviour of the function + * \param lineLen If the flags parameter contains B64_F_LINE_LEN_USE_PARAM, then + * this parameter represents the length of the lines into which the encoded form is split, + * with a hard line break ('\\r\\n'). If this value is 0, then the line is not + * split. If it is <0, then the RFC-1113 recommended line length of 64 is used + * \param rc The return code representing the status of the operation. May be NULL. + * + * \return 0 if the size of the buffer was insufficient, or the length of the + * converted buffer was longer than \c destLen + * + * \note The function returns the required length if \c dest is NULL. The returned size + * might be larger than the actual required size, but will never be smaller. + * + * \note Threading: The function is fully re-entrant. + * + * \see b64::encode() + */ +size_t b64_encode2( void const *src + , size_t srcSize + , char *dest + , size_t destLen + , unsigned flags + , int lineLen /* = 0 */ + , B64_RC *rc /* = NULL */); + +/** \brief Decodes a sequence of Base-64 into a block of binary data + * + * \param src Pointer to the Base-64 block to be decoded. May not be NULL, except when + * \c dest is NULL, in which case it is ignored. If \c dest is NULL, and \c src is + * <b>not</b> NULL, then the returned value is calculated exactly, otherwise a value + * is returned that is guaranteed to be large enough to hold the decoded block. + * + * \param srcLen Length of block to be encoded. Must be an integral of 4, the Base-64 + * encoding quantum, otherwise the Base-64 block is assumed to be invalid + * \param dest Pointer to the buffer into which the result is to be written. May + * be NULL, in which case the function returns the required length + * \param destSize Length of the buffer into which the result is to be written. Must + * be at least as large as that indicated by the return value from + * \c b64_decode(src, srcSize, NULL, 0), even in the case where the encoded form + * contains a number of characters that will be ignored, resulting in a lower total + * length of converted form. + * + * \return 0 if the size of the buffer was insufficient, or the length of the + * converted buffer was longer than \c destSize + * + * \note The function returns the required length if \c dest is NULL. The returned size + * might be larger than the actual required size, but will never be smaller. + * + * \note \anchor anchor__4_characters The behaviour of both + * \link b64::b64_encode2 b64_encode2()\endlink + * and + * \link b64::b64_decode2 b64_decode2()\endlink + * are undefined if the line length is not a multiple of 4. + * + * \note Threading: The function is fully re-entrant. + * + * \see b64::decode() + */ +size_t b64_decode(char const *src, size_t srcLen, void *dest, size_t destSize); + +/** \brief Decodes a sequence of Base-64 into a block of binary data + * + * \param src Pointer to the Base-64 block to be decoded. May not be NULL, except when + * \c dest is NULL, in which case it is ignored. If \c dest is NULL, and \c src is + * <b>not</b> NULL, then the returned value is calculated exactly, otherwise a value + * is returned that is guaranteed to be large enough to hold the decoded block. + * + * \param srcLen Length of block to be encoded. Must be an integral of 4, the Base-64 + * encoding quantum, otherwise the Base-64 block is assumed to be invalid + * \param dest Pointer to the buffer into which the result is to be written. May + * be NULL, in which case the function returns the required length + * \param destSize Length of the buffer into which the result is to be written. Must + * be at least as large as that indicated by the return value from + * \c b64_decode(src, srcSize, NULL, 0), even in the case where the encoded form + * contains a number of characters that will be ignored, resulting in a lower total + * length of converted form. + * \param flags A combination of the B64_FLAGS enumeration, that moderate the + * behaviour of the function. + * \param rc The return code representing the status of the operation. May be NULL. + * \param badChar If the flags parameter does not contain B64_F_STOP_ON_NOTHING, this + * parameter specifies the address of a pointer that will be set to point to any + * character in the sequence that stops the parsing, as dictated by the flags + * parameter. May be NULL. + * + * \return 0 if the size of the buffer was insufficient, or the length of the + * converted buffer was longer than \c destSize, or a bad character stopped parsing. + * + * \note The function returns the required length if \c dest is NULL. The returned size + * might be larger than the actual required size, but will never be smaller. + * + * \note The behaviour of both + * \link b64::b64_encode2 b64_encode2()\endlink + * and + * \link b64::b64_decode2 b64_decode2()\endlink + * are undefined if the line length is not a multiple of 4. + * + * \note Threading: The function is fully re-entrant. + * + * \see b64::decode() + */ +size_t b64_decode2( char const *src + , size_t srcLen + , void *dest + , size_t destSize + , unsigned flags + , char const **badChar /* = NULL */ + , B64_RC *rc /* = NULL */); + + +/** \brief Returns the textual description of the error + * + * \param code The \link b64::B64_RC error code\endlink + */ +char const *b64_getErrorString(B64_RC code); + +/** \brief Returns the length of the textual description of the error + * + * \see b64_getErrorString() + * + * \param code The \link b64::B64_RC error code\endlink + */ +size_t b64_getErrorStringLength(B64_RC code); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +/* ///////////////////////////////////////////////////////////////////////////// + * Namespace + */ + +#ifndef B64_NO_NAMESPACE +} /* namespace B64_NAMESPACE */ + +# ifndef B64_DOCUMENTATION_SKIP_SECTION + +namespace stlsoft +{ + + inline char const *c_str_data_a( B64_NAMESPACE_QUALIFIER::B64_RC code) + { + return B64_NAMESPACE_QUALIFIER::b64_getErrorString(code); + } + inline char const *c_str_data( B64_NAMESPACE_QUALIFIER::B64_RC code) + { + return B64_NAMESPACE_QUALIFIER::b64_getErrorString(code); + } + + inline size_t c_str_len_a( B64_NAMESPACE_QUALIFIER::B64_RC code) + { + return B64_NAMESPACE_QUALIFIER::b64_getErrorStringLength(code); + } + inline size_t c_str_len( B64_NAMESPACE_QUALIFIER::B64_RC code) + { + return B64_NAMESPACE_QUALIFIER::b64_getErrorStringLength(code); + } + + inline char const *c_str_ptr_a( B64_NAMESPACE_QUALIFIER::B64_RC code) + { + return B64_NAMESPACE_QUALIFIER::b64_getErrorString(code); + } + inline char const *c_str_ptr( B64_NAMESPACE_QUALIFIER::B64_RC code) + { + return B64_NAMESPACE_QUALIFIER::b64_getErrorString(code); + } + +} /* namespace stlsoft */ + +# endif /* !B64_DOCUMENTATION_SKIP_SECTION */ + +#endif /* !B64_NO_NAMESPACE */ + +/* ////////////////////////////////////////////////////////////////////////// */ + +#endif /* B64_INCL_B64_H_B64 */ + +/* ////////////////////////////////////////////////////////////////////////// */ diff --git a/Src/auth/decode.cpp b/Src/auth/decode.cpp new file mode 100644 index 00000000..691f8d43 --- /dev/null +++ b/Src/auth/decode.cpp @@ -0,0 +1,78 @@ +#include <stdio.h> +#include <bfc/platform/types.h> +#include <strsafe.h> + +static uint8_t quickhex(char c) +{ + int hexvalue = c; + if (hexvalue & 0x10) + hexvalue &= ~0x30; + else + { + hexvalue &= 0xF; + hexvalue += 9; + } + return hexvalue; +} + +static uint8_t DecodeEscape(const char *&str) +{ + uint8_t a = quickhex(*++str); + uint8_t b = quickhex(*++str); + str++; + return a * 16 + b; +} + +static void DecodeEscapedUTF8(char *&output, const char *&input) +{ + uint8_t utf8_data[1024] = {0}; // hopefully big enough!! + int num_utf8_words=0; + bool error=false; + + while (input && *input && *input == '%' && num_utf8_words < sizeof(utf8_data)) + { + if (isxdigit(input[1]) && isxdigit(input[2])) + { + utf8_data[num_utf8_words++]=DecodeEscape(input); + } + else if (input[1] == '%') + { + input+=2; + utf8_data[num_utf8_words++]='%'; + } + else + { + error = true; + break; + } + } + + memcpy(output, utf8_data, num_utf8_words); + output+=num_utf8_words; + //StringCchCopyExA(output, num_utf8_words, (char *)utf8_data, &output, 0, 0); + + if (error) + { + *output++ = *input++; + } +} + +// benski> We have the luxury of knowing that decoding will ALWAYS produce smaller strings +// so we can do it in-place +void UrlDecode(char *str) +{ + const char *itr = str; + while (itr && *itr) + { + switch (*itr) + { + case '%': + DecodeEscapedUTF8(str, itr); + break; + default: + *str++ = *itr++; + break; + } + } + *str = 0; +}
\ No newline at end of file diff --git a/Src/auth/hmac_sha256.c b/Src/auth/hmac_sha256.c new file mode 100644 index 00000000..7cfba799 --- /dev/null +++ b/Src/auth/hmac_sha256.c @@ -0,0 +1,195 @@ +/* + * hmac_sha1.c + * + * Version 1.0.0 + * + * Written by Aaron D. Gifford <me@aarongifford.com> + * + * Copyright 1998, 2000 Aaron D. Gifford. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * The HMAC-SHA256 has is defined as: + * + * HMAC = SHA256(K XOR opad, SHA256(K XOR ipad, message)) + * + * "opad" is 64 bytes filled with 0x5c + * "ipad" is 64 bytes filled with 0x36 + * "K" is the key material + * + * If the key material "K" is longer than 64 bytes, then the key material + * will first be digested (K = SHA1(K)) resulting in a 20-byte hash. + * If the key material is shorter than 64 bytes, it is padded with zero + * bytes. + * + * This code precomputes "K XOR ipad" and "K XOR opad" since that just makes + * sense. + * + * This code was heavily influenced by Eric A. Young's in how the interface + * was designed and how this file is formatted. + */ + +#ifndef __HMAC_SHA256_H__ +#define __HMAC_SHA256_H__ + +#include "hmac_sha256.h" +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Filler bytes: */ +#define IPAD_BYTE 0x36 +#define OPAD_BYTE 0x5c +#define ZERO_BYTE 0x00 + +void HMAC_SHA256_Init(HMAC_SHA256_CTX *ctx) { + memset(&(ctx->key[0]), ZERO_BYTE, HMAC_SHA256_BLOCK_LENGTH); + memset(&(ctx->ipad[0]), IPAD_BYTE, HMAC_SHA256_BLOCK_LENGTH); + memset(&(ctx->opad[0]), OPAD_BYTE, HMAC_SHA256_BLOCK_LENGTH); + ctx->keylen = 0; + ctx->hashkey = 0; +} + +void HMAC_SHA256_UpdateKey(HMAC_SHA256_CTX *ctx, unsigned char *key, unsigned int keylen) { + + /* Do we have anything to work with? If not, return right away. */ + if (keylen < 1) + return; + + /* + * Is the total key length (current data and any previous data) + * longer than the hash block length? + */ + if (ctx->hashkey !=0 || (keylen + ctx->keylen) > HMAC_SHA256_BLOCK_LENGTH) { + /* + * Looks like the key data exceeds the hash block length, + * so that means we use a hash of the key as the key data + * instead. + */ + if (ctx->hashkey == 0) { + /* + * Ah, we haven't started hashing the key + * data yet, so we must init. the hash + * monster to begin feeding it. + */ + + /* Set the hash key flag to true (non-zero) */ + ctx->hashkey = 1; + + /* Init. the hash beastie... */ + SHA256_Init(&ctx->shactx); + + /* If there's any previous key data, use it */ + if (ctx->keylen > 0) { + SHA256_Update(&ctx->shactx, &(ctx->key[0]), ctx->keylen); + } + + /* + * Reset the key length to the future true + * key length, HMAC_SHA256_DIGEST_LENGTH + */ + ctx->keylen = HMAC_SHA256_DIGEST_LENGTH; + } + /* Now feed the latest key data to the has monster */ + SHA256_Update(&ctx->shactx, key, keylen); + } else { + /* + * Key data length hasn't yet exceeded the hash + * block length (HMAC_SHA1_BLOCK_LENGTH), so theres + * no need to hash the key data (yet). Copy it + * into the key buffer. + */ + memcpy(&(ctx->key[ctx->keylen]), key, keylen); + ctx->keylen += keylen; + } +} + +void HMAC_SHA256_EndKey(HMAC_SHA256_CTX *ctx) { + unsigned char *ipad, *opad, *key; + int i; + unsigned int j; + + /* Did we end up hashing the key? */ + if (ctx->hashkey) { + memset(&(ctx->key[0]), ZERO_BYTE, HMAC_SHA256_BLOCK_LENGTH); + /* Yes, so finish up and copy the key data */ + SHA256_Final(&(ctx->key[0]), &ctx->shactx); + /* ctx->keylen was already set correctly */ + } + /* Pad the key if necessary with zero bytes */ + if ((i = HMAC_SHA256_BLOCK_LENGTH - ctx->keylen) > 0) { + memset(&(ctx->key[ctx->keylen]), ZERO_BYTE, i); + } + + ipad = &(ctx->ipad[0]); + opad = &(ctx->opad[0]); + + /* Precompute the respective pads XORed with the key */ + key = &(ctx->key[0]); + for (j = 0; j < ctx->keylen; j++, key++) { + /* XOR the key byte with the appropriate pad filler byte */ + *ipad++ ^= *key; + *opad++ ^= *key; + } +} + +void HMAC_SHA256_StartMessage(HMAC_SHA256_CTX *ctx) { + SHA256_Init(&ctx->shactx); + SHA256_Update(&ctx->shactx, &(ctx->ipad[0]), HMAC_SHA256_BLOCK_LENGTH); +} + +void HMAC_SHA256_UpdateMessage(HMAC_SHA256_CTX *ctx, unsigned char *data, unsigned int datalen) { + SHA256_Update(&ctx->shactx, data, datalen); +} + +void HMAC_SHA256_EndMessage(unsigned char *out, HMAC_SHA256_CTX *ctx) { + unsigned char buf[HMAC_SHA256_DIGEST_LENGTH]; + SHA256_CTX *c = &ctx->shactx; + + SHA256_Final(&(buf[0]), c); + SHA256_Init(c); + SHA256_Update(c, &(ctx->opad[0]), HMAC_SHA256_BLOCK_LENGTH); + SHA256_Update(c, buf, HMAC_SHA256_DIGEST_LENGTH); + SHA256_Final(out, c); +} + +void HMAC_SHA256_Done(HMAC_SHA256_CTX *ctx) { + /* Just to be safe, toast all context data */ + memset(&(ctx->ipad[0]), ZERO_BYTE, HMAC_SHA256_BLOCK_LENGTH); + memset(&(ctx->ipad[0]), ZERO_BYTE, HMAC_SHA256_BLOCK_LENGTH); + memset(&(ctx->key[0]), ZERO_BYTE, HMAC_SHA256_BLOCK_LENGTH); + ctx->keylen = 0; + ctx->hashkey = 0; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Src/auth/hmac_sha256.h b/Src/auth/hmac_sha256.h new file mode 100644 index 00000000..f3ede172 --- /dev/null +++ b/Src/auth/hmac_sha256.h @@ -0,0 +1,79 @@ +/* + * hmac_sha256.h + * + * Version 1.0.0 + * + * Written by Aaron D. Gifford <me@aarongifford.com> + * + * Copyright 1998, 2000 Aaron D. Gifford. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef HEADER_HMAC_SHA256_H +#define HEADER_HMAC_SHA256_H + +#include "sha2.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define HMAC_SHA256_DIGEST_LENGTH 32 +#define HMAC_SHA256_BLOCK_LENGTH 64 + +/* The HMAC_SHA1 structure: */ +typedef struct _HMAC_SHA256_CTX { + unsigned char ipad[HMAC_SHA256_BLOCK_LENGTH]; + unsigned char opad[HMAC_SHA256_BLOCK_LENGTH]; + SHA256_CTX shactx; + unsigned char key[HMAC_SHA256_BLOCK_LENGTH]; + unsigned int keylen; + unsigned int hashkey; +} HMAC_SHA256_CTX; + +#ifndef NOPROTO +void HMAC_SHA256_Init(HMAC_SHA256_CTX *ctx); +void HMAC_SHA256_UpdateKey(HMAC_SHA256_CTX *ctx, unsigned char *key, unsigned int keylen); +void HMAC_SHA256_EndKey(HMAC_SHA256_CTX *ctx); +void HMAC_SHA256_StartMessage(HMAC_SHA256_CTX *ctx); +void HMAC_SHA256_UpdateMessage(HMAC_SHA256_CTX *ctx, unsigned char *data, unsigned int datalen); +void HMAC_SHA256_EndMessage(unsigned char *out, HMAC_SHA256_CTX *ctx); +void HMAC_SHA256_Done(HMAC_SHA256_CTX *ctx); +#else +void HMAC_SHA256_Init(); +void HMAC_SHA256_UpdateKey(); +void HMAC_SHA256_EndKey(); +void HMAC_SHA256_StartMessage(); +void HMAC_SHA256_UpdateMessage(); +void HMAC_SHA256_EndMessage(); +void HMAC_SHA256_Done(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Src/auth/ifc_authcallback.h b/Src/auth/ifc_authcallback.h new file mode 100644 index 00000000..f6b44c2f --- /dev/null +++ b/Src/auth/ifc_authcallback.h @@ -0,0 +1,51 @@ +#pragma once + +#include <bfc/dispatch.h> +#include <windows.h> //need this for Sleep() + +class ifc_authcallback : public Dispatchable +{ +protected: + ifc_authcallback() {} + ~ifc_authcallback() {} +public: + int OnConnecting(); + int OnSending(); + int OnReceiving(); + // pump your message loop for a little while + int OnIdle(); + + enum + { + ONCONNECTING=0, + ONSENDING=1, + ONRECEIVING=2, + ONIDLE=3, + }; +}; + +inline int ifc_authcallback::OnConnecting() +{ + return _call(ONCONNECTING, (int)0); +} +inline int ifc_authcallback::OnSending() +{ + return _call(ONSENDING, (int)0); +} +inline int ifc_authcallback::OnReceiving() +{ + return _call(ONRECEIVING, (int)0); +} +inline int ifc_authcallback::OnIdle() +{ + int retval; + if (_dispatch(ONIDLE, &retval)) + return retval; + else + { + // default implementation + Sleep(50); + return 0; + } + +} diff --git a/Src/auth/log.cpp b/Src/auth/log.cpp new file mode 100644 index 00000000..4a51abd4 --- /dev/null +++ b/Src/auth/log.cpp @@ -0,0 +1,41 @@ +#include "api.h" +#include <api/syscb/callbacks/consolecb.h> +#include <time.h> +#include <strsafe.h> +void Log(const wchar_t *format, ...) +{ + wchar_t buffer[2048] = {0}; // hope it's big enough :) + va_list args; + va_start (args, format); + StringCbVPrintf(buffer, sizeof(buffer), format, args); + WASABI_API_SYSCB->syscb_issueCallback(SysCallback::CONSOLE, ConsoleCallback::DEBUGMESSAGE, 0, reinterpret_cast<intptr_t>(buffer)); + va_end(args); +} + +const wchar_t *MakeDateString(__time64_t convertTime) +{ + static wchar_t dest[2048]; + + SYSTEMTIME sysTime; + tm *newtime = _localtime64(&convertTime); + if (newtime) + { + sysTime.wYear = (WORD)(newtime->tm_year + 1900); + sysTime.wMonth = (WORD)(newtime->tm_mon + 1); + sysTime.wDayOfWeek = (WORD)newtime->tm_wday; + sysTime.wDay = (WORD)newtime->tm_mday; + sysTime.wHour = (WORD)newtime->tm_hour; + sysTime.wMinute = (WORD)newtime->tm_min; + sysTime.wSecond = (WORD)newtime->tm_sec; + sysTime.wMilliseconds = 0; + + GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &sysTime, NULL, dest, 2048); + + //wcsftime(expire_time, 63, L"%b %d, %Y", _localtime64(&convertTime)); + } + else + dest[0] = 0; + + return dest; +} + diff --git a/Src/auth/main.cpp b/Src/auth/main.cpp new file mode 100644 index 00000000..0cec0981 --- /dev/null +++ b/Src/auth/main.cpp @@ -0,0 +1,106 @@ +#include "api.h" +#include "../Agave/Component/ifc_wa5component.h" +#include <api/service/waservicefactory.h> +#include "../nu/Singleton.h" +#include "auth.h" + +EXTERN_C const GUID pngLoaderGUID = +{ 0x5e04fb28, 0x53f5, 0x4032, { 0xbd, 0x29, 0x3, 0x2b, 0x87, 0xec, 0x37, 0x25 } }; + + +class AuthComponent : public ifc_wa5component +{ +public: + void RegisterServices(api_service *service); + int RegisterServicesSafeModeOk(); + void DeregisterServices(api_service *service); +protected: + RECVS_DISPATCH; +}; + +api_service *WASABI_API_SVC=0; +api_application *WASABI_API_APP =0; +api_config *AGAVE_API_CONFIG = 0; +api_syscb *WASABI_API_SYSCB = 0; +api_winamp *WASABI_API_WINAMP = 0; +api_memmgr *WASABI_API_MEMMNGR = NULL; +svc_imageLoader *WASABI_API_PNGLOADER = NULL; + +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +static Auth auth; +static SingletonServiceFactory<api_auth, Auth> authFactory; + +template <class api_T> +void ServiceBuild(api_T *&api_t, GUID factoryGUID_t) +{ + if (WASABI_API_SVC) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t); + if (factory) + api_t = (api_T *)factory->getInterface(); + } +} + +template <class api_T> +void ServiceRelease(api_T *api_t, GUID factoryGUID_t) +{ + if (WASABI_API_SVC && api_t) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t); + if (factory) + factory->releaseInterface(api_t); + } + api_t = 0; +} + +void AuthComponent::RegisterServices(api_service *service) +{ + WASABI_API_SVC = service; + ServiceBuild(WASABI_API_APP, applicationApiServiceGuid); + ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID); + ServiceBuild(WASABI_API_SYSCB, syscbApiServiceGuid); + ServiceBuild(WASABI_API_WINAMP, winampApiGuid); + + ServiceBuild(WASABI_API_LNG, languageApiGUID); + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(hModule, authLangGUID); + + auth.Init(); + authFactory.Register(WASABI_API_SVC, &auth); +} + +int AuthComponent::RegisterServicesSafeModeOk() +{ + return 1; +} + +void AuthComponent::DeregisterServices(api_service *service) +{ + ServiceRelease(WASABI_API_APP, applicationApiServiceGuid); + ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID); + ServiceRelease(WASABI_API_SYSCB, syscbApiServiceGuid); + ServiceRelease(WASABI_API_WINAMP, winampApiGuid); + ServiceRelease(WASABI_API_LNG, languageApiGUID); + ServiceRelease(WASABI_API_MEMMNGR, memMgrApiServiceGuid); + ServiceRelease(WASABI_API_PNGLOADER, pngLoaderGUID); + + authFactory.Deregister(WASABI_API_SVC); + auth.Quit(); +} + +static AuthComponent authComponent; + +extern "C" __declspec(dllexport) ifc_wa5component *GetWinamp5SystemComponent() +{ + return &authComponent; +} + +#define CBCLASS AuthComponent +START_DISPATCH; +VCB(API_WA5COMPONENT_REGISTERSERVICES, RegisterServices) +CB(15, RegisterServicesSafeModeOk) +VCB(API_WA5COMPONENT_DEREEGISTERSERVICES, DeregisterServices) +END_DISPATCH; +#undef CBCLASS diff --git a/Src/auth/main.h b/Src/auth/main.h new file mode 100644 index 00000000..11d60bfa --- /dev/null +++ b/Src/auth/main.h @@ -0,0 +1,10 @@ +#pragma once + +#define OPENAUTH_DEVID "ao1Ve8gG_VGE9aNP" +//#define OPENAUTH_DEVID "co1-aGYgRvsTuOey" + +void Log(const wchar_t *format, ...); +const wchar_t *MakeDateString(__time64_t convertTime); + +/* decode.cpp */ +void UrlDecode(char *str);
\ No newline at end of file diff --git a/Src/auth/post.cpp b/Src/auth/post.cpp new file mode 100644 index 00000000..db755671 --- /dev/null +++ b/Src/auth/post.cpp @@ -0,0 +1,218 @@ +#include "api.h" +#include "../xml/obj_xml.h" +#include "api_auth.h" +#include "../nu/AutoChar.h" +#include "../jnetlib/api_httpget.h" +#include "ifc_authcallback.h" +#include <api/service/waservicefactory.h> +#include <strsafe.h> + + +static const GUID internetConfigGroupGUID = +{ + 0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c } +}; + + +#define USER_AGENT_SIZE (10 /*User-Agent*/ + 2 /*: */ + 6 /*Winamp*/ + 1 /*/*/ + 1 /*5*/ + 3/*.55*/ + 1 /*Null*/) +static void SetUserAgent(api_httpreceiver *http) +{ + char user_agent[USER_AGENT_SIZE] = {0}; + StringCchPrintfA(user_agent, USER_AGENT_SIZE, "User-Agent: Winamp/%S", WASABI_API_APP->main_getVersionNumString()); + http->addheader(user_agent); +} + + +#define HTTP_BUFFER_SIZE 8192 + +static int FeedXMLHTTP(api_httpreceiver *http, obj_xml *parser, bool *noData) +{ + char downloadedData[HTTP_BUFFER_SIZE] = {0}; + int xmlResult = API_XML_SUCCESS; + int downloadSize = http->get_bytes(downloadedData, HTTP_BUFFER_SIZE); + if (downloadSize) + { + xmlResult = parser->xmlreader_feed((void *)downloadedData, downloadSize); + *noData=false; + } + else + *noData = true; + + return xmlResult; +} + + +static int RunXMLDownload(api_httpreceiver *http, obj_xml *parser, ifc_authcallback *callback) +{ + int ret; + bool noData; + do + { + if (callback && callback->OnIdle()) + { + return AUTH_ABORT; + } + else if (!callback) + { + Sleep(50); + } + + ret = http->run(); + if (FeedXMLHTTP(http, parser, &noData) != API_XML_SUCCESS) + return AUTH_ERROR_PARSING_XML; + } + while (ret == HTTPRECEIVER_RUN_OK); + + // finish off the data + do + { + if (FeedXMLHTTP(http, parser, &noData) != API_XML_SUCCESS) + return AUTH_ERROR_PARSING_XML; + } while (!noData); + + parser->xmlreader_feed(0, 0); + if (ret != HTTPRECEIVER_RUN_ERROR) + return AUTH_SUCCESS; + else + return AUTH_CONNECTIONRESET; +} + + +int PostXML(const char *url, const char *post_data, obj_xml *parser, ifc_authcallback *callback) +{ + if (!parser) + return AUTH_NOPARSER; + + api_httpreceiver *http = 0; + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(httpreceiverGUID); + if (sf) + http = (api_httpreceiver *)sf->getInterface(); + + if (!http) + return AUTH_NOHTTP; + + int use_proxy = 1; + bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false); + if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3))) + use_proxy = 0; + + const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0; + size_t clen = strlen(post_data); + + http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL); + SetUserAgent(http); + + char clen_header[1024] = {0}; + StringCbPrintfA(clen_header, sizeof(clen_header), "Content-Length: %u", clen); + http->addheader(clen_header); + http->addheader("Content-Type: application/x-www-form-urlencoded; charset=UTF-8"); + if (callback && callback->OnConnecting()) + { + sf->releaseInterface(http); + return AUTH_ABORT; + } + http->connect(url, 0, "POST"); + + // POST the data + api_connection *connection = http->GetConnection(); + if (connection) + { + if (callback && callback->OnIdle()) + { + sf->releaseInterface(http); + return AUTH_ABORT; + } + else if (!callback) + { + Sleep(50); + } + + if (http->run() == -1) + goto connection_failed; + + if (callback && callback->OnSending()) + { + sf->releaseInterface(http); + return AUTH_ABORT; + } + + const char *dataIndex = post_data; + while (clen) + { + if (callback && callback->OnIdle()) + { + sf->releaseInterface(http); + return AUTH_ABORT; + } + else if (!callback) + { + Sleep(50); + } + + if (http->run() == -1) + goto connection_failed; + + size_t lengthToSend = min(clen, connection->GetSendBytesAvailable()); + if (lengthToSend) + { + connection->send(dataIndex, (int)lengthToSend); + dataIndex+=lengthToSend; + clen-=lengthToSend; + } + } + } + + // retrieve reply + if (callback && callback->OnReceiving()) + { + sf->releaseInterface(http); + return AUTH_ABORT; + } + + int ret; + do + { + if (callback && callback->OnIdle()) + { + sf->releaseInterface(http); + return AUTH_ABORT; + } + else if (!callback) + { + Sleep(50); + } + + ret = http->run(); + if (ret == -1) // connection failed + break; + + // ---- check our reply code ---- + int status = http->get_status(); + switch (status) + { + case HTTPRECEIVER_STATUS_CONNECTING: + case HTTPRECEIVER_STATUS_READING_HEADERS: + break; + + case HTTPRECEIVER_STATUS_READING_CONTENT: + { + int downloadError; + downloadError = RunXMLDownload(http, parser, callback); + sf->releaseInterface(http); + return downloadError; + } + break; + case HTTPRECEIVER_STATUS_ERROR: + default: + sf->releaseInterface(http); + return AUTH_404; + } + } + while (ret == HTTPRECEIVER_RUN_OK); + + +connection_failed: + sf->releaseInterface(http); + return AUTH_404; +} + diff --git a/Src/auth/resource.h b/Src/auth/resource.h new file mode 100644 index 00000000..c6c24b54 --- /dev/null +++ b/Src/auth/resource.h @@ -0,0 +1,108 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by auth.rc +// +#define IDD_LOGINBOX 101 +#define IDD_POPUP_AGREEMENT 102 +#define IDS_ERR_SUCCESS 103 +#define IDD_PAGE_CREDENTIALS 103 +#define IDS_ERR_404 104 +#define IDD_PAGE_ADDRESS 104 +#define IDS_ERR_TIMEOUT 105 +#define IDD_PAGE_INFO 105 +#define IDS_ERR_NOHTTP 106 +#define IDD_POPUP_PASSCODE 106 +#define IDS_ERR_NOPARSER 107 +#define IDD_POPUP_MESSAGE 107 +#define IDS_ERR_CONNECTIONRESET 108 +#define IDD_PAGE_EMPTY 108 +#define IDS_ERR_PARSING_XML 109 +#define IDR_MENU_TABCONTEXT 109 +#define IDS_ERR_NOT_AUTHORIZED 110 +#define IDD_PAGE_ERROR 110 +#define IDS_ERR_SECURID 111 +#define IDS_ERR_ABORT 112 +#define IDS_ERR_INVALIDCRED 113 +#define IDS_ERR_UNCONFIRMED 114 +#define IDS_ERR_UNEXPECTED 115 +#define IDS_ERR_USERNAME_EMPTY 116 +#define IDS_ERR_USERNAME_TOOSHORT 117 +#define IDS_ERR_USERNAME_TOOLONG 118 +#define IDS_ERR_USERNAME_BADFORMAT 119 +#define IDS_ERR_PASSWORD_EMPTY 120 +#define IDS_ERR_PASSWORD_TOOSHORT 121 +#define IDS_ERR_PASSWORD_TOOLONG 122 +#define IDS_ERR_PASSWORD_BADFORMAT 123 +#define IDS_ERR_PASSCODE_EMPTY 124 +#define IDS_ERR_PASSCODE_TOOSHORT 125 +#define IDS_ERR_PASSCODE_TOOLONG 126 +#define IDS_ERR_PASSCODE_BADFORMAT 127 +#define IDS_ERR_UNKNOWN 128 +#define IDS_LOGIN_FAILURE 129 +#define IDS_ERR_PASSCODE_INVALID 130 +#define IDS_WEBAUTH_CAPTION_TEMPLATE 131 +#define IDS_ERR_PASSCODE_BADLENGTH_TEMPLATE 132 +#define IDS_ERR_PASSCODE_ONLYDIGITS 133 +#define IDS_PASSCODE_EDIT_HINT 134 +#define IDS_STATUS_INITIALIZING 135 +#define IDS_STATUS_CONNECTING 136 +#define IDS_STATUS_PASSCODE_REQUIRED 137 +#define IDS_STATUS_RECEIVING 138 +#define IDS_STATUS_SENDING 139 +#define IDS_STATUS_ABORTING 140 +#define IDS_STATUS_USERACTION_REQUIRED 141 +#define IDS_STATUS_SUCCEEDED 142 +#define IDS_STATUS_FAILED 143 +#define IDS_STATUS_ABORTED 144 +#define IDS_STATUS_UPDATEBEGIN 145 +#define IDS_POPUP_AGREEMENT_TITLE 146 +#define IDS_POPUP_PASSCODE_TITLE 147 +#define IDS_TOSLINK_TEMPLATE 148 +#define IDS_PRIVACYLINK_TEMPLATE 149 +#define IDS_AND 150 +#define IDS_STATUS_AGREEMENT_REQUIRED 151 +#define IDS_BUTTON_CONTINUE 152 +#define IDS_BUTTON_YES 153 +#define IDS_BUTTON_NO 154 +#define IDS_PROVIDERUPDATE_TITLE 155 +#define IDS_PROVIDER_CHANGED 156 +#define IDS_PROVIDER_REMOVED 157 +#define IDS_LOGINTAB_MOREPROVIDERS 158 +#define IDS_ERR_OPENID_INVALIDURL 159 +#define IDC_TEXT 1001 +#define IDC_USERNAME 1001 +#define IDC_PASSWORD 1002 +#define IDC_CREATE_ACCOUNT 1004 +#define IDC_RECOVER_ACCOUNT 1005 +#define IDC_RECOVER_ACCOUNT2 1006 +#define IDC_USERNAME_LABEL 1009 +#define IDC_PASSWORD_LABEL 1010 +#define IDC_ADDRESS_EDIT 1011 +#define IDC_PASSCODE_EDIT 1011 +#define IDC_PASSCODE_HINT 1015 +#define IDC_ADDRESS_LABEL 1016 +#define IDC_STATUS 1018 +#define IDC_MESSAGE 1019 +#define IDC_CHECK_STATUS 1020 +#define IDC_PASSCODE_TITLE 1021 +#define IDC_CHECKNOW 1022 +#define IDR_POPUPBORDER_IMAGE 2000 +#define IDR_NOTIFIERICONS_IMAGE 2001 +#define IDR_SELECTIONFRAME_IMAGE 2002 +#define IDR_ARROW_IMAGE 2003 +#define ID_TABCONTEXT_UPDATEPROVIDERS 40001 +#define ID_TABCONTEXT_RESETORDER 40002 +#define ID_LOGINBOX_UPDATEPORIVDES 40003 +#define ID_LOGINBOX_UPDATEPROVIDERS 40004 +#define ID_LOGINTAB_RESETORDER 40005 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 111 +#define _APS_NEXT_COMMAND_VALUE 40006 +#define _APS_NEXT_CONTROL_VALUE 1023 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/auth/sha2.c b/Src/auth/sha2.c new file mode 100644 index 00000000..17971836 --- /dev/null +++ b/Src/auth/sha2.c @@ -0,0 +1,609 @@ +/* + * FILE: sha2.c + * AUTHOR: Aaron D. Gifford <me@aarongifford.com> + * + * Copyright (c) 2000-2001, Aaron D. Gifford + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */ +#include <assert.h> /* assert() */ +#include "sha2.h" + +/* + * ASSERT NOTE: + * Some sanity checking code is included using assert(). On my FreeBSD + * system, this additional code can be removed by compiling with NDEBUG + * defined. Check your own systems manpage on assert() to see how to + * compile WITHOUT the sanity checking code on your system. + * + * UNROLLED TRANSFORM LOOP NOTE: + * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform + * loop version for the hash transform rounds (defined using macros + * later in this file). Either define on the command line, for example: + * + * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c + * + * or define below: + * + * #define SHA2_UNROLL_TRANSFORM + * + */ + + +/*** SHA-256/384/512 Machine Architecture Definitions *****************/ +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivilent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including <sys/types.h> (which in turn includes + * <machine/endian.h> where the appropriate definitions are actually + * made). + */ +#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) +#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN +#endif + +/* + * Define the followingsha2_* types to types of the correct length on + * the native archtecture. Most BSD systems and Linux define u_intXX_t + * types. Machines with very recent ANSI C headers, can use the + * uintXX_t definintions from inttypes.h by defining SHA2_USE_INTTYPES_H + * during compile or in the sha.h header file. + * + * Machines that support neither u_intXX_t nor inttypes.h's uintXX_t + * will need to define these three typedefs below (and the appropriate + * ones in sha.h too) by hand according to their system architecture. + * + * Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t + * types and pointing out recent ANSI C support for uintXX_t in inttypes.h. + */ +#ifdef SHA2_USE_INTTYPES_H + +typedef uint8_t sha2_byte; /* Exactly 1 byte */ +typedef uint32_t sha2_word32; /* Exactly 4 bytes */ +typedef uint64_t sha2_word64; /* Exactly 8 bytes */ + +#else /* SHA2_USE_INTTYPES_H */ + +typedef u_int8_t sha2_byte; /* Exactly 1 byte */ +typedef u_int32_t sha2_word32; /* Exactly 4 bytes */ +typedef u_int64_t sha2_word64; /* Exactly 8 bytes */ + +#endif /* SHA2_USE_INTTYPES_H */ + + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +/* NOTE: Most of these are in sha2.h */ +#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) +#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) +#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) + + +/*** ENDIAN REVERSAL MACROS *******************************************/ +#if BYTE_ORDER == LITTLE_ENDIAN +#define REVERSE32(w,x) { \ + sha2_word32 tmp = (w); \ + tmp = (tmp >> 16) | (tmp << 16); \ + (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ +} + +#ifdef WIN32 + +#define REVERSE64(w,x) { \ + sha2_word64 tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00Ui64) >> 8) | \ + ((tmp & 0x00ff00ff00ff00ffUi64) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000Ui64) >> 16) | \ + ((tmp & 0x0000ffff0000ffffUi64) << 16); \ +} + +#else + +#define REVERSE64(w,x) { \ + sha2_word64 tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ + ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ + ((tmp & 0x0000ffff0000ffffULL) << 16); \ +} + +#endif /* WIN32 */ + +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +/* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ +#define ADDINC128(w,n) { \ + (w)[0] += (sha2_word64)(n); \ + if ((w)[0] < (n)) { \ + (w)[1]++; \ + } \ +} + +/* + * Macros for copying blocks of memory and for zeroing out ranges + * of memory. Using these macros makes it easy to switch from + * using memset()/memcpy() and using bzero()/bcopy(). + * + * Please define either SHA2_USE_MEMSET_MEMCPY or define + * SHA2_USE_BZERO_BCOPY depending on which function set you + * choose to use: + */ +#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY) +/* Default to memset()/memcpy() if no option is specified */ +#define SHA2_USE_MEMSET_MEMCPY 1 +#endif +#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY) +/* Abort with an error if BOTH options are defined */ +#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both! +#endif + +#ifdef SHA2_USE_MEMSET_MEMCPY +#define MEMSET_BZERO(p,l) memset((p), 0, (l)) +#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l)) +#endif +#ifdef SHA2_USE_BZERO_BCOPY +#define MEMSET_BZERO(p,l) bzero((p), (l)) +#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l)) +#endif + + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: The naming of R and S appears backwards here (R is a SHIFT and + * S is a ROTATION) because the SHA-256/384/512 description document + * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this + * same "backwards" definition. + */ +/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ +#define R(b,x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-256): */ +#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) +/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ +#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) + +/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Four of six logical functions used in SHA-256: */ +#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) +#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) +#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) +#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) + +/* Four of six logical functions used in SHA-384 and SHA-512: */ +#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) +#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) +#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) +#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) + +/*** INTERNAL FUNCTION PROTOTYPES *************************************/ +/* NOTE: These should not be accessed directly from outside this + * library -- they are intended for private internal visibility/use + * only. + */ +void SHA512_Last(SHA512_CTX*); +void SHA256_Transform(SHA256_CTX*, const sha2_word32*); +void SHA512_Transform(SHA512_CTX*, const sha2_word64*); + + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ +/* Hash constant words K for SHA-256: */ +const static sha2_word32 K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Initial hash value H for SHA-256: */ +const static sha2_word32 sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL +}; + +/* + * Constant used by SHA256/384/512_End() functions for converting the + * digest to a readable hexadecimal character string: + */ +static const char *sha2_hex_digits = "0123456789abcdef"; + + +/*** SHA-256: *********************************************************/ +void SHA256_Init(SHA256_CTX* context) { + if (context == (SHA256_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH); + MEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH); + context->bitcount = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#if BYTE_ORDER == LITTLE_ENDIAN + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ + REVERSE32(*data++, W256[j]); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ + K256[j] + W256[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + + +#else /* BYTE_ORDER == LITTLE_ENDIAN */ + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ + K256[j] + (W256[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +#define ROUND256(a,b,c,d,e,f,g,h) \ + s0 = W256[(j+1)&0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j+14)&0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) { + sha2_word32 a, b, c, d, e, f, g, h, s0, s1; + sha2_word32 T1, *W256; + int j; + + W256 = (sha2_word32*)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a,b,c,d,e,f,g,h); + ROUND256_0_TO_15(h,a,b,c,d,e,f,g); + ROUND256_0_TO_15(g,h,a,b,c,d,e,f); + ROUND256_0_TO_15(f,g,h,a,b,c,d,e); + ROUND256_0_TO_15(e,f,g,h,a,b,c,d); + ROUND256_0_TO_15(d,e,f,g,h,a,b,c); + ROUND256_0_TO_15(c,d,e,f,g,h,a,b); + ROUND256_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds to 64: */ + do { + ROUND256(a,b,c,d,e,f,g,h); + ROUND256(h,a,b,c,d,e,f,g); + ROUND256(g,h,a,b,c,d,e,f); + ROUND256(f,g,h,a,b,c,d,e); + ROUND256(e,f,g,h,a,b,c,d); + ROUND256(d,e,f,g,h,a,b,c); + ROUND256(c,d,e,f,g,h,a,b); + ROUND256(b,c,d,e,f,g,h,a); + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) { + sha2_word32 a, b, c, d, e, f, g, h; + sha2_word32 T1, T2, *W256; + int j; + + W256 = (sha2_word32*)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { +#if BYTE_ORDER == LITTLE_ENDIAN + /* Copy data while converting to host byte order */ + REVERSE32(*data++,W256[j]); + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; +#else /* BYTE_ORDER == LITTLE_ENDIAN */ + /* Apply the SHA-256 compression function to update a..h with copy */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + sha2_word32 s0, s1; + /* Part of the message block expansion: */ + s0 = W256[(j+1)&0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j+14)&0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) { + unsigned int freespace, usedspace; + + if (len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0); + + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; + SHA256_Transform(context, (sha2_word32*)context->buffer); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(&context->buffer[usedspace], data, len); + context->bitcount += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA256_Transform(context, (sha2_word32*)data); + context->bitcount += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + context->bitcount += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) { + sha2_word32 *d = (sha2_word32*)digest; + unsigned int usedspace; + + /* Sanity check: */ + assert(context != (SHA256_CTX*)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (sha2_byte*)0) { + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + REVERSE64(context->bitcount,context->bitcount); +#endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + MEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA256_BLOCK_LENGTH) { + MEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA256_Transform(context, (sha2_word32*)context->buffer); + + /* And set-up for the last transform: */ + MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH); + } + } else { + /* Set-up for the last transform: */ + MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Set the bit count: */ + *(sha2_word64*)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount; + + /* Final transform: */ + SHA256_Transform(context, (sha2_word32*)context->buffer); + +#if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE32(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } +#else + MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH); +#endif + } + + /* Clean up state data: */ + MEMSET_BZERO(context, sizeof(context)); + usedspace = 0; +} + +char *SHA256_End(SHA256_CTX* context, char buffer[]) { + sha2_byte digest[SHA256_DIGEST_LENGTH], *d = digest; + + /* Sanity check: */ + assert(context != (SHA256_CTX*)0); + + if (buffer != (char*)0) { + int i = 0; + SHA256_Final(digest, context); + for (; i < SHA256_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + MEMSET_BZERO(context, sizeof(context)); + } + MEMSET_BZERO(digest, SHA256_DIGEST_LENGTH); + return buffer; +} + +char* SHA256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) { + SHA256_CTX context; + + SHA256_Init(&context); + SHA256_Update(&context, data, len); + return SHA256_End(&context, digest); +} + diff --git a/Src/auth/sha2.h b/Src/auth/sha2.h new file mode 100644 index 00000000..28fde191 --- /dev/null +++ b/Src/auth/sha2.h @@ -0,0 +1,209 @@ +/* + * FILE: sha2.h + * AUTHOR: Aaron D. Gifford <me@aarongifford.com> + * + * Copyright (c) 2000-2001, Aaron D. Gifford + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef __SHA2_H__ +#define __SHA2_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +// Brandon: Added to cover Windows lack of these types + +#if defined (_WIN64) || (_WIN32) + +#define BYTE_ORDER LITTLE_ENDIAN + +typedef unsigned __int8 u_int8_t; +typedef unsigned __int32 u_int32_t; +typedef unsigned __int64 u_int64_t; + +#include <string.h> + +#endif + +/* + * Import u_intXX_t size_t type definitions from system headers. You + * may need to change this, or define these things yourself in this + * file. + */ +#include <sys/types.h> + +#ifdef SHA2_USE_INTTYPES_H + +#include <inttypes.h> + +#endif /* SHA2_USE_INTTYPES_H */ + + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +#define SHA256_BLOCK_LENGTH 64 +#define SHA256_DIGEST_LENGTH 32 +#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) +#define SHA384_BLOCK_LENGTH 128 +#define SHA384_DIGEST_LENGTH 48 +#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) +#define SHA512_BLOCK_LENGTH 128 +#define SHA512_DIGEST_LENGTH 64 +#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) + + +/*** SHA-256/384/512 Context Structures *******************************/ +/* NOTE: If your architecture does not define either u_intXX_t types or + * uintXX_t (from inttypes.h), you may need to define things by hand + * for your system: + */ +#if 0 +typedef unsigned char u_int8_t; /* 1-byte (8-bits) */ +typedef unsigned int u_int32_t; /* 4-bytes (32-bits) */ +typedef unsigned long long u_int64_t; /* 8-bytes (64-bits) */ +#endif +/* + * Most BSD systems already define u_intXX_t types, as does Linux. + * Some systems, however, like Compaq's Tru64 Unix instead can use + * uintXX_t types defined by very recent ANSI C standards and included + * in the file: + * + * #include <inttypes.h> + * + * If you choose to use <inttypes.h> then please define: + * + * #define SHA2_USE_INTTYPES_H + * + * Or on the command line during compile: + * + * cc -DSHA2_USE_INTTYPES_H ... + */ +#ifdef SHA2_USE_INTTYPES_H + +typedef struct _SHA256_CTX { + uint32_t state[8]; + uint64_t bitcount; + uint8_t buffer[SHA256_BLOCK_LENGTH]; +} SHA256_CTX; +typedef struct _SHA512_CTX { + uint64_t state[8]; + uint64_t bitcount[2]; + uint8_t buffer[SHA512_BLOCK_LENGTH]; +} SHA512_CTX; + +#else /* SHA2_USE_INTTYPES_H */ + +typedef struct _SHA256_CTX { + u_int32_t state[8]; + u_int64_t bitcount; + u_int8_t buffer[SHA256_BLOCK_LENGTH]; +} SHA256_CTX; +typedef struct _SHA512_CTX { + u_int64_t state[8]; + u_int64_t bitcount[2]; + u_int8_t buffer[SHA512_BLOCK_LENGTH]; +} SHA512_CTX; + +#endif /* SHA2_USE_INTTYPES_H */ + +typedef SHA512_CTX SHA384_CTX; + + +/*** SHA-256/384/512 Function Prototypes ******************************/ +#ifndef NOPROTO +#ifdef SHA2_USE_INTTYPES_H + +void SHA256_Init(SHA256_CTX *); +void SHA256_Update(SHA256_CTX*, const uint8_t*, size_t); +void SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*); +char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]); +char* SHA256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]); + +void SHA384_Init(SHA384_CTX*); +void SHA384_Update(SHA384_CTX*, const uint8_t*, size_t); +void SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*); +char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]); +char* SHA384_Data(const uint8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]); + +void SHA512_Init(SHA512_CTX*); +void SHA512_Update(SHA512_CTX*, const uint8_t*, size_t); +void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*); +char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]); +char* SHA512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]); + +#else /* SHA2_USE_INTTYPES_H */ + +void SHA256_Init(SHA256_CTX *); +void SHA256_Update(SHA256_CTX*, const u_int8_t*, size_t); +void SHA256_Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*); +char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]); +char* SHA256_Data(const u_int8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]); + +void SHA384_Init(SHA384_CTX*); +void SHA384_Update(SHA384_CTX*, const u_int8_t*, size_t); +void SHA384_Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*); +char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]); +char* SHA384_Data(const u_int8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]); + +void SHA512_Init(SHA512_CTX*); +void SHA512_Update(SHA512_CTX*, const u_int8_t*, size_t); +void SHA512_Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*); +char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]); +char* SHA512_Data(const u_int8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]); + +#endif /* SHA2_USE_INTTYPES_H */ + +#else /* NOPROTO */ + +void SHA256_Init(); +void SHA256_Update(); +void SHA256_Final(); +char* SHA256_End(); +char* SHA256_Data(); + +void SHA384_Init(); +void SHA384_Update(); +void SHA384_Final(); +char* SHA384_End(); +char* SHA384_Data(); + +void SHA512_Init(); +void SHA512_Update(); +void SHA512_Final(); +char* SHA512_End(); +char* SHA512_Data(); + +#endif /* NOPROTO */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __SHA2_H__ */ + diff --git a/Src/auth/version.rc2 b/Src/auth/version.rc2 new file mode 100644 index 00000000..e51a415e --- /dev/null +++ b/Src/auth/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION WINAMP_PRODUCTVER + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp 5.x System Component" + VALUE "FileVersion", STR_WINAMP_PRODUCTVER + VALUE "InternalName", "auth.w5s" + VALUE "LegalCopyright", "Copyright © 2006-2019 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "auth.w5s" + VALUE "ProductName", "Winamp Authentication Service" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END |