@@ -51,7 +51,7 @@ plugin.checkError = function(e, resp, expectedStatus) {
5151
5252plugin . init = function ( ) {
5353config . app = 'leetcode' ;
54- }
54+ } ;
5555
5656plugin . getProblems = function ( cb ) {
5757log . debug ( 'running leetcode.getProblems' ) ;
@@ -95,7 +95,7 @@ plugin.getCategoryProblems = function(category, cb) {
9595}
9696
9797const problems = json . stat_status_pairs
98- . filter ( p => ! p . stat . question__hide )
98+ . filter ( ( p ) => ! p . stat . question__hide )
9999. map ( function ( p ) {
100100return {
101101state :p . status || 'None' ,
@@ -167,7 +167,7 @@ plugin.getProblem = function(problem, cb) {
167167problem . testable = q . enableRunCode ;
168168problem . templateMeta = JSON . parse ( q . metaData ) ;
169169//@si -yao: seems below property is never used.
170- //problem.discuss = q.discussCategoryId;
170+ // problem.discuss = q.discussCategoryId;
171171
172172return cb ( null , problem ) ;
173173} ) ;
@@ -254,9 +254,9 @@ function formatResult(result) {
254254} ;
255255
256256x . error = _ . chain ( result )
257- . pick ( ( v , k ) => / _ e r r o r $ / . test ( k ) && v . length > 0 )
258- . values ( )
259- . value ( ) ;
257+ . pick ( ( v , k ) => / _ e r r o r $ / . test ( k ) && v . length > 0 )
258+ . values ( )
259+ . value ( ) ;
260260
261261if ( / [ r u n c o d e | i n t e r p r e t ] .* / . test ( result . submission_id ) ) {
262262// It's testing
@@ -374,8 +374,8 @@ plugin.starProblem = function(problem, starred, cb) {
374374} ;
375375} else {
376376opts . url = config . sys . urls . favorite_delete
377- . replace ( '$hash' , user . hash )
378- . replace ( '$id' , problem . id ) ;
377+ . replace ( '$hash' , user . hash )
378+ . replace ( '$id' , problem . id ) ;
379379opts . method = 'DELETE' ;
380380}
381381
@@ -508,7 +508,7 @@ plugin.signin = function(user, cb) {
508508plugin . getUser = function ( user , cb ) {
509509plugin . getFavorites ( function ( e , favorites ) {
510510if ( ! e ) {
511- const f = favorites . favorites . private_favorites . find ( f => f . name === 'Favorite' ) ;
511+ const f = favorites . favorites . private_favorites . find ( ( f ) => f . name === 'Favorite' ) ;
512512if ( f ) {
513513user . hash = f . id_hash ;
514514user . name = favorites . user_name ;
@@ -538,19 +538,118 @@ plugin.login = function(user, cb) {
538538} ) ;
539539} ;
540540
541- plugin . cookieLogin = function ( user , cb ) {
542- // re pattern for cookie chrome or firefox
541+ function parseCookie ( cookie , cb ) {
543542const SessionPattern = / L E E T C O D E _ S E S S I O N = ( .+ ?) ( ; | $ ) / ;
544543const csrfPattern = / c s r f t o k e n = ( .+ ?) ( ; | $ ) / ;
545- const reSessionResult = SessionPattern . exec ( user . cookie ) ;
546- const reCsrfResult = csrfPattern . exec ( user . cookie ) ;
544+ const reSessionResult = SessionPattern . exec ( cookie ) ;
545+ const reCsrfResult = csrfPattern . exec ( cookie ) ;
547546if ( reSessionResult === null || reCsrfResult === null ) {
548- return cb ( 'invalid cookie?' )
547+ return cb ( 'invalid cookie?' ) ;
549548}
550- user . sessionId = reSessionResult [ 1 ] ;
551- user . sessionCSRF = reCsrfResult [ 1 ] ;
549+ return {
550+ sessionId :reSessionResult [ 1 ] ,
551+ sessionCSRF :reCsrfResult [ 1 ] ,
552+ } ;
553+ }
554+
555+ function saveAndGetUser ( user , cb , cookieData ) {
556+ user . sessionId = cookieData . sessionId ;
557+ user . sessionCSRF = cookieData . sessionCSRF ;
552558session . saveUser ( user ) ;
553559plugin . getUser ( user , cb ) ;
554560}
555561
562+ plugin . cookieLogin = function ( user , cb ) {
563+ const cookieData = parseCookie ( user . cookie , cb ) ;
564+ user . sessionId = cookieData . sessionId ;
565+ user . sessionCSRF = cookieData . sessionCSRF ;
566+ session . saveUser ( user ) ;
567+ plugin . getUser ( user , cb ) ;
568+ } ;
569+
570+ plugin . githubLogin = function ( user , cb ) {
571+ const leetcodeUrl = config . sys . urls . github_login ;
572+ const _request = request . defaults ( { jar :true } ) ;
573+ _request ( 'https://github.com/login' , function ( e , resp , body ) {
574+ const authenticityToken = body . match ( / n a m e = " a u t h e n t i c i t y _ t o k e n " v a l u e = " ( .* ?) " / ) ;
575+ if ( authenticityToken === null ) {
576+ return cb ( 'Get GitHub token failed' ) ;
577+ }
578+ const options = {
579+ url :'https://github.com/session' ,
580+ method :'POST' ,
581+ headers :{
582+ 'Content-Type' :'application/x-www-form-urlencoded' ,
583+ } ,
584+ followAllRedirects :true ,
585+ form :{
586+ 'login' :user . login ,
587+ 'password' :user . pass ,
588+ 'authenticity_token' :authenticityToken [ 1 ] ,
589+ 'utf8' :encodeURIComponent ( '✓' ) ,
590+ 'commit' :encodeURIComponent ( 'Sign in' )
591+ } ,
592+ } ;
593+ _request ( options , function ( e , resp , body ) {
594+ if ( resp . statusCode !== 200 ) {
595+ return cb ( 'GitHub login failed' ) ;
596+ }
597+ _request . get ( { url :leetcodeUrl } , function ( e , resp , body ) {
598+ const redirectUri = resp . request . uri . href ;
599+ if ( redirectUri !== 'https://leetcode.com/' ) {
600+ return cb ( 'GitHub login failed or GitHub did not link to LeetCode' ) ;
601+ }
602+ const cookieData = parseCookie ( resp . request . headers . cookie , cb ) ;
603+ saveAndGetUser ( user , cb , cookieData ) ;
604+ } ) ;
605+ } ) ;
606+ } ) ;
607+ } ;
608+
609+ plugin . linkedinLogin = function ( user , cb ) {
610+ const leetcodeUrl = config . sys . urls . linkedin_login ;
611+ const _request = request . defaults ( {
612+ jar :true ,
613+ headers :{
614+ 'User-Agent' :'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
615+ }
616+ } ) ;
617+ _request ( 'https://www.linkedin.com' , function ( e , resp , body ) {
618+ if ( resp . statusCode !== 200 ) {
619+ return cb ( 'Get LinkedIn session failed' ) ;
620+ }
621+ const authenticityToken = body . match ( / i n p u t n a m e = " l o g i n C s r f P a r a m " v a l u e = " ( .* ) " / ) ;
622+ if ( authenticityToken === null ) {
623+ return cb ( 'Get LinkedIn token failed' ) ;
624+ }
625+ const options = {
626+ url :'https://www.linkedin.com/uas/login-submit' ,
627+ method :'POST' ,
628+ headers :{
629+ 'Content-Type' :'application/x-www-form-urlencoded' ,
630+ } ,
631+ followAllRedirects :true ,
632+ form :{
633+ 'session_key' :user . login ,
634+ 'session_password' :user . pass ,
635+ 'loginCsrfParam' :authenticityToken [ 1 ] ,
636+ 'trk' :'guest_homepage-basic_sign-in-submit'
637+ } ,
638+ } ;
639+ _request ( options , function ( e , resp , body ) {
640+ if ( resp . statusCode !== 200 ) {
641+ return cb ( 'LinkedIn login failed' ) ;
642+ }
643+ _request . get ( { url :leetcodeUrl } , function ( e , resp , body ) {
644+ const redirectUri = resp . request . uri . href ;
645+ if ( redirectUri !== 'https://leetcode.com/' ) {
646+ return cb ( 'LinkedIn login failed or LinkedIn did not link to LeetCode' ) ;
647+ }
648+ const cookieData = parseCookie ( resp . request . headers . cookie , cb ) ;
649+ saveAndGetUser ( user , cb , cookieData ) ;
650+ } ) ;
651+ } ) ;
652+ } ) ;
653+ } ;
654+
556655module . exports = plugin ;