From 38b0a8e8b11983b0f2a910dde7fa5443458e71eb Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Thu, 26 Sep 2013 13:26:57 +0200 Subject: [PATCH] refactor test and storage code --- Gruntfile.js | 3 +- res/copy-deps.sh | 2 +- src/img/icon.png | Bin 0 -> 5540 bytes src/img/mail-128.png | Bin 17285 -> 0 bytes src/js/app-controller.js | 8 +- src/js/controller/mail-list.js | 113 ++++++------ src/js/crypto/crypto.js | 33 ++-- src/js/dao/devicestorage-dao.js | 29 +-- src/js/dao/email-dao.js | 267 +++++++++++++++++++--------- src/js/dao/lawnchair-dao.js | 9 +- src/lib/moment/moment.min.js | 6 - src/manifest.json | 2 +- src/require-config.js | 1 - src/tpl/mail-list.html | 2 +- src/tpl/read.html | 2 +- test/new-unit/email-dao-test.js | 87 ++++++++- test/unit/crypto-test.js | 5 +- test/unit/devicestorage-dao-test.js | 145 +++++++-------- test/unit/keychain-dao-test.js | 14 +- test/unit/lawnchair-dao-test.js | 13 +- 20 files changed, 475 insertions(+), 266 deletions(-) create mode 100644 src/img/icon.png delete mode 100755 src/img/mail-128.png delete mode 100644 src/lib/moment/moment.min.js diff --git a/Gruntfile.js b/Gruntfile.js index 0a97452..835d5da 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -135,7 +135,8 @@ module.exports = function(grunt) { 'imap-client/node_modules/mimelib/node_modules/encoding/node_modules/iconv-lite/src/*.js', 'imap-client/node_modules/mimelib/node_modules/encoding/node_modules/mime/src/*.js', 'imap-client/node_modules/mailparser/src/*.js', - 'imap-client/node_modules/mailparser/node_modules/mime/src/mime.js' + 'imap-client/node_modules/mailparser/node_modules/mime/src/mime.js', + 'smtp-client/src-gen/*.js' ], dest: 'src/lib/' }, diff --git a/res/copy-deps.sh b/res/copy-deps.sh index 0480159..043ce60 100755 --- a/res/copy-deps.sh +++ b/res/copy-deps.sh @@ -8,7 +8,7 @@ cd .. # build imap/smtp modules and copy cd ./node_modules/smtp-client/ -node build.js && cp ./src-gen/*.js ../../src/lib/ +node build.js cd ../../ echo "\n--> finished building dependencies.\n" diff --git a/src/img/icon.png b/src/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a3e3860397ecb41d2bcf2208d370eaaec7f59bbb GIT binary patch literal 5540 zcmbVQcQjmWw?CtIqXr>Hi;!S2dN-qGwCEASV8jSB!-!r+Cy5}9=p`aVqC_VmT7-m% z7QH1}v;;At-O2mD`R-kJed}9y-Luv?PdUHce$U>2JUh|c)PR|Rj{yJxW+Ov{1$iX? ze(7k)-*!z@FL~hg)3ful#JT$gpnTDQjvLMe4KeaYT|---QEov#@6hT1KuN$@+4)x4s-h~Z0Qu(; zCR_7$b62%M=>KDjywVW%@bkl~%E$x+1V{(SOXGa6$;hgxsK`L&WaQ+e$OtJy5Y`VB zD1{}6{Lz3w6I^{Ucs~pd3;C@P<$}B6ry)%C^zRV7@g^q!Qj8`1Lll`ZnLrd?Mphas zaHk{Qg&5V z{-fvL$tx;A_4JjXFj+kv9a&jDc^x@jxQddTzAg-^tfQn0`y*?FCHSGRuIN9uF=X5S z$jbjmSyi|%8s&%cwZh@N{$zo<2hI;i@WA0Aa5&_g3Cb0N{k@j>eMWzWj6nNh{Lyau zzBq5lUnHwy{*43wx8VPlcl-a6Cqs5h=69z2kIebAMNY@x(|^?gdGW7GLSxD0<4dlS zog77Z0APD-gwU}H9A9$`!*dODX~}>!;Uu~kV!|6c2}xrvzYZ(=tcNQk1Buv&EZ&LM zLc&rM_Nye>qxNK->QEC2eLv3ee6*ynX>av1=k+jMt^s<6&YOv$XN6``QZ`4od#6U5 zn!5<05%7R zL<)e46M*Ca0Eik`KJGN{cEWMrad?wESZ^}-u-BWtjvrgtkn}~-;m0#otiq&Z!vml5 zb2dHe%5^S1tl7I9jPJW^0{{rW(Lz7(q&uN^Nzsy{dXUPQzeAuwVAelfXMhcWDCi2l z&kgX+?DuA=E8|qx>0Ln8H zjP=jID2~y#Z_;B=H+Y*B*=%V6ZK+Jh4sX0`I3&Xn5d~|0E#Q+6YX-1$&%ZfvB!Agm zFB_alkhD+qoZvq#JVKawuU*dxv^LMudPGHhzB4Pd{HQ$vI}dVM1^ZLhe`Vp+RYC%A zjC|%v62hQC07V_KRp9Lm&|O!6R8s*^?qi4JL*_d5ms&+SG$+A2@N2@yD_RXv9BA$=5ofB`+=!Seg4QOxF;pP-a*UUDY&BEE#LbmM;Zm(`0 z(^-EQcV^vN&tBs{F6{ec1|ohfU0^r{X)9ZNEID%-nR#h5)^&_|)_)hWX&2@MtK4@o zwX(tnr$61~U;F`|IFJU~*boah>!pl$hWQnzM=I{vwMF$W8{`xrz0|y1!DS0Ajw7C{ z;!X8ja^Tfp++?~+GG?;Up1#%6jP$t7GB6AK!R+WBjk?Y|z;VqO6LT-yC+YCxKnsY7 zry6Jyn2cMVW}S+PJ^q52t1Zc63Ev&`y65yx(;%aOjRpJFwb<^1EDt+A;{($EJ>>IL z69{~#Y_9*>?G|5+_IbS##yS?uQ8;Qq(7nGo3j}=#f{MVCmcT{uHQnjnqr<_j7^fqy zXlKVwN^RvRMz5j6kg2OD;N)#H2O}8tgTS1@J7zF!J4sN>WM^_LW*ngTevwA^@$dYK z9IJgW!TzfJHcAAyRD+n|d+}Lm@h2PONtq}=fjWA&iwT5yu*01ha!XUB(b-EByHI;u z=i>$rNXT(=6Ft0=zEWQ=O89Lj#@ln`t^MKR&!rP=~`CkUEpP+GyuOeF}eFdvh@VYUwm>W17dIrM{9YY{(svwq?{e3LMi9GjVqNw{9ae3gyE)5+N zI^`*5cb^YP*xHDH%rO@|KBN3jATE4b^ag=*wsc(qf+KWcI z1dmF!?Z~e2G2tZXIShFi{M^~hJmES>$#^1{+#IEJ`X_6t!yX&im!oq(_-rRx^PYKG zhX`)5hBB>$P@lIb1|g=yC5$ZUHhYD}gKO*;hpH2;)_-zJ<2O#RODv0Ui5E#9L@n>j ztw=o1TIv}QsLu!4bjKvaUWkjAo;uFwWE6}!SWxjCADX5;5)yc zR8QCTY=c!45c|T5j9%to!YhBJ@BR4WPPc&(*0n%^ z-U>rhL$|%vH7SmrVP}hb;5hf%G1rci=(cSUYe((X>yA04h`iN4%|Hr`S8vv|x|eJr zBr+Zx*|z@O8r&Lw!<0k3h=P z^`&igMjw^jljou_NA8Zk{V_8x8?7bXVkC|8mP%)#@L@3YiYMo7zr`YF7@Rb0WLnY9 zbL748MypqGFXV#G+r5RzeVx13;idMS2P$9rdA7o8$FAG0*X>tD6ZDn0b<-yF#;vQW z7j*kwU*|Zl^U*c6T%I_x(*v-Q?7abjGlbHfh`|Sqm*RocZ6$m6OKQAZ z!Ouo~UkQ{jgOP^g6~At$FA8K%6Zp;IQYv>Uqu-Tc_)f%N_V>~X6*mQh+Rp*>kfZ;$B&sN0EL7@A= zwB8&=&uA&4F)f$A_tUgBog3xxk}5N>l9A)kxiZ}(nRti9NoAMu@V&WvsVz(O*>X1^ zMPwV0<4W^tuQ+&n(X!@-y51IoWvIl=lJ?}rljkO#QqSunHPne~egWz9XE&Zg?6agS zK5)oIG{SYOgR*5ZW0e+cy7<08zMJc-F%!@D6kj;i`_jd9ne|k{2JZx+NcwDecwpPn z)jL&+g$t@68)h96*1c?{8})b);E}e}!m>|Rr1d0jput}{iNHPeld=Fo;0Ue}ds4>- zvQaWJV0kj)D#q2$dq1it8+g;p6lEzp&@v}+j#Ho}(xnDmnow6Xt8zCrI{9X~|0Ry- z+1RQV7P#dExI_58&qGS0X~KcS^X&qpPtnR10KYD&U=f?Lqy|9o~HYTu_22KEw zafJH?7>8>Y`viUB1DYYBlqy3i&q1E^tX4It;!j^VzQ6g18dMd!c?Zc78G=zfIJI{< zMU1;NFw*af`2q}(Twt}TzI%vr|cQ$Yg1 zM$wISv2$N(wm@A7-8uB3Y~F35Ih66?1B$Am-)bluQ3?vQM_y%641WJZrcv-fFR?<* zE#_Mpp8+hKxHmmaJSj4zoULD*y_DSdNk~1|w)U8Dv4fsacE{ep^E15cXF1=O4|}lL z6Q5#``31GW$C2ttKqr!gZhmP1y}e4By12355Z!!mhiufJ8z?x3V(n$`%~5Uc%{9Fq z=2J@#7{)aFHiyI(teeMoMU>Dw`&wRnmf9L!^?fJ(?L7skBSU8EO&Zd^@+FDMUaFRF zfdK7?tL>6NTUGSI^>^DbvYP%n=+NY3YT#m}vB3OMfZS||9T&jT&9d=K$O-IFA6pn} zEr=T^JeFD9OTE5TlYbm2NZBH2PEBkVwaN7t5!JM*+mFU@&?MAE2qo4ekLxR4BQ0pO z;<94~B~45jfE2-~sY zI&xWSEbm?X5C*tJ^hjRgl=C~(|44Oe;%N6hP3Sa-6u8KjZaI`@>`j61rMG>!nCdE; zTy-VV6)UWXbRVTQr^s4rVsc)GqM#?}BX2GdI2@)w&PtGCFR`a2KE9IiLhZbw#*fpn zFfc5bhidj)^%j75ElGI0$!ZV@poG?Osge)V16<+8{T<*qrBHtD0_9xfxsF)Z<-{KV?~pFPtSCqHD&+X zQPutB20~-;lJ8~&{8XznPT)Y*NHMvi{HN1q-(u>kB=c{9eZ`HhB4ij(LgM(!WHtmQ zuXIO*(B6tq#X7~M+2PxAK?PP|5>>e4N2j2gHm| znSV+=!85vzKcBhk*MaSpwY@LX;x-4K@1vd9@bI=cKt%_>(Do}zy|6t0j7%-Q{idT< z5JSaQYI{6Pnj_Ohs7yC%>`G}jh>DTXT2^oY@=dGo1q+CDDstG!*NCHdkPqGG(_VuNccz4n8Z zY&*rQB39H$EI3AL=+X}As*NXul1rkZ&5BF@lg?Kxd%jmThVElG0qs-Z7Nqc$djFc4 z`4-4pvocM(*0S?XhU9zd$MSg>7$pgaJ;mz!G)UL4VSS7Ph^(0=#ZWpZU9{;zChu8u zHJ(h#f+5CQ8#q1Bd9|!O3h|rJ0v5rGmHB+5ItN$AMLsvx64HBFYwA1oPbl&j>3I3T zHWQ#ICZ2QK8Aa><+)aZtBY^fYe!@JYZsbv{ly!#UM3(WY{|q(RHS==DIN`H;XAfE# zJK8QAM|syZxrhTqO&%<{50D*Ba7ew$X&KEu8u+G9atZ)`R<~tOf~~U_I>0E)&O&$g zf`9{yv5A8-Kw~thaGweLvf(Qs)99>{X|>_g_tC<)pSH(ac*Q^GbYlQ!rO$zFTe(+1 z>#3!6Ty0e0gI1Ijc+K-phbEo%DC>eVT$e~}>+LK(@;9S|+{&=!yz5Kmh@W9a4F2|=J6go!$7E--A zwaY6l4B)|EOpKML2eAtP<2j3p1n5!C?BOE&@s4>Um97sH7z)$lnkb}<6o?1-zAS%5AV0M9~sKp3#Z4&YMMu}}ciGy{4ju~6#(NN4~qjfgO20GtoNc!ZYL6A+pX zz>~Yu5xZoq!a87qo>Y3BSSy{dY|vLEb|*v~9S#bnNku|dJOPsrCK;bN`n=MKd4l*b zH?PM5fV@O}=x(oHJtr}$CMN~rnlOymkGc`wX-rLb-u9*{96td7Tdw}oZ)}|PB!Qv` zf%b0&^k;BZhNyXN_t6%0n4cN}d50@H*N*?6jck5G>+3B6B2BoRmj$q$NRa&t9%|kAkaebrxO5RC_~4g^`~B97zqH7$qQnsktDt8C1UGA zAnS!+>qUJr76_H18|ah5l0q>LBy%=qt_+r93ZJMUXEElS`$W#yqhS@E)b%{F-9Q&;BSQ$=1RP{^e|V?KO#il0Xr^^p?;}aAvWYBv$0y5O0Nlbdg40tvadT! z`>$8>_UMo0W%2!Or>ep{!trv=giy8q$PYRt=!-A zZOc>BQgtg7Dl{tOk2?jbPD`^(I%V7So`WzXhm{!h8RaeGC*iXXG?z8kOHoP*R6mN` zR&kWu7L(?;%XoBuFFpH}(`6W#qvBAy;njKYMCQl&s`YCBRC+6qri)OIMu)J9Hc2El z$nWGJM$S3BdZ{68(Emxg*d)ayZ7Vdfby}=&UM^M+Gsb#Ie}`_ze#m~PkO6^#nn79h zNcAiOsM=b>Tq3F>sxmw=K2b2Skj|g^k$aKnAoCz|F%!~k4DtlAHT`aC`KDO63o6k& zYwGwO1`^gQ*8Ku9sFwq0gXtIA!R@8krLAg7Y85#Pmi;xCO`}a^&BQf(O9-n~P4-QG zCYGjzmd%4z{bxy?Nq%j9v9BKy(SIysRFH$n{aqTI@kF#mHZmr&&LsQ>Z&D9AEp?}` zrj~ejnsCRM2duL-GiT*32`yV)LarmCBb9^4IMT#9GD(NlM>=61tRk#lPa^3Y00fFT+Ob; z&mvyVVI#I8*v8i;F~nh^H$JbMt*dCKOeW01QYG-+t_$*Vdi`TNdmDAfhwvBYh=HS5 z{$$@xSSdjn2M(J^>L-;5HKF7SNi3G%1TpkixE}OzC|-T!eU>zw(*n~GOVau*6lzxr zEUMl=PZB%$z32vci`n*CLmA5wI#keA)-y$N2r}HM?0G(lbaIFMOiXZ+DyLRWYO*}C z+_!KatNa#L_cr)Fx|x$A1##52KA{p`j~%5+s^p&*h-r}pvCi_Fjz#xGPvy=9+!u%ip0e~HZ++NO1s@!CDSM~2)}po^@y$M{rkABH5HSclI`cu@wjHZ zTF~TkU^WS{!E`up!*bo4k$sOo5bY41)W2$Ry{cQ?!_{fg$*2}FvOjV?5_?OURUdB8 zYR{-@)wgXu^X_c)vA91Z%@Lejzv=Y4BzRw#&k_E(-g)iqa6fo|3n5tdcV>K@9BW_p zU_1kNsCO9Md^(j}mR|ocFY)zlZRvb1z^2C|?|OPo{vIJLWa=&PHXc(NK0ZBuEF3-j zI7diaSa@5)`StLnRC>yMiXn&at>A5}ijaWQ|6%@kadP_Cbbi`ZT2i;YZ}IC<&oOcJ z>QLVhMc1e&FSp^1_Va14$C?NEc_9Q-eb<*Nfa%@pvG!K;Z|QRqaZ=Jd9PF>_7c*Y= zRVd!1GE)Gm0s!9hP#hWp06e}!?}q?@8y5g@WDEd^`~m<79ex@O$^igEHHtElTAnK> zogTmRm-Pdt)V{uTs}R%wFH zW)chY!;hfeu!g%=AWq2G$3l!EzK`igDhPN`r7xIu@w}t`MaM(AZYaiL*NIZ%p17jo z8SHcJ)qfu=uWPFZg0zplUUxri)<3~cUH@M66I<*jiro=HV#bo93WY-*B5BZ8piTsu z#`L-w6fpI<8T8N;$*{&I;K(as<GP|Jz`#m z1k(tLjhj>}N?sfWK$O=wLVJjkZv5U$#ipKXeZ`wsob<87^Q-2MAUIN8teF%3b61^( z_}!L7pQw;N1avPvt*^phb9o2Wi2l+cdASHJvcBjGD`eio_{o&ExUmS z-{Qb_5EX5ZB!L%>Bo-GT*DlSFCc%2UWu8BKBK7t2OJvBBBH24Zh-W)D>9Z1^7n{8I z6$KU@_N@k+-<{zkFhjO}^w*WOgzj9CnI1ZeyQg3+=g=E-8t+x#v9i#_8i}wRf@k z%)rR-CXN(rzZhMWrqY8-b*q8Lu9Nt*$8W3M=`xjL(DJ?Ga%f555MP0ri6FX7m#v@? zSasZ*ot^FK>MAt!D}2~0J)1pA#rtHVGjnTeOGn~%u2)p4c*X(hJ@5HgeH`39stx7y zN5@x>YYyvfn{gAY+Gs$QFU$nC?2(9$WIwpjCc;n;PM4N`{RJuaSPx*2Z*HP|6cb|; zn@R%NhGK=94D9Z*wdx~cesm3h)S3~~n|#=bM6%2xX5_fUSWHCnxa`l zjlf%ub8Gmqh~08s?ABKPkM760*<4OMl=HJgy*nSNh$kFav8j>`kaDB8k57Qnc>vVG z?vKvSC{V(9G>Sl^4&XsIgJ3GIgQ?u+ogK3o5G&cRwckb;*>FCPd5l&*ZmG8yrqg+k zC0&882#7-c$#&xAhV9Tb>iMj*bmwHbzWZ&Qci3pF<&2YRAT5n(^AX$U0|dfG{L{68Ja+SYd+`z#7T!XlmT7P6rkLGs<7fn_QhfBD<1pHA z(5}*F|2bMTC$fNG41MgU62nr!YS%z{inK()#b#ofy1M#n@J>ta%XcT@ZdPBCXnLgn z)zE}oamRxWYzrEitJCxIkCCRe7aLSa;a#NqLz#xKOvTP}$GOWj zChJpYOSQC&q5X4nz6pz;F10H7{kHm$W@l&BG^27+^Ok$CuLdG_+$}7IDnX4=t|F&Q z#h`8-mM@oLY2>2rHO_l~<9&}0r7=^KijCChH7UE&8+%p901Kc#F7MyX9?R`_XX}1G zMn)3Fz%Z_u{h*HUi6%l$!@0u3LY^ooFaCskhi`Dyp}(@S9F>ZLi75<=pPrw|sY4?S zbXDb>v_HB%jeB`^yF>l z+p0m}xbx@Augy8Vy+MnVBCfkt>FMbn-|Cko2&8^xGPk;FatGF?=ACsr4Q}>0oNaX4 znu$Xs%e2x7`|OoExrbun3~SOj41OvxU+VvUK1NVOUlr%&-APuW=jG$8L_k0&N(KsO zjtuEqSVq$&Dc(W+pL@ch3j-d;dF3fnq~3lWd&jeL_XOj^^)gH;zdYrvi$WUQPeqR0 za?{hLq-Rzoa~cLsr}bMLp;5T2;I&iy23qMMqMgbXWkn6s`#)~7$M(o~RREamD#ZR%g(=l{}5}hbdnU$*Y4q9k*JYE99!{U*7hez158(&?2*lkElQT2 zI+$PMb~c~8T0JidvNJN0dsL}!7rh0BoqlVxUM`xB$cFzO^OD)aXu%f>qw5U$+*@hpV#lJ{?}cA=cwKG5)+>W=-kon_AtA5<&YL|!r%+2L zN8$G|WiU>wE}m@*u?~78Cp413m1*&e zn$MH~tzjZ`Ndtih5X^r^prMt|TI%+nyUNA`U; zfUeJfy~;q;X&o+1s;u~qAf!VBnzg+q^k1XD3C9&_u?R!m*Z>Zv*OWbZ3}1iIpRcyLv35}cE9R{& zM-UWvc6N52ZVqP)blHS}e+aYZj7YJ*YOtj2j*rJUkquk$5N`>oDU%Z6+J@+J3_rQq zel(j~FDy)>zy`(@X&xr=`5%;U!2p$#f$c0QS3efOU$5g}5i!i`m&uBO@L*Nj>jN4u zc$cF&>!IOcecQ%_nJphD{*BD6^z_s{=fM)7VvNHS>Qv5iBmcL) z$7Xz#us2tRYxF~*FOYj9V+2RK6z4q&hK>QFjoN_syI4{|jf%&IuI>>DjJ}?jn3$!+ zMC`-ox9AVMALXeMy*n97N=jt#WEnCiw%e+>oFr)$hi+o+SKYb^aZU&3c4GSbNAr(e2X`z92C5?<6AkgSvEuXg$$yN(sPgIb* z5aSM9$e_9~+7p>HI&Zf>C(jhHw|=Y#{*AH~jf3LpMN73@XvK*W0oh(k(SPsC9GAkA zHTCvZnmg_q-E)Sv?}P&9E^1z=sIn((zMqm{hfhN-QFy;s*UG5Bm>E%UF~k#?4Af@X zGa}aQdQm^`xQ_cq{;0;1!ZJhx=LD1ks5hVn-&u`{yz(sI70Z z1h59a9U*BIeO9VBwMzK5zFskzBYOYD6*bA2G)fyOv;9Q8*;wtjE++8?N^5NDzkkKF zTWz|{ZeDY)h)AZP_+69tNP{$y&Rcr=@89p)vlVvmITBse;h{A)T-WG<=!L!xk=b^Y zoGo%l0@RZ({%ozy%?*$$??oLQ9krd|xLxMIziu7x|A7Kx+T+FQfb#|RuV!B%R^Xu~ z7T1%LlNfQIJ3G`}ehDKGq)a425M-;$kR$*NmiqPeb+DuJwTrw+(G2q2E$h3Ix&x%j zPT<+qr|7agD9^pYAN&oBFZHLnlFU%C5o8Z$~Bt+BF@cdr# z?O==9>tu=7-O96=f`sHJRj9pP-3&^xzBbr!V&w7KL8TZ7WipF}Ic9>zT|Q7Cb|%BK zjC3-PFp$V0N%XJYQjM3F7gx{-skPy9N|PWhN}pBEH@D>GN-JA!+GlU!4w2CKYttPPzt|xEE$~`RcBye zP<{UI-@tuHxFL?N?dh_MrF|pFO@Au3*x&j*moo65Q1TyoOLKD>hmDRwkS+WtqA)f} z-N~Di@yxK~+z0pe<0r|BQ(~$ZilqjpSq8AGavM1_GagEb7K=#S4x9Z8Y9PA)aIr4a zh*Gg#U2BI~3l-;Ki>adCHa z&EVn&bn6?Kr`q|kn2HB>2k2V$CrwS+v1BveHa0eDFE1BeJv_SaL9>%pkuW6n?%_HZ zmEJN+KbU~%GAw1izjD%XU_)R95*3hQ=z-MP1_t^h?JxT- z2a|8<$yep1yN0Z|mf*qbj>WtCdk=XH=_k?-L1;~+#YbLrtF?%PF;rlO*5Rz2oCPSc z$=6_aS*x!+sESVGSkG|W?X4JXT_q{>4lAai_wi2c6i!jX4yFDOl<)ykoq1Sh-MV|W zPNzZzmz0!rb9+xp9Xc?in16P5Hn3M)EBNT|cwsXtDA<95WM_AB_O>^6uEi23qQ_R{ zn4Xcr({1H>MpBT)3dU;Qj`YW^wW-!`8mJ`Q)qY~4wFglmAq*v{A+7# z+YsaDe~e_6_1pVW=WouF{zJ6AO@N6!-k@2xVZp-Gv|zcn_GZDMuDAgQHwo7XrKHwU z&i3i)iBge4GbHC=%PZ0=ZS}-9xB4yi-?7n~_M#hUS3p(_)KX1*&UymRSE+!_iDDSC zL#~e>Pj7DTmbFCKw=Zg_Gso^Mc+$^V@lXRgKJTk)GEP*REp_$cEP<<{n9PF~~~kmHzMOWPqrD?uHNY zFeFGM>~XdiX?_(?Xc7oq1M^H($0XwrGBTXRt@}6{U{cdp9Xn2aMD#p4Hrjz=OjkFz zdOJM_eX=$<1f$OgzZY%ij`@d%hdk$LtBvRXn3N~>B1<>$#Kgz)iirHF`!+^kZNK=q zFpEkAsMMB$DHll#ie&b#gmT|3?TGp)U$DbW z!@hoBp6u%M5pv#Tdh}%+H7{SjZl=qb3c2}VqR^^e00Lh$HNUpY!G0G$tFNmi`cd^m zjbXE_)Gpl@X{S3I=Usn#l`)BYqfWuqsc_}gVbUPVQw7E9Q%w=!rG zK{)^~Y$tU-35rJv*5-5& zLz3D;q~`QHREAN`=aSYJs)Ivv7=U-0KA4-sBNebShH@J`i!m3{wVv-=b9mm*?w(VW zJ%0BgMu=ur5qIlh^HP`js08wXx5v7}gKxX)W;)=$_amKA7Wg>afb>A;RwV}q3B7=Dr1cBVRoNDRntaQ$6Z4t^{%r=8yP+ zoE|AON2e77xDP2($$z96#1cgMv{^5X7I99yaq(qafRn9J3qY2X>r-DN>IF+fq0?Dw|c(NE=5%Soe%Pw$Yj3igpVk7ZJVuBD0{}Pyqh{sXsx6r?>MS zG7RxMG1FrC*4A!7(g~|9`as48=7(~8D7_oG`}#d!i^EJ+Of4MYsabnxQF`MxU=zRj z>&6|ICs3Nm)Y+?|U8EWdB{elH?RX&c{jK7ng0ewBC(Q0o@PdqnjPK`QD+!Mzo8qCU zUi_1_*68$f>pm{f&$oj~z~|~kZaJr6a*u)(2DC4QsJ0xAgyGauL=EBZkK7O7?%;Av z#i`(F7lyo3=jvZ0^;GcYKQHEK9lw9@zjAXt5crKiAaOC;w(NBqI7GyfLgI6Gveg%c z3e8zJcX!mG=4Fl-5R2zVZMsN73?pSjwa`YOg4Cn>MNhj}_oMnqja_V(%lT{g52jEp9Z z$SP1~U*2R$={(x)|Lk=Og~MyI;$NZ}o0)PE5^cW%@Rg{fr_ zzsM`wP0A?-HT!Ncd)}NqpuY4NxiZAVmp6(?OZVF?)g)dY$SctOR9@~oM|*+^=0ZX4 z4}A4FL8LRmVarb3Krk1u&&4K^r)6 zZ(Vy@6^*Iwf}EUx6a2T3ej*c{g`nd8O;%^_NhDxqbMH+YJ}cj_!rtVP)3c1`-h(5{ zXu34c*k1&Jju;m{Y!r}U(#kSqn-0fii`^|PLhCJ*AkZ^-rZ#m0%YLerF1NXfc2OEv z8c@9!(7)C))U;r{Y_D$my(8uNqJ=iu!*{r2Q@ErUk|6waG8x8}w!jZ7ZbCFYJ3BZOONy<~ z$7JVa=;AIe{&UERJjC8s1Eo&!VOg;E3IAfFyAcjMq3i0tHTpA%R^2WpkD^EUEcCYj z;_Ex&pIdj&B{IGJ9TCG-^_}7d>PX|UR8^=`IXzrpGcYpN#>S=4%jtGp0vc`bw-liK za_r2YnWnhESUHIb?iw={+o}+$k7{zW$&bJ$XeL!~4UwGUkGBn~VKuV-`3!O&cww!A!rm^vm}Cq}PIH&M#MG=4!KU58A`j7*i91=9Ij`j`ZHLG`0Z+H zOVuH`m-;A;Iw_c)HRbT1ZWh>WFnBi*izapJlKWv+qSu`>#OGXo-uB-o&d)dKZj`e2 zpdM2+b0Bh2@`FDSBrj}VqXMcIoItvBFE6<vF?iC)qr0|>gbg}%*?4$) zRo}1t&jL1Kv@3O|Gv3y{tD!LwrNSUvt7~r}Obr)AY1|-*G97-&)`H=CiF&cLxFzvU z<8BQ2_KB2Bo6Y8*^Vw>PlScNhAXjUPyX*>K)5wHTES&1!8PynC_gIDVQFG7OTd60;CNF4UQ%;dLCt}b8~a61-v`gE!&~u z2MF4Pxsw_(;Xy$cZQ7Xg{rY_kly?;+I2~sjjPp(GAyPm3hP#R=V##b`DR)N}2RL2) zzPvBv3#Inme66uyw_Aam(bLBn`tlP7oL94B?BaEz)Ad2A-{D2Ok~zkp2>zvqk=B4m zxLWnqZMJhFEM`BIYKIokC3*WOjJGM%R4 zS<5v>ll^Mkfmf)Br@>TVBLfm`-7;R7RPp6`9(GOp%A26L<9Z9l#b3!apaz3Zb?-k} z+5AZw!M~u8`3aQ#A*Es}q-{1}y|X7Z!RIK2F!TB`q{1bCWa_Gl#HAHahOXq`b)UN$ z>uZ9Xw*^fyu?IdW1q4JLDC7ch-dUK3?+=mMGo_&Fx49w@J1x~aaEiL0(PS%O!%42{ zp=^t6phEQ#gKS|Omt z#y?h*SwjoQORgesRTe6c6;=he%JRlIg<_m!)-M^uxnh~cs$?=(`f0Ep>x0nxyfx|| z*2_C37INT)hKBZYVxN@hY2P@sBx4bz$&x}$+Kxo@z0-}b{r-eHcM2Dd`nCQ-j}N(#zCG;Wvnp9j!C_+J7&^Sg?Ljg722 zde58vOVt7B&vQvjsnyoiGxj4i?RQ8a({pbbAWEAUdA8|m}ix}1)O zpb62?AfLUD|KM=fiYRtoBQ=s#-Lh*IYR&eaKmEEj*c~s}x}7Bc?;n>6<@Zs%sy}TA zJ8pg|{KeHpf|gKD5Ph!O>7q0I_v;2pMQmv|eCNlDpJ~n3LGUfTAZ;`bc@)W3(AN%Z zg5)Nb0B>^AZx^0DYNMnSl z+aMS|p)C1)E!6$Q=FMcrM^0{_zbXnOE#>U?(Of3z|%3*Vk1=vZ+&)F7;J-tZizA6mWqRUzqSvpo`Z=L%;s% z*Ia8efAsLHxhL0h`-GoDMo^%O->B8?pG8bPp{WZPy%Cf>87D@#w}gM0g7q15^90S# z06rhZENH!+Fhz+}BFUhE@oZ1eVH(z!eV{f;%3a)gxfT@|H(0$$4Utq`3!Xjbw+4J*nlVd^3{O@8MpW1A23gYn#GWE_#&A4Eb6u0+}Kw{7h8cwfLi2- zxgD$`wsTMLx8IAv+j;zZs8md|l{it9&Yn!_`HAiGJ4F0fVFL^~CBrG-uOQnTA)Fs- zVOVw>9bt{Oi!Lt@8aYqq)%I@SN6N74p?{JZ2Zw?e?dgA`>3Z??)u2?3RIQg#ww-QP2bh zx~g*Q6%9YIb|H{ilOt_(ua2zcm_xu}Ly9Mz2Blxf;@w_f#MTU4n%SenhSq2hf|xAn z?VN}VUi1~Y@+u-TxIS%uQ}0XBm5fwo|e&s>wsAQbWd zxwuQ~J3b3n;Cul|>R2M)dIv5zB^0c`&JGPRMu?MoRBSC2NTP)7Ady*eH2lF|H;%6M z(;qfD?^<1I@Z$Y}NxuEBwot3$%hjXUvR$gHh)Y-ooFty?+_4)=3Qvksq&d%EPN&au zlRas=3IV<>+^PIYL%Zu~rY)P2AiaN8!1gkpJnO3G2E0V|qF*ZeF-?lnj2Qoojo~)| z^g>+6JE9YkR8!BtCMGa{`J5l}>8xiC z;V{_|bhXg=R^mmsrKVScCWr1YsE$~`WK(~=9mTo2Ld3*k&%ILfX*2h>eprR>f=u|_ zArpHcF3xuh{ksD?U%0jg69Epz9A6f(OA#l~s{`I~UECE!4mV8TW^y+d=$M$GlVXlfP#rIOBhTJQ&WmFxsO{iBEc< zv*)2z!7fZ%2bYkpBu+V%nGj^u8IzETuVEHyP89rYA)3P`7{!m0`jHB+gLKH!?or6! zh)0%D_it!zv^Dtiit}tLG|}8<#MioSjOIHHCPuNShPA`POX zO11?GiRUAlU3}rklYUiNXb&5YOqE|@0^U5ZepiSxR~b-cXd_i(5s}iq`8d>9k~h4> zzgw2UgR3rtZ*!+iW2kLzKdgA|OR~C1w})gDeJLW1p>i}R9YoUMp>_r@GM1s@JrZkZO`LS`cfDz|$7+XWF@HV;q8#P^sb*K#-Lm;tS^i_-z)^q` z`!l+}0Ee{>%bkq=Vz1k`ijW14goVGM(VP8Z3|QQNFNu-sOQdX3W!7TZSCBbii`aCo6Qh_z#<^?&oVj_qMxp$>u;@WF)=+3FC=#<)xrAW90=Ag4&n$cQ5X%WEkz@4o3xbS znv7dN0`Q*F%nBl8`e@UYctkZbbeOOUwUmqa1kK2+`3WF`NW@$GJq1y&tXh7&1e9M% z5d%Bqtd=ID-I$!0$)GX;9q*y=cU5|`w9RUX7z1H6Qh`CQ{V<|UmgQ`LSSsqwkYUPb zw-L^FmOn%yE?x~YR$#1&bjetoEGD3?Sxfp4SY3PM6)Gp+itUn*NV3dfGaP5ZBYD6m zSB8*jhBK`13~+3<7Yw+LCrsaq88lgS4Dy+@^i6=jxs3T1mQqja%jiNSUtG{0eRI4u zUz_FMVyI)Zj+1N8Q@@?MkPfs`gQ#L0XGdEI^4Q;@!tba#j>Ltca!{<)=h}7)Y+cSE zBv|T-ltIlt8&W|wxxdD-+$%XBNvD1(7n34sK}R@U3~(ah22G2<6@n;ail)(TyXT%C z*Q&T;6d1%pvy4(WE9o=GxP^rk+o~y_7tZBh2A{VDYWIsiQ=4*{NM;z4aP}4`i!5MZ z==onr7<-)bX;Pi- zo9BheN%f21NgMi-lABu6>L7GF#c`1J2HWp->HS4{Nw5Q(R2D0Yh_oUAf@w-K)bxQ) ziMUU$%G0{;Ahxyd)8O;0=lR6gtv;{}&#gP|w|4FEqFKP+;^xNlwm(MUY9SfeRzt%| zx|C{1X@CgsttTKb-{fYoD=KcZXb-kwL70YoHv-{e(t| zAvRZuGY&R}kSx^ItQmX=PsFU2h0XJ>I>z2CB|?npKU2onI;j>+l_jsh*Oy{a^xa11 zNVe|-oQVw2;RGiyPr`+MhfH|)!w;5fpPJ||ehn-#t|DfIalWj)tf8MoFuwb3kI{(; zB#on+kRAfLT4t^|A$1SPW0;*YJZq)M{Vnhv+V@{=O4A>$7r>ll?+3$ZGKqcHYhEOa zM9N0$gz6L$Q@z*OFAv9T(vO}NR3Pa>9%ZFS?Woa9?yNn(Y^`X*3mkIK(X*ij>!cj! zd~Z9{mSx$CPL;ELhcrM1Os;XmClUX>55PA2 z?^*QO4R|c6_W?9AN&WwRe~aF3VpJw#c4fV84xMv&X@yB;(y{^{oQ83ShaNaS-?7E9 z=cD`HjHRjv6gpWXCtI0$?T=71Fb+GBF>;_1_Vt1$PKt;11n5TG(1)0f$X8Jd@xLx8 zKBdu@Jx^!dI=tle#D9NMSc$f0HbeR#%aZbo)wm8TG~>0IZAUs+D5SQNPG3ma=C=-x zvnSW!%^)MwRNOFQ9}4#(Ay%mide~U}Y`T7RGaBRVBNYWoF-@+IyM-e;0iNdou5>?_ z4L=t&LdHmI9wdoEdYky%@QA|^ukG~cANH3;6qyw~KS+EwJ$Xrj+Zz*#4KkEM(g(ZE zfL5)+Zl83ElSO>?2kK70Hjg}?Cy9xpOl4s#w>M%!;#ayVgv;4%8iGgZ9|LldU^R&N zdyKzY3n47twp=fW8GQuNrKeDlxpHMkj9WD)81{i!VMxPGmT&!xbZwsltAr{Y@cT|t zXpZl8;m>{T2Q-J_{nnX-KXWow@Hj0uG`f7>o$-E?>R;GKgsI9*FZB!g^sRKj%L`%LW~duM07}Q&Qq%#HTT?z>=+=HU zv0u6<>F^P}9_#(ig>-Oql)8Y5h;Z!m&*Kf5BO`8-;!jGy-PhuDV*ayfT9aJYtYq$z z%ElawU_i6em4`Vv`?JzI%0r)9y@fMy-1p+OsOh^b9gc3m_sLj0R~UH>GmM@Y@s3Rk zsQ?b?XkMzmGpBv$^LL=m-CPX!VPwNmp zEXwi%FZty)hZ-FhC3&|5yrHuL7AX5$Spl@F%+!x-(+%1?h}g>;z7?PmfEtaYrGrEY z?T=|~1vA$^P^L=hEUgI<$d>*4HC~Mt3%?CGTU!a7EahoJ6g3AHRluy4v5kFwImi== zcGRaaLt^C*1v);H|6PX#?X+Hg4wkmuc5WbGny%>}b$5vPI%u7m|2lS%2eKxWkSD8K z31D<}590RZ$jS|*wLF+b!>{}omVLYv1^HDH#OX_5Gylk^>g!ICotTSH(*0NRqMdtBQUAj{sKqaTK=AC2YpqB`TBa1ww@PP2I)+I89=)&M&9TKs` z&Ve|5S9c(T<8aWgk~r&}F0^5ez~;dHa{rcZ_8}$75A!64)qcdjxfaFEPGoJ9+4*;| zjGtSx3S%cgxbV<{l~Pd=5w4+e_~4@CAgpaqJlRZg=5OXP|62C6%>|)nrUM%emGSq zrZ0+G%;Xo9bfNNzsgBy(^NHt|KFh7CH>Rp)hf=TSY;IND*-H3}H}&4K=xb|~O-sny z0$;XT@4*}K+ljjA^!J%c?X8}V7k}6MwEaBa8Mnsw;Q8QRUNR3>X6!{|9}d;A{5SpR z^f@g6Fm2k5ew!RPj~FcS7_A7-##5mtLIUFA6zA;=ZF_%;fn__1KoDz6-7*_FRHZ$3 z8<2lD5)+_F8#JGRtBU@22%V?LG)-du8?N<1{FyGR1(2<$;ByIo@zbv+Kv}GFT!h2j z8|Aq+-WV6zj)4@j;6}_2;(W5|kCmws%uV<4VLZvv*_C@DZ3E$1U@%eXxMgWn@dWa! z>I-mZz@2sdcybl=%Yp`5xg7ER1um-jT78^YD=m zQQH`D2dleiKm9U?>-gP{dqU^zhr#ixRh+?)|4ZT0_=gPX{-uC>_sX3HzCVlQQ* z{OyXRc(83dru<-m_I%Rt{hb$otK2g_h(BSU-0O(N&tWtuA)iu+_tPWd4ahoQHdGQ(3#zB><;usd!G z^;zj7zU?b>eZ(k(`xkzSj=$!Qs)fqMuclw3&Ixi!EY8i_~? z6g>x;6Ox`hmkN55BhU_0E)n>bAJ@WksH+hv4@<+4Y5ommW=HwD}gU@Ks=Y!pW zNl@t)JT{^l1>tfL;P>NL5c4zMS?c?XH;My9=>jw>}$xP=VzUL-L zwhM!}o$-{?IPo)&6;s3aNlJk>f9DYH&Z8lbkRq=eF5kUqq^W*5aZTXQIGU4^i3m5+ zU*b<_VK(sT=L$UH;z<)0@|hnnH&e&wp)W|(>6D^mJ0t1Spug0Xs5YqImr3~ znxFj?)a!G`w<)b_*1^f+Et)4U#s_oHqp4waJ5AXby6~A##K3PM&DCq;ar{w;=Lk-W z#&aD#59)3Axpok!#x#+QvjINy$wt3MJcY%zmaOZ-hK-2BhbT=?a{l}Ul;dU`*S)N@ zj@rYgaDsTb4CSvY#Md7lo9oQ3fQO+805 zxn>=5auP0G!uRXU%*-%8G4a*T&dz`8=;&zG5detZ-rhx#rDCy|@Eqs0-LCunDMVj0 z#%QpZWj(Q2nz(Yf7C^BYzqhtT73pLQ+;r2WwHBtQW0$Ob(w6{wdg7gE(5(7Us?Dcq zY&^DZG0my?!}ejzLcp>f8u{yIPjhN&{v?bh&7Pk4#W!eHd?;UmW;PWAw{J^R3Ru<~ zOS65JG>wJn$;HFBrNHUykFl1u!WdJlR4QL86lUMs+uJ+d+uPf+|5s#y_z>s{p63yS z;VKoW9@@`}zs?rvtEPt*43S#&?6y;}OGO;t+w1=y Y0Qth%CdZD%-~a#s07*qoM6N<$g40#It^fc4 diff --git a/src/js/app-controller.js b/src/js/app-controller.js index ee1f0a3..95c7a84 100644 --- a/src/js/app-controller.js +++ b/src/js/app-controller.js @@ -10,6 +10,8 @@ define(function(require) { EmailDAO = require('js/dao/email-dao'), KeychainDAO = require('js/dao/keychain-dao'), cloudstorage = require('js/dao/cloudstorage-dao'), + DeviceStorageDAO = require('js/dao/devicestorage-dao'), + Crypto = require('js/crypto/crypto'), config = require('js/app-config').config; require('cordova'); @@ -66,7 +68,7 @@ define(function(require) { self.login = function(userId, password, token, callback) { var auth, imapOptions, smtpOptions, - keychain, imapClient, smtpClient; + keychain, imapClient, smtpClient, crypto, deviceStorage; // create mail credentials objects for imap/smtp auth = { @@ -93,7 +95,9 @@ define(function(require) { keychain = new KeychainDAO(cloudstorage); imapClient = new ImapClient(imapOptions); smtpClient = new SmtpClient(smtpOptions); - self._emailDao = new EmailDAO(keychain, imapClient, smtpClient); + crypto = new Crypto(); + deviceStorage = new DeviceStorageDAO(); + self._emailDao = new EmailDAO(keychain, imapClient, smtpClient, crypto, deviceStorage); // init email dao var account = { diff --git a/src/js/controller/mail-list.js b/src/js/controller/mail-list.js index df0a537..681400b 100644 --- a/src/js/controller/mail-list.js +++ b/src/js/controller/mail-list.js @@ -3,10 +3,13 @@ define(function(require) { var _ = require('underscore'), appController = require('js/app-controller'), - moment = require('moment'), emailDao; var MailListCtrl = function($scope) { + var offset = -6, + num = 0; + + // show inbox at the beginning $scope.folder = 'INBOX'; emailDao = appController._emailDao; @@ -17,72 +20,83 @@ define(function(require) { $scope.$parent.selected = $scope.selected; }; + // production... in chrome packaged app if (window.chrome && chrome.identity) { - fetchList($scope.folder, function(emails) { - $scope.emails = emails; - $scope.select($scope.emails[0]); - $scope.$apply(); - }); + initList(); return; } + // development createDummyMails(function(emails) { $scope.emails = emails; $scope.select($scope.emails[0]); }); - }; - function fetchList(folder, callback) { - // fetch imap folder's message list - emailDao.imapListMessages({ - folder: folder, - offset: -6, - num: 0 - }, function(err, emails) { - if (err) { - console.log(err); - return; - } - - // fetch message bodies - fetchBodies(emails, folder, function(messages) { - addDisplayDate(messages); - callback(messages); + function initList() { + // list messaged from local db + listLocalMessages({ + folder: $scope.folder, + offset: offset, + num: num + }, function() { + // sync from imap to local db + syncImapFolder({ + folder: $scope.folder, + offset: offset, + num: num + }, function() { + // list again from local db after syncing + listLocalMessages({ + folder: $scope.folder, + offset: offset, + num: num + }, function() { + console.log('syncing ' + $scope.folder + ' complete'); + }); + }); }); - }); - } + } - function fetchBodies(messageList, folder, callback) { - var emails = []; - - var after = _.after(messageList.length, function() { - callback(emails); - }); - - _.each(messageList, function(messageItem) { - emailDao.imapGetMessage({ - folder: folder, - uid: messageItem.uid - }, function(err, message) { + function syncImapFolder(options, callback) { + // sync if emails are empty + emailDao.imapSync(options, function(err) { if (err) { console.log(err); return; } - emails.push(message); - after(); + callback(); }); - }); - } + } - function addDisplayDate(emails) { - emails.forEach(function(email) { - // set display date - email.displayDate = moment(email.sentDate).format('DD.MM.YY'); - }); + function listLocalMessages(options, callback) { + emailDao.listMessages(options, function(err, emails) { + if (err) { + console.log(err); + return; + } + // add display dates + displayEmails(emails); - return emails; - } + callback(emails); + }); + } + + function displayEmails(emails) { + if (!emails || emails.length < 1) { + return; + } + + // sort by uid + emails = _.sortBy(emails, function(e) { + return -e.uid; + }); + + $scope.emails = emails; + $scope.select($scope.emails[0]); + $scope.$apply(); + } + }; function createDummyMails(callback) { var Email = function(unread, attachments, replied) { @@ -97,8 +111,7 @@ define(function(require) { this.attachments = (attachments) ? [true] : undefined; this.unread = unread; this.replied = replied; - this.displayDate = '23.08.13'; - this.longDisplayDate = 'Wednesday, 23.08.2013 19:23'; + this.sentDate = new Date('Thu Sep 19 2013 20:41:23 GMT+0200 (CEST)'); this.subject = "Welcome Max"; // Subject line this.body = "Hi Max,\n\n" + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n" + diff --git a/src/js/crypto/crypto.js b/src/js/crypto/crypto.js index 588709f..e764da4 100644 --- a/src/js/crypto/crypto.js +++ b/src/js/crypto/crypto.js @@ -12,16 +12,21 @@ define(function(require) { pbkdf2 = require('js/crypto/pbkdf2'), config = require('js/app-config').config; - var self = {}, - passBasedKey, + var passBasedKey, BATCH_WORKER = '/crypto/crypto-batch-worker.js', PBKDF2_WORKER = '/crypto/pbkdf2-worker.js'; + var Crypto = function() { + + }; + /** * Initializes the crypto modules by fetching the user's * encrypted secret key from storage and storing it in memory. */ - self.init = function(args, callback) { + Crypto.prototype.init = function(args, callback) { + var self = this; + // valdiate input if (!args.emailAddress || !args.keySize || !args.rsaKeySize) { callback({ @@ -119,7 +124,7 @@ define(function(require) { /** * Do PBKDF2 key derivation in a WebWorker thread */ - self.deriveKey = function(password, keySize, callback) { + Crypto.prototype.deriveKey = function(password, keySize, callback) { startWorker({ script: PBKDF2_WORKER, args: { @@ -137,8 +142,9 @@ define(function(require) { // En/Decrypt a list of items with AES in a WebWorker thread // - self.symEncryptList = function(list, callback) { - var key, envelope, envelopes = []; + Crypto.prototype.symEncryptList = function(list, callback) { + var self = this, + key, envelope, envelopes = []; // generate single secret key shared for all list items key = util.random(self.keySize); @@ -173,7 +179,7 @@ define(function(require) { }); }; - self.symDecryptList = function(list, keys, callback) { + Crypto.prototype.symDecryptList = function(list, keys, callback) { startWorker({ script: BATCH_WORKER, args: { @@ -192,8 +198,9 @@ define(function(require) { // En/Decrypt something speficially using the user's secret key // - self.encryptListForUser = function(list, receiverPubkeys, callback) { - var envelope, envelopes = []; + Crypto.prototype.encryptListForUser = function(list, receiverPubkeys, callback) { + var self = this, + envelope, envelopes = []; if (!receiverPubkeys || receiverPubkeys.length !== 1) { callback({ @@ -235,7 +242,7 @@ define(function(require) { }); }; - self.decryptListForUser = function(list, senderPubkeys, callback) { + Crypto.prototype.decryptListForUser = function(list, senderPubkeys, callback) { if (!senderPubkeys || senderPubkeys < 1) { callback({ errMsg: 'Sender public keys must be set!' @@ -268,7 +275,7 @@ define(function(require) { // Re-encrypt keys item and items seperately // - self.reencryptListKeysForUser = function(list, senderPubkeys, callback) { + Crypto.prototype.reencryptListKeysForUser = function(list, senderPubkeys, callback) { var keypair = rsa.exportKeys(); var receiverPrivkey = { _id: keypair._id, @@ -291,7 +298,7 @@ define(function(require) { }); }; - self.decryptKeysAndList = function(list, callback) { + Crypto.prototype.decryptKeysAndList = function(list, callback) { startWorker({ script: BATCH_WORKER, args: { @@ -351,5 +358,5 @@ define(function(require) { options.callback(null, result); } - return self; + return Crypto; }); \ No newline at end of file diff --git a/src/js/dao/devicestorage-dao.js b/src/js/dao/devicestorage-dao.js index 300ba41..24fd2f5 100644 --- a/src/js/dao/devicestorage-dao.js +++ b/src/js/dao/devicestorage-dao.js @@ -4,17 +4,26 @@ * through transparent encryption. If not, the crypto API is * used to encrypt data on the fly before persisting via a JSON store. */ -define(['cryptoLib/util', 'js/crypto/crypto', 'js/dao/lawnchair-dao'], function(util, crypto, jsonDao) { +define(function(require) { 'use strict'; - var self = {}; + var util = require('cryptoLib/util'), + jsonDao = require('js/dao/lawnchair-dao'); + + var DeviceStorageDAO = function() { + + }; + + DeviceStorageDAO.prototype.init = function(emailAddress, callback) { + jsonDao.init(emailAddress, callback); + }; /** * Stores a list of encrypted items in the object store * @param list [Array] The list of items to be persisted * @param type [String] The type of item to be persisted e.g. 'email' */ - self.storeEcryptedList = function(list, type, callback) { + DeviceStorageDAO.prototype.storeEcryptedList = function(list, type, callback) { var date, key, items = []; // nothing to store @@ -25,9 +34,10 @@ define(['cryptoLib/util', 'js/crypto/crypto', 'js/dao/lawnchair-dao'], function( // format items for batch storing in dao list.forEach(function(i) { - - // put date in key if available... for easy querying - if (i.sentDate) { + // put uid in key if available... for easy querying + if (i.uid) { + key = type + '_' + i.uid; + } else if (i.sentDate) { date = util.parseDate(i.sentDate); key = type + '_' + i.sentDate + '_' + i.id; } else { @@ -38,7 +48,6 @@ define(['cryptoLib/util', 'js/crypto/crypto', 'js/dao/lawnchair-dao'], function( key: key, object: i }); - }); jsonDao.batch(items, function() { @@ -52,7 +61,7 @@ define(['cryptoLib/util', 'js/crypto/crypto', 'js/dao/lawnchair-dao'], function( * @param offset [Number] The offset of items to fetch (0 is the last stored item) * @param num [Number] The number of items to fetch (null means fetch all) */ - self.listEncryptedItems = function(type, offset, num, callback) { + DeviceStorageDAO.prototype.listEncryptedItems = function(type, offset, num, callback) { // fetch all items of a certain type from the data-store jsonDao.list(type, offset, num, function(encryptedList) { @@ -63,9 +72,9 @@ define(['cryptoLib/util', 'js/crypto/crypto', 'js/dao/lawnchair-dao'], function( /** * Clear the whole device data-store */ - self.clear = function(callback) { + DeviceStorageDAO.prototype.clear = function(callback) { jsonDao.clear(callback); }; - return self; + return DeviceStorageDAO; }); \ No newline at end of file diff --git a/src/js/dao/email-dao.js b/src/js/dao/email-dao.js index 2a5302d..fb22578 100644 --- a/src/js/dao/email-dao.js +++ b/src/js/dao/email-dao.js @@ -3,20 +3,20 @@ define(function(require) { var _ = require('underscore'), util = require('cryptoLib/util'), - crypto = require('js/crypto/crypto'), - jsonDB = require('js/dao/lawnchair-dao'), str = require('js/app-config').string; /** * A high-level Data-Access Api for handling Email synchronization * between the cloud service and the device's local storage */ - var EmailDAO = function(keychain, imapClient, smtpClient) { + var EmailDAO = function(keychain, imapClient, smtpClient, crypto, devicestorage) { var self = this; self._keychain = keychain; self._imapClient = imapClient; self._smtpClient = smtpClient; + self._crypto = crypto; + self._devicestorage = devicestorage; }; /** @@ -51,21 +51,21 @@ define(function(require) { function initKeychain() { // init user's local database - jsonDB.init(emailAddress); - - // call getUserKeyPair to read/sync keypair with devicestorage/cloud - self._keychain.getUserKeyPair(emailAddress, function(err, storedKeypair) { - if (err) { - callback(err); - return; - } - // init crypto - initCrypto(storedKeypair); + self._devicestorage.init(emailAddress, function() { + // call getUserKeyPair to read/sync keypair with devicestorage/cloud + self._keychain.getUserKeyPair(emailAddress, function(err, storedKeypair) { + if (err) { + callback(err); + return; + } + // init crypto + initCrypto(storedKeypair); + }); }); } function initCrypto(storedKeypair) { - crypto.init({ + self._crypto.init({ emailAddress: emailAddress, password: password, keySize: self._account.symKeySize, @@ -144,8 +144,11 @@ define(function(require) { // validate public key if (!receiverPubkey) { + callback({ + errMsg: 'User has no public key yet!' + }); // user hasn't registered a public key yet... invite - self.encryptForNewUser(email, callback); + //self.encryptForNewUser(email, callback); return; } @@ -163,7 +166,7 @@ define(function(require) { receiverPubkeys = [receiverPubkey]; // encrypt the email - crypto.encryptListForUser(ptItems, receiverPubkeys, function(err, encryptedList) { + self._crypto.encryptListForUser(ptItems, receiverPubkeys, function(err, encryptedList) { if (err) { callback(err); return; @@ -183,7 +186,7 @@ define(function(require) { var self = this, ptItems = bundleForEncryption(email); - crypto.symEncryptList(ptItems, function(err, result) { + self._crypto.symEncryptList(ptItems, function(err, result) { if (err) { callback(err); return; @@ -295,6 +298,168 @@ define(function(require) { self._imapClient.unreadMessages(path, callback); }; + /** + * Fetch a list of emails from the device's local storage + */ + EmailDAO.prototype.listMessages = function(options, callback) { + var self = this, + encryptedList = []; + + // validate options + if (!options.folder || typeof options.offset === 'undefined' || typeof options.num === 'undefined') { + callback({ + errMsg: 'Invalid options!' + }); + return; + } + + // fetch items from device storage + self._devicestorage.listEncryptedItems('email_' + options.folder, options.offset, options.num, function(err, emails) { + if (err) { + callback(err); + return; + } + if (emails.length === 0) { + callback(null, []); + return; + } + + // find encrypted items + emails.forEach(function(i) { + if (i.body.indexOf(str.cryptPrefix) !== -1 && i.body.indexOf(str.cryptSuffix) !== -1) { + // parse ct object from ascii armored message block + encryptedList.push(parseMessageBlock(i)); + } + }); + + // decrypt items + decryptList(encryptedList, function(err, decryptedList) { + // return only decrypted items + callback(null, decryptedList); + }); + }); + + function parseMessageBlock(email) { + var ctMessageBase64, ctMessageJson, ctMessage; + + // parse email body for encrypted message block + try { + // get base64 encoded message block + ctMessageBase64 = email.body.split(str.cryptPrefix)[1].split(str.cryptSuffix)[0].trim(); + // decode bae64 + ctMessageJson = atob(ctMessageBase64); + // parse json string to get ciphertext object + ctMessage = JSON.parse(ctMessageJson); + } catch (e) { + callback({ + errMsg: 'Error parsing encrypted message block!' + }); + return; + } + + return ctMessage; + } + + function decryptList(encryptedList, callback) { + var already, pubkeyIds = []; + + // gather public key ids required to verify signatures + encryptedList.forEach(function(i) { + already = null; + already = _.findWhere(pubkeyIds, { + _id: i.senderPk + }); + if (!already) { + pubkeyIds.push({ + _id: i.senderPk + }); + } + }); + + // fetch public keys from keychain + self._keychain.getPublicKeys(pubkeyIds, function(err, senderPubkeys) { + if (err) { + callback(err); + return; + } + + // verfiy signatures and re-encrypt item keys + self._crypto.decryptListForUser(encryptedList, senderPubkeys, function(err, decryptedList) { + if (err) { + callback(err); + return; + } + + callback(null, decryptedList); + }); + }); + } + }; + + /** + * High level sync operation for the delta from the user's IMAP inbox + */ + EmailDAO.prototype.imapSync = function(options, callback) { + var self = this; + + // validate options + if (!options.folder || typeof options.offset === 'undefined' || typeof options.num === 'undefined') { + callback({ + errMsg: 'Invalid options!' + }); + return; + } + + fetchList(options, function(emails) { + // persist encrypted list in device storage + self._devicestorage.storeEcryptedList(emails, 'email_' + options.folder, function() { + callback(); + }); + }); + + function fetchList(folder, callback) { + // fetch imap folder's message list + self.imapListMessages({ + folder: options.folder, + offset: options.offset, + num: options.num + }, function(err, emails) { + if (err) { + console.log(err); + return; + } + + // fetch message bodies + fetchBodies(emails, folder, function(messages) { + callback(messages); + }); + }); + } + + function fetchBodies(messageList, folder, callback) { + var emails = []; + + var after = _.after(messageList.length, function() { + callback(emails); + }); + + _.each(messageList, function(messageItem) { + self.imapGetMessage({ + folder: folder, + uid: messageItem.uid + }, function(err, message) { + if (err) { + console.log(err); + return; + } + + emails.push(message); + after(); + }); + }); + } + }; + /** * List messages from an imap folder. This will not yet fetch the email body. * @param {String} options.folderName The name of the imap folder. @@ -337,16 +502,6 @@ define(function(require) { return; } - // try fetching from cache before doing a roundtrip - message = self.readCache(options.folder, options.uid); - if (message) { - // message was fetched from cache successfully - callback(null, message); - return; - } - - /* message was not found in cache... fetch from imap server */ - function messageReady(err, gottenMessage) { message = gottenMessage; itemCounter++; @@ -358,68 +513,12 @@ define(function(require) { return; } - // decrypt Message body - if (message.body.indexOf(str.cryptPrefix) !== -1 && message.body.indexOf(str.cryptSuffix) !== -1) { - decryptBody(message, function(err, ptMessage) { - message = ptMessage; - // return decrypted message - callback(err, message); - }); - return; - } - - // return unencrypted message + // return message callback(null, message); //check(); } - function decryptBody(email, callback) { - var ctMessageBase64, ctMessageJson, ctMessage, pubkeyIds; - - // parse email body for encrypted message block - try { - // get base64 encoded message block - ctMessageBase64 = email.body.split(str.cryptPrefix)[1].split(str.cryptSuffix)[0].trim(); - // decode bae64 - ctMessageJson = atob(ctMessageBase64); - // parse json string to get ciphertext object - ctMessage = JSON.parse(ctMessageJson); - } catch (e) { - callback({ - errMsg: 'Error parsing encrypted message block!' - }); - return; - } - - // gather public key ids required to verify signatures - pubkeyIds = [{ - _id: ctMessage.senderPk - }]; - - // fetch public keys from keychain - self._keychain.getPublicKeys(pubkeyIds, function(err, senderPubkeys) { - if (err) { - callback(err); - return; - } - - // verfiy signatures and re-encrypt item keys - crypto.decryptListForUser([ctMessage], senderPubkeys, function(err, decryptedList) { - if (err) { - callback(err); - return; - } - - var ptEmail = decryptedList[0]; - email.body = ptEmail.body; - email.subject = ptEmail.subject; - - callback(null, email); - }); - }); - } - // function attachmentReady(err, gottenAttachment) { // attachments.push(gottenAttachment); // itemCounter++; diff --git a/src/js/dao/lawnchair-dao.js b/src/js/dao/lawnchair-dao.js index e8637ed..1623d8d 100644 --- a/src/js/dao/lawnchair-dao.js +++ b/src/js/dao/lawnchair-dao.js @@ -7,17 +7,22 @@ define(['lawnchair', 'lawnchairSQL', 'lawnchairIDB'], function(Lawnchair) { var self = {}, db; - self.init = function(dbName) { + self.init = function(dbName, callback) { + var temp; + if (!dbName) { throw new Error('Lawnchair DB name must be specified!'); } - db = new Lawnchair({ + temp = new Lawnchair({ name: dbName }, function(lc) { if (!lc) { throw new Error('Lawnchair init failed!'); } + + db = lc; + callback(); }); }; diff --git a/src/lib/moment/moment.min.js b/src/lib/moment/moment.min.js deleted file mode 100644 index 62b1697..0000000 --- a/src/lib/moment/moment.min.js +++ /dev/null @@ -1,6 +0,0 @@ -// moment.js -// version : 2.1.0 -// author : Tim Wood -// license : MIT -// momentjs.com -!function(t){function e(t,e){return function(n){return u(t.call(this,n),e)}}function n(t,e){return function(n){return this.lang().ordinal(t.call(this,n),e)}}function s(){}function i(t){a(this,t)}function r(t){var e=t.years||t.year||t.y||0,n=t.months||t.month||t.M||0,s=t.weeks||t.week||t.w||0,i=t.days||t.day||t.d||0,r=t.hours||t.hour||t.h||0,a=t.minutes||t.minute||t.m||0,o=t.seconds||t.second||t.s||0,u=t.milliseconds||t.millisecond||t.ms||0;this._input=t,this._milliseconds=u+1e3*o+6e4*a+36e5*r,this._days=i+7*s,this._months=n+12*e,this._data={},this._bubble()}function a(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}function o(t){return 0>t?Math.ceil(t):Math.floor(t)}function u(t,e){for(var n=t+"";n.lengthn;n++)~~t[n]!==~~e[n]&&r++;return r+i}function f(t){return t?ie[t]||t.toLowerCase().replace(/(.)s$/,"$1"):t}function l(t,e){return e.abbr=t,x[t]||(x[t]=new s),x[t].set(e),x[t]}function _(t){if(!t)return H.fn._lang;if(!x[t]&&A)try{require("./lang/"+t)}catch(e){return H.fn._lang}return x[t]}function m(t){return t.match(/\[.*\]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function y(t){var e,n,s=t.match(E);for(e=0,n=s.length;n>e;e++)s[e]=ue[s[e]]?ue[s[e]]:m(s[e]);return function(i){var r="";for(e=0;n>e;e++)r+=s[e]instanceof Function?s[e].call(i,t):s[e];return r}}function M(t,e){function n(e){return t.lang().longDateFormat(e)||e}for(var s=5;s--&&N.test(e);)e=e.replace(N,n);return re[e]||(re[e]=y(e)),re[e](t)}function g(t,e){switch(t){case"DDDD":return V;case"YYYY":return X;case"YYYYY":return $;case"S":case"SS":case"SSS":case"DDD":return I;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return R;case"a":case"A":return _(e._l)._meridiemParse;case"X":return B;case"Z":case"ZZ":return j;case"T":return q;case"MM":case"DD":case"YY":case"HH":case"hh":case"mm":case"ss":case"M":case"D":case"d":case"H":case"h":case"m":case"s":return J;default:return new RegExp(t.replace("\\",""))}}function p(t){var e=(j.exec(t)||[])[0],n=(e+"").match(ee)||["-",0,0],s=+(60*n[1])+~~n[2];return"+"===n[0]?-s:s}function D(t,e,n){var s,i=n._a;switch(t){case"M":case"MM":i[1]=null==e?0:~~e-1;break;case"MMM":case"MMMM":s=_(n._l).monthsParse(e),null!=s?i[1]=s:n._isValid=!1;break;case"D":case"DD":case"DDD":case"DDDD":null!=e&&(i[2]=~~e);break;case"YY":i[0]=~~e+(~~e>68?1900:2e3);break;case"YYYY":case"YYYYY":i[0]=~~e;break;case"a":case"A":n._isPm=_(n._l).isPM(e);break;case"H":case"HH":case"h":case"hh":i[3]=~~e;break;case"m":case"mm":i[4]=~~e;break;case"s":case"ss":i[5]=~~e;break;case"S":case"SS":case"SSS":i[6]=~~(1e3*("0."+e));break;case"X":n._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":n._useUTC=!0,n._tzm=p(e)}null==e&&(n._isValid=!1)}function Y(t){var e,n,s=[];if(!t._d){for(e=0;7>e;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];s[3]+=~~((t._tzm||0)/60),s[4]+=~~((t._tzm||0)%60),n=new Date(0),t._useUTC?(n.setUTCFullYear(s[0],s[1],s[2]),n.setUTCHours(s[3],s[4],s[5],s[6])):(n.setFullYear(s[0],s[1],s[2]),n.setHours(s[3],s[4],s[5],s[6])),t._d=n}}function w(t){var e,n,s=t._f.match(E),i=t._i;for(t._a=[],e=0;eo&&(u=o,s=n);a(t,s)}function v(t){var e,n=t._i,s=K.exec(n);if(s){for(t._f="YYYY-MM-DD"+(s[2]||" "),e=0;4>e;e++)if(te[e][1].exec(n)){t._f+=te[e][0];break}j.exec(n)&&(t._f+=" Z"),w(t)}else t._d=new Date(n)}function T(e){var n=e._i,s=G.exec(n);n===t?e._d=new Date:s?e._d=new Date(+s[1]):"string"==typeof n?v(e):d(n)?(e._a=n.slice(0),Y(e)):e._d=n instanceof Date?new Date(+n):new Date(n)}function b(t,e,n,s,i){return i.relativeTime(e||1,!!n,t,s)}function S(t,e,n){var s=W(Math.abs(t)/1e3),i=W(s/60),r=W(i/60),a=W(r/24),o=W(a/365),u=45>s&&["s",s]||1===i&&["m"]||45>i&&["mm",i]||1===r&&["h"]||22>r&&["hh",r]||1===a&&["d"]||25>=a&&["dd",a]||45>=a&&["M"]||345>a&&["MM",W(a/30)]||1===o&&["y"]||["yy",o];return u[2]=e,u[3]=t>0,u[4]=n,b.apply({},u)}function F(t,e,n){var s,i=n-e,r=n-t.day();return r>i&&(r-=7),i-7>r&&(r+=7),s=H(t).add("d",r),{week:Math.ceil(s.dayOfYear()/7),year:s.year()}}function O(t){var e=t._i,n=t._f;return null===e||""===e?null:("string"==typeof e&&(t._i=e=_().preparse(e)),H.isMoment(e)?(t=a({},e),t._d=new Date(+e._d)):n?d(n)?k(t):w(t):T(t),new i(t))}function z(t,e){H.fn[t]=H.fn[t+"s"]=function(t){var n=this._isUTC?"UTC":"";return null!=t?(this._d["set"+n+e](t),H.updateOffset(this),this):this._d["get"+n+e]()}}function C(t){H.duration.fn[t]=function(){return this._data[t]}}function L(t,e){H.duration.fn["as"+t]=function(){return+this/e}}for(var H,P,U="2.1.0",W=Math.round,x={},A="undefined"!=typeof module&&module.exports,G=/^\/?Date\((\-?\d+)/i,Z=/(\-)?(\d*)?\.?(\d+)\:(\d+)\:(\d+)\.?(\d{3})?/,E=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,N=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,J=/\d\d?/,I=/\d{1,3}/,V=/\d{3}/,X=/\d{1,4}/,$=/[+\-]?\d{1,6}/,R=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,j=/Z|[\+\-]\d\d:?\d\d/i,q=/T/i,B=/[\+\-]?\d+(\.\d{1,3})?/,K=/^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,Q="YYYY-MM-DDTHH:mm:ssZ",te=[["HH:mm:ss.S",/(T| )\d\d:\d\d:\d\d\.\d{1,3}/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],ee=/([\+\-]|\d\d)/gi,ne="Date|Hours|Minutes|Seconds|Milliseconds".split("|"),se={Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6},ie={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",w:"week",M:"month",y:"year"},re={},ae="DDD w W M D d".split(" "),oe="M D H h m s w W".split(" "),ue={M:function(){return this.month()+1},MMM:function(t){return this.lang().monthsShort(this,t)},MMMM:function(t){return this.lang().months(this,t)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(t){return this.lang().weekdaysMin(this,t)},ddd:function(t){return this.lang().weekdaysShort(this,t)},dddd:function(t){return this.lang().weekdays(this,t)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return u(this.year()%100,2)},YYYY:function(){return u(this.year(),4)},YYYYY:function(){return u(this.year(),5)},gg:function(){return u(this.weekYear()%100,2)},gggg:function(){return this.weekYear()},ggggg:function(){return u(this.weekYear(),5)},GG:function(){return u(this.isoWeekYear()%100,2)},GGGG:function(){return this.isoWeekYear()},GGGGG:function(){return u(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return~~(this.milliseconds()/100)},SS:function(){return u(~~(this.milliseconds()/10),2)},SSS:function(){return u(this.milliseconds(),3)},Z:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+u(~~(t/60),2)+":"+u(~~t%60,2)},ZZ:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+u(~~(10*t/6),4)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()}};ae.length;)P=ae.pop(),ue[P+"o"]=n(ue[P],P);for(;oe.length;)P=oe.pop(),ue[P+P]=e(ue[P],2);for(ue.DDDD=e(ue.DDD,3),s.prototype={set:function(t){var e,n;for(n in t)e=t[n],"function"==typeof e?this[n]=e:this["_"+n]=e},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t){var e,n,s;for(this._monthsParse||(this._monthsParse=[]),e=0;12>e;e++)if(this._monthsParse[e]||(n=H([2e3,e]),s="^"+this.months(n,"")+"|^"+this.monthsShort(n,""),this._monthsParse[e]=new RegExp(s.replace(".",""),"i")),this._monthsParse[e].test(t))return e},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,n,s;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(n=H([2e3,1]).day(e),s="^"+this.weekdays(n,"")+"|^"+this.weekdaysShort(n,"")+"|^"+this.weekdaysMin(n,""),this._weekdaysParse[e]=new RegExp(s.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase()[0]},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e){var n=this._calendar[t];return"function"==typeof n?n.apply(e):n},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,n,s){var i=this._relativeTime[n];return"function"==typeof i?i(t,e,n,s):i.replace(/%d/i,t)},pastFuture:function(t,e){var n=this._relativeTime[t>0?"future":"past"];return"function"==typeof n?n(e):n.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",preparse:function(t){return t},postformat:function(t){return t},week:function(t){return F(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6}},H=function(t,e,n){return O({_i:t,_f:e,_l:n,_isUTC:!1})},H.utc=function(t,e,n){return O({_useUTC:!0,_isUTC:!0,_l:n,_i:t,_f:e})},H.unix=function(t){return H(1e3*t)},H.duration=function(t,e){var n,s,i=H.isDuration(t),a="number"==typeof t,o=i?t._input:a?{}:t,u=Z.exec(t);return a?e?o[e]=t:o.milliseconds=t:u&&(n="-"===u[1]?-1:1,o={y:0,d:~~u[2]*n,h:~~u[3]*n,m:~~u[4]*n,s:~~u[5]*n,ms:~~u[6]*n}),s=new r(o),i&&t.hasOwnProperty("_lang")&&(s._lang=t._lang),s},H.version=U,H.defaultFormat=Q,H.updateOffset=function(){},H.lang=function(t,e){return t?(e?l(t,e):x[t]||_(t),H.duration.fn._lang=H.fn._lang=_(t),void 0):H.fn._lang._abbr},H.langData=function(t){return t&&t._lang&&t._lang._abbr&&(t=t._lang._abbr),_(t)},H.isMoment=function(t){return t instanceof i},H.isDuration=function(t){return t instanceof r},H.fn=i.prototype={clone:function(){return H(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){return M(H(this).utc(),"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var t=this;return[t.year(),t.month(),t.date(),t.hours(),t.minutes(),t.seconds(),t.milliseconds()]},isValid:function(){return null==this._isValid&&(this._isValid=this._a?!c(this._a,(this._isUTC?H.utc(this._a):H(this._a)).toArray()):!isNaN(this._d.getTime())),!!this._isValid},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(t){var e=M(this,t||H.defaultFormat);return this.lang().postformat(e)},add:function(t,e){var n;return n="string"==typeof t?H.duration(+e,t):H.duration(t,e),h(this,n,1),this},subtract:function(t,e){var n;return n="string"==typeof t?H.duration(+e,t):H.duration(t,e),h(this,n,-1),this},diff:function(t,e,n){var s,i,r=this._isUTC?H(t).zone(this._offset||0):H(t).local(),a=6e4*(this.zone()-r.zone());return e=f(e),"year"===e||"month"===e?(s=432e5*(this.daysInMonth()+r.daysInMonth()),i=12*(this.year()-r.year())+(this.month()-r.month()),i+=(this-H(this).startOf("month")-(r-H(r).startOf("month")))/s,i-=6e4*(this.zone()-H(this).startOf("month").zone()-(r.zone()-H(r).startOf("month").zone()))/s,"year"===e&&(i/=12)):(s=this-r,i="second"===e?s/1e3:"minute"===e?s/6e4:"hour"===e?s/36e5:"day"===e?(s-a)/864e5:"week"===e?(s-a)/6048e5:s),n?i:o(i)},from:function(t,e){return H.duration(this.diff(t)).lang(this.lang()._abbr).humanize(!e)},fromNow:function(t){return this.from(H(),t)},calendar:function(){var t=this.diff(H().startOf("day"),"days",!0),e=-6>t?"sameElse":-1>t?"lastWeek":0>t?"lastDay":1>t?"sameDay":2>t?"nextDay":7>t?"nextWeek":"sameElse";return this.format(this.lang().calendar(e,this))},isLeapYear:function(){var t=this.year();return 0===t%4&&0!==t%100||0===t%400},isDST:function(){return this.zone()+H(t).startOf(e)},isBefore:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)<+H(t).startOf(e)},isSame:function(t,e){return e="undefined"!=typeof e?e:"millisecond",+this.clone().startOf(e)===+H(t).startOf(e)},min:function(t){return t=H.apply(null,arguments),this>t?this:t},max:function(t){return t=H.apply(null,arguments),t>this?this:t},zone:function(t){var e=this._offset||0;return null==t?this._isUTC?e:this._d.getTimezoneOffset():("string"==typeof t&&(t=p(t)),Math.abs(t)<16&&(t=60*t),this._offset=t,this._isUTC=!0,e!==t&&h(this,H.duration(e-t,"m"),1,!0),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},daysInMonth:function(){return H.utc([this.year(),this.month()+1,0]).date()},dayOfYear:function(t){var e=W((H(this).startOf("day")-H(this).startOf("year"))/864e5)+1;return null==t?e:this.add("d",t-e)},weekYear:function(t){var e=F(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==t?e:this.add("y",t-e)},isoWeekYear:function(t){var e=F(this,1,4).year;return null==t?e:this.add("y",t-e)},week:function(t){var e=this.lang().week(this);return null==t?e:this.add("d",7*(t-e))},isoWeek:function(t){var e=F(this,1,4).week;return null==t?e:this.add("d",7*(t-e))},weekday:function(t){var e=(this._d.getDay()+7-this.lang()._week.dow)%7;return null==t?e:this.add("d",t-e)},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},lang:function(e){return e===t?this._lang:(this._lang=_(e),this)}},P=0;P{{email.from[0].name || email.from[0].address}}

