diff options
Diffstat (limited to 'gui/patternpassword.cpp')
-rw-r--r-- | gui/patternpassword.cpp | 469 |
1 files changed, 469 insertions, 0 deletions
diff --git a/gui/patternpassword.cpp b/gui/patternpassword.cpp new file mode 100644 index 000000000..ab446b9f0 --- /dev/null +++ b/gui/patternpassword.cpp @@ -0,0 +1,469 @@ +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#include <stdlib.h> + +#include <string> +#include <sstream> + +extern "C" { +#include "../twcommon.h" +} +#include "../minuitwrp/minui.h" +#include "../twrp-functions.hpp" +#include "rapidxml.hpp" +#include "objects.hpp" + +GUIPatternPassword::GUIPatternPassword(xml_node<>* node) + : GUIObject(node) +{ + xml_attribute<>* attr; + xml_node<>* child; + + // 3x3 is the default. + mGridSize = 3; + mDots = new Dot[mGridSize * mGridSize]; + mConnectedDots = new int[mGridSize * mGridSize]; + + ResetActiveDots(); + mTrackingTouch = false; + mNeedRender = true; + + ConvertStrToColor("blue", &mDotColor); + ConvertStrToColor("white", &mActiveDotColor); + ConvertStrToColor("blue", &mLineColor); + + mDotImage = mActiveDotImage = NULL; + mDotCircle = mActiveDotCircle = NULL; + mDotRadius = 50; + mLineWidth = 35; + + mAction = NULL; + mUpdate = 0; + + if (!node) + return; + + LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH, &mPlacement); + + mAction = new GUIAction(node); + + child = FindNode(node, "dot"); + if(child) + { + mDotColor = LoadAttrColor(child, "color", mDotColor); + mActiveDotColor = LoadAttrColor(child, "activecolor", mActiveDotColor); + mDotRadius = LoadAttrIntScaleX(child, "radius", mDotRadius); + + mDotImage = LoadAttrImage(child, "image"); + mActiveDotImage = LoadAttrImage(child, "activeimage"); + } + + child = FindNode(node, "line"); + if(child) + { + mLineColor = LoadAttrColor(child, "color", mLineColor); + mLineWidth = LoadAttrIntScaleX(child, "width", mLineWidth); + } + + child = FindNode(node, "data"); + if(child) + mPassVar = LoadAttrString(child, "name", ""); + + child = FindNode(node, "size"); + if(child) { + mSizeVar = LoadAttrString(child, "name", ""); + + // Use the configured default, if set. + size_t size = LoadAttrInt(child, "default", mGridSize); + Resize(size); + } + + if(!mDotImage || !mDotImage->GetResource() || !mActiveDotImage || !mActiveDotImage->GetResource()) + { + mDotCircle = gr_render_circle(mDotRadius, mDotColor.red, mDotColor.green, mDotColor.blue, mDotColor.alpha); + mActiveDotCircle = gr_render_circle(mDotRadius/2, mActiveDotColor.red, mActiveDotColor.green, mActiveDotColor.blue, mActiveDotColor.alpha); + } + else + mDotRadius = mDotImage->GetWidth()/2; + + SetRenderPos(mRenderX, mRenderY, mRenderW, mRenderH); +} + +GUIPatternPassword::~GUIPatternPassword() +{ + delete mDotImage; + delete mActiveDotImage; + delete mAction; + + delete[] mDots; + delete[] mConnectedDots; + + if(mDotCircle) + gr_free_surface(mDotCircle); + + if(mActiveDotCircle) + gr_free_surface(mActiveDotCircle); +} + +void GUIPatternPassword::ResetActiveDots() +{ + mConnectedDotsLen = 0; + mCurLineX = mCurLineY = -1; + for(size_t i = 0; i < mGridSize * mGridSize; ++i) + mDots[i].active = false; +} + +int GUIPatternPassword::SetRenderPos(int x, int y, int w, int h) +{ + mRenderX = x; + mRenderY = y; + + if (w || h) + { + mRenderW = w; + mRenderH = h; + + mAction->SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH); + SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH); + } + + CalculateDotPositions(); + return 0; +} + +void GUIPatternPassword::CalculateDotPositions(void) +{ + const int num_gaps = mGridSize - 1; + const int step_x = (mRenderW - mDotRadius*2) / num_gaps; + const int step_y = (mRenderH - mDotRadius*2) / num_gaps; + int x = mRenderX; + int y = mRenderY; + + /* Order is important for keyphrase generation: + * + * 0 1 2 3 ... n-1 + * n n+1 n+2 n+3 ... 2n-1 + * 2n 2n+1 2n+2 2n+3 ... 3n-1 + * 3n 3n+1 3n+2 3n+3 ... 4n-1 + * : : : : + * n*n-1 + */ + + for(size_t r = 0; r < mGridSize; ++r) + { + for(size_t c = 0; c < mGridSize; ++c) + { + mDots[mGridSize*r + c].x = x; + mDots[mGridSize*r + c].y = y; + x += step_x; + } + x = mRenderX; + y += step_y; + } +} + +int GUIPatternPassword::Render(void) +{ + if(!isConditionTrue()) + return 0; + + gr_color(mLineColor.red, mLineColor.green, mLineColor.blue, mLineColor.alpha); + for(size_t i = 1; i < mConnectedDotsLen; ++i) { + const Dot& dp = mDots[mConnectedDots[i-1]]; + const Dot& dc = mDots[mConnectedDots[i]]; + gr_line(dp.x + mDotRadius, dp.y + mDotRadius, dc.x + mDotRadius, dc.y + mDotRadius, mLineWidth); + } + + if(mConnectedDotsLen > 0 && mTrackingTouch) { + const Dot& dc = mDots[mConnectedDots[mConnectedDotsLen-1]]; + gr_line(dc.x + mDotRadius, dc.y + mDotRadius, mCurLineX, mCurLineY, mLineWidth); + } + + for(size_t i = 0; i < mGridSize * mGridSize; ++i) { + if(mDotCircle) { + gr_blit(mDotCircle, 0, 0, gr_get_width(mDotCircle), gr_get_height(mDotCircle), mDots[i].x, mDots[i].y); + if(mDots[i].active) { + gr_blit(mActiveDotCircle, 0, 0, gr_get_width(mActiveDotCircle), gr_get_height(mActiveDotCircle), mDots[i].x + mDotRadius/2, mDots[i].y + mDotRadius/2); + } + } else { + if(mDots[i].active) { + gr_blit(mActiveDotImage->GetResource(), 0, 0, mActiveDotImage->GetWidth(), mActiveDotImage->GetHeight(), + mDots[i].x + (mDotRadius - mActiveDotImage->GetWidth()/2), mDots[i].y + (mDotRadius - mActiveDotImage->GetHeight()/2)); + } else { + gr_blit(mDotImage->GetResource(), 0, 0, mDotImage->GetWidth(), mDotImage->GetHeight(), mDots[i].x, mDots[i].y); + } + } + } + return 0; +} + +int GUIPatternPassword::Update(void) +{ + if(!isConditionTrue()) + return 0; + + int res = mNeedRender ? 2 : 1; + mNeedRender = false; + return res; +} + +void GUIPatternPassword::Resize(size_t n) { + if(mGridSize == n) + return; + + delete[] mDots; + delete[] mConnectedDots; + + mGridSize = n; + mDots = new Dot[n*n]; + mConnectedDots = new int[n*n]; + + ResetActiveDots(); + CalculateDotPositions(); + mTrackingTouch = false; + mNeedRender = true; +} + +static int pow(int x, int i) +{ + int result = 1; + if (i<0) + return 0; + while(i-- > 0) + result *= x; + return result; +} + +static bool IsInCircle(int x, int y, int ox, int oy, int r) +{ + return pow(x - ox, 2) + pow(y - oy, 2) <= pow(r, 2); +} + +int GUIPatternPassword::InDot(int x, int y) +{ + for(size_t i = 0; i < mGridSize * mGridSize; ++i) { + if(IsInCircle(x, y, mDots[i].x + mDotRadius, mDots[i].y + mDotRadius, mDotRadius*3)) + return i; + } + return -1; +} + +bool GUIPatternPassword::DotUsed(int dot_idx) +{ + for(size_t i = 0; i < mConnectedDotsLen; ++i) { + if(mConnectedDots[i] == dot_idx) + return true; + } + return false; +} + +void GUIPatternPassword::ConnectDot(int dot_idx) +{ + if(mConnectedDotsLen >= mGridSize * mGridSize) + { + LOGERR("mConnectedDots in GUIPatternPassword has overflown!\n"); + return; + } + + mConnectedDots[mConnectedDotsLen++] = dot_idx; + mDots[dot_idx].active = true; +} + +void GUIPatternPassword::ConnectIntermediateDots(int next_dot_idx) +{ + if(mConnectedDotsLen == 0) + return; + + const int prev_dot_idx = mConnectedDots[mConnectedDotsLen-1]; + + int px = prev_dot_idx % mGridSize; + int py = prev_dot_idx / mGridSize; + + int nx = next_dot_idx % mGridSize; + int ny = next_dot_idx / mGridSize; + + /* + * We connect all dots that are in a straight line between the previous dot + * and the next one. This is simple for 3x3, but is more complicated for + * larger grids. + * + * Weirdly, Android doesn't do the logical thing when it comes to connecting + * dots between two points. Rather than simply adding all points that lie + * on the line between the start and end points, it instead only connects + * dots that are adjacent in only three directions -- horizontal, vertical + * and diagonal (45°). + * + * So we can just iterate over the correct axes, taking care to ensure that + * the order in which the intermediate points are added to the pattern is + * correct. + */ + + int x = px; + int y = py; + + int Dx = (nx > px) ? 1 : -1; + int Dy = (ny > py) ? 1 : -1; + + // Vertical lines. + if(px == nx) + Dx = 0; + + // Horizontal lines. + else if(py == ny) + Dy = 0; + + // Diagonal lines (|∆x| = |∆y|). + else if(abs(px - nx) == abs(py - ny)) + ; + + // No valid intermediate dots. + else + return; + + // Iterate along axis, adding dots in the correct order. + while((Dy == 0 || y != ny - Dy) && (Dx == 0 || x != nx - Dx)) { + x += Dx; + y += Dy; + + int idx = mGridSize * y + x; + if(!DotUsed(idx)) + ConnectDot(idx); + } +} + +int GUIPatternPassword::NotifyTouch(TOUCH_STATE state, int x, int y) +{ + if(!isConditionTrue()) + return -1; + + switch (state) + { + case TOUCH_START: + { + const int dot_idx = InDot(x, y); + if(dot_idx == -1) + break; + + mTrackingTouch = true; + ResetActiveDots(); + ConnectDot(dot_idx); + DataManager::Vibrate("tw_button_vibrate"); + mCurLineX = x; + mCurLineY = y; + mNeedRender = true; + break; + } + case TOUCH_DRAG: + { + if(!mTrackingTouch) + break; + + const int dot_idx = InDot(x, y); + if(dot_idx != -1 && !DotUsed(dot_idx)) + { + ConnectIntermediateDots(dot_idx); + ConnectDot(dot_idx); + DataManager::Vibrate("tw_button_vibrate"); + } + + mCurLineX = x; + mCurLineY = y; + mNeedRender = true; + break; + } + case TOUCH_RELEASE: + { + if(!mTrackingTouch) + break; + + mNeedRender = true; + mTrackingTouch = false; + PatternDrawn(); + ResetActiveDots(); + break; + } + default: + break; + } + return 0; +} + +int GUIPatternPassword::NotifyVarChange(const std::string& varName, const std::string& value) +{ + if(!isConditionTrue()) + return 0; + + if(varName == mSizeVar) { + Resize(atoi(value.c_str())); + mUpdate = true; + } + return 0; +} + +static unsigned int getSDKVersion(void) { + unsigned int sdkver = 23; + string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk"); + if (!sdkverstr.empty()) { + sdkver = (unsigned int)strtoull(sdkverstr.c_str(), NULL, 10); + sdkver = (sdkver != 0) ? sdkver : 23; + } + LOGINFO("sdk version is %u\n", sdkver); + return sdkver; +} + +std::string GUIPatternPassword::GeneratePassphrase() +{ + char pattern[mConnectedDotsLen]; + for(size_t i = 0; i < mConnectedDotsLen; i++) { + pattern[i] = (char) mConnectedDots[i]; + } + + std::stringstream pass; + char buffer[3] = {0}; + + if ((mGridSize == 3) || (getSDKVersion() >= 23)) { + // Marshmallow uses a consistent method + for (size_t i = 0; i < mConnectedDotsLen; i++) { + buffer[0] = (pattern[i] & 0xff) + '1'; + pass << std::string(buffer); + } + } else { + /* + * Okay, rant time for pre-Marshmallow ROMs. + * It turns out that Android and CyanogenMod have *two* separate methods + * for generating passphrases from patterns. This is a legacy issue, as + * Android only supports 3x3 grids, and so we need to support both. + * Luckily, CyanogenMod is in the same boat as us and needs to support + * Android's 3x3 encryption style. + * + * In order to generate a 3x3 passphrase, add 1 to each dot index + * and concatenate the string representation of the integers. No + * padding should be added. + * + * For *all* other NxN passphrases (until a 16x16 grid comes along), + * they are generated by taking "%.2x" for each dot index and + * concatenating the results (without adding 1). + */ + for (size_t i = 0; i < mConnectedDotsLen; i++) { + snprintf(buffer, 3, "%.2x", pattern[i] & 0xff); + pass << std::string(buffer); + } + } + + return pass.str(); +} + +void GUIPatternPassword::PatternDrawn() +{ + if(!mPassVar.empty() && mConnectedDotsLen > 0) + DataManager::SetValue(mPassVar, GeneratePassphrase()); + + if(mAction) + mAction->doActions(); +} |