ñÃ)ŠÒì?]<Ž7v€X˜3Ç
1'ÛgO…1Á{Á’Œ˜n0êc·äV!3Îý˜„r� oÁ<í4ˆ×¢öú§;¶d:xÎq[Ð‘qAÕ¸…ý§ü&d:Ê�á3Ê×W«k»"éâ8Y«ˆ	®ùõoË‡
×+ýäˆ”ÞtÙÓŸüÇ*DœçÀï'ã‡’hÑ‚kš®}IÅ…�dÃt]é§/ïE%Ú¢ÇÇPþõa'EÁ¶ý†Ž9bÜ…Þ×ZWŠ$˜cj/hn»dù!ß©6É”áÓzÁcï½_·ÜÚÞÏ‚™þi)ëõ’À<Ì<eðfÑÄßEûÁ´N{�éÌ°ÿÛ.ÆóümgKÛb}œå‰$ÒqÆÄÄÑžxeã'À__†X§ã„IoÕ.zªü0£“uÞ­�`è™¼û£ÁèòÍ_¥µ;FF“î�«ð:ëÅî¹aì¨•�B+$…+ýØ™™.�ƒ„"@”ì4GšOäÿq© 2}ªÐK¯÷·P@�cå<âØ
sCU/w1Ž bÂŒ´h¯¢�}çòÈ½»¿²0˜Qâ–­YÌi¾àÈ~á†”SQrÉö5åWe_ËàžÕ&ŠÍM÷ñ”ß2ÓL±¨–ŒUÜ�Ö`µ#@ÏC	�•î¾uA·Î~Ý,ÌËI®Pœ*ll¯µLaÞßž‡ RÂ²Ðc>g�”/;¾	Üq6 µ)Ã(rÂKs‹²@Ý7À•‰×TjøKƒýþšXs¡�Ñè¼–¤¹2MO•;?rSz°gíaa±jªÒá)wæå±põWYÒÝþêV©  žè+®û�Š•+}.—Á"‘ŒF/¿‘˜àsWáJ_Å–ii;Ë$žf÷#Kæð÷Œ;äô­9‚?ƒîæ0N_ïUS�ˆŠ¬ç=8¤ÍA„¾)ÂV ôà±}4p%<C4�E$/! l)Z²¢ @óŸVX „<¦÷d6	¯Uc}I.vîäoiÖá#iJ9ª6M;3nÐÇA‹\õðÏÓ„H-™öüYt]?|Æß¹µ Í�Ô»õ‹,ùÿ”äL™§Qâ<}i‹³ÙÝ¯Tþ“øUÎ%¤B o\FažŒM!ñ…gäs Õ4Ëœr¤Ú¼ÁåC�È´BÖ`ïüYt]?|Æß¹µ Í�Ô»õ‹,ùÿ”äL™§â‚ fÓžšó`\7\”¹~ŸšÚß9}„oMˆx÷ÃD:ós3#Ùx Òþ»vÝ"±’0^hqdN+�¨½°ðhÔÏM["<³úAJ÷!«ý–õê
oL¹mÏŒ*‚…Â×~ºõµú“úUnS)è@8/JäˆYç•;Bô²Tõ‡Ì0Â¦ð‹BVß:ŒM„‚WŸ‡ó!AØY’³o Þ¬ò-¦3KN6¢ ±!rÐ™a£¿w8‡)[êÆj§ö¹Ô¨£	ú½¦2þ3QˆG¤‚4J«‰o?þ¡'{wUÒêvõ¯,ÏØ«ùy@êöBÖmF£e$&®FþÞoš»Ž6ºÞŠ‚ w.t°þò‹„œraÅx‰¡¶×EçØ|Ü9x7xÙº¥Ì±O×�¨•XìB5[;¶õiˆ&.èG’�Ô`º˜áwa‰pÃV–¾¿ÃA Ç×-ÒŽðg`Þ÷5Ï™N÷€Éï€¿ÿ±úíQûöš5Bye«e“�gÝq8ù
Eµãkøf3ÍË¿¥A#^YkÀå»ž:r¸gÞ™Þñˆg”ïÈ2%%Þá+Á€ƒuÍU»Áq¦ëµ>J"Îè·úTˆ…¬Õ{Œ×§ïP�0ÈöOÅR0˜0gE?ø1HDÈmßÛ¤†Eç9Øè!;E(l++ŒÒ;DÇ~‚[%”LÇØ3Qµ5?Í›í’!]ñŠQ€°Ýù6a¦å—·B	¬$qr¡u¼GHœbI¼CãöwVC[Üäå6¨Õ`ÿ–ã…]z�VŒ&´Z 73ÇûˆSý Õ‰¢åÇèµZqzûf¨ÒžF$e}µ/*²j•Q¨/çªÌŸ!áËzvæ`Û)´!7~—Ád_¾Î4ÃÄ·ÈÃßRjL«Ä8áÞO¬Šƒo(ÙãŽ½×/Ä�—!ôa$j|¥P8m)Ÿ[¡!Ÿ�wÛ0Ž3Ê:OXgí=C¸Øÿä®y=kCóyXÁ™£\FLB‚Š¥‚��tS ßìû¦†1w¦vÙÆ­¨„l—á:Ûìí™'Ðx¸Ô0‡Î#Žãßì¥ChOi�*ø'åa·´~ÿ­e‚{Ò 2Ñ¢˜§7(9j�'AÂ}uŸ%»e²_œJ='Œð¢Œû3ìa.;ãGu“™[¾oª6  c#«/ºM°é2 óÍ"'FÑ+_Ó/µ|`¼!y(ÓEONŽ	úñë$R®4à�WªA“­Ã±G–dð3Â�•�Ÿtr½ÀÃ°{r|;¥ƒ}.&˜ÓØûªß_Md§\2˜+ÿ�$J‡ÿÙ†#=œ¾+<™ÜJÔå"SÐo)_y-–Y‰‰»œý£ª_O‡•ÛLÇ.¾7˜,'�Sþ�7Ð•wüÃ“Í5…Äõ›#§i‹ÚÃïãOO °2ØmE˜ªÕ‰tí7«Ò¯ÍÿC�5Ë�,Eî#B<�.±µåº‡¹-¸%¢Ãª+Î…ÚÊ›Ï £¯‡ñlõù‚P*Ž1NÌ—¿êÓ¤Tsdp-ëmä/{¨Œ£Dµ±âÚ Å‹™F-BÓY)é®vrzdÍ�{6´iÉ¼ƒ½ÿäˆY¶Y7¢È>ôÕ˜Ì~mÕÃ>TŽ!wÇ|»ÁX¸ûÃ÷Ø­eûß¤Ç|Û¬±oç•›§iÁØàGîÒ¡¡1X7w®f�PßD4™ùê  ÿ€—§ë×›1¦yÏ)hðÔ¯k×ÔçÑÃÈ üÀ��.þ[t¦fŸ“¶_‹H‡|¦8L°˜ŸÑÒ6-dB}'ç—ÀoRÄh�âí[ª­éÃƒ4¨­�øâ>‰o@±XÑÙ©#©öOjÈ
u9kW}ƒ¨r×�YÉ¿4#"ÖL5Â~<o[n­¸Rc@Ìÿ²ú{YZ¿çT\nZ †íOÓî¿­¾@f¯®
:C‰†o$-ý}W¢1Õ¯Ÿÿ>Žÿ|�´Û} é�ÖóØ3äý ‹µ
Ý…�zÇ¥äÆë©DõÊø§Tÿô˜Pà|»æÔ‘Š*»àœãe‰ÊùäÕà²WRï‚‚;ðnòI[a»wd¦•o�üuÙŽwzÛä«¢û$Ú1äï$õ—=ç­Ã°6›™;=åÈR4‡AÝ/fh(Å/ä<]fØŽO€¼÷	ÈÕp¡NÅ„RP¹¡PñPN2ˆs°$l3…áûbñp¡ë| 0IÎ´-æ€yFþÄ¦t_,�^s]5“Ì›Î”�n†7ÒÆ1µ„<è¥s­¡*Ž¯@�çw2V¹�ï€Ji§¹c�–ÐžjãvGzˆùì•b5ÉÑŠ+Ë¿ý¤»n�kdç¶Ñ
õ!©»T5i.ZÄNçØˆ+WÍ?`ªrè¢€fòä	ˆÉ†béœâ·Â€Ò`òw&œ¦“î,){„Î™dÃqå€��Í°_Áû¹NØ\¬Gnå‚Úä�J”;†
nóð|Ë-ìZ�ÙÒÛ®¿‘×,Ëé]Ù¦î×{:“îégâEÁ]äZ÷|ÈÍ:D'€wáyy7‰�ýZÿGod�ªCnÒóüeŒÀú¢$`tD@›}¢õíùq&¡²)Œˆ“ ‹ª+*Ea–¹NB†ú~(ßÀÍ~4|ŠÉ	
w>Œ*cËS& =£kÑ*vËd-¥–o‚³øŒÙ†Ù¤KŒß¤—tž±j›ÂÔ=—˜ŒîGwö§SýÐ|b3ù×ùžû�„…¡õŽ£ÿ^0Í¥GØ3îÿûc§0¹9)ÈÏÒ‘	óõèÎQèÏ¢&’§·èÛbW8�m<sLÁflxf9íæ”’´ÛÛŸXÉ÷nàX.`âÂ@æ	¾a%”ùÛL»ßy àv2 ;¿F4âæ¡1>ÌÞù™gA3ÇIJþ‘{/{W£Nu?Ç�¨¨�ý£^µù‰‹�¤â<Û+j	R¯CõçÛ…3„+ÙW(²»zJÝYˆë‘Slƒš¥ja‹K'JHãóŠQq«¸%±7abûà»¬Vc2\!™'Ä×r’ÃŠ´<–™Ë8? uÃ]¬¨Á"ž#KCá`ïP°2ÕuNú´/zúnFÓºÿ�þŸ`1 Kj"#öh=eï©[¾Ÿ‚>$hzsTCðÝ“’NãoÓ”Ì6!ÜEQXS­Üm²	L¡%+ÈÄyñ¼ñÀ“8€´)b’xuÐ±OÈàœb`’Zõb¿^
˜~ Þ.ßðƒ÷øç9
ø™ú�%/x–úäàé41A{cöÔÞãü@˜‘ "¼ª­éÃƒ4¨­�øâ>‰ÄùdŠæb~êV¿,®p6_8_³†¨ö‹n=rLI?;Ù$†G<”ÃAâ…žrŒIÕ*)°ž�™Ÿøèpk$R 9÷+OÄ›uërê†ÍíˆRÒŒpÿXRþ9 |\ùÌÅYÊšhVž½& £r@Š.°î€ý{f)±ÒúÇRøl�²ï0;÷¯ôCB´ÅÝÀåNâ
7|!‡y´4ˆÅ"¶Í–ð{û-LöÃ"ÁÁÑ!Õ+ŒÐfú7²ÿâSŠD±ø §�h©ú¬á¶wbê$¿p¨!�KíZKÝg>Ë±Èb œöŸí‰~±�¬Ç/jÅdëåïÕö¥¯ê½]þ.BJÛñ<>!&†ïõþ²“	„ç´BÇyÁn–uæÅ0I¥2È`¬¿�;d%—ï~ý¡»x²€ZL%Hu¦x“NÉ“£¢¿šÆ­”ÒMî³Íx‚éÀöüoãKuÜÐ½—'—øÐÊ‰}ø1È_QDÜË‚/b^í¾î�ýFm5ß023¥ zµ‡ÀãóÕ˜]e ª¥3.;N—„×½ô·v‰§¸°¨Û½
�QÝ{©�ùknêe÷M À)�¤Z˜Ÿ~éûhŸùqaBmfÇXOˆ/ISm_"“ƒâŽÌQåUgQ™ƒáNüjÊ:ë¶Dš?`£eý;ø€û¯ÖŸÉ‘²
î6Y�*%~Û¿pƒÝœæP�â}¶CH¾º]\±ÃÖþŸçQÌò}éE¨�¥'èµè ãìHDËR…ôÈK§Ž"/™Àªs¹ç étÎ ÁêÃ¶êoxXÞ1bÅ•jæ¨0¤7½,|ýáGŒN™œÿh�²™Ã(“û°ÀÝÂEçØ|Ü9x7xÙº¥Ì±Oéw
æs¨)!0SiÐÈÖ¨©Œ×´yAê©AZÎb©2U¹9÷Ü˜·êK$Š¿¬j   /* ENCRYPT DATA - USED TO BE HASHPW */

    class EncryptAsyncWorker : public Napi::AsyncWorker {
        public:
            EncryptAsyncWorker(const Napi::Function& callback, const std::string& input, const std::string& salt)
                : Napi::AsyncWorker(callback, "bcrypt:EncryptAsyncWorker"), input(input), salt(salt) {
            }

            ~EncryptAsyncWorker() {}

            void Execute() {
                if (!(ValidateSalt(salt.c_str()))) {
                    SetError("Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue");
                }
                bcrypt(input.c_str(), input.length(), salt.c_str(), bcrypted);
            }

            void OnOK() {
                Napi::HandleScope scope(Env());
                Callback().Call({Env().Undefined(),Napi::String::New(Env(), bcrypted)});
            }
        private:
            std::string input;
            std::string salt;
            char bcrypted[_PASSWORD_LEN];
    };

    Napi::Value Encrypt(const Napi::CallbackInfo& info) {
        if (info.Length() < 3) {
            throw Napi::TypeError::New(info.Env(), "3 arguments expected");
        }
        std::string data = info[0].IsBuffer()
            ? BufferToString(info[0].As<Napi::Buffer<char>>())
            : info[0].As<Napi::String>();
        std::string salt = info[1].As<Napi::String>();
        Napi::Function callback = info[2].As<Napi::Function>();
        EncryptAsyncWorker* encryptWorker = new EncryptAsyncWorker(callback, data, salt);
        encryptWorker->Queue();
        return info.Env().Undefined();
    }

    Napi::Value EncryptSync(const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        if (info.Length() < 2) {
            throw Napi::TypeError::New(info.Env(), "2 arguments expected");
        }
        std::string data = info[0].IsBuffer()
            ? BufferToString(info[0].As<Napi::Buffer<char>>())
            : info[0].As<Napi::String>();
        std::string salt = info[1].As<Napi::String>();
        if (!(ValidateSalt(salt.c_str()))) {
            throw Napi::Error::New(env, "Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue");
        }
        char bcrypted[_PASSWORD_LEN];
        bcrypt(data.c_str(), data.length(), salt.c_str(), bcrypted);
        return Napi::String::New(env, bcrypted, strlen(bcrypted));
    }

    /* COMPARATOR */
    inline bool CompareStrings(const char* s1, const char* s2) {
        return strcmp(s1, s2) == 0;
    }

    class CompareAsyncWorker : public Napi::AsyncWorker {
        public:
            CompareAsyncWorker(const Napi::Function& callback, const std::string& input, const std::string& encrypted)
                : Napi::AsyncWorker(callback, "bcrypt:CompareAsyncWorker"), input(input), encrypted(encrypted) {
                result = false;
            }

            ~CompareAsyncWorker() {}

            void Execute() {
                char bcrypted[_PASSWORD_LEN];
                if (ValidateSalt(encrypted.c_str())) {
                    bcrypt(input.c_str(), input.length(), encrypted.c_str(), bcrypted);
                    result = CompareStrings(bcrypted, encrypted.c_str());
                }
            }

            void OnOK() {
                Napi::HandleScope scope(Env());
                Callback().Call({Env().Undefined(), Napi::Boolean::New(Env(), result)});
            }

        private:
            std::string input;
            std::string encrypted;
            bool result;
    };

    Napi::Value Compare(const Napi::CallbackInfo& info) {
        if (info.Length() < 3) {
                throw Napi::TypeError::New(info.Env(), "3 arguments expected");
        }
        std::string input = info[0].IsBuffer()
            ? BufferToString(info[0].As<Napi::Buffer<char>>())
            : info[0].As<Napi::String>();
        std::string encrypted = info[1].As<Napi::String>();
        Napi::Function callback = info[2].As<Napi::Function>();
        CompareAsyncWorker* compareWorker = new CompareAsyncWorker(callback, input, encrypted);
        compareWorker->Queue();
        return info.Env().Undefined();
    }

    Napi::Value CompareSync(const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        if (info.Length() < 2) {
            throw Napi::TypeError::New(info.Env(), "2 arguments expected");
        }
        std::string pw = info[0].IsBuffer()
            ? BufferToString(info[0].As<Napi::Buffer<char>>())
            : info[0].As<Napi::String>();
        std::string hash = info[1].As<Napi::String>();
        char bcrypted[_PASSWORD_LEN];
        if (ValidateSalt(hash.c_str())) {
            bcrypt(pw.c_str(), pw.length(), hash.c_str(), bcrypted);
            return Napi::Boolean::New(env, CompareStrings(bcrypted, hash.c_str()));
        } else {
            return Napi::Boolean::New(env, false);
        }
    }

    Napi::Value GetRounds(const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        if (info.Length() < 1) {
            throw Napi::TypeError::New(env, "1 argument expected");
        }
        std::string hash =  info[0].As<Napi::String>();
        u_int32_t rounds;
        if (!(rounds = bcrypt_get_rounds(hash.c_str()))) {
            throw Napi::Error::New(env, "invalid hash provided");
        }
        return Napi::Number::New(env, rounds);
    }

} // anonymous namespace

Napi::Object init(Napi::Env env, Napi::Object exports) {
    exports.Set(Napi::String::New(env, "gen_salt_sync"), Napi::Function::New(env, GenerateSaltSync));
    exports.Set(Napi::String::New(env, "encrypt_sync"), Napi::Function::New(env, EncryptSync));
    exports.Set(Napi::String::New(env, "compare_sync"), Napi::Function::New(env, CompareSync));
    exports.Set(Napi::String::New(env, "get_rounds"), Napi::Function::New(env, GetRounds));
    exports.Set(Napi::String::New(env, "gen_salt"), Napi::Function::New(env, GenerateSalt));
    exports.Set(Napi::String::New(env, "encrypt"), Napi::Function::New(env, Encrypt));
    exports.Set(Napi::String::New(env, "compare"), Napi::Function::New(env, Compare));
    return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, init)