{{email.subject}}

- +

{{email.body}}

diff --git a/src/tpl/read.html b/src/tpl/read.html index ed79dd8..fedb4eb 100644 --- a/src/tpl/read.html +++ b/src/tpl/read.html @@ -7,7 +7,7 @@

{{selected.subject}}

-

{{selected.longDisplayDate}}

+

{{selected.sentDate | date:'EEEE, MMM d, yyyy h:mm a'}}

From: {{selected.from[0].name || selected.from[0].address}}

To: {{t.address}}

diff --git a/test/new-unit/email-dao-test.js b/test/new-unit/email-dao-test.js index 70ae2ec..4283d8f 100644 --- a/test/new-unit/email-dao-test.js +++ b/test/new-unit/email-dao-test.js @@ -3,8 +3,10 @@ define(function(require) { var KeychainDAO = require('js/dao/keychain-dao'), EmailDAO = require('js/dao/email-dao'), + DeviceStorageDAO = require('js/dao/devicestorage-dao'), SmtpClient = require('smtp-client'), ImapClient = require('imap-client'), + Crypto = require('js/crypto/crypto'), app = require('js/app-config'), expect = chai.expect; @@ -19,12 +21,12 @@ define(function(require) { var publicKey = "-----BEGIN PUBLIC KEY-----\r\n" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxy+Te5dyeWd7g0P+8LNO7fZDQ\r\n" + "g96xTb1J6pYE/pPTMlqhB6BRItIYjZ1US5q2vk5Zk/5KasBHAc9RbCqvh9v4XFEY\r\n" + "JVmTXC4p8ft1LYuNWIaDk+R3dyYXmRNct/JC4tks2+8fD3aOvpt0WNn3R75/FGBt\r\n" + "h4BgojAXDE+PRQtcVQIDAQAB\r\n" + "-----END PUBLIC KEY-----"; describe('Email DAO unit tests', function() { - this.timeout(20000); var emailDao, account, - keychainStub, imapClientStub, smtpClientStub; + keychainStub, imapClientStub, smtpClientStub, cryptoStub, devicestorageStub; beforeEach(function() { + // init dummy object dummyMail = { from: [{ name: 'Whiteout Test', @@ -47,8 +49,10 @@ define(function(require) { keychainStub = sinon.createStubInstance(KeychainDAO); imapClientStub = sinon.createStubInstance(ImapClient); smtpClientStub = sinon.createStubInstance(SmtpClient); + cryptoStub = sinon.createStubInstance(Crypto); + devicestorageStub = sinon.createStubInstance(DeviceStorageDAO); - emailDao = new EmailDAO(keychainStub, imapClientStub, smtpClientStub); + emailDao = new EmailDAO(keychainStub, imapClientStub, smtpClientStub, cryptoStub, devicestorageStub); }); afterEach(function() {}); @@ -65,9 +69,11 @@ define(function(require) { it('should fail due to error in getUserKeyPair', function(done) { imapClientStub.login.yields(); + devicestorageStub.init.yields(); keychainStub.getUserKeyPair.yields(42); emailDao.init(account, emaildaoTest.passphrase, function(err) { + expect(devicestorageStub.init.calledOnce).to.be.true; expect(imapClientStub.login.calledOnce).to.be.true; expect(err).to.equal(42); done(); @@ -76,12 +82,16 @@ define(function(require) { it('should init with new keygen', function(done) { imapClientStub.login.yields(); + devicestorageStub.init.yields(); keychainStub.getUserKeyPair.yields(); + cryptoStub.init.yields(null, {}); keychainStub.putUserKeyPair.yields(); emailDao.init(account, emaildaoTest.passphrase, function(err) { expect(imapClientStub.login.calledOnce).to.be.true; + expect(devicestorageStub.init.calledOnce).to.be.true; expect(keychainStub.getUserKeyPair.calledOnce).to.be.true; + expect(cryptoStub.init.calledOnce).to.be.true; expect(keychainStub.putUserKeyPair.calledOnce).to.be.true; expect(err).to.not.exist; done(); @@ -92,12 +102,16 @@ define(function(require) { describe('IMAP/SMTP tests', function() { beforeEach(function(done) { imapClientStub.login.yields(); + devicestorageStub.init.yields(); keychainStub.getUserKeyPair.yields(); + cryptoStub.init.yields(null, {}); keychainStub.putUserKeyPair.yields(); emailDao.init(account, emaildaoTest.passphrase, function(err) { expect(imapClientStub.login.calledOnce).to.be.true; + expect(devicestorageStub.init.calledOnce).to.be.true; expect(keychainStub.getUserKeyPair.calledOnce).to.be.true; + expect(cryptoStub.init.calledOnce).to.be.true; expect(keychainStub.putUserKeyPair.calledOnce).to.be.true; expect(err).to.not.exist; done(); @@ -141,11 +155,11 @@ define(function(require) { emailDao.smtpSend(dummyMail, function(err) { expect(keychainStub.getReveiverPublicKey.calledOnce).to.be.true; - expect(smtpClientStub.send.calledOnce).to.be.true; - smtpClientStub.send.calledWith(sinon.match(function(o) { - return typeof o.attachments === 'undefined'; - })); - expect(err).to.not.exist; + // expect(smtpClientStub.send.called).to.be.true; + // smtpClientStub.send.calledWith(sinon.match(function(o) { + // return typeof o.attachments === 'undefined'; + // })); + expect(err).to.exist; done(); }); }); @@ -157,6 +171,7 @@ define(function(require) { publicKey: publicKey }); smtpClientStub.send.yields(); + cryptoStub.encryptListForUser.yields(null, []); emailDao.smtpSend(dummyMail, function(err) { expect(keychainStub.getReveiverPublicKey.calledOnce).to.be.true; @@ -181,6 +196,7 @@ define(function(require) { publicKey: publicKey }); smtpClientStub.send.yields(); + cryptoStub.encryptListForUser.yields(null, [{}, {}]); emailDao.smtpSend(dummyMail, function(err) { expect(keychainStub.getReveiverPublicKey.calledOnce).to.be.true; @@ -344,6 +360,61 @@ define(function(require) { // }); // }); }); + + describe('IMAP: sync messages to local storage', function() { + it('should work', function(done) { + imapClientStub.listMessages.yields(null, [{ + uid: 413, + }, { + uid: 414, + }]); + imapClientStub.getMessage.yields(null, { + body: 'asdf' + }); + devicestorageStub.storeEcryptedList.yields(); + + emailDao.imapSync({ + folder: 'INBOX', + offset: 0, + num: 2 + }, function(err) { + expect(err).to.not.exist; + expect(imapClientStub.listMessages.calledOnce).to.be.true; + expect(imapClientStub.getMessage.calledTwice).to.be.true; + expect(devicestorageStub.storeEcryptedList.calledOnce).to.be.true; + done(); + }); + }); + }); + + describe('IMAP: list messages from local storage', function() { + it('should work', function(done) { + + devicestorageStub.listEncryptedItems.yields(null, [{ + body: '' + }]); + keychainStub.getPublicKeys.yields(null, [{ + _id: "fcf8b4aa-5d09-4089-8b4f-e3bc5091daf3", + userId: "safewithme.testuser@gmail.com", + publicKey: publicKey + }]); + cryptoStub.decryptListForUser.yields(null, []); + + emailDao.listMessages({ + folder: 'INBOX', + offset: 0, + num: 2 + }, function(err, emails) { + expect(devicestorageStub.listEncryptedItems.calledOnce).to.be.true; + expect(keychainStub.getPublicKeys.calledOnce).to.be.true; + expect(cryptoStub.decryptListForUser.calledOnce).to.be.true; + expect(err).to.not.exist; + expect(emails.length).to.equal(0); + done(); + }); + }); + }); + }); }); diff --git a/test/unit/crypto-test.js b/test/unit/crypto-test.js index 4aa1276..8b75d72 100644 --- a/test/unit/crypto-test.js +++ b/test/unit/crypto-test.js @@ -1,4 +1,4 @@ -define(['js/crypto/crypto', 'cryptoLib/util', 'test/test-data'], function(crypto, util, testData) { +define(['js/crypto/crypto', 'cryptoLib/util', 'test/test-data'], function(Crypto, util, testData) { 'use strict'; module("Crypto Api"); @@ -11,7 +11,10 @@ define(['js/crypto/crypto', 'cryptoLib/util', 'test/test-data'], function(crypto rsaKeySize: 1024 }; + var crypto; + asyncTest("Init without keypair", 4, function() { + crypto = new Crypto(); // init dependencies ok(crypto, 'Crypto'); diff --git a/test/unit/devicestorage-dao-test.js b/test/unit/devicestorage-dao-test.js index becddd0..5e6ace3 100644 --- a/test/unit/devicestorage-dao-test.js +++ b/test/unit/devicestorage-dao-test.js @@ -1,94 +1,97 @@ -define(['underscore', 'cryptoLib/util', 'js/crypto/crypto', 'js/dao/lawnchair-dao', - 'js/dao/devicestorage-dao', 'test/test-data' -], function(_, util, crypto, jsonDao, storage, testData) { - 'use strict'; +define(['underscore', 'cryptoLib/util', 'js/crypto/crypto', 'js/dao/devicestorage-dao', 'test/test-data'], function(_, util, Crypto, DeviceStorageDAO, testData) { + 'use strict'; - module("DeviceStorage"); + module("DeviceStorage"); - var devicestorageTest = { - user: 'devicestorage_test@example.com', - password: 'Password', - keySize: 128, - ivSize: 128, - rsaKeySize: 1024 - }; + var devicestorageTest = { + user: 'devicestorage_test@example.com', + password: 'Password', + keySize: 128, + ivSize: 128, + rsaKeySize: 1024 + }; - asyncTest("Init", 3, function() { - // init dependencies - jsonDao.init(devicestorageTest.user); - ok(storage, 'DeviceStorageDAO'); + var crypto, storage; - // generate test data - devicestorageTest.list = testData.getEmailCollection(100); + asyncTest("Init", 3, function() { + // init dependencies + storage = new DeviceStorageDAO(); + storage.init(devicestorageTest.user, function() { + ok(storage, 'DeviceStorageDAO'); - // init crypto - crypto.init({ - emailAddress: devicestorageTest.user, - password: devicestorageTest.password, - keySize: devicestorageTest.keySize, - rsaKeySize: devicestorageTest.rsaKeySize - }, function(err, generatedKeypair) { - ok(!err && generatedKeypair, 'Init crypto'); - devicestorageTest.generatedKeypair = generatedKeypair; + // generate test data + devicestorageTest.list = testData.getEmailCollection(100); - // clear db before tests - jsonDao.clear(function(err) { - ok(!err, 'DB cleared. Error status: ' + err); + // init crypto + crypto = new Crypto(); + crypto.init({ + emailAddress: devicestorageTest.user, + password: devicestorageTest.password, + keySize: devicestorageTest.keySize, + rsaKeySize: devicestorageTest.rsaKeySize + }, function(err, generatedKeypair) { + ok(!err && generatedKeypair, 'Init crypto'); + devicestorageTest.generatedKeypair = generatedKeypair; - start(); - }); + // clear db before tests + storage.clear(function(err) { + ok(!err, 'DB cleared. Error status: ' + err); - }); - }); + start(); + }); - asyncTest("Encrypt list for user", 2, function() { - var receiverPubkeys = [devicestorageTest.generatedKeypair.publicKey]; + }); + }); + }); - crypto.encryptListForUser(devicestorageTest.list, receiverPubkeys, function(err, encryptedList) { - ok(!err); - equal(encryptedList.length, devicestorageTest.list.length, 'Encrypt list'); + asyncTest("Encrypt list for user", 2, function() { + var receiverPubkeys = [devicestorageTest.generatedKeypair.publicKey]; - encryptedList.forEach(function(i) { - i.sentDate = _.findWhere(devicestorageTest.list, { - id: i.id - }).sentDate; - }); + crypto.encryptListForUser(devicestorageTest.list, receiverPubkeys, function(err, encryptedList) { + ok(!err); + equal(encryptedList.length, devicestorageTest.list.length, 'Encrypt list'); - devicestorageTest.encryptedList = encryptedList; - start(); - }); - }); + encryptedList.forEach(function(i) { + i.sentDate = _.findWhere(devicestorageTest.list, { + id: i.id + }).sentDate; + }); - asyncTest("Store encrypted list", 1, function() { - storage.storeEcryptedList(devicestorageTest.encryptedList, 'email_inbox', function() { - ok(true, 'Store encrypted list'); + devicestorageTest.encryptedList = encryptedList; + start(); + }); + }); - start(); - }); - }); + asyncTest("Store encrypted list", 1, function() { + storage.storeEcryptedList(devicestorageTest.encryptedList, 'email_inbox', function() { + ok(true, 'Store encrypted list'); - asyncTest("List items", 4, function() { + start(); + }); + }); - var senderPubkeys = [devicestorageTest.generatedKeypair.publicKey]; + asyncTest("List items", 4, function() { - var offset = 2, - num = 6; + var senderPubkeys = [devicestorageTest.generatedKeypair.publicKey]; - // list encrypted items from storage - storage.listEncryptedItems('email_inbox', offset, num, function(err, encryptedList) { - ok(!err); + var offset = 2, + num = 6; - // decrypt list - crypto.decryptListForUser(encryptedList, senderPubkeys, function(err, decryptedList) { - ok(!err); - equal(decryptedList.length, num, 'Found ' + decryptedList.length + ' items in store (and decrypted)'); + // list encrypted items from storage + storage.listEncryptedItems('email_inbox', offset, num, function(err, encryptedList) { + ok(!err); - var origSet = devicestorageTest.list.splice(92, num); - deepEqual(decryptedList, origSet, 'Messages decrypted correctly'); + // decrypt list + crypto.decryptListForUser(encryptedList, senderPubkeys, function(err, decryptedList) { + ok(!err); + equal(decryptedList.length, num, 'Found ' + decryptedList.length + ' items in store (and decrypted)'); - start(); - }); - }); - }); + var origSet = devicestorageTest.list.splice(92, num); + deepEqual(decryptedList, origSet, 'Messages decrypted correctly'); + + start(); + }); + }); + }); }); \ No newline at end of file diff --git a/test/unit/keychain-dao-test.js b/test/unit/keychain-dao-test.js index 8d4a4cc..8424c3b 100644 --- a/test/unit/keychain-dao-test.js +++ b/test/unit/keychain-dao-test.js @@ -28,11 +28,12 @@ define(['js/dao/keychain-dao', 'js/dao/lawnchair-dao'], function(KeychainDAO, js ok(keychaindaoTest.keychainDao); // init and clear db before test - jsonDao.init(keychaindaoTest.user); - jsonDao.clear(function() { - ok(true, 'cleared db'); + jsonDao.init(keychaindaoTest.user, function() { + jsonDao.clear(function() { + ok(true, 'cleared db'); - start(); + start(); + }); }); }); @@ -70,9 +71,8 @@ define(['js/dao/keychain-dao', 'js/dao/lawnchair-dao'], function(KeychainDAO, js asyncTest("Get Public Keys", 2, function() { var pubkeyIds = [{ - _id: keychaindaoTest.keypair.publicKey._id - } - ]; + _id: keychaindaoTest.keypair.publicKey._id + }]; keychaindaoTest.keychainDao.getPublicKeys(pubkeyIds, function(err, pubkeys) { ok(!err); deepEqual(pubkeys[0], keychaindaoTest.keypair.publicKey, "Fetch public key"); diff --git a/test/unit/lawnchair-dao-test.js b/test/unit/lawnchair-dao-test.js index 1ed9675..ec9b72a 100644 --- a/test/unit/lawnchair-dao-test.js +++ b/test/unit/lawnchair-dao-test.js @@ -9,14 +9,15 @@ define(['js/dao/lawnchair-dao'], function(jsonDao) { asyncTest("Init", 2, function() { // init dependencies - jsonDao.init(lawnchairdaoTest.user); - ok(jsonDao, 'LanwchairDAO'); + jsonDao.init(lawnchairdaoTest.user, function() { + ok(true, 'init db'); - // clear db before test - jsonDao.clear(function() { - ok(true, 'cleared db'); + // clear db before test + jsonDao.clear(function() { + ok(true, 'cleared db'); - start(); + start(); + }); }); });